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