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