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