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