    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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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         * @param a
    875         * @return true if all elements are true
    876         */
    877        public static BooleanDataset allTrue(IDataset a, int axis) {
    878                axis = AbstractDataset.checkAxis(a.getRank(), axis);
    879
    880                int rank = a.getRank();
    881                int[] oshape = a.getShape();
    882                int alen = oshape[axis];
    883                oshape[axis] = 1;
    884
    885                int[] nshape = ShapeUtils.squeezeShape(oshape, false);
    886
    887                BooleanDataset result = DatasetFactory.zeros(BooleanDataset.class, nshape);
    888
    889                IndexIterator qiter = result.getIterator(true);
    890                int[] qpos = qiter.getPos();
    891                int[] spos = oshape;
    892
    893                while (qiter.hasNext()) {
    894                        int i = 0;
    895                        for (; i < axis; i++) {
    896                                spos[i] = qpos[i];
    897                        }
    898                        spos[i++] = 0;
    899                        for (; i < rank; i++) {
    900                                spos[i] = qpos[i-1];
    901                        }
    902
    903                        boolean br = true;
    904                        for (int j = 0; br && j < alen; j++) {
    905                                spos[axis] = j;
    906                                br &= a.getBoolean(spos);
    907                        }
    908                        result.set(br, qpos);
    909                }
    910                return result;
    911        }
    912
    913        /**
    914         * @param a
    915         * @return true if any element is true
    916         */
    917        public static boolean anyTrue(Object a) {
    918                final Dataset da = a instanceof Dataset ? (Dataset) a : DatasetFactory.createFromObject(a);
    919                final IndexIterator it = da.getIterator();
    920                final int as = da.getElementsPerItem();
    921
    922                if (as == 1) {
    923                        while (it.hasNext()) {
    924                                if (da.getElementBooleanAbs(it.index))
    925                                        return true;
    926                        }
    927                } else {
    928                        while (it.hasNext()) {
    929                                for (int j = 0; j < as; j++) {
    930                                        if (da.getElementBooleanAbs(it.index + j))
    931                                                return true;
    932                                }
    933                        }
    934                }
    935                return false;
    936        }
    937
    938        /**
    939         * @param a
    940         * @return true if any element is true
    941         */
    942        public static BooleanDataset anyTrue(IDataset a, int axis) {
    943                axis = AbstractDataset.checkAxis(a.getRank(), axis);
    944
    945                int rank = a.getRank();
    946                int[] oshape = a.getShape();
    947                int alen = oshape[axis];
    948                oshape[axis] = 1;
    949
    950                int[] nshape = ShapeUtils.squeezeShape(oshape, false);
    951
    952                BooleanDataset result = DatasetFactory.zeros(BooleanDataset.class, nshape);
    953
    954                IndexIterator qiter = result.getIterator(true);
    955                int[] qpos = qiter.getPos();
    956                int[] spos = oshape;
    957
    958                while (qiter.hasNext()) {
    959                        int i = 0;
    960                        for (; i < axis; i++) {
    961                                spos[i] = qpos[i];
    962                        }
    963                        spos[i++] = 0;
    964                        for (; i < rank; i++) {
    965                                spos[i] = qpos[i-1];
    966                        }
    967
    968                        boolean br = false;
    969                        for (int j = 0; !br && j < alen; j++) {
    970                                spos[axis] = j;
    971                                br |= a.getBoolean(spos);
    972                        }
    973                        result.set(br, qpos);
    974                }
    975                return result;
    976        }
    977
    978        /**
    979         * Negate item-wise
    980         * <p>
    981         * For multi-element items, negation is false if all elements in a pair of items
    982         * are true.
    983         * @param a
    984         * @return dataset where item is true when a is false
    985         */
    986        public static BooleanDataset logicalNot(Object a) {
    987                return logicalNot(a, null);
    988        }
    989
    990        /**
    991         * Negate item-wise
    992         * <p>
    993         * For multi-element items, negation is false if all elements in a pair of items
    994         * are true.
    995         * @param a
    996         * @param o output can be null - in which case, a new dataset is created
    997         * @return dataset where item is true when a is false
    998         */
    999        public static BooleanDataset logicalNot(Object a, BooleanDataset o) {
    1000                final Dataset da = a instanceof Dataset ? (Dataset) a : DatasetFactory.createFromObject(a);
    1001
    1002                List<int[]> sl = BroadcastUtils.broadcastShapes(da.getShapeRef(), o == null ? null : o.getShapeRef());
    1003
    1004                final BooleanDataset r = o == null ? DatasetFactory.zeros(BooleanDataset.class, sl.get(0)) : o;
    1005
    1006                final SingleInputBroadcastIterator it = new SingleInputBroadcastIterator(da, r);
    1007                final int as = da.getElementsPerItem();
    1008
    1009                if (as == 1) {
    1010                        while (it.hasNext()) {
    1011                                r.setAbs(it.oIndex, !da.getElementBooleanAbs(it.aIndex));
    1012                        }
    1013                } else {
    1014                        boolean br = true;
    1015                        while (it.hasNext()) {
    1016                                for (int j = 0; j < as; j++) {
    1017                                        br &= da.getElementBooleanAbs(it.aIndex + j);
    1018                                }
    1019                                r.setAbs(it.oIndex, !br);
    1020                        }
    1021                }
    1022                return r;
    1023        }
    1024
    1025        /**
    1026         * Compare item-wise for whether a's item is true and b's true too.
    1027         * <p>
    1028         * For multi-element items, comparison is true if all elements in a pair of items
    1029         * are true. Where the datasets have mismatched item sizes, the first element
    1030         * of the dataset with smaller items is used for comparison.
    1031         * @param a
    1032         * @param b
    1033         * @return dataset where item is true if a && b is true
    1034         */
    1035        public static BooleanDataset logicalAnd(Object a, Object b) {
    1036                return logicalAnd(a, b, null);
    1037        }
    1038
    1039        /**
    1040         * Compare item-wise for whether a's item is true and b's true too.
    1041         * <p>
    1042         * For multi-element items, comparison is true if all elements in a pair of items
    1043         * are true. Where the datasets have mismatched item sizes, the first element
    1044         * of the dataset with smaller items is used for comparison.
    1045         * @param a
    1046         * @param b
    1047         * @param o output can be null - in which case, a new dataset is created
    1048         * @return dataset where item is true if a && b is true
    1049         */
    1050        public static BooleanDataset logicalAnd(Object a, Object b, BooleanDataset o) {
    1051                final Dataset da = a instanceof Dataset ? (Dataset) a : DatasetFactory.createFromObject(a);
    1052                final Dataset db = b instanceof Dataset ? (Dataset) b : DatasetFactory.createFromObject(b);
    1053
    1054                List<int[]> sl = BroadcastUtils.broadcastShapes(da.getShapeRef(), db.getShapeRef(), o == null ? null : o.getShapeRef());
    1055
    1056                final BooleanDataset r = o == null ? DatasetFactory.zeros(BooleanDataset.class, sl.get(0)) : o;
    1057
    1058                final BroadcastIterator it = BroadcastIterator.createIterator(da, db, r);
    1059                it.setOutputDouble(true);
    1060                final int as = da.getElementsPerItem();
    1061                final int bs = db.getElementsPerItem();
    1062
    1063                if (as > bs) {
    1064                        while (it.hasNext()) {
    1065                                final boolean bb = db.getElementBooleanAbs(it.bIndex);
    1066                                boolean rb = true;
    1067                                for (int j = 0; rb && j < as; j++) {
    1068                                        rb &= da.getElementBooleanAbs(it.aIndex + j) && bb;
    1069                                }
    1070                                r.setAbs(it.oIndex, rb);
    1071                        }
    1072                } else if (as < bs) {
    1073                        while (it.hasNext()) {
    1074                                final boolean ab = da.getElementBooleanAbs(it.aIndex);
    1075                                boolean rb = true;
    1076                                for (int j = 0; rb && j < bs; j++) {
    1077                                        rb &= ab && db.getElementBooleanAbs(it.bIndex + j);
    1078                                }
    1079                                r.setAbs(it.oIndex, rb);
    1080                        }
    1081                } else {
    1082                        if (as == 1) {
    1083                                while (it.hasNext()) {
    1084                                        r.setAbs(it.oIndex, da.getElementBooleanAbs(it.aIndex) && db.getElementBooleanAbs(it.bIndex));
    1085                                }
    1086                        } else {
    1087                                while (it.hasNext()) {
    1088                                        boolean rb = true;
    1089                                        for (int j = 0; rb && j < bs; j++) {
    1090                                                rb &= da.getElementBooleanAbs(it.aIndex + j) && db.getElementBooleanAbs(it.bIndex + j);
    1091                                        }
    1092                                        r.setAbs(it.oIndex, rb);
    1093                                }
    1094                        }
    1095                }
    1096
    1097                return r;
    1098        }
    1099
    1100        /**
    1101         * Compare item-wise for whether a's item is true or b's true.
    1102         * <p>
    1103         * For multi-element items, comparison is true if any elements in a pair of items
    1104         * are true. Where the datasets have mismatched item sizes, the first element
    1105         * of the dataset with smaller items is used for comparison.
    1106         * @param a
    1107         * @param b
    1108         * @return dataset where item is true if a || b is true
    1109         */
    1110        public static BooleanDataset logicalOr(Object a, Object b) {
    1111                return logicalOr(a, b, null);
    1112        }
    1113
    1114        /**
    1115         * Compare item-wise for whether a's item is true or b's true.
    1116         * <p>
    1117         * For multi-element items, comparison is true if any elements in a pair of items
    1118         * are true. Where the datasets have mismatched item sizes, the first element
    1119         * of the dataset with smaller items is used for comparison.
    1120         * @param a
    1121         * @param b
    1122         * @param o output can be null - in which case, a new dataset is created
    1123         * @return dataset where item is true if a || b is true
    1124         */
    1125        public static BooleanDataset logicalOr(Object a, Object b, BooleanDataset o) {
    1126                final Dataset da = a instanceof Dataset ? (Dataset) a : DatasetFactory.createFromObject(a);
    1127                final Dataset db = b instanceof Dataset ? (Dataset) b : DatasetFactory.createFromObject(b);
    1128
    1129                List<int[]> sl = BroadcastUtils.broadcastShapes(da.getShapeRef(), db.getShapeRef(), o == null ? null : o.getShapeRef());
    1130
    1131                final BooleanDataset r = o == null ? DatasetFactory.zeros(BooleanDataset.class, sl.get(0)) : o;
    1132
    1133                final BroadcastIterator it = BroadcastIterator.createIterator(da, db, r);
    1134                it.setOutputDouble(true);
    1135                final int as = da.getElementsPerItem();
    1136                final int bs = db.getElementsPerItem();
    1137
    1138                if (as > bs) {
    1139                        while (it.hasNext()) {
    1140                                final boolean bb = db.getElementBooleanAbs(it.bIndex);
    1141                                boolean rb = true;
    1142                                for (int j = 0; j < as; j++) {
    1143                                        rb |= da.getElementBooleanAbs(it.aIndex + j) || bb;
    1144                                }
    1145                                r.setAbs(it.oIndex, rb);
    1146                        }
    1147                } else if (as < bs) {
    1148                        while (it.hasNext()) {
    1149                                final boolean ab = da.getElementBooleanAbs(it.aIndex);
    1150                                boolean rb = true;
    1151                                for (int j = 0; rb && j < bs; j++) {
    1152                                        rb |= ab || db.getElementBooleanAbs(it.bIndex + j);
    1153                                }
    1154                                r.setAbs(it.oIndex, rb);
    1155                        }
    1156                } else {
    1157                        if (as == 1) {
    1158                                while (it.hasNext()) {
    1159                                        r.setAbs(it.oIndex, da.getElementBooleanAbs(it.aIndex) || db.getElementBooleanAbs(it.bIndex));
    1160                                }
    1161                        } else {
    1162                                while (it.hasNext()) {
    1163                                        boolean rb = true;
    1164                                        for (int j = 0; rb && j < bs; j++) {
    1165                                                rb &= da.getElementBooleanAbs(it.aIndex + j) || db.getElementBooleanAbs(it.bIndex + j);
    1166                                        }
    1167                                        r.setAbs(it.oIndex, rb);
    1168                                }
    1169                        }
    1170                }
    1171
    1172                return r;
    1173        }
    1174
    1175        /**
    1176         * Compare item-wise for whether a's item is true or b's true exclusively.
    1177         * <p>
    1178         * For multi-element items, comparison is true if one element in a pair of items
    1179         * is true. Where the datasets have mismatched item sizes, the first element
    1180         * of the dataset with smaller items is used for comparison.
    1181         * @param a
    1182         * @param b
    1183         * @return dataset where item is true if a ^ b is true
    1184         */
    1185        public static BooleanDataset logicalXor(Object a, Object b) {
    1186                return logicalXor(a, b, null);
    1187        }
    1188
    1189        /**
    1190         * Compare item-wise for whether a's item is true or b's true exclusively.
    1191         * <p>
    1192         * For multi-element items, comparison is true if one element in a pair of items
    1193         * is true. Where the datasets have mismatched item sizes, the first element
    1194         * of the dataset with smaller items is used for comparison.
    1195         * @param a
    1196         * @param b
    1197         * @param o output can be null - in which case, a new dataset is created
    1198         * @return dataset where item is true if a ^ b is true
    1199         */
    1200        public static BooleanDataset logicalXor(Object a, Object b, BooleanDataset o) {
    1201                final Dataset da = a instanceof Dataset ? (Dataset) a : DatasetFactory.createFromObject(a);
    1202                final Dataset db = b instanceof Dataset ? (Dataset) b : DatasetFactory.createFromObject(b);
    1203
    1204                List<int[]> sl = BroadcastUtils.broadcastShapes(da.getShapeRef(), db.getShapeRef(), o == null ? null : o.getShapeRef());
    1205
    1206                final BooleanDataset r = o == null ? DatasetFactory.zeros(BooleanDataset.class, sl.get(0)) : o;
    1207
    1208                final BroadcastIterator it = BroadcastIterator.createIterator(da, db, r);
    1209                it.setOutputDouble(true);
    1210                final int as = da.getElementsPerItem();
    1211                final int bs = db.getElementsPerItem();
    1212
    1213                if (as > bs) {
    1214                        while (it.hasNext()) {
    1215                                boolean rb = db.getElementBooleanAbs(it.bIndex);
    1216                                for (int j = 0; j < as; j++) {
    1217                                        rb ^= da.getElementBooleanAbs(it.aIndex + j);
    1218                                }
    1219                                r.setAbs(it.oIndex, rb);
    1220                        }
    1221                } else if (as < bs) {
    1222                        while (it.hasNext()) {
    1223                                boolean rb = da.getElementBooleanAbs(it.aIndex);
    1224                                for (int j = 0; rb && j < bs; j++) {
    1225                                        rb ^= db.getElementBooleanAbs(it.bIndex + j);
    1226                                }
    1227                                r.setAbs(it.oIndex, rb);
    1228                        }
    1229                } else {
    1230                        if (as == 1) {
    1231                                while (it.hasNext()) {
    1232                                        r.setAbs(it.oIndex, da.getElementBooleanAbs(it.aIndex) ^ db.getElementBooleanAbs(it.bIndex));
    1233                                }
    1234                        } else {
    1235                                while (it.hasNext()) {
    1236                                        boolean rb = true;
    1237                                        for (int j = 0; rb && j < bs; j++) {
    1238                                                rb &= da.getElementBooleanAbs(it.aIndex + j) ^ db.getElementBooleanAbs(it.bIndex + j);
    1239                                        }
    1240                                        r.setAbs(it.oIndex, rb);
    1241                                }
    1242                        }
    1243                }
    1244
    1245                return r;
    1246        }
    1247
    1248        /**
    1249         * Create a list of indices of positions where items are non-zero
    1250         * @param a
    1251         * @return list of positions as integer datasets
    1252         */
    1253        @SuppressWarnings("deprecation")
    1254        public static List<IntegerDataset> nonZero(Dataset a) {
    1255                final int rank = a.getRank();
    1256                final List<List<Integer>> indices = new ArrayList<List<Integer>>();
    1257                List<IntegerDataset> indexList = new ArrayList<IntegerDataset>();
    1258
    1259                if (rank == 0)
    1260                        return indexList;
    1261
    1262                for (int j = 0; j < rank; j++) {
    1263                        indices.add(new ArrayList<Integer>());
    1264                }
    1265
    1266                final IndexIterator iter = a.getIterator(true);
    1267                final int[] pos = iter.getPos();
    1268                while (iter.hasNext()) {
    1269                        if (a.getElementBooleanAbs(iter.index)) {
    1270                                for (int j = 0; j < rank; j++) {
    1271                                        indices.get(j).add(pos[j]);
    1272                                }
    1273                        }
    1274                }
    1275
    1276                for (int j = 0; j < rank; j++) {
    1277                        indexList.add((IntegerDataset) DatasetFactory.createFromList(Dataset.INT32, indices.get(j)));
    1278                }
    1279                return indexList;
    1280        }
    1281
    1282        /**
    1283         * Check item-wise for whether any a's elements are Not-a-Numbers
    1284         * <p>
    1285         * For multi-element items, check is true if any elements in an item is Not-a-Number.
    1286         * @param a
    1287         * @return dataset where item is true if any of its elements are NaNs
    1288         */
    1289        public static BooleanDataset isNaN(Object a) {
    1290                return isNaN(a, null);
    1291        }
    1292
    1293        /**
    1294         * Check item-wise for whether any a's elements are Not-a-Numbers
    1295         * <p>
    1296         * For multi-element items, check is true if any elements in an item is Not-a-Number.
    1297         * @param a
    1298         * @param o output can be null - in which case, a new dataset is created
    1299         * @return dataset where item is true if any of its elements are NaNs
    1300         */
    1301        public static BooleanDataset isNaN(Object a, BooleanDataset o) {
    1302                final Dataset da = a instanceof Dataset ? (Dataset) a : DatasetFactory.createFromObject(a);
    1303
    1304                List<int[]> sl = BroadcastUtils.broadcastShapes(da.getShapeRef(), o == null ? null : o.getShapeRef());
    1305
    1306                final BooleanDataset r = o == null ? DatasetFactory.zeros(BooleanDataset.class, sl.get(0)) : o;
    1307
    1308                if (!da.hasFloatingPointElements()) {
    1309                        if (r == o) {
    1310                                r.fill(false);
    1311                        }
    1312                        return r;
    1313                }
    1314
    1315                final SingleInputBroadcastIterator it = new SingleInputBroadcastIterator(da, r);
    1316                it.setOutputDouble(true);
    1317                final int as = da.getElementsPerItem();
    1318
    1319                if (as == 1) {
    1320                        while (it.hasNext()) {
    1321                                r.setAbs(it.oIndex, Double.isNaN(it.aDouble));
    1322                        }
    1323                } else {
    1324                        if (da instanceof ComplexFloatDataset || da instanceof ComplexDoubleDataset) {
    1325                                while (it.hasNext()) {
    1326                                        r.setAbs(it.oIndex, Double.isNaN(it.aDouble) || Double.isNaN(da.getElementDoubleAbs(it.aIndex + 1)));
    1327                                }
    1328                        } else {
    1329                                while (it.hasNext()) {
    1330                                        boolean rb = false;
    1331                                        for (int j = 0; !rb && j < as; j++) {
    1332                                                rb &= Double.isNaN(da.getElementDoubleAbs(it.aIndex + j));
    1333                                        }
    1334                                        r.setAbs(it.oIndex, rb);
    1335                                }
    1336                        }
    1337                }
    1338                return r;
    1339        }
    1340
    1341        /**
    1342         * Check item-wise for whether any a's elements are infinite
    1343         * <p>
    1344         * For multi-element items, check is true if any elements in an item is infinite
    1345         * @param a
    1346         * @return dataset where item is true if any of its elements are infinite
    1347         */
    1348        public static BooleanDataset isInfinite(Object a) {
    1349                return isInfinite(a, null);
    1350        }
    1351
    1352        /**
    1353         * Check item-wise for whether any a's elements are infinite
    1354         * <p>
    1355         * For multi-element items, check is true if any elements in an item is infinite
    1356         * @param a
    1357         * @param o output can be null - in which case, a new dataset is created
    1358         * @return dataset where item is true if any of its elements are infinite
    1359         */
    1360        public static BooleanDataset isInfinite(Object a, BooleanDataset o) {
    1361                final Dataset da = a instanceof Dataset ? (Dataset) a : DatasetFactory.createFromObject(a);
    1362
    1363                List<int[]> sl = BroadcastUtils.broadcastShapes(da.getShapeRef(), o == null ? null : o.getShapeRef());
    1364
    1365                final BooleanDataset r = o == null ? DatasetFactory.zeros(BooleanDataset.class, sl.get(0)) : o;
    1366
    1367                if (!da.hasFloatingPointElements()) {
    1368                        if (r == o) {
    1369                                r.fill(false);
    1370                        }
    1371                        return r;
    1372                }
    1373
    1374                final SingleInputBroadcastIterator it = new SingleInputBroadcastIterator(da, r);
    1375                it.setOutputDouble(true);
    1376                final int as = da.getElementsPerItem();
    1377
    1378                if (as == 1) {
    1379                        while (it.hasNext()) {
    1380                                r.setAbs(it.oIndex, Double.isInfinite(it.aDouble));
    1381                        }
    1382                } else {
    1383                        if (da instanceof ComplexFloatDataset || da instanceof ComplexDoubleDataset) {
    1384                                while (it.hasNext()) {
    1385                                        r.setAbs(it.oIndex, Double.isInfinite(it.aDouble) || Double.isInfinite(da.getElementDoubleAbs(it.aIndex + 1)));
    1386                                }
    1387                        } else {
    1388                                while (it.hasNext()) {
    1389                                        boolean rb = false;
    1390                                        for (int j = 0; !rb && j < as; j++) {
    1391                                                rb &= Double.isInfinite(da.getElementDoubleAbs(it.aIndex + j));
    1392                                        }
    1393                                        r.setAbs(it.oIndex, rb);
    1394                                }
    1395                        }
    1396                }
    1397                return r;
    1398        }
    1399
    1400        /**
    1401         * Check item-wise for whether any a's elements are positive infinite
    1402         * <p>
    1403         * For multi-element items, the check is true if any elements in an item is positive infinite
    1404         * @param a
    1405         * @return dataset where items are true if any of its elements are positive infinite
    1406         */
    1407        public static BooleanDataset isPositiveInfinite(Object a) {
    1408                return isEqual(a, null, Double.POSITIVE_INFINITY);
    1409        }
    1410
    1411        /**
    1412         * Check item-wise for whether any a's elements are positive infinite
    1413         * <p>
    1414         * For multi-element items, the check is true if any elements in an item is positive infinite
    1415         * @param a
    1416         * @param o output can be null - in which case, a new dataset is created
    1417         * @return dataset where items are true if any of its elements are positive infinite
    1418         */
    1419        public static BooleanDataset isPositiveInfinite(Object a, BooleanDataset o) {
    1420                return isEqual(a, o, Double.POSITIVE_INFINITY);
    1421        }
    1422
    1423        /**
    1424         * Check item-wise for whether any a's elements are negative infinite
    1425         * <p>
    1426         * For multi-element items, the check is true if any elements in an item is negative infinite
    1427         * @param a
    1428         * @return dataset where items are true if any of its elements are negative infinite
    1429         */
    1430        public static BooleanDataset isNegativeInfinite(Object a) {
    1431                return isEqual(a, null, Double.NEGATIVE_INFINITY);
    1432        }
    1433
    1434        /**
    1435         * Check item-wise for whether any a's elements are negative infinite
    1436         * <p>
    1437         * For multi-element items, the check is true if any elements in an item is negative infinite
    1438         * @param a
    1439         * @param o output can be null - in which case, a new dataset is created
    1440         * @return dataset where items are true if any of its elements are negative infinite
    1441         */
    1442        public static BooleanDataset isNegativeInfinite(Object a, BooleanDataset o) {
    1443                return isEqual(a, o, Double.NEGATIVE_INFINITY);
    1444        }
    1445
    1446        /**
    1447         * Check item-wise for whether any a's elements match given item
    1448         * <p>
    1449         * For multi-element items, the check is true if any elements in an item matches
    1450         * @param a
    1451         * @param o output can be null - in which case, a new dataset is created
    1452         * @param match
    1453         * @return dataset where items are true if any of its elements match
    1454         */
    1455        private static BooleanDataset isEqual(Object a, BooleanDataset o, final double match) {
    1456                final Dataset da = a instanceof Dataset ? (Dataset) a : DatasetFactory.createFromObject(a);
    1457
    1458                List<int[]> sl = BroadcastUtils.broadcastShapes(da.getShapeRef(), o == null ? null : o.getShapeRef());
    1459
    1460                final BooleanDataset r = o == null ? DatasetFactory.zeros(BooleanDataset.class, sl.get(0)) : o;
    1461
    1462                if (!da.hasFloatingPointElements()) {
    1463                        if (r == o) {
    1464                                r.fill(false);
    1465                        }
    1466                        return r;
    1467                }
    1468
    1469                final SingleInputBroadcastIterator it = new SingleInputBroadcastIterator(da, r);
    1470                it.setOutputDouble(true);
    1471                final int as = da.getElementsPerItem();
    1472
    1473                if (as == 1) {
    1474                        while (it.hasNext()) {
    1475                                r.setAbs(it.oIndex, it.aDouble == match);
    1476                        }
    1477                } else {
    1478                        if (da instanceof ComplexFloatDataset || da instanceof ComplexDoubleDataset) {
    1479                                while (it.hasNext()) {
    1480                                        final double rv = it.aDouble;
    1481                                        final double iv = da.getElementDoubleAbs(it.aIndex + 1);
    1482                                        r.setAbs(it.oIndex, (rv == match) || (iv == match));
    1483                                }
    1484                        } else {
    1485                                while (it.hasNext()) {
    1486                                        boolean rb = false;
    1487                                        for (int j = 0; !rb && j < as; j++) {
    1488                                                rb &= da.getElementDoubleAbs(it.aIndex + j) == match;
    1489                                        }
    1490                                        r.setAbs(it.oIndex, rb);
    1491                                }
    1492                        }
    1493                }
    1494                return r;
    1495        }
    1496
    1497        /**
    1498         * Check item-wise for whether any a's elements are finite (or not infinite and not Not-a-Number)
    1499         * <p>
    1500         * For multi-element items, check is true if any elements in an item is finite
    1501         * @param a
    1502         * @return dataset where item is true if any of its elements are finite
    1503         */
    1504        public static BooleanDataset isFinite(Object a) {
    1505                return isFinite(a, null);
    1506        }
    1507
    1508        /**
    1509         * Check item-wise for whether any a's elements are finite (or not infinite and not Not-a-Number)
    1510         * <p>
    1511         * For multi-element items, check is true if any elements in an item is finite
    1512         * @param a
    1513         * @param o output can be null - in which case, a new dataset is created
    1514         * @return dataset where item is true if any of its elements are finite
    1515         */
    1516        public static BooleanDataset isFinite(Object a, BooleanDataset o) {
    1517                final Dataset da = a instanceof Dataset ? (Dataset) a : DatasetFactory.createFromObject(a);
    1518
    1519                List<int[]> sl = BroadcastUtils.broadcastShapes(da.getShapeRef(), o == null ? null : o.getShapeRef());
    1520
    1521                final BooleanDataset r = o == null ? DatasetFactory.zeros(BooleanDataset.class, sl.get(0)) : o;
    1522
    1523                if (!da.hasFloatingPointElements()) {
    1524                        r.fill(true);
    1525                        return r;
    1526                }
    1527
    1528                final SingleInputBroadcastIterator it = new SingleInputBroadcastIterator(da, r);
    1529                it.setOutputDouble(true);
    1530                final int as = da.getElementsPerItem();
    1531
    1532                if (as == 1) {
    1533                        while (it.hasNext()) {
    1534                                final double rv = it.aDouble;
    1535                                r.setAbs(it.oIndex, !(Double.isInfinite(rv) || Double.isNaN(rv)));
    1536                        }
    1537                } else {
    1538                        if (da instanceof ComplexFloatDataset || da instanceof ComplexDoubleDataset) {
    1539                                while (it.hasNext()) {
    1540                                        final double rv = it.aDouble;
    1541                                        final double iv = da.getElementDoubleAbs(it.aIndex + 1);
    1542                                        r.setAbs(it.oIndex, !(Double.isInfinite(rv) || Double.isNaN(rv) || Double.isInfinite(iv) || Double.isNaN(iv)));
    1543                                }
    1544                        } else {
    1545                                while (it.hasNext()) {
    1546                                        boolean rb = false;
    1547                                        for (int j = 0; !rb && j < as; j++) {
    1548                                                final double rv = it.aDouble;
    1549                                                rb &= !(Double.isInfinite(rv) || Double.isNaN(rv));
    1550                                        }
    1551                                        r.setAbs(it.oIndex, rb);
    1552                                }
    1553                        }
    1554                }
    1555                return r;
    1556        }
    1557
    1558        /**
    1559         * Enumeration of monotonicity. NaNs are ignored or considered not equal
    1560         */
    1561        public static enum Monotonicity {
    1562                /**
    1563                 * No order: x_0 != x_1 != x_2 ...
    1564                 */
    1565                NOT_ORDERED,
    1566                /**
    1567                 * All equal: x_0 == x_1 == x_2 ...
    1568                 */
    1569                ALL_EQUAL,
    1570                /**
    1571                 * Strictly decreasing x_0 > x_1 > x_2 ...
    1572                 */
    1573                STRICTLY_DECREASING,
    1574                /**
    1575                 * Non-increasing or weakly decreasing x_0 >= x_1 >= x_2 ...
    1576                 */
    1577                NONINCREASING,
    1578                /**
    1579                 * Non-decreasing or weakly increasing x_0 <= x_1 <= x_2 ...
    1580                 */
    1581                NONDECREASING,
    1582                /**
    1583                 * Strictly increasing x_0 < x_1 < x_2 ...
    1584                 */
    1585                STRICTLY_INCREASING,
    1586        }
    1587
    1588        /**
    1589         * @param a
    1590         * @return true if all elements are in a monotonic order
    1591         * @see #findMonotonicity(Object)
    1592         */
    1593        public static boolean isMonotonic(Object a) {
    1594                return findMonotonicity(a) != Monotonicity.NOT_ORDERED;
    1595        }
    1596
    1597        /**
    1598         * @param a
    1599         * @param monotonicity
    1600         * @return true if all elements are in given monotonic ordering
    1601         */
    1602        public static boolean isMonotonic(Object a, Monotonicity monotonicity) {
    1603                final Dataset da = a instanceof Dataset ? (Dataset) a : DatasetFactory.createFromObject(a);
    1604                if (da.getRank() > 1) {
    1605                        throw new IllegalArgumentException("Only 0 or 1D datasets are allowed");
    1606                }
    1607
    1608                if (da.getElementsPerItem() > 1) {
    1609                        throw new IllegalArgumentException("Cannot compare compound datsets");
    1610                }
    1611
    1612                final IndexIterator it = da.getIterator();
    1613                double previous = Double.NaN;
    1614                while (Double.isNaN(previous) && it.hasNext()) { // look for first non-NaN
    1615                        previous = da.getElementDoubleAbs(it.index);
    1616                }
    1617
    1618                Boolean increasing = null;
    1619                boolean equality = false;
    1620                while (it.hasNext()) { // look for first change
    1621                        double next = da.getElementDoubleAbs(it.index);
    1622                        if (!Double.isNaN(next)) {
    1623                                if (previous != next) {
    1624                                        increasing = previous < next;
    1625                                        previous = next;
    1626                                        break;
    1627                                } else if (!equality) {
    1628                                        equality = true;
    1629                                        if (monotonicity == Monotonicity.STRICTLY_DECREASING || monotonicity == Monotonicity.STRICTLY_DECREASING)
    1630                                                return false;
    1631                                }
    1632                        }
    1633                }
    1634
    1635                if (increasing == null) {
    1636                        if (equality)
    1637                                return monotonicity == Monotonicity.ALL_EQUAL || monotonicity == Monotonicity.NONDECREASING || monotonicity == Monotonicity.NONINCREASING;
    1638                        return Double.isNaN(previous) ? monotonicity == Monotonicity.NOT_ORDERED : true;
    1639                }
    1640
    1641                if (increasing) {
    1642                        if (monotonicity == Monotonicity.ALL_EQUAL || monotonicity == Monotonicity.NONINCREASING || monotonicity == Monotonicity.STRICTLY_DECREASING)
    1643                                return false;
    1644
    1645                        while (it.hasNext()) {
    1646                                double next = da.getElementDoubleAbs(it.index);
    1647                                if (!Double.isNaN(next)) {
    1648                                        if (previous > next) {
    1649                                                return monotonicity == Monotonicity.NOT_ORDERED;
    1650                                        } else if (previous < next) {
    1651                                                previous = next;
    1652                                        } else if (!equality) {
    1653                                                equality = true;
    1654                                                if (monotonicity == Monotonicity.STRICTLY_INCREASING)
    1655                                                        return false;
    1656                                        }
    1657                                }
    1658                        }
    1659
    1660                        return monotonicity != Monotonicity.NOT_ORDERED;
    1661                }
    1662
    1663                if (monotonicity == Monotonicity.ALL_EQUAL || monotonicity == Monotonicity.NONDECREASING || monotonicity == Monotonicity.STRICTLY_INCREASING)
    1664                        return false;
    1665
    1666                while (it.hasNext()) {
    1667                        double next = da.getElementDoubleAbs(it.index);
    1668                        if (!Double.isNaN(next)) {
    1669                                if (previous < next) {
    1670                                        return monotonicity == Monotonicity.NOT_ORDERED;
    1671                                } else if (previous > next) {
    1672                                        previous = next;
    1673                                } else if (!equality) {
    1674                                        equality = true;
    1675                                        if (monotonicity == Monotonicity.STRICTLY_DECREASING)
    1676                                                return false;
    1677                                }
    1678                        }
    1679                }
    1680
    1681                return monotonicity != Monotonicity.NOT_ORDERED;
    1682        }
    1683
    1684        /**
    1685         * @param a
    1686         * @return true if all elements are in a strictly monotonic order
    1687         * @see #findMonotonicity(Object)
    1688         */
    1689        public static boolean isStrictlyMonotonic(Object a) {
    1690                Monotonicity mono = findMonotonicity(a);
    1691                return mono == Monotonicity.STRICTLY_DECREASING || mono == Monotonicity.STRICTLY_INCREASING;
    1692        }
    1693
    1694        /**
    1695         * Find monotonicity. NaNs are ignored or considered not equal
    1696         * @param a
    1697         * @return monotonicity
    1698         */
    1699        public static Monotonicity findMonotonicity(Object a) {
    1700                final Dataset da = a instanceof Dataset ? (Dataset) a : DatasetFactory.createFromObject(a);
    1701                if (da.getRank() > 1) {
    1702                        throw new IllegalArgumentException("Only 0 or 1D datasets are allowed");
    1703                }
    1704
    1705                if (da.getElementsPerItem() > 1) {
    1706                        throw new IllegalArgumentException("Cannot compare compound datsets");
    1707                }
    1708
    1709                final IndexIterator it = da.getIterator();
    1710                double previous = Double.NaN;
    1711                while (Double.isNaN(previous) && it.hasNext()) { // look for first non-NaN
    1712                        previous = da.getElementDoubleAbs(it.index);
    1713                }
    1714
    1715                Boolean increasing = null;
    1716                boolean equality = false;
    1717                while (it.hasNext()) { // look for first change
    1718                        double next = da.getElementDoubleAbs(it.index);
    1719                        if (!Double.isNaN(next)) {
    1720                                if (previous != next) {
    1721                                        increasing = previous < next;
    1722                                        previous = next;
    1723                                        break;
    1724                                } else if (!equality) {
    1725                                        equality = true;
    1726                                }
    1727                        }
    1728                }
    1729
    1730                if (increasing == null) {
    1731                        return Double.isNaN(previous) ? Monotonicity.NOT_ORDERED : Monotonicity.ALL_EQUAL;
    1732                }
    1733
    1734                if (increasing) {
    1735                        while (it.hasNext()) {
    1736                                double next = da.getElementDoubleAbs(it.index);
    1737                                if (!Double.isNaN(next)) {
    1738                                        if (previous > next) {
    1739                                                return Monotonicity.NOT_ORDERED;
    1740                                        } else if (previous < next) {
    1741                                                previous = next;
    1742                                        } else if (!equality) {
    1743                                                equality = true;
    1744                                        }
    1745                                }
    1746                        }
    1747                        return equality ? Monotonicity.NONDECREASING : Monotonicity.STRICTLY_INCREASING;
    1748                }
    1749
    1750                while (it.hasNext()) {
    1751                        double next = da.getElementDoubleAbs(it.index);
    1752                        if (!Double.isNaN(next)) {
    1753                                if (previous < next) {
    1754                                        return Monotonicity.NOT_ORDERED;
    1755                                } else if (previous > next) {
    1756                                        previous = next;
    1757                                } else if (!equality) {
    1758                                        equality = true;
    1759                                }
    1760                        }
    1761                }
    1762                return equality ? Monotonicity.NONINCREASING : Monotonicity.STRICTLY_DECREASING;
    1763        }
    1764}