    001/*-
    002 *******************************************************************************
    003 * Copyright (c) 2011, 2016 Diamond Light Source Ltd.
    004 * All rights reserved. This program and the accompanying materials
    005 * are made available under the terms of the Eclipse Public License v1.0
    006 * which accompanies this distribution, and is available at
    007 * http://www.eclipse.org/legal/epl-v10.html
    008 *
    009 * Contributors:
    010 *    Peter Chang - initial API and implementation and/or initial documentation
    011 *******************************************************************************/
    012
    013package org.eclipse.january.dataset;
    014
    015import java.util.ArrayList;
    016import java.util.List;
    017
    018/**
    019 * Comparison and logical methods
    020 */
    021public class Comparisons {
    022        /**
    023         * Compare item-wise for whether a's element is equal b's
    024         * <p>
    025         * For multi-element items, comparison is true if all elements in an item
    026         * are equal. Where the datasets have mismatched item sizes, the first element
    027         * of the dataset with smaller items is used for comparison.
    028         * @param a
    029         * @param b
    030         * @return dataset where item is true if {@code a == b}
    031         */
    032        public static BooleanDataset equalTo(Object a, Object b) {
    033                return equalTo(a, b, null);
    034        }
    035
    036        /**
    037         * Compare item-wise for whether a's element is equal b's
    038         * <p>
    039         * For multi-element items, comparison is true if all elements in an item
    040         * are equal. Where the datasets have mismatched item sizes, the first element
    041         * of the dataset with smaller items is used for comparison.
    042         * @param a
    043         * @param b
    044         * @param o output can be null - in which case, a new dataset is created
    045         * @return dataset where item is true if {@code a == b}
    046         */
    047        public static BooleanDataset equalTo(Object a, Object b, BooleanDataset o) {
    048                final Dataset da = a instanceof Dataset ? (Dataset) a : DatasetFactory.createFromObject(a);
    049                final Dataset db = b instanceof Dataset ? (Dataset) b : DatasetFactory.createFromObject(b);
    050
    051                List<int[]> sl = BroadcastUtils.broadcastShapes(da.getShapeRef(), db.getShapeRef(), o == null ? null : o.getShapeRef());
    052
    053                final BooleanDataset r = o == null ? DatasetFactory.zeros(BooleanDataset.class, sl.get(0)) : o;
    054
    055                final BroadcastIterator it = BroadcastIterator.createIterator(da, db, r);
    056                final int as = da.getElementsPerItem();
    057                final int bs = db.getElementsPerItem();
    058
    059                if (as > bs) {
    060                        if (da.isComplex()) {
    061                                while (it.hasNext()) {
    062                                        final double bd = it.bDouble;
    063                                        boolean rb = it.aDouble == bd && da.getElementDoubleAbs(it.aIndex + 1) == 0;
    064                                        r.setAbs(it.oIndex, rb);
    065                                }
    066                        } else if (it.isOutputDouble()) {
    067                                while (it.hasNext()) {
    068                                        final double bd = it.bDouble;
    069                                        boolean rb = true;
    070                                        for (int j = 0; rb && j < as; j++) {
    071                                                rb &= da.getElementDoubleAbs(it.aIndex + j) == bd;
    072                                        }
    073                                        r.setAbs(it.oIndex, rb);
    074                                }
    075                        } else {
    076                                while (it.hasNext()) {
    077                                        final long bl = it.bLong;
    078                                        boolean rb = true;
    079                                        for (int j = 0; rb && j < as; j++) {
    080                                                rb &= da.getElementLongAbs(it.aIndex + j) == bl;
    081                                        }
    082                                        r.setAbs(it.oIndex, rb);
    083                                }
    084                        }
    085                } else if (as < bs) {
    086                        if (db.isComplex()) {
    087                                while (it.hasNext()) {
    088                                        final double ad = it.aDouble;
    089                                        boolean rb = ad == it.bDouble && 0 == db.getElementDoubleAbs(it.bIndex + 1);
    090                                        r.setAbs(it.oIndex, rb);
    091                                }
    092                        } else if (it.isOutputDouble()) {
    093                                while (it.hasNext()) {
    094                                        final double ad = it.aDouble;
    095                                        boolean rb = true;
    096                                        for (int j = 0; rb && j < bs; j++) {
    097                                                rb &= ad == db.getElementDoubleAbs(it.bIndex + j);
    098                                        }
    099                                        r.setAbs(it.oIndex, rb);
    100                                }
    101                        } else {
    102                                while (it.hasNext()) {
    103                                        final long al = it.aLong;
    104                                        boolean rb = true;
    105                                        for (int j = 0; rb && j < bs; j++) {
    106                                                rb &= al == db.getElementLongAbs(it.bIndex + j);
    107                                        }
    108                                        r.setAbs(it.oIndex, rb);
    109                                }
    110                        }
    111                } else {
    112                        if (as == 1) {
    113                                if (it.isOutputDouble()) {
    114                                        while (it.hasNext()) {
    115                                                r.setAbs(it.oIndex, it.aDouble == it.bDouble);
    116                                        }
    117                                } else {
    118                                        while (it.hasNext()) {
    119                                                r.setAbs(it.oIndex, it.aLong == it.bLong);
    120                                        }
    121                                }
    122                        } else if (it.isOutputDouble()) {
    123                                while (it.hasNext()) {
    124                                        boolean rb = true;
    125                                        for (int j = 0; rb && j < bs; j++) {
    126                                                rb &= da.getElementDoubleAbs(it.aIndex + j) == db.getElementDoubleAbs(it.bIndex + j);
    127                                        }
    128                                        r.setAbs(it.oIndex, rb);
    129                                }
    130                        } else {
    131                                while (it.hasNext()) {
    132                                        boolean rb = true;
    133                                        for (int j = 0; rb && j < bs; j++) {
    134                                                rb &= da.getElementLongAbs(it.aIndex + j) == db.getElementLongAbs(it.bIndex + j);
    135                                        }
    136                                        r.setAbs(it.oIndex, rb);
    137                                }
    138                        }
    139                }
    140
    141                return r;
    142        }
    143
    144        /**
    145         * Compare item-wise for whether a's element is equal b's
    146         * <p>
    147         * For multi-element items, comparison is true if all elements in an item
    148         * are equal. Where the datasets have mismatched item sizes, the first element
    149         * of the dataset with smaller items is used for comparison.
    150         * @param a
    151         * @param b
    152         * @param relTolerance
    153         * @param absTolerance
    154         * @return dataset where item is true if {@code abs(a - b) <= absTol + relTol*max(abs(a),abs(b))}
    155         */
    156        public static BooleanDataset almostEqualTo(Object a, Object b, double relTolerance, double absTolerance) {
    157                return almostEqualTo(a, b, null, relTolerance, absTolerance);
    158        }
    159
    160        /**
    161         *
    162         * @param a
    163         * @param b
    164         * @param relTol
    165         * @param absTol
    166         * @return true if {@code abs(a - b) <= max(absTol, relTol*max(abs(a),abs(b)))}
    167         */
    168        public final static boolean isClose(double a, double b, double relTol, double absTol) {
    169                return Math.abs(a - b) <= Math.max(absTol, relTol * Math.max(Math.abs(a), Math.abs(b)));
    170        }
    171
    172        private final static boolean isCloseNP(double a, double b, double rt, double at) {
    173                return Math.abs(a - b) <= at + rt * Math.max(Math.abs(a), Math.abs(b));
    174        }
    175
    176        private final static boolean isCloseNP(double a, double rt, double at) {
    177                double aa = Math.abs(a);
    178                return aa <= at + rt * aa;
    179        }
    180
    181        /**
    182         * Compare item-wise for whether a's element is equal b's
    183         * <p>
    184         * For multi-element items, comparison is true if all elements in an item
    185         * are equal. Where the datasets have mismatched item sizes, the first element
    186         * of the dataset with smaller items is used for comparison.
    187         * @param a
    188         * @param b
    189         * @param o output can be null - in which case, a new dataset is created
    190         * @param relTolerance
    191         * @param absTolerance
    192         * @return dataset where item is true if {@code abs(a - b) <= absTol + relTol*max(abs(a),abs(b))}
    193         */
    194        public static BooleanDataset almostEqualTo(Object a, Object b, BooleanDataset o, double relTolerance, double absTolerance) {
    195                final Dataset da = a instanceof Dataset ? (Dataset) a : DatasetFactory.createFromObject(a);
    196                final Dataset db = b instanceof Dataset ? (Dataset) b : DatasetFactory.createFromObject(b);
    197
    198                List<int[]> sl = BroadcastUtils.broadcastShapes(da.getShapeRef(), db.getShapeRef(), o == null ? null : o.getShapeRef());
    199
    200                final BooleanDataset r = o == null ? DatasetFactory.zeros(BooleanDataset.class, sl.get(0)) : o;
    201
    202                final BroadcastIterator it = BroadcastIterator.createIterator(da, db, r);
    203                it.setOutputDouble(true);
    204                final int as = da.getElementsPerItem();
    205                final int bs = db.getElementsPerItem();
    206
    207                if (as > bs) {
    208                        if (da.isComplex()) {
    209                                while (it.hasNext()) {
    210                                        boolean rb = isCloseNP(it.aDouble, it.bDouble, relTolerance, absTolerance);
    211                                        if (rb) {
    212                                                rb = isCloseNP(da.getElementDoubleAbs(it.aIndex + 1), relTolerance, absTolerance);
    213                                        }
    214                                        r.setAbs(it.oIndex, rb);
    215                                }
    216                        } else {
    217                                while (it.hasNext()) {
    218                                        final double bd = it.bDouble;
    219                                        boolean rb = true;
    220                                        for (int j = 0; rb && j < as; j++) {
    221                                                rb &= isCloseNP(da.getElementDoubleAbs(it.aIndex + j), bd, relTolerance, absTolerance);
    222                                        }
    223                                        r.setAbs(it.oIndex, rb);
    224                                }
    225                        }
    226                } else if (as < bs) {
    227                        if (db.isComplex()) {
    228                                while (it.hasNext()) {
    229                                        boolean rb = isCloseNP(it.aDouble, it.bDouble, relTolerance, absTolerance);
    230                                        if (rb) {
    231                                                rb = isCloseNP(db.getElementDoubleAbs(it.bIndex + 1), relTolerance, absTolerance);
    232                                        }
    233                                        r.setAbs(it.oIndex, rb);
    234                                }
    235                        } else {
    236                                while (it.hasNext()) {
    237                                        final double ad = it.aDouble;
    238                                        boolean rb = true;
    239                                        for (int j = 0; rb && j < bs; j++) {
    240                                                rb &= isCloseNP(ad, db.getElementDoubleAbs(it.bIndex + j), relTolerance, absTolerance);
    241                                        }
    242                                        r.setAbs(it.oIndex, rb);
    243                                }
    244                        }
    245                } else {
    246                        if (as == 1) {
    247                                while (it.hasNext()) {
    248                                        r.setAbs(it.oIndex, isCloseNP(it.aDouble, it.bDouble, relTolerance, absTolerance));
    249                                }
    250                        } else {
    251                                while (it.hasNext()) {
    252                                        boolean rb = isCloseNP(it.aDouble, it.bDouble, relTolerance, absTolerance);
    253                                        for (int j = 1; rb && j < bs; j++) {
    254                                                rb &= isCloseNP(da.getElementDoubleAbs(it.aIndex + j), db.getElementDoubleAbs(it.bIndex + j), relTolerance, absTolerance);
    255                                        }
    256                                        r.setAbs(it.oIndex, rb);
    257                                }
    258                        }
    259                }
    260
    261                return r;
    262        }
    263
    264        /**
    265         * Compare item-wise for whether a's element is greater than b's
    266         * <p>
    267         * For multi-element items, comparison is true if all elements in an item
    268         * are greater. Where the datasets have mismatched item sizes, the first element
    269         * of the dataset with smaller items is used for comparison.
    270         * @param a
    271         * @param b
    272         * @return dataset where item is true if {@code a > b}
    273         */
    274        public static BooleanDataset greaterThan(Object a, Object b) {
    275                return greaterThan(a, b, null);
    276        }
    277
    278        /**
    279         * Compare item-wise for whether a's element is greater than b's
    280         * <p>
    281         * For multi-element items, comparison is true if all elements in an item
    282         * are greater. Where the datasets have mismatched item sizes, the first element
    283         * of the dataset with smaller items is used for comparison.
    284         * @param a
    285         * @param b
    286         * @param o output can be null - in which case, a new dataset is created
    287         * @return dataset where item is true if {@code a > b}
    288         */
    289        public static BooleanDataset greaterThan(Object a, Object b, BooleanDataset o) {
    290                final Dataset da = a instanceof Dataset ? (Dataset) a : DatasetFactory.createFromObject(a);
    291                final Dataset db = b instanceof Dataset ? (Dataset) b : DatasetFactory.createFromObject(b);
    292
    293                List<int[]> sl = BroadcastUtils.broadcastShapes(da.getShapeRef(), db.getShapeRef(), o == null ? null : o.getShapeRef());
    294
    295                final BooleanDataset r = o == null ? DatasetFactory.zeros(BooleanDataset.class, sl.get(0)) : o;
    296
    297                final BroadcastIterator it = BroadcastIterator.createIterator(da, db, r);
    298                final int as = da.getElementsPerItem();
    299                final int bs = db.getElementsPerItem();
    300
    301                if (it.isOutputDouble()) {
    302                        if (as > bs) {
    303                                while (it.hasNext()) {
    304                                        final double bd = it.bDouble;
    305                                        boolean rb = true;
    306                                        for (int j = 0; rb && j < as; j++) {
    307                                                rb &= da.getElementDoubleAbs(it.aIndex + j) > bd;
    308                                        }
    309                                        r.setAbs(it.oIndex, rb);
    310                                }
    311                        } else if (as < bs) {
    312                                while (it.hasNext()) {
    313                                        final double ad = it.aDouble;
    314                                        boolean rb = true;
    315                                        for (int j = 0; rb && j < bs; j++) {
    316                                                rb &= ad > db.getElementDoubleAbs(it.bIndex + j);
    317                                        }
    318                                        r.setAbs(it.oIndex, rb);
    319                                }
    320                        } else {
    321                                if (as == 1) {
    322                                        while (it.hasNext()) {
    323                                                r.setAbs(it.oIndex, it.aDouble > it.bDouble);
    324                                        }
    325                                } else {
    326                                        while (it.hasNext()) {
    327                                                boolean rb = true;
    328                                                for (int j = 0; rb && j < bs; j++) {
    329                                                        rb &= da.getElementDoubleAbs(it.aIndex + j) > db.getElementDoubleAbs(it.bIndex + j);
    330                                                }
    331                                                r.setAbs(it.oIndex, rb);
    332                                        }
    333                                }
    334                        }
    335                } else {
    336                        if (as > bs) {
    337                                while (it.hasNext()) {
    338                                        final double bl = it.bLong;
    339                                        boolean rb = true;
    340                                        for (int j = 0; rb && j < as; j++) {
    341                                                rb &= da.getElementLongAbs(it.aIndex + j) > bl;
    342                                        }
    343                                        r.setAbs(it.oIndex, rb);
    344                                }
    345                        } else if (as < bs) {
    346                                while (it.hasNext()) {
    347                                        final double al = it.aLong;
    348                                        boolean rb = true;
    349                                        for (int j = 0; rb && j < bs; j++) {
    350                                                rb &= al > db.getElementLongAbs(it.bIndex + j);
    351                                        }
    352                                        r.setAbs(it.oIndex, rb);
    353                                }
    354                        } else {
    355                                if (as == 1) {
    356                                        while (it.hasNext()) {
    357                                                r.setAbs(it.oIndex, it.aLong > it.bLong);
    358                                        }
    359                                } else {
    360                                        while (it.hasNext()) {
    361                                                boolean rb = true;
    362                                                for (int j = 0; rb && j < bs; j++) {
    363                                                        rb &= da.getElementLongAbs(it.aIndex + j) > db.getElementLongAbs(it.bIndex + j);
    364                                                }
    365                                                r.setAbs(it.oIndex, rb);
    366                                        }
    367                                }
    368                        }
    369                }
    370
    371                return r;
    372        }
    373
    374        /**
    375         * Compare item-wise for whether a's element is greater than or equal to b's
    376         * <p>
    377         * For multi-element items, comparison is true if all elements in an item
    378         * are greater or equal. Where the datasets have mismatched item sizes, the first element
    379         * of the dataset with smaller items is used for comparison.
    380         * @param a
    381         * @param b
    382         * @return dataset where item is true if {@code a >= b}
    383         */
    384        public static BooleanDataset greaterThanOrEqualTo(Object a, Object b) {
    385                return greaterThanOrEqualTo(a, b, null);
    386        }
    387
    388        /**
    389         * Compare item-wise for whether a's element is greater than or equal to b's
    390         * <p>
    391         * For multi-element items, comparison is true if all elements in an item
    392         * are greater or equal. Where the datasets have mismatched item sizes, the first element
    393         * of the dataset with smaller items is used for comparison.
    394         * @param a
    395         * @param b
    396         * @param o output can be null - in which case, a new dataset is created
    397         * @return dataset where item is true if {@code a >= b}
    398         */
    399        public static BooleanDataset greaterThanOrEqualTo(Object a, Object b, BooleanDataset o) {
    400                final Dataset da = a instanceof Dataset ? (Dataset) a : DatasetFactory.createFromObject(a);
    401                final Dataset db = b instanceof Dataset ? (Dataset) b : DatasetFactory.createFromObject(b);
    402
    403                List<int[]> sl = BroadcastUtils.broadcastShapes(da.getShapeRef(), db.getShapeRef(), o == null ? null : o.getShapeRef());
    404
    405                final BooleanDataset r = o == null ? DatasetFactory.zeros(BooleanDataset.class, sl.get(0)) : o;
    406
    407                final BroadcastIterator it = BroadcastIterator.createIterator(da, db, r);
    408                final int as = da.getElementsPerItem();
    409                final int bs = db.getElementsPerItem();
    410
    411                if (it.isOutputDouble()) {
    412                        if (as > bs) {
    413                                while (it.hasNext()) {
    414                                        final double bd = it.bDouble;
    415                                        boolean rb = true;
    416                                        for (int j = 0; rb && j < as; j++) {
    417                                                rb &= da.getElementDoubleAbs(it.aIndex + j) >= bd;
    418                                        }
    419                                        r.setAbs(it.oIndex, rb);
    420                                }
    421                        } else if (as < bs) {
    422                                while (it.hasNext()) {
    423                                        final double ad = it.aDouble;
    424                                        boolean rb = true;
    425                                        for (int j = 0; rb && j < bs; j++) {
    426                                                rb &= ad >= db.getElementDoubleAbs(it.bIndex + j);
    427                                        }
    428                                        r.setAbs(it.oIndex, rb);
    429                                }
    430                        } else {
    431                                if (as == 1) {
    432                                        while (it.hasNext()) {
    433                                                r.setAbs(it.oIndex, it.aDouble >= it.bDouble);
    434                                        }
    435                                } else {
    436                                        while (it.hasNext()) {
    437                                                boolean rb = true;
    438                                                for (int j = 0; rb && j < bs; j++) {
    439                                                        rb &= da.getElementDoubleAbs(it.aIndex + j) >= db.getElementDoubleAbs(it.bIndex + j);
    440                                                }
    441                                                r.setAbs(it.oIndex, rb);
    442                                        }
    443                                }
    444                        }
    445                } else {
    446                        if (as > bs) {
    447                                while (it.hasNext()) {
    448                                        final double bl = it.bLong;
    449                                        boolean rb = true;
    450                                        for (int j = 0; rb && j < as; j++) {
    451                                                rb &= da.getElementLongAbs(it.aIndex + j) >= bl;
    452                                        }
    453                                        r.setAbs(it.oIndex, rb);
    454                                }
    455                        } else if (as < bs) {
    456                                while (it.hasNext()) {
    457                                        final double al = it.aLong;
    458                                        boolean rb = true;
    459                                        for (int j = 0; rb && j < bs; j++) {
    460                                                rb &= al >= db.getElementLongAbs(it.bIndex + j);
    461                                        }
    462                                        r.setAbs(it.oIndex, rb);
    463                                }
    464                        } else {
    465                                if (as == 1) {
    466                                        while (it.hasNext()) {
    467                                                r.setAbs(it.oIndex, it.aLong >= it.bLong);
    468                                        }
    469                                } else {
    470                                        while (it.hasNext()) {
    471                                                boolean rb = true;
    472                                                for (int j = 0; rb && j < bs; j++) {
    473                                                        rb &= da.getElementLongAbs(it.aIndex + j) >= db.getElementLongAbs(it.bIndex + j);
    474                                                }
    475                                                r.setAbs(it.oIndex, rb);
    476                                        }
    477                                }
    478                        }
    479                }
    480
    481                return r;
    482        }
    483
    484        /**
    485         * Compare item-wise for whether a's element is less than b's
    486         * <p>
    487         * For multi-element items, comparison is true if all elements in an item
    488         * are lesser. Where the datasets have mismatched item sizes, the first element
    489         * of the dataset with smaller items is used for comparison.
    490         * @param a
    491         * @param b
    492         * @return dataset where item is true if {@code a < b}
    493         */
    494        public static BooleanDataset lessThan(Object a, Object b) {
    495                return lessThan(a, b, null);
    496        }
    497
    498        /**
    499         * Compare item-wise for whether a's element is less than b's
    500         * <p>
    501         * For multi-element items, comparison is true if all elements in an item
    502         * are lesser. Where the datasets have mismatched item sizes, the first element
    503         * of the dataset with smaller items is used for comparison.
    504         * @param a
    505         * @param b
    506         * @param o output can be null - in which case, a new dataset is created
    507         * @return dataset where item is true if {@code a < b}
    508         */
    509        public static BooleanDataset lessThan(Object a, Object b, BooleanDataset o) {
    510                final Dataset da = a instanceof Dataset ? (Dataset) a : DatasetFactory.createFromObject(a);
    511                final Dataset db = b instanceof Dataset ? (Dataset) b : DatasetFactory.createFromObject(b);
    512
    513                List<int[]> sl = BroadcastUtils.broadcastShapes(da.getShapeRef(), db.getShapeRef(), o == null ? null : o.getShapeRef());
    514
    515                final BooleanDataset r = o == null ? DatasetFactory.zeros(BooleanDataset.class, sl.get(0)) : o;
    516
    517                final BroadcastIterator it = BroadcastIterator.createIterator(da, db, r);
    518                it.setOutputDouble(true);
    519                final int as = da.getElementsPerItem();
    520                final int bs = db.getElementsPerItem();
    521
    522                if (it.isOutputDouble()) {
    523                        if (as > bs) {
    524                                while (it.hasNext()) {
    525                                        final double bd = it.bDouble;
    526                                        boolean rb = true;
    527                                        for (int j = 0; rb && j < as; j++) {
    528                                                rb &= da.getElementDoubleAbs(it.aIndex + j) < bd;
    529                                        }
    530                                        r.setAbs(it.oIndex, rb);
    531                                }
    532                        } else if (as < bs) {
    533                                while (it.hasNext()) {
    534                                        final double ad = it.aDouble;
    535                                        boolean rb = true;
    536                                        for (int j = 0; rb && j < bs; j++) {
    537                                                rb &= ad < db.getElementDoubleAbs(it.bIndex + j);
    538                                        }
    539                                        r.setAbs(it.oIndex, rb);
    540                                }
    541                        } else {
    542                                if (as == 1) {
    543                                        while (it.hasNext()) {
    544                                                r.setAbs(it.oIndex, it.aDouble < it.bDouble);
    545                                        }
    546                                } else {
    547                                        while (it.hasNext()) {
    548                                                boolean rb = true;
    549                                                for (int j = 0; rb && j < bs; j++) {
    550                                                        rb &= da.getElementDoubleAbs(it.aIndex + j) < db.getElementDoubleAbs(it.bIndex + j);
    551                                                }
    552                                                r.setAbs(it.oIndex, rb);
    553                                        }
    554                                }
    555                        }
    556                } else {
    557                        if (as > bs) {
    558                                while (it.hasNext()) {
    559                                        final double bl = it.bLong;
    560                                        boolean rb = true;
    561                                        for (int j = 0; rb && j < as; j++) {
    562                                                rb &= da.getElementLongAbs(it.aIndex + j) < bl;
    563                                        }
    564                                        r.setAbs(it.oIndex, rb);
    565                                }
    566                        } else if (as < bs) {
    567                                while (it.hasNext()) {
    568                                        final double al = it.aLong;
    569                                        boolean rb = true;
    570                                        for (int j = 0; rb && j < bs; j++) {
    571                                                rb &= al < db.getElementLongAbs(it.bIndex + j);
    572                                        }
    573                                        r.setAbs(it.oIndex, rb);
    574                                }
    575                        } else {
    576                                if (as == 1) {
    577                                        while (it.hasNext()) {
    578                                                r.setAbs(it.oIndex, it.aLong < it.bLong);
    579                                        }
    580                                } else {
    581                                        while (it.hasNext()) {
    582                                                boolean rb = true;
    583                                                for (int j = 0; rb && j < bs; j++) {
    584                                                        rb &= da.getElementLongAbs(it.aIndex + j) < db.getElementLongAbs(it.bIndex + j);
    585                                                }
    586                                                r.setAbs(it.oIndex, rb);
    587                                        }
    588                                }
    589                        }
    590                }
    591
    592                return r;
    593        }
    594
    595        /**
    596         * Compare item-wise for whether a's element is less than or equal to b's
    597         * <p>
    598         * For multi-element items, comparison is true if all elements in an item
    599         * are lesser or equal. Where the datasets have mismatched item sizes, the first element
    600         * of the dataset with smaller items is used for comparison.
    601         * @param a
    602         * @param b
    603         * @return dataset where item is true if {@code a <= b}
    604         */
    605        public static BooleanDataset lessThanOrEqualTo(Object a, Object b) {
    606                return lessThanOrEqualTo(a, b, null);
    607        }
    608
    609        /**
    610         * Compare item-wise for whether a's element is less than or equal to b's
    611         * <p>
    612         * For multi-element items, comparison is true if all elements in an item
    613         * are lesser or equal. Where the datasets have mismatched item sizes, the first element
    614         * of the dataset with smaller items is used for comparison.
    615         * @param a
    616         * @param b
    617         * @param o output can be null - in which case, a new dataset is created
    618         * @return dataset where item is true if {@code a <= b}
    619         */
    620        public static BooleanDataset lessThanOrEqualTo(Object a, Object b, BooleanDataset o) {
    621                final Dataset da = a instanceof Dataset ? (Dataset) a : DatasetFactory.createFromObject(a);
    622                final Dataset db = b instanceof Dataset ? (Dataset) b : DatasetFactory.createFromObject(b);
    623
    624                List<int[]> sl = BroadcastUtils.broadcastShapes(da.getShapeRef(), db.getShapeRef(), o == null ? null : o.getShapeRef());
    625
    626                final BooleanDataset r = o == null ? DatasetFactory.zeros(BooleanDataset.class, sl.get(0)) : o;
    627
    628                final BroadcastIterator it = BroadcastIterator.createIterator(da, db, r);
    629                it.setOutputDouble(true);
    630                final int as = da.getElementsPerItem();
    631                final int bs = db.getElementsPerItem();
    632
    633                if (it.isOutputDouble()) {
    634                        if (as > bs) {
    635                                while (it.hasNext()) {
    636                                        final double bd = it.bDouble;
    637                                        boolean rb = true;
    638                                        for (int j = 0; rb && j < as; j++) {
    639                                                rb &= da.getElementDoubleAbs(it.aIndex + j) <= bd;
    640                                        }
    641                                        r.setAbs(it.oIndex, rb);
    642                                }
    643                        } else if (as < bs) {
    644                                while (it.hasNext()) {
    645                                        final double ad = it.aDouble;
    646                                        boolean rb = true;
    647                                        for (int j = 0; rb && j < bs; j++) {
    648                                                rb &= ad <= db.getElementDoubleAbs(it.bIndex + j);
    649                                        }
    650                                        r.setAbs(it.oIndex, rb);
    651                                }
    652                        } else {
    653                                if (as == 1) {
    654                                        while (it.hasNext()) {
    655                                                r.setAbs(it.oIndex, it.aDouble <= it.bDouble);
    656                                        }
    657                                } else {
    658                                        while (it.hasNext()) {
    659                                                boolean rb = true;
    660                                                for (int j = 0; rb && j < bs; j++) {
    661                                                        rb &= da.getElementDoubleAbs(it.aIndex + j) <= db.getElementDoubleAbs(it.bIndex + j);
    662                                                }
    663                                                r.setAbs(it.oIndex, rb);
    664                                        }
    665                                }
    666                        }
    667                } else {
    668                        if (as > bs) {
    669                                while (it.hasNext()) {
    670                                        final double bl = it.bLong;
    671                                        boolean rb = true;
    672                                        for (int j = 0; rb && j < as; j++) {
    673                                                rb &= da.getElementLongAbs(it.aIndex + j) <= bl;
    674                                        }
    675                                        r.setAbs(it.oIndex, rb);
    676                                }
    677                        } else if (as < bs) {
    678                                while (it.hasNext()) {
    679                                        final double al = it.aLong;
    680                                        boolean rb = true;
    681                                        for (int j = 0; rb && j < bs; j++) {
    682                                                rb &= al <= db.getElementLongAbs(it.bIndex + j);
    683                                        }
    684                                        r.setAbs(it.oIndex, rb);
    685                                }
    686                        } else {
    687                                if (as == 1) {
    688                                        while (it.hasNext()) {
    689                                                r.setAbs(it.oIndex, it.aLong <= it.bLong);
    690                                        }
    691                                } else {
    692                                        while (it.hasNext()) {
    693                                                boolean rb = true;
    694                                                for (int j = 0; rb && j < bs; j++) {
    695                                                        rb &= da.getElementLongAbs(it.aIndex + j) <= db.getElementLongAbs(it.bIndex + j);
    696                                                }
    697                                                r.setAbs(it.oIndex, rb);
    698                                        }
    699                                }
    700                        }
    701                }
    702
    703                return r;
    704        }
    705
    706        /**
    707         * @param a
    708         * @param lo lower bound
    709         * @param hi upper bound
    710         * @return dataset where item is true if {@code l <= a <= h}
    711         */
    712        public static BooleanDataset withinRange(Object a, Number lo, Number hi) {
    713                return withinRange(a, null, lo, hi);
    714        }
    715
    716        /**
    717         * @param a
    718         * @param lo lower bound
    719         * @param hi upper bound
    720         * @param o output can be null - in which case, a new dataset is created
    721         * @return dataset where item is true if {@code l <= a <= h}
    722         */
    723        public static BooleanDataset withinRange(Object a, BooleanDataset o, Number lo, Number hi) {
    724                final Dataset da = a instanceof Dataset ? (Dataset) a : DatasetFactory.createFromObject(a);
    725
    726                List<int[]> sl = BroadcastUtils.broadcastShapes(da.getShapeRef(), o == null ? null : o.getShapeRef());
    727
    728                final BooleanDataset r = o == null ? DatasetFactory.zeros(BooleanDataset.class, sl.get(0)) : o;
    729
    730                final SingleInputBroadcastIterator it = new SingleInputBroadcastIterator(da, r);
    731                final int as = da.getElementsPerItem();
    732
    733                if (it.isOutputDouble()) {
    734                        final double l = lo.doubleValue();
    735                        final double h = hi.doubleValue();
    736                        if (as == 1) {
    737                                while (it.hasNext()) {
    738                                        final double ad = it.aDouble;
    739                                        r.setAbs(it.oIndex, ad >= l && ad <= h);
    740                                }
    741                        } else {
    742                                while (it.hasNext()) {
    743                                        boolean rb = true;
    744                                        for (int j = 0; rb && j < as; j++) {
    745                                                final double ad = da.getElementDoubleAbs(it.aIndex);
    746                                                rb &= ad >= l && ad <= h;
    747                                        }
    748                                        r.setAbs(it.oIndex, rb);
    749                                }
    750                        }
    751                } else {
    752                        final long l = lo.longValue();
    753                        final long h = hi.longValue();
    754                        if (as == 1) {
    755                                while (it.hasNext()) {
    756                                        final long al = it.aLong;
    757                                        r.setAbs(it.oIndex, al >= l && al <= h);
    758                                }
    759                        } else {
    760                                while (it.hasNext()) {
    761                                        boolean rb = true;
    762                                        for (int j = 0; rb && j < as; j++) {
    763                                                final long al = da.getElementLongAbs(it.aIndex);
    764                                                rb &= al >= l && al <= h;
    765                                        }
    766                                        r.setAbs(it.oIndex, rb);
    767                                }
    768                        }
    769                }
    770
    771                return r;
    772        }
    773
    774        /**
    775         * Compare item-wise for whether a's element is almost equal to b's
    776         * <p>
    777         * For multi-element items, comparison is true if all elements in an item
    778         * are equal up to a tolerance. Where the datasets have mismatched item sizes, the first element
    779         * of the dataset with smaller items is used for comparison.
    780         * @param a
    781         * @param b
    782         * @param relTolerance
    783         * @param absTolerance
    784         * @return true if all items satisfy {@code abs(a - b) <= absTol + relTol*max(abs(a),abs(b))}
    785         */
    786        public static boolean allCloseTo(Object a, Object b, double relTolerance, double absTolerance) {
    787                final Dataset da = a instanceof Dataset ? (Dataset) a : DatasetFactory.createFromObject(a);
    788                final Dataset db = b instanceof Dataset ? (Dataset) b : DatasetFactory.createFromObject(b);
    789
    790                final BroadcastIterator it = BroadcastIterator.createIterator(da, db);
    791                it.setOutputDouble(true);
    792                final int as = da.getElementsPerItem();
    793                final int bs = db.getElementsPerItem();
    794
    795                if (as > bs) {
    796                        if (da.isComplex()) {
    797                                while (it.hasNext()) {
    798                                        if (!isCloseNP(it.aDouble, it.bDouble, relTolerance, absTolerance))
    799                                                return false;
    800                                        if (!isCloseNP(da.getElementDoubleAbs(it.aIndex + 1), relTolerance, absTolerance))
    801                                                return false;
    802                                }
    803                        } else {
    804                                while (it.hasNext()) {
    805                                        final double bd = it.bDouble;
    806                                        for (int j = 0; j < as; j++) {
    807                                                if (!isCloseNP(da.getElementDoubleAbs(it.aIndex + j), bd, relTolerance, absTolerance))
    808                                                        return false;
    809                                        }
    810                                }
    811                        }
    812                } else if (as < bs) {
    813                        if (db.isComplex()) {
    814                                while (it.hasNext()) {
    815                                        if (!isCloseNP(it.aDouble, it.bDouble, relTolerance, absTolerance))
    816                                                return false;
    817                                        if (!isCloseNP(db.getElementDoubleAbs(it.bIndex + 1), relTolerance, absTolerance))
    818                                                return false;
    819                                }
    820                        } else {
    821                                while (it.hasNext()) {
    822                                        final double ad = it.aDouble;
    823                                        for (int j = 0; j < bs; j++) {
    824                                                if (!isCloseNP(ad, db.getElementDoubleAbs(it.bIndex + j), relTolerance, absTolerance))
    825                                                        return false;
    826                                        }
    827                                }
    828                        }
    829                } else {
    830                        if (as == 1) {
    831                                while (it.hasNext()) {
    832                                        if (!isCloseNP(it.aDouble, it.bDouble, relTolerance, absTolerance))
    833                                                return false;
    834                                }
    835                        } else {
    836                                while (it.hasNext()) {
    837                                        for (int j = 0; j < bs; j++) {
    838                                                if (!isCloseNP(da.getElementDoubleAbs(it.aIndex + j), db.getElementDoubleAbs(it.bIndex + j), relTolerance, absTolerance))
    839                                                        return false;
    840                                        }
    841                                }
    842                        }
    843                }
    844
    845                return true;
    846        }
    847
    848        /**
    849         * @param a
    850         * @return true if all elements are true
    851         */
    852        public static boolean allTrue(Object a) {
    853                final Dataset da = a instanceof Dataset ? (Dataset) a : DatasetFactory.createFromObject(a);
    854                final IndexIterator it = da.getIterator();
    855                final int as = da.getElementsPerItem();
    856
    857                if (as == 1) {
    858                        while (it.hasNext()) {
    859                                if (!da.getElementBooleanAbs(it.index))
    860                                        return false;
    861                        }
    862                } else {
    863                        while (it.hasNext()) {
    864                                for (int j = 0; j < as; j++) {
    865                                        if (!da.getElementBooleanAbs(it.index + j))
    866                                                return false;
    867                                }
    868                        }
    869                }
    870                return true;
    871        }
    872
    873        /**
    874         * Test if all items along given axis are true in the input dataset
    875         * @param a
    876         * @param axis axis to reduce
    877         * @return boolean dataset
    878         */
    879        public static BooleanDataset allTrue(IDataset a, int axis) {
    880                axis = ShapeUtils.checkAxis(a.getRank(), axis);
    881
    882                int rank = a.getRank();
    883                int[] oshape = a.getShape();
    884                int alen = oshape[axis];
    885                oshape[axis] = 1;
    886
    887                int[] nshape = ShapeUtils.squeezeShape(oshape, false);
    888
    889                BooleanDataset result = DatasetFactory.zeros(BooleanDataset.class, nshape);
    890
    891                IndexIterator qiter = result.getIterator(true);
    892                int[] qpos = qiter.getPos();
    893                int[] spos = oshape;
    894
    895                while (qiter.hasNext()) {
    896                        int i = 0;
    897                        for (; i < axis; i++) {
    898                                spos[i] = qpos[i];
    899                        }
    900                        spos[i++] = 0;
    901                        for (; i < rank; i++) {
    902                                spos[i] = qpos[i-1];
    903                        }
    904
    905                        boolean br = true;
    906                        for (int j = 0; br && j < alen; j++) {
    907                                spos[axis] = j;
    908                                br &= a.getBoolean(spos);
    909                        }
    910                        result.set(br, qpos);
    911                }
    912                return result;
    913        }
    914
    915        /**
    916         * @param a
    917         * @return true if any element is true
    918         */
    919        public static boolean anyTrue(Object a) {
    920                final Dataset da = a instanceof Dataset ? (Dataset) a : DatasetFactory.createFromObject(a);
    921                final IndexIterator it = da.getIterator();
    922                final int as = da.getElementsPerItem();
    923
    924                if (as == 1) {
    925                        while (it.hasNext()) {
    926                                if (da.getElementBooleanAbs(it.index))
    927                                        return true;
    928                        }
    929                } else {
    930                        while (it.hasNext()) {
    931                                for (int j = 0; j < as; j++) {
    932                                        if (da.getElementBooleanAbs(it.index + j))
    933                                                return true;
    934                                }
    935                        }
    936                }
    937                return false;
    938        }
    939
    940        /**
    941         * Test if any items along given axis are true in the input dataset
    942         * @param a
    943         * @param axis axis to reduce
    944         * @return boolean dataset
    945         */
    946        public static BooleanDataset anyTrue(IDataset a, int axis) {
    947                axis = ShapeUtils.checkAxis(a.getRank(), axis);
    948
    949                int rank = a.getRank();
    950                int[] oshape = a.getShape();
    951                int alen = oshape[axis];
    952                oshape[axis] = 1;
    953
    954                int[] nshape = ShapeUtils.squeezeShape(oshape, false);
    955
    956                BooleanDataset result = DatasetFactory.zeros(BooleanDataset.class, nshape);
    957
    958                IndexIterator qiter = result.getIterator(true);
    959                int[] qpos = qiter.getPos();
    960                int[] spos = oshape;
    961
    962                while (qiter.hasNext()) {
    963                        int i = 0;
    964                        for (; i < axis; i++) {
    965                                spos[i] = qpos[i];
    966                        }
    967                        spos[i++] = 0;
    968                        for (; i < rank; i++) {
    969                                spos[i] = qpos[i-1];
    970                        }
    971
    972                        boolean br = false;
    973                        for (int j = 0; !br && j < alen; j++) {
    974                                spos[axis] = j;
    975                                br |= a.getBoolean(spos);
    976                        }
    977                        result.set(br, qpos);
    978                }
    979                return result;
    980        }
    981
    982        /**
    983         * Negate item-wise
    984         * <p>
    985         * For multi-element items, negation is false if all elements in a pair of items
    986         * are true.
    987         * @param a
    988         * @return dataset where item is true when a is false
    989         */
    990        public static BooleanDataset logicalNot(Object a) {
    991                return logicalNot(a, null);
    992        }
    993
    994        /**
    995         * Negate item-wise
    996         * <p>
    997         * For multi-element items, negation is false if all elements in a pair of items
    998         * are true.
    999         * @param a
    1000         * @param o output can be null - in which case, a new dataset is created
    1001         * @return dataset where item is true when a is false
    1002         */
    1003        public static BooleanDataset logicalNot(Object a, BooleanDataset o) {
    1004                final Dataset da = a instanceof Dataset ? (Dataset) a : DatasetFactory.createFromObject(a);
    1005
    1006                List<int[]> sl = BroadcastUtils.broadcastShapes(da.getShapeRef(), o == null ? null : o.getShapeRef());
    1007
    1008                final BooleanDataset r = o == null ? DatasetFactory.zeros(BooleanDataset.class, sl.get(0)) : o;
    1009
    1010                final SingleInputBroadcastIterator it = new SingleInputBroadcastIterator(da, r);
    1011                final int as = da.getElementsPerItem();
    1012
    1013                if (as == 1) {
    1014                        while (it.hasNext()) {
    1015                                r.setAbs(it.oIndex, !da.getElementBooleanAbs(it.aIndex));
    1016                        }
    1017                } else {
    1018                        boolean br = true;
    1019                        while (it.hasNext()) {
    1020                                for (int j = 0; j < as; j++) {
    1021                                        br &= da.getElementBooleanAbs(it.aIndex + j);
    1022                                }
    1023                                r.setAbs(it.oIndex, !br);
    1024                        }
    1025                }
    1026                return r;
    1027        }
    1028
    1029        /**
    1030         * Compare item-wise for whether a's item is true and b's true too.
    1031         * <p>
    1032         * For multi-element items, comparison is true if all elements in a pair of items
    1033         * are true. Where the datasets have mismatched item sizes, the first element
    1034         * of the dataset with smaller items is used for comparison.
    1035         * @param a
    1036         * @param b
    1037         * @return dataset where item is true if {@code a && b} is true
    1038         */
    1039        public static BooleanDataset logicalAnd(Object a, Object b) {
    1040                return logicalAnd(a, b, null);
    1041        }
    1042
    1043        /**
    1044         * Compare item-wise for whether a's item is true and b's true too.
    1045         * <p>
    1046         * For multi-element items, comparison is true if all elements in a pair of items
    1047         * are true. Where the datasets have mismatched item sizes, the first element
    1048         * of the dataset with smaller items is used for comparison.
    1049         * @param a
    1050         * @param b
    1051         * @param o output can be null - in which case, a new dataset is created
    1052         * @return dataset where item is true if {@code a && b} is true
    1053         */
    1054        public static BooleanDataset logicalAnd(Object a, Object b, BooleanDataset o) {
    1055                final Dataset da = a instanceof Dataset ? (Dataset) a : DatasetFactory.createFromObject(a);
    1056                final Dataset db = b instanceof Dataset ? (Dataset) b : DatasetFactory.createFromObject(b);
    1057
    1058                List<int[]> sl = BroadcastUtils.broadcastShapes(da.getShapeRef(), db.getShapeRef(), o == null ? null : o.getShapeRef());
    1059
    1060                final BooleanDataset r = o == null ? DatasetFactory.zeros(BooleanDataset.class, sl.get(0)) : o;
    1061
    1062                final BroadcastIterator it = BroadcastIterator.createIterator(da, db, r);
    1063                it.setOutputDouble(true);
    1064                final int as = da.getElementsPerItem();
    1065                final int bs = db.getElementsPerItem();
    1066
    1067                if (as > bs) {
    1068                        while (it.hasNext()) {
    1069                                final boolean bb = db.getElementBooleanAbs(it.bIndex);
    1070                                boolean rb = true;
    1071                                for (int j = 0; rb && j < as; j++) {
    1072                                        rb &= da.getElementBooleanAbs(it.aIndex + j) && bb;
    1073                                }
    1074                                r.setAbs(it.oIndex, rb);
    1075                        }
    1076                } else if (as < bs) {
    1077                        while (it.hasNext()) {
    1078                                final boolean ab = da.getElementBooleanAbs(it.aIndex);
    1079                                boolean rb = true;
    1080                                for (int j = 0; rb && j < bs; j++) {
    1081                                        rb &= ab && db.getElementBooleanAbs(it.bIndex + j);
    1082                                }
    1083                                r.setAbs(it.oIndex, rb);
    1084                        }
    1085                } else {
    1086                        if (as == 1) {
    1087                                while (it.hasNext()) {
    1088                                        r.setAbs(it.oIndex, da.getElementBooleanAbs(it.aIndex) && db.getElementBooleanAbs(it.bIndex));
    1089                                }
    1090                        } else {
    1091                                while (it.hasNext()) {
    1092                                        boolean rb = true;
    1093                                        for (int j = 0; rb && j < bs; j++) {
    1094                                                rb &= da.getElementBooleanAbs(it.aIndex + j) && db.getElementBooleanAbs(it.bIndex + j);
    1095                                        }
    1096                                        r.setAbs(it.oIndex, rb);
    1097                                }
    1098                        }
    1099                }
    1100
    1101                return r;
    1102        }
    1103
    1104        /**
    1105         * Compare item-wise for whether a's item is true or b's true.
    1106         * <p>
    1107         * For multi-element items, comparison is true if any elements in a pair of items
    1108         * are true. Where the datasets have mismatched item sizes, the first element
    1109         * of the dataset with smaller items is used for comparison.
    1110         * @param a
    1111         * @param b
    1112         * @return dataset where item is true if {@code a || b} is true
    1113         */
    1114        public static BooleanDataset logicalOr(Object a, Object b) {
    1115                return logicalOr(a, b, null);
    1116        }
    1117
    1118        /**
    1119         * Compare item-wise for whether a's item is true or b's true.
    1120         * <p>
    1121         * For multi-element items, comparison is true if any elements in a pair of items
    1122         * are true. Where the datasets have mismatched item sizes, the first element
    1123         * of the dataset with smaller items is used for comparison.
    1124         * @param a
    1125         * @param b
    1126         * @param o output can be null - in which case, a new dataset is created
    1127         * @return dataset where item is true if {@code a || b} is true
    1128         */
    1129        public static BooleanDataset logicalOr(Object a, Object b, BooleanDataset o) {
    1130                final Dataset da = a instanceof Dataset ? (Dataset) a : DatasetFactory.createFromObject(a);
    1131                final Dataset db = b instanceof Dataset ? (Dataset) b : DatasetFactory.createFromObject(b);
    1132
    1133                List<int[]> sl = BroadcastUtils.broadcastShapes(da.getShapeRef(), db.getShapeRef(), o == null ? null : o.getShapeRef());
    1134
    1135                final BooleanDataset r = o == null ? DatasetFactory.zeros(BooleanDataset.class, sl.get(0)) : o;
    1136
    1137                final BroadcastIterator it = BroadcastIterator.createIterator(da, db, r);
    1138                it.setOutputDouble(true);
    1139                final int as = da.getElementsPerItem();
    1140                final int bs = db.getElementsPerItem();
    1141
    1142                if (as > bs) {
    1143                        while (it.hasNext()) {
    1144                                final boolean bb = db.getElementBooleanAbs(it.bIndex);
    1145                                boolean rb = true;
    1146                                for (int j = 0; j < as; j++) {
    1147                                        rb |= da.getElementBooleanAbs(it.aIndex + j) || bb;
    1148                                }
    1149                                r.setAbs(it.oIndex, rb);
    1150                        }
    1151                } else if (as < bs) {
    1152                        while (it.hasNext()) {
    1153                                final boolean ab = da.getElementBooleanAbs(it.aIndex);
    1154                                boolean rb = true;
    1155                                for (int j = 0; rb && j < bs; j++) {
    1156                                        rb |= ab || db.getElementBooleanAbs(it.bIndex + j);
    1157                                }
    1158                                r.setAbs(it.oIndex, rb);
    1159                        }
    1160                } else {
    1161                        if (as == 1) {
    1162                                while (it.hasNext()) {
    1163                                        r.setAbs(it.oIndex, da.getElementBooleanAbs(it.aIndex) || db.getElementBooleanAbs(it.bIndex));
    1164                                }
    1165                        } else {
    1166                                while (it.hasNext()) {
    1167                                        boolean rb = true;
    1168                                        for (int j = 0; rb && j < bs; j++) {
    1169                                                rb &= da.getElementBooleanAbs(it.aIndex + j) || db.getElementBooleanAbs(it.bIndex + j);
    1170                                        }
    1171                                        r.setAbs(it.oIndex, rb);
    1172                                }
    1173                        }
    1174                }
    1175
    1176                return r;
    1177        }
    1178
    1179        /**
    1180         * Compare item-wise for whether a's item is true or b's true exclusively.
    1181         * <p>
    1182         * For multi-element items, comparison is true if one element in a pair of items
    1183         * is true. Where the datasets have mismatched item sizes, the first element
    1184         * of the dataset with smaller items is used for comparison.
    1185         * @param a
    1186         * @param b
    1187         * @return dataset where item is true if {@code a ^ b} is true
    1188         */
    1189        public static BooleanDataset logicalXor(Object a, Object b) {
    1190                return logicalXor(a, b, null);
    1191        }
    1192
    1193        /**
    1194         * Compare item-wise for whether a's item is true or b's true exclusively.
    1195         * <p>
    1196         * For multi-element items, comparison is true if one element in a pair of items
    1197         * is true. Where the datasets have mismatched item sizes, the first element
    1198         * of the dataset with smaller items is used for comparison.
    1199         * @param a
    1200         * @param b
    1201         * @param o output can be null - in which case, a new dataset is created
    1202         * @return dataset where item is true if a ^ b is true
    1203         */
    1204        public static BooleanDataset logicalXor(Object a, Object b, BooleanDataset o) {
    1205                final Dataset da = a instanceof Dataset ? (Dataset) a : DatasetFactory.createFromObject(a);
    1206                final Dataset db = b instanceof Dataset ? (Dataset) b : DatasetFactory.createFromObject(b);
    1207
    1208                List<int[]> sl = BroadcastUtils.broadcastShapes(da.getShapeRef(), db.getShapeRef(), o == null ? null : o.getShapeRef());
    1209
    1210                final BooleanDataset r = o == null ? DatasetFactory.zeros(BooleanDataset.class, sl.get(0)) : o;
    1211
    1212                final BroadcastIterator it = BroadcastIterator.createIterator(da, db, r);
    1213                it.setOutputDouble(true);
    1214                final int as = da.getElementsPerItem();
    1215                final int bs = db.getElementsPerItem();
    1216
    1217                if (as > bs) {
    1218                        while (it.hasNext()) {
    1219                                boolean rb = db.getElementBooleanAbs(it.bIndex);
    1220                                for (int j = 0; j < as; j++) {
    1221                                        rb ^= da.getElementBooleanAbs(it.aIndex + j);
    1222                                }
    1223                                r.setAbs(it.oIndex, rb);
    1224                        }
    1225                } else if (as < bs) {
    1226                        while (it.hasNext()) {
    1227                                boolean rb = da.getElementBooleanAbs(it.aIndex);
    1228                                for (int j = 0; rb && j < bs; j++) {
    1229                                        rb ^= db.getElementBooleanAbs(it.bIndex + j);
    1230                                }
    1231                                r.setAbs(it.oIndex, rb);
    1232                        }
    1233                } else {
    1234                        if (as == 1) {
    1235                                while (it.hasNext()) {
    1236                                        r.setAbs(it.oIndex, da.getElementBooleanAbs(it.aIndex) ^ db.getElementBooleanAbs(it.bIndex));
    1237                                }
    1238                        } else {
    1239                                while (it.hasNext()) {
    1240                                        boolean rb = true;
    1241                                        for (int j = 0; rb && j < bs; j++) {
    1242                                                rb &= da.getElementBooleanAbs(it.aIndex + j) ^ db.getElementBooleanAbs(it.bIndex + j);
    1243                                        }
    1244                                        r.setAbs(it.oIndex, rb);
    1245                                }
    1246                        }
    1247                }
    1248
    1249                return r;
    1250        }
    1251
    1252        /**
    1253         * Create a list of indices of positions where items are non-zero
    1254         * @param a
    1255         * @return list of positions as integer datasets
    1256         */
    1257        public static List<IntegerDataset> nonZero(Dataset a) {
    1258                final int rank = a.getRank();
    1259                final List<List<Integer>> indices = new ArrayList<List<Integer>>();
    1260                List<IntegerDataset> indexList = new ArrayList<IntegerDataset>();
    1261
    1262                if (rank == 0)
    1263                        return indexList;
    1264
    1265                for (int j = 0; j < rank; j++) {
    1266                        indices.add(new ArrayList<Integer>());
    1267                }
    1268
    1269                final IndexIterator iter = a.getIterator(true);
    1270                final int[] pos = iter.getPos();
    1271                while (iter.hasNext()) {
    1272                        if (a.getElementBooleanAbs(iter.index)) {
    1273                                for (int j = 0; j < rank; j++) {
    1274                                        indices.get(j).add(pos[j]);
    1275                                }
    1276                        }
    1277                }
    1278
    1279                for (int j = 0; j < rank; j++) {
    1280                        indexList.add((IntegerDataset) DatasetFactory.createFromList(IntegerDataset.class, indices.get(j)));
    1281                }
    1282                return indexList;
    1283        }
    1284
    1285        /**
    1286         * Check item-wise for whether any a's elements are Not-a-Numbers
    1287         * <p>
    1288         * For multi-element items, check is true if any elements in an item is Not-a-Number.
    1289         * @param a
    1290         * @return dataset where item is true if any of its elements are NaNs
    1291         */
    1292        public static BooleanDataset isNaN(Object a) {
    1293                return isNaN(a, null);
    1294        }
    1295
    1296        /**
    1297         * Check item-wise for whether any a's elements are Not-a-Numbers
    1298         * <p>
    1299         * For multi-element items, check is true if any elements in an item is Not-a-Number.
    1300         * @param a
    1301         * @param o output can be null - in which case, a new dataset is created
    1302         * @return dataset where item is true if any of its elements are NaNs
    1303         */
    1304        public static BooleanDataset isNaN(Object a, BooleanDataset o) {
    1305                final Dataset da = a instanceof Dataset ? (Dataset) a : DatasetFactory.createFromObject(a);
    1306
    1307                List<int[]> sl = BroadcastUtils.broadcastShapes(da.getShapeRef(), o == null ? null : o.getShapeRef());
    1308
    1309                final BooleanDataset r = o == null ? DatasetFactory.zeros(BooleanDataset.class, sl.get(0)) : o;
    1310
    1311                if (!da.hasFloatingPointElements()) {
    1312                        if (r == o) {
    1313                                r.fill(false);
    1314                        }
    1315                        return r;
    1316                }
    1317
    1318                final SingleInputBroadcastIterator it = new SingleInputBroadcastIterator(da, r);
    1319                it.setOutputDouble(true);
    1320                final int as = da.getElementsPerItem();
    1321
    1322                if (as == 1) {
    1323                        while (it.hasNext()) {
    1324                                r.setAbs(it.oIndex, Double.isNaN(it.aDouble));
    1325                        }
    1326                } else {
    1327                        if (da instanceof ComplexFloatDataset || da instanceof ComplexDoubleDataset) {
    1328                                while (it.hasNext()) {
    1329                                        r.setAbs(it.oIndex, Double.isNaN(it.aDouble) || Double.isNaN(da.getElementDoubleAbs(it.aIndex + 1)));
    1330                                }
    1331                        } else {
    1332                                while (it.hasNext()) {
    1333                                        boolean rb = false;
    1334                                        for (int j = 0; !rb && j < as; j++) {
    1335                                                rb &= Double.isNaN(da.getElementDoubleAbs(it.aIndex + j));
    1336                                        }
    1337                                        r.setAbs(it.oIndex, rb);
    1338                                }
    1339                        }
    1340                }
    1341                return r;
    1342        }
    1343
    1344        /**
    1345         * Check item-wise for whether any a's elements are infinite
    1346         * <p>
    1347         * For multi-element items, check is true if any elements in an item is infinite
    1348         * @param a
    1349         * @return dataset where item is true if any of its elements are infinite
    1350         */
    1351        public static BooleanDataset isInfinite(Object a) {
    1352                return isInfinite(a, null);
    1353        }
    1354
    1355        /**
    1356         * Check item-wise for whether any a's elements are infinite
    1357         * <p>
    1358         * For multi-element items, check is true if any elements in an item is infinite
    1359         * @param a
    1360         * @param o output can be null - in which case, a new dataset is created
    1361         * @return dataset where item is true if any of its elements are infinite
    1362         */
    1363        public static BooleanDataset isInfinite(Object a, BooleanDataset o) {
    1364                final Dataset da = a instanceof Dataset ? (Dataset) a : DatasetFactory.createFromObject(a);
    1365
    1366                List<int[]> sl = BroadcastUtils.broadcastShapes(da.getShapeRef(), o == null ? null : o.getShapeRef());
    1367
    1368                final BooleanDataset r = o == null ? DatasetFactory.zeros(BooleanDataset.class, sl.get(0)) : o;
    1369
    1370                if (!da.hasFloatingPointElements()) {
    1371                        if (r == o) {
    1372                                r.fill(false);
    1373                        }
    1374                        return r;
    1375                }
    1376
    1377                final SingleInputBroadcastIterator it = new SingleInputBroadcastIterator(da, r);
    1378                it.setOutputDouble(true);
    1379                final int as = da.getElementsPerItem();
    1380
    1381                if (as == 1) {
    1382                        while (it.hasNext()) {
    1383                                r.setAbs(it.oIndex, Double.isInfinite(it.aDouble));
    1384                        }
    1385                } else {
    1386                        if (da instanceof ComplexFloatDataset || da instanceof ComplexDoubleDataset) {
    1387                                while (it.hasNext()) {
    1388                                        r.setAbs(it.oIndex, Double.isInfinite(it.aDouble) || Double.isInfinite(da.getElementDoubleAbs(it.aIndex + 1)));
    1389                                }
    1390                        } else {
    1391                                while (it.hasNext()) {
    1392                                        boolean rb = false;
    1393                                        for (int j = 0; !rb && j < as; j++) {
    1394                                                rb &= Double.isInfinite(da.getElementDoubleAbs(it.aIndex + j));
    1395                                        }
    1396                                        r.setAbs(it.oIndex, rb);
    1397                                }
    1398                        }
    1399                }
    1400                return r;
    1401        }
    1402
    1403        /**
    1404         * Check item-wise for whether any a's elements are positive infinite
    1405         * <p>
    1406         * For multi-element items, the check is true if any elements in an item is positive infinite
    1407         * @param a
    1408         * @return dataset where items are true if any of its elements are positive infinite
    1409         */
    1410        public static BooleanDataset isPositiveInfinite(Object a) {
    1411                return isEqual(a, null, Double.POSITIVE_INFINITY);
    1412        }
    1413
    1414        /**
    1415         * Check item-wise for whether any a's elements are positive infinite
    1416         * <p>
    1417         * For multi-element items, the check is true if any elements in an item is positive infinite
    1418         * @param a
    1419         * @param o output can be null - in which case, a new dataset is created
    1420         * @return dataset where items are true if any of its elements are positive infinite
    1421         */
    1422        public static BooleanDataset isPositiveInfinite(Object a, BooleanDataset o) {
    1423                return isEqual(a, o, Double.POSITIVE_INFINITY);
    1424        }
    1425
    1426        /**
    1427         * Check item-wise for whether any a's elements are negative infinite
    1428         * <p>
    1429         * For multi-element items, the check is true if any elements in an item is negative infinite
    1430         * @param a
    1431         * @return dataset where items are true if any of its elements are negative infinite
    1432         */
    1433        public static BooleanDataset isNegativeInfinite(Object a) {
    1434                return isEqual(a, null, Double.NEGATIVE_INFINITY);
    1435        }
    1436
    1437        /**
    1438         * Check item-wise for whether any a's elements are negative infinite
    1439         * <p>
    1440         * For multi-element items, the check is true if any elements in an item is negative infinite
    1441         * @param a
    1442         * @param o output can be null - in which case, a new dataset is created
    1443         * @return dataset where items are true if any of its elements are negative infinite
    1444         */
    1445        public static BooleanDataset isNegativeInfinite(Object a, BooleanDataset o) {
    1446                return isEqual(a, o, Double.NEGATIVE_INFINITY);
    1447        }
    1448
    1449        /**
    1450         * Check item-wise for whether any a's elements match given item
    1451         * <p>
    1452         * For multi-element items, the check is true if any elements in an item matches
    1453         * @param a
    1454         * @param o output can be null - in which case, a new dataset is created
    1455         * @param match
    1456         * @return dataset where items are true if any of its elements match
    1457         */
    1458        private static BooleanDataset isEqual(Object a, BooleanDataset o, final double match) {
    1459                final Dataset da = a instanceof Dataset ? (Dataset) a : DatasetFactory.createFromObject(a);
    1460
    1461                List<int[]> sl = BroadcastUtils.broadcastShapes(da.getShapeRef(), o == null ? null : o.getShapeRef());
    1462
    1463                final BooleanDataset r = o == null ? DatasetFactory.zeros(BooleanDataset.class, sl.get(0)) : o;
    1464
    1465                if (!da.hasFloatingPointElements()) {
    1466                        if (r == o) {
    1467                                r.fill(false);
    1468                        }
    1469                        return r;
    1470                }
    1471
    1472                final SingleInputBroadcastIterator it = new SingleInputBroadcastIterator(da, r);
    1473                it.setOutputDouble(true);
    1474                final int as = da.getElementsPerItem();
    1475
    1476                if (as == 1) {
    1477                        while (it.hasNext()) {
    1478                                r.setAbs(it.oIndex, it.aDouble == match);
    1479                        }
    1480                } else {
    1481                        if (da instanceof ComplexFloatDataset || da instanceof ComplexDoubleDataset) {
    1482                                while (it.hasNext()) {
    1483                                        final double rv = it.aDouble;
    1484                                        final double iv = da.getElementDoubleAbs(it.aIndex + 1);
    1485                                        r.setAbs(it.oIndex, (rv == match) || (iv == match));
    1486                                }
    1487                        } else {
    1488                                while (it.hasNext()) {
    1489                                        boolean rb = false;
    1490                                        for (int j = 0; !rb && j < as; j++) {
    1491                                                rb &= da.getElementDoubleAbs(it.aIndex + j) == match;
    1492                                        }
    1493                                        r.setAbs(it.oIndex, rb);
    1494                                }
    1495                        }
    1496                }
    1497                return r;
    1498        }
    1499
    1500        /**
    1501         * Check item-wise for whether any a's elements are finite (or not infinite and not Not-a-Number)
    1502         * <p>
    1503         * For multi-element items, check is true if any elements in an item is finite
    1504         * @param a
    1505         * @return dataset where item is true if any of its elements are finite
    1506         */
    1507        public static BooleanDataset isFinite(Object a) {
    1508                return isFinite(a, null);
    1509        }
    1510
    1511        /**
    1512         * Check item-wise for whether any a's elements are finite (or not infinite and not Not-a-Number)
    1513         * <p>
    1514         * For multi-element items, check is true if any elements in an item is finite
    1515         * @param a
    1516         * @param o output can be null - in which case, a new dataset is created
    1517         * @return dataset where item is true if any of its elements are finite
    1518         */
    1519        public static BooleanDataset isFinite(Object a, BooleanDataset o) {
    1520                final Dataset da = a instanceof Dataset ? (Dataset) a : DatasetFactory.createFromObject(a);
    1521
    1522                List<int[]> sl = BroadcastUtils.broadcastShapes(da.getShapeRef(), o == null ? null : o.getShapeRef());
    1523
    1524                final BooleanDataset r = o == null ? DatasetFactory.zeros(BooleanDataset.class, sl.get(0)) : o;
    1525
    1526                if (!da.hasFloatingPointElements()) {
    1527                        r.fill(true);
    1528                        return r;
    1529                }
    1530
    1531                final SingleInputBroadcastIterator it = new SingleInputBroadcastIterator(da, r);
    1532                it.setOutputDouble(true);
    1533                final int as = da.getElementsPerItem();
    1534
    1535                if (as == 1) {
    1536                        while (it.hasNext()) {
    1537                                final double rv = it.aDouble;
    1538                                r.setAbs(it.oIndex, !(Double.isInfinite(rv) || Double.isNaN(rv)));
    1539                        }
    1540                } else {
    1541                        if (da instanceof ComplexFloatDataset || da instanceof ComplexDoubleDataset) {
    1542                                while (it.hasNext()) {
    1543                                        final double rv = it.aDouble;
    1544                                        final double iv = da.getElementDoubleAbs(it.aIndex + 1);
    1545                                        r.setAbs(it.oIndex, !(Double.isInfinite(rv) || Double.isNaN(rv) || Double.isInfinite(iv) || Double.isNaN(iv)));
    1546                                }
    1547                        } else {
    1548                                while (it.hasNext()) {
    1549                                        boolean rb = false;
    1550                                        for (int j = 0; !rb && j < as; j++) {
    1551                                                final double rv = it.aDouble;
    1552                                                rb &= !(Double.isInfinite(rv) || Double.isNaN(rv));
    1553                                        }
    1554                                        r.setAbs(it.oIndex, rb);
    1555                                }
    1556                        }
    1557                }
    1558                return r;
    1559        }
    1560
    1561        /**
    1562         * Enumeration of monotonicity. NaNs are ignored or considered not equal
    1563         */
    1564        public static enum Monotonicity {
    1565                /**
    1566                 * No order: {@code x_0 != x_1 != x_2 ...}
    1567                 */
    1568                NOT_ORDERED,
    1569                /**
    1570                 * All equal: {@code x_0 == x_1 == x_2 ...}
    1571                 */
    1572                ALL_EQUAL,
    1573                /**
    1574                 * Strictly decreasing {@code x_0 > x_1 > x_2 ...}
    1575                 */
    1576                STRICTLY_DECREASING,
    1577                /**
    1578                 * Non-increasing or weakly decreasing {@code x_0 >= x_1 >= x_2 ...}
    1579                 */
    1580                NONINCREASING,
    1581                /**
    1582                 * Non-decreasing or weakly increasing {@code x_0 <= x_1 <= x_2 ...}
    1583                 */
    1584                NONDECREASING,
    1585                /**
    1586                 * Strictly increasing {@code x_0 < x_1 < x_2 ...}
    1587                 */
    1588                STRICTLY_INCREASING,
    1589        }
    1590
    1591        /**
    1592         * @param a
    1593         * @return true if all elements are in a monotonic order
    1594         * @see #findMonotonicity(Object)
    1595         */
    1596        public static boolean isMonotonic(Object a) {
    1597                return findMonotonicity(a) != Monotonicity.NOT_ORDERED;
    1598        }
    1599
    1600        /**
    1601         * @param a
    1602         * @param monotonicity
    1603         * @return true if all elements are in given monotonic ordering
    1604         */
    1605        public static boolean isMonotonic(Object a, Monotonicity monotonicity) {
    1606                final Dataset da = a instanceof Dataset ? (Dataset) a : DatasetFactory.createFromObject(a);
    1607                if (da.getRank() > 1) {
    1608                        throw new IllegalArgumentException("Only 0 or 1D datasets are allowed");
    1609                }
    1610
    1611                if (da.getElementsPerItem() > 1) {
    1612                        throw new IllegalArgumentException("Cannot compare compound datsets");
    1613                }
    1614
    1615                final IndexIterator it = da.getIterator();
    1616                double previous = Double.NaN;
    1617                while (Double.isNaN(previous) && it.hasNext()) { // look for first non-NaN
    1618                        previous = da.getElementDoubleAbs(it.index);
    1619                }
    1620
    1621                Boolean increasing = null;
    1622                boolean equality = false;
    1623                while (it.hasNext()) { // look for first change
    1624                        double next = da.getElementDoubleAbs(it.index);
    1625                        if (!Double.isNaN(next)) {
    1626                                if (previous != next) {
    1627                                        increasing = previous < next;
    1628                                        previous = next;
    1629                                        break;
    1630                                } else if (!equality) {
    1631                                        equality = true;
    1632                                        if (monotonicity == Monotonicity.STRICTLY_DECREASING || monotonicity == Monotonicity.STRICTLY_DECREASING)
    1633                                                return false;
    1634                                }
    1635                        }
    1636                }
    1637
    1638                if (increasing == null) {
    1639                        if (equality)
    1640                                return monotonicity == Monotonicity.ALL_EQUAL || monotonicity == Monotonicity.NONDECREASING || monotonicity == Monotonicity.NONINCREASING;
    1641                        return Double.isNaN(previous) ? monotonicity == Monotonicity.NOT_ORDERED : true;
    1642                }
    1643
    1644                if (increasing) {
    1645                        if (monotonicity == Monotonicity.ALL_EQUAL || monotonicity == Monotonicity.NONINCREASING || monotonicity == Monotonicity.STRICTLY_DECREASING)
    1646                                return false;
    1647
    1648                        while (it.hasNext()) {
    1649                                double next = da.getElementDoubleAbs(it.index);
    1650                                if (!Double.isNaN(next)) {
    1651                                        if (previous > next) {
    1652                                                return monotonicity == Monotonicity.NOT_ORDERED;
    1653                                        } else if (previous < next) {
    1654                                                previous = next;
    1655                                        } else if (!equality) {
    1656                                                equality = true;
    1657                                                if (monotonicity == Monotonicity.STRICTLY_INCREASING)
    1658                                                        return false;
    1659                                        }
    1660                                }
    1661                        }
    1662
    1663                        return monotonicity != Monotonicity.NOT_ORDERED;
    1664                }
    1665
    1666                if (monotonicity == Monotonicity.ALL_EQUAL || monotonicity == Monotonicity.NONDECREASING || monotonicity == Monotonicity.STRICTLY_INCREASING)
    1667                        return false;
    1668
    1669                while (it.hasNext()) {
    1670                        double next = da.getElementDoubleAbs(it.index);
    1671                        if (!Double.isNaN(next)) {
    1672                                if (previous < next) {
    1673                                        return monotonicity == Monotonicity.NOT_ORDERED;
    1674                                } else if (previous > next) {
    1675                                        previous = next;
    1676                                } else if (!equality) {
    1677                                        equality = true;
    1678                                        if (monotonicity == Monotonicity.STRICTLY_DECREASING)
    1679                                                return false;
    1680                                }
    1681                        }
    1682                }
    1683
    1684                return monotonicity != Monotonicity.NOT_ORDERED;
    1685        }
    1686
    1687        /**
    1688         * @param a
    1689         * @return true if all elements are in a strictly monotonic order
    1690         * @see #findMonotonicity(Object)
    1691         */
    1692        public static boolean isStrictlyMonotonic(Object a) {
    1693                Monotonicity mono = findMonotonicity(a);
    1694                return mono == Monotonicity.STRICTLY_DECREASING || mono == Monotonicity.STRICTLY_INCREASING;
    1695        }
    1696
    1697        /**
    1698         * Find monotonicity. NaNs are ignored or considered not equal
    1699         * @param a
    1700         * @return monotonicity
    1701         */
    1702        public static Monotonicity findMonotonicity(Object a) {
    1703                final Dataset da = a instanceof Dataset ? (Dataset) a : DatasetFactory.createFromObject(a);
    1704                if (da.getRank() > 1) {
    1705                        throw new IllegalArgumentException("Only 0 or 1D datasets are allowed");
    1706                }
    1707
    1708                if (da.getElementsPerItem() > 1) {
    1709                        throw new IllegalArgumentException("Cannot compare compound datsets");
    1710                }
    1711
    1712                final IndexIterator it = da.getIterator();
    1713                double previous = Double.NaN;
    1714                while (Double.isNaN(previous) && it.hasNext()) { // look for first non-NaN
    1715                        previous = da.getElementDoubleAbs(it.index);
    1716                }
    1717
    1718                Boolean increasing = null;
    1719                boolean equality = false;
    1720                while (it.hasNext()) { // look for first change
    1721                        double next = da.getElementDoubleAbs(it.index);
    1722                        if (!Double.isNaN(next)) {
    1723                                if (previous != next) {
    1724                                        increasing = previous < next;
    1725                                        previous = next;
    1726                                        break;
    1727                                } else if (!equality) {
    1728                                        equality = true;
    1729                                }
    1730                        }
    1731                }
    1732
    1733                if (increasing == null) {
    1734                        return Double.isNaN(previous) ? Monotonicity.NOT_ORDERED : Monotonicity.ALL_EQUAL;
    1735                }
    1736
    1737                if (increasing) {
    1738                        while (it.hasNext()) {
    1739                                double next = da.getElementDoubleAbs(it.index);
    1740                                if (!Double.isNaN(next)) {
    1741                                        if (previous > next) {
    1742                                                return Monotonicity.NOT_ORDERED;
    1743                                        } else if (previous < next) {
    1744                                                previous = next;
    1745                                        } else if (!equality) {
    1746                                                equality = true;
    1747                                        }
    1748                                }
    1749                        }
    1750                        return equality ? Monotonicity.NONDECREASING : Monotonicity.STRICTLY_INCREASING;
    1751                }
    1752
    1753                while (it.hasNext()) {
    1754                        double next = da.getElementDoubleAbs(it.index);
    1755                        if (!Double.isNaN(next)) {
    1756                                if (previous < next) {
    1757                                        return Monotonicity.NOT_ORDERED;
    1758                                } else if (previous > next) {
    1759                                        previous = next;
    1760                                } else if (!equality) {
    1761                                        equality = true;
    1762                                }
    1763                        }
    1764                }
    1765                return equality ? Monotonicity.NONINCREASING : Monotonicity.STRICTLY_DECREASING;
    1766        }
    1767}