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.Arrays;
016
017import org.slf4j.Logger;
018import org.slf4j.LoggerFactory;
019
020/**
021 * Class to hold colour datasets as red, green, blue tuples of short integers
022 */
023public class RGBDataset extends CompoundShortDataset implements Cloneable {
024        // pin UID to base class
025        private static final long serialVersionUID = Dataset.serialVersionUID;
026
027        private static final Logger logger = LoggerFactory.getLogger(RGBDataset.class);
028
029        private static final int ISIZE = 3; // number of elements per item
030
031        /**
032         * Create a null dataset
033         */
034        public RGBDataset() {
035                super(ISIZE);
036        }
037
038        /**
039         * @param shape output shape
040         */
041        public RGBDataset(final int... shape) {
042                super(ISIZE, shape);
043        }
044
045        /**
046         * @param data interleaved RGB values
047         * @param shape output shape
048         */
049        public RGBDataset(final short[] data, final int... shape) {
050                super(ISIZE, data, shape);
051        }
052
053        /**
054         * Copy a dataset
055         * @param dataset to clone
056         */
057        public RGBDataset(final RGBDataset dataset) {
058                super(dataset);
059        }
060
061        @Override
062        public RGBDataset clone() {
063                return new RGBDataset(this);
064        }
065
066        /**
067         * Create a dataset using given data (red, green and blue parts are given separately)
068         * @param redData data for red
069         * @param greenData data for green
070         * @param blueData data for blue
071         * @param shape (can be null to create 1D dataset)
072         */
073        public RGBDataset(final int[] redData, final int[] greenData, final int[] blueData, int... shape) {
074                int dsize = redData.length > greenData.length ? greenData.length : redData.length;
075                dsize = dsize > blueData.length ? blueData.length : dsize;
076                if (shape == null || shape.length == 0) {
077                        shape = new int[] {dsize};
078                }
079                isize = ISIZE;
080                size = ShapeUtils.calcSize(shape);
081                if (size != dsize) {
082                        logger.error("Shape is not compatible with size of data array");
083                        throw new IllegalArgumentException("Shape is not compatible with size of data array");
084                }
085                this.shape = shape.clone();
086
087                try {
088                        odata = data = createArray(size);
089                } catch (Throwable t) {
090                        logger.error("Could not create a dataset of shape {}", Arrays.toString(shape), t);
091                        throw new IllegalArgumentException(t);
092                }
093
094                for (int i = 0, n = 0; i < size; i++) {
095                        data[n++] = (short) redData[i];
096                        data[n++] = (short) greenData[i];
097                        data[n++] = (short) blueData[i];
098                }
099        }
100
101        /**
102         * Create a dataset using given data (red, green and blue parts are given separately)
103         * @param redData data for red
104         * @param greenData data for green
105         * @param blueData data for blue
106         * @param shape (can be null to create 1D dataset)
107         */
108        public RGBDataset(final short[] redData, final short[] greenData, final short[] blueData, int... shape) {
109                int dsize = redData.length > greenData.length ? greenData.length : redData.length;
110                dsize = dsize > blueData.length ? blueData.length : dsize;
111                if (shape == null || shape.length == 0) {
112                        shape = new int[] {dsize};
113                }
114                isize = ISIZE;
115                size = ShapeUtils.calcSize(shape);
116                if (size != dsize) {
117                        logger.error("Shape is not compatible with size of data array");
118                        throw new IllegalArgumentException("Shape is not compatible with size of data array");
119                }
120                this.shape = shape.clone();
121
122                try {
123                        odata = data = createArray(size);
124                } catch (Throwable t) {
125                        logger.error("Could not create a dataset of shape {}", Arrays.toString(shape), t);
126                        throw new IllegalArgumentException(t);
127                }
128
129                for (int i = 0, n = 0; i < size; i++) {
130                        data[n++] = redData[i];
131                        data[n++] = greenData[i];
132                        data[n++] = blueData[i];
133                }
134        }
135
136        /**
137         * Create a dataset using given data (red, green and blue parts are given separately)
138         * @param redData data for red
139         * @param greenData data for green
140         * @param blueData data for blue
141         * @param shape (can be null to create 1D dataset)
142         */
143        public RGBDataset(final byte[] redData, final byte[] greenData, final byte[] blueData, int... shape) {
144                int dsize = redData.length > greenData.length ? greenData.length : redData.length;
145                dsize = dsize > blueData.length ? blueData.length : dsize;
146                if (shape == null || shape.length == 0) {
147                        shape = new int[] {dsize};
148                }
149                isize = ISIZE;
150                size = ShapeUtils.calcSize(shape);
151                if (size != dsize) {
152                        logger.error("Shape is not compatible with size of data array");
153                        throw new IllegalArgumentException("Shape is not compatible with size of data array");
154                }
155                this.shape = shape.clone();
156
157                try {
158                        odata = data = createArray(size);
159                } catch (Throwable t) {
160                        logger.error("Could not create a dataset of shape {}", Arrays.toString(shape), t);
161                        throw new IllegalArgumentException(t);
162                }
163
164                for (int i = 0, n = 0; i < size; i++) {
165                        data[n++] = (short) (0xff & redData[i]);
166                        data[n++] = (short) (0xff & greenData[i]);
167                        data[n++] = (short) (0xff & blueData[i]);
168                }
169        }
170
171        /**
172         * Create a dataset using given colour data (colour components are given separately)
173         * @param red dataset
174         * @param green dataset
175         * @param blue dataset
176         */
177        public RGBDataset(final Dataset red, final Dataset green, final Dataset blue) {
178                super(ISIZE, red.getShapeRef());
179                red.checkCompatibility(green);
180                red.checkCompatibility(blue);
181
182                if (red.max().doubleValue() > Short.MAX_VALUE || red.min().doubleValue() < Short.MIN_VALUE ||
183                                green.max().doubleValue() > Short.MAX_VALUE || green.min().doubleValue() < Short.MIN_VALUE || 
184                                blue.max().doubleValue() > Short.MAX_VALUE || blue.min().doubleValue() < Short.MIN_VALUE) {
185                        logger.warn("Some values are out of range and will be truncated");
186                }
187
188                IndexIterator riter = red.getIterator();
189                IndexIterator giter = green.getIterator();
190                IndexIterator biter = blue.getIterator();
191
192                for (int i = 0; riter.hasNext() && giter.hasNext() && biter.hasNext();) {
193                        data[i++] = (short) red.getElementLongAbs(riter.index);
194                        data[i++] = (short) green.getElementLongAbs(giter.index);
195                        data[i++] = (short) blue.getElementLongAbs(biter.index);
196                }
197        }
198
199        /**
200         * Create a dataset using given grey data
201         * @param grey dataset
202         */
203        public RGBDataset(final Dataset grey) {
204                super(ISIZE, grey.getShapeRef());
205
206                IndexIterator giter = grey.getIterator();
207
208                for (int i = 0; giter.hasNext();) {
209                        final short g = (short) grey.getElementLongAbs(giter.index); 
210                        data[i++] = g;
211                        data[i++] = g;
212                        data[i++] = g;
213                }
214        }
215
216        /**
217         * Create a dataset using given compound data
218         * @param colour dataset with colour data
219         * @since 2.3
220         */
221        public RGBDataset(final CompoundDataset colour) {
222                super(ISIZE, colour.getShapeRef());
223
224                if (colour.getElementsPerItem() != 3) {
225                        throw new IllegalArgumentException("Compound dataset must have three elements per item");
226                }
227
228                final IndexIterator it = colour.getIterator();
229                for (int i = 0; it.hasNext();) {
230                        data[i++] = (short) colour.getElementLongAbs(it.index);
231                        data[i++] = (short) colour.getElementLongAbs(it.index + 1);
232                        data[i++] = (short) colour.getElementLongAbs(it.index + 2);
233                }
234        }
235        /**
236         * Create a RGB dataset from an object which could be a Java list, array (of arrays...) or Number. Ragged
237         * sequences or arrays are padded with zeros. The item size is the last dimension of the corresponding
238         * elemental dataset
239         *
240         * @param obj object
241         * @return dataset with contents given by input
242         */
243        public static RGBDataset createFromObject(final Object obj) {
244                CompoundShortDataset result = (CompoundShortDataset) DatasetUtils.createCompoundDataset(ShortDataset.createFromObject(obj), ISIZE);
245                return new RGBDataset(result.data, result.shape);
246        }
247
248        /**
249         * Create a RGB dataset from a compound dataset (no normalisation performed)
250         * @param a dataset
251         * @return RGB dataset (grey if input dataset has less than 3 elements per item)
252         */
253        public static RGBDataset createFromCompoundDataset(final CompoundDataset a) {
254                if (a instanceof RGBDataset)
255                        return (RGBDataset) a;
256                final int is = a.getElementsPerItem();
257                if (is < 3) {
258                        return new RGBDataset((Dataset) a);
259                }
260
261                if (a instanceof CompoundShortDataset && is == 3) {
262                        return new RGBDataset((short[]) a.getBuffer(), a.getShapeRef());
263                }
264
265                final RGBDataset rgb = new RGBDataset(a.getShapeRef());
266                final IndexIterator it = a.getIterator();
267
268                int n = 0;
269                while (it.hasNext()) {
270                        rgb.data[n++] = (short) a.getElementLongAbs(it.index);
271                        rgb.data[n++] = (short) a.getElementLongAbs(it.index + 1);
272                        rgb.data[n++] = (short) a.getElementLongAbs(it.index + 2);
273                }
274
275                return rgb;
276        }
277
278        /**
279         * Create a RGB dataset from hue, saturation and value dataset
280         * @param hue (in degrees from -360 to 360)
281         * @param saturation (from 0 to 1), can be null to denote 1
282         * @param value (from 0 to 1)
283         * @return RGB dataset
284         */
285        public static RGBDataset createFromHSV(final Dataset hue, final Dataset saturation, final Dataset value) {
286                if ((saturation != null && !hue.isCompatibleWith(saturation)) || !hue.isCompatibleWith(value)) {
287                        throw new IllegalArgumentException("Hue, saturation and value datasets must have the same shape");
288                }
289
290                RGBDataset result = new RGBDataset(hue.getShapeRef());
291                IndexIterator it = result.getIterator(true);
292                int[] pos = it.getPos();
293                short[] rgb = new short[3];
294
295                if (saturation == null) {
296                        while (it.hasNext()) {
297                                convertHSVToRGB(hue.getDouble(pos), 1, value.getDouble(pos), rgb);
298                                result.setAbs(it.index, rgb);
299                        }
300                } else {
301                        while (it.hasNext()) {
302                                convertHSVToRGB(hue.getDouble(pos), saturation.getDouble(pos), value.getDouble(pos), rgb);
303                                result.setAbs(it.index, rgb);
304                        }
305                }
306
307                return result;
308        }
309
310        /**
311         * Create a RGB dataset from hue, saturation and lightness dataset
312         * @param hue (in degrees from -360 to 360)
313         * @param saturation (from 0 to 1), can be null to denote 1
314         * @param lightness (from 0 to 1)
315         * @return RGB dataset
316         */
317        public static RGBDataset createFromHSL(final Dataset hue, final Dataset saturation, final Dataset lightness) {
318                if ((saturation != null && !hue.isCompatibleWith(saturation)) || !hue.isCompatibleWith(lightness)) {
319                        throw new IllegalArgumentException("Hue, saturation and lightness datasets must have the same shape");
320                }
321
322                RGBDataset result = new RGBDataset(hue.getShapeRef());
323                IndexIterator it = result.getIterator(true);
324                int[] pos = it.getPos();
325                short[] rgb = new short[3];
326
327                if (saturation == null) {
328                        while (it.hasNext()) {
329                                convertHSLToRGB(hue.getDouble(pos), 1, lightness.getDouble(pos), rgb);
330                                result.setAbs(it.index, rgb);
331                        }
332                } else {
333                        while (it.hasNext()) {
334                                convertHSLToRGB(hue.getDouble(pos), saturation.getDouble(pos), lightness.getDouble(pos), rgb);
335                                result.setAbs(it.index, rgb);
336                        }
337                }
338
339                return result;
340        }
341
342        private static void convertHSVToRGB(double h, double s, double v, short[] rgb) {
343                double m = 255 * v;
344                double chroma = s * m;
345                m -= chroma;
346                double hprime = h / 60.;
347                if (hprime < 0) {
348                        hprime += 6;
349                }
350                short sx = (short) (chroma * (1 - Math.abs((hprime % 2) - 1)) + m);
351                short sc = (short) (chroma + m);
352                short sm = (short) m;
353                
354                if (hprime < 1) {
355                        rgb[0] = sc;
356                        rgb[1] = sx;
357                        rgb[2] = sm;
358                } else if (hprime < 2) {
359                        rgb[0] = sx;
360                        rgb[1] = sc;
361                        rgb[2] = sm;
362                } else if (hprime < 3) {
363                        rgb[0] = sm;
364                        rgb[1] = sc;
365                        rgb[2] = sx;
366                } else if (hprime < 4) {
367                        rgb[0] = sm;
368                        rgb[1] = sx;
369                        rgb[2] = sc;
370                } else if (hprime < 5) {
371                        rgb[0] = sx;
372                        rgb[1] = sm;
373                        rgb[2] = sc;
374                } else if (hprime < 6) {
375                        rgb[0] = sc;
376                        rgb[1] = sm;
377                        rgb[2] = sx;
378                } else { // if hue is outside domain
379                        rgb[0] = sm;
380                        rgb[1] = sm;
381                        rgb[2] = sm;
382                }
383        }
384
385        private static void convertHSLToRGB(double h, double s, double l, short[] rgb) {
386                double m = l;
387                double chroma = s * (1 - Math.abs(2 * m - 1));
388                m -= chroma * 0.5;
389                m *= 255;
390                chroma *= 255;
391                double hprime = h / 60.;
392                if (hprime < 0) {
393                        hprime += 6;
394                }
395                short sx = (short) (chroma * (1 - Math.abs((hprime % 2) - 1)) + m);
396                short sc = (short) (chroma + m);
397                short sm = (short) m;
398                
399                if (hprime < 1) {
400                        rgb[0] = sc;
401                        rgb[1] = sx;
402                        rgb[2] = sm;
403                } else if (hprime < 2) {
404                        rgb[0] = sx;
405                        rgb[1] = sc;
406                        rgb[2] = sm;
407                } else if (hprime < 3) {
408                        rgb[0] = sm;
409                        rgb[1] = sc;
410                        rgb[2] = sx;
411                } else if (hprime < 4) {
412                        rgb[0] = sm;
413                        rgb[1] = sx;
414                        rgb[2] = sc;
415                } else if (hprime < 5) {
416                        rgb[0] = sx;
417                        rgb[1] = sm;
418                        rgb[2] = sc;
419                } else if (hprime < 6) {
420                        rgb[0] = sc;
421                        rgb[1] = sm;
422                        rgb[2] = sx;
423                } else { // if hue is outside domain
424                        rgb[0] = sm;
425                        rgb[1] = sm;
426                        rgb[2] = sm;
427                }
428        }
429
430        @Override
431        public RGBDataset getSlice(SliceIterator siter) {
432                CompoundShortDataset base = super.getSlice(siter);
433
434                RGBDataset slice = new RGBDataset();
435                copyToView(base, slice, false, false);
436                slice.setData();
437                return slice;
438        }
439
440        @Override
441        public RGBDataset getView(boolean deepCopyMetadata) {
442                RGBDataset view = new RGBDataset();
443                copyToView(this, view, true, deepCopyMetadata);
444                view.setData();
445                return view;
446        }
447
448        /**
449         * @return red value in the first position
450         * @since 2.0
451         */
452        public short getRed() {
453                return data[getFirst1DIndex()];
454        }
455
456        /**
457         * @param i position in first dimension
458         * @return red value in given position
459         */
460        public short getRed(final int i) {
461                return data[get1DIndex(i)];
462        }
463
464        /**
465         * @param i position in first dimension
466         * @param j position in second dimension
467         * @return red value in given position
468         */
469        public short getRed(final int i, final int j) {
470                return data[get1DIndex(i, j)];
471        }
472
473        /**
474         * @param pos position
475         * @return red value in given position
476         */
477        public short getRed(final int... pos) {
478                return data[get1DIndex(pos)];
479        }
480
481        /**
482         * @return green value in the first position
483         * @since 2.0
484         */
485        public short getGreen() {
486                return data[getFirst1DIndex() + 1];
487        }
488
489        /**
490         * @param i position in first dimension
491         * @return green value in given position
492         */
493        public short getGreen(final int i) {
494                return data[get1DIndex(i) + 1];
495        }
496
497        /**
498         * @param i position in first dimension
499         * @param j position in second dimension
500         * @return green value in given position
501         */
502        public short getGreen(final int i, final int j) {
503                return data[get1DIndex(i, j) + 1];
504        }
505
506        /**
507         * @param pos position
508         * @return green value in given position
509         */
510        public short getGreen(final int... pos) {
511                return data[get1DIndex(pos) + 1];
512        }
513
514        /**
515         * @return blue value in the first position
516         * @since 2.0
517         */
518        public short getBlue() {
519                return data[getFirst1DIndex() + 2];
520        }
521
522        /**
523         * @param i position in first dimension
524         * @return blue value in given position
525         */
526        public short getBlue(final int i) {
527                return data[get1DIndex(i) + 2];
528        }
529
530        /**
531         * @param i position in first dimension
532         * @param j position in second dimension
533         * @return blue value in given position
534         */
535        public short getBlue(final int i, final int j) {
536                return data[get1DIndex(i, j) + 2];
537        }
538
539        /**
540         * @param pos position
541         * @return blue value in given position
542         */
543        public short getBlue(final int... pos) {
544                return data[get1DIndex(pos) + 2];
545        }
546
547        /**
548         * Get a red value from given absolute index as a short - note this index does not
549         * take in account the item size so be careful when using with multi-element items
550         * 
551         * @param n absolute index
552         * @return red value
553         */
554        public short getRedAbs(int n) {
555                return data[n*isize];
556        }
557
558        /**
559         * Get a green value from given absolute index as a short - note this index does not
560         * take in account the item size so be careful when using with multi-element items
561         * 
562         * @param n absolute index
563         * @return green value
564         */
565        public short getGreenAbs(int n) {
566                return data[n*isize + 1];
567        }
568
569        /**
570         * Get a blue value from given absolute index as a short - note this index does not
571         * take in account the item size so be careful when using with multi-element items
572         * 
573         * @param n absolute index
574         * @return blue value
575         */
576        public short getBlueAbs(int n) {
577                return data[n*isize + 2];
578        }
579
580
581        // weights from NTSC formula aka ITU-R BT.601 for mapping RGB to luma
582        private static final double Wr = 0.299, Wg = 0.587, Wb = 0.114;
583
584        /**
585         * Convert colour dataset to a grey-scale one using the NTSC formula, aka ITU-R BT.601, for RGB to luma mapping
586         * @param <T> dataset sub-interface
587         * @param clazz dataset sub-interface
588         * @return a grey-scale dataset of given type
589         * @since 2.2
590         */
591        public <T extends Dataset> T createGreyDataset(final Class<T> clazz) {
592                return createGreyDataset(clazz, Wr, Wg, Wb);
593        }
594
595        /**
596         * Convert colour dataset to a grey-scale one using given RGB to luma mapping
597         * @param <T> dataset sub-interface
598         * @param clazz dataset sub-interface
599         * @param red weight
600         * @param green weight
601         * @param blue weight
602         * @return a grey-scale dataset of given class
603         * @since 2.2
604         */
605        public <T extends Dataset> T createGreyDataset(final Class<T> clazz, final double red, final double green, final double blue) {
606                final T grey = DatasetFactory.zeros(clazz, shape);
607                final IndexIterator it = getIterator();
608
609                int i = 0;
610                while (it.hasNext()) {
611                        grey.setObjectAbs(i++, red*data[it.index] + green*data[it.index + 1] + blue*data[it.index + 2]);
612                }
613                return grey;
614        }
615
616        /**
617         * Convert colour dataset to a grey-scale one using the NTSC formula, aka ITU-R BT.601, for RGB to luma mapping
618         * @param dtype dataset type
619         * @return a grey-scale dataset of given class
620         * @deprecated Use {@link RGBDataset#createGreyDataset(Class)}
621         */
622        @Deprecated
623        public Dataset createGreyDataset(final int dtype) {
624                return createGreyDataset(Wr, Wg, Wb, dtype);
625        }
626
627        /**
628         * Convert colour dataset to a grey-scale one using given RGB to luma mapping
629         * @param red weight
630         * @param green weight
631         * @param blue weight
632         * @param dtype dataset type
633         * @return a grey-scale dataset of given type
634         * @deprecated Use {@link RGBDataset#createGreyDataset(Class, double, double, double)}
635         */
636        @Deprecated
637        public Dataset createGreyDataset(final double red, final double green, final double blue, final int dtype) {
638                return createGreyDataset(DTypeUtils.getInterface(dtype), red, green, blue);
639        }
640
641        /**
642         * Extract red colour channel
643         * @param <T> dataset sub-interface
644         * @param clazz dataset sub-interface
645         * @return a dataset of given class
646         * @since 2.1
647         */
648        public <T extends Dataset> T createRedDataset(final Class<T> clazz) {
649                return createColourChannelDataset(0, clazz, "red");
650        }
651
652        /**
653         * Extract green colour channel
654         * @param <T> dataset sub-interface
655         * @param clazz dataset sub-interface
656         * @return a dataset of given class
657         * @since 2.1
658         */
659        public <T extends Dataset> T createGreenDataset(final Class<T> clazz) {
660                return createColourChannelDataset(1, clazz, "green");
661        }
662
663        /**
664         * Extract blue colour channel
665         * @param <T> dataset sub-interface
666         * @param clazz dataset sub-interface
667         * @return a dataset of given class
668         * @since 2.1
669         */
670        public <T extends Dataset> T createBlueDataset(final Class<T> clazz) {
671                return createColourChannelDataset(2, clazz, "blue");
672        }
673
674        /**
675         * Extract red colour channel
676         * @param dtype dataset type
677         * @return a dataset of given type
678         * @deprecated Use {@link #createRedDataset}
679         */
680        @Deprecated
681        public Dataset createRedDataset(final int dtype) {
682                return createColourChannelDataset(0, DTypeUtils.getInterface(dtype), "red");
683        }
684
685        /**
686         * Extract green colour channel
687         * @param dtype dataset type
688         * @return a dataset of given type
689         * @deprecated Use {@link #createGreenDataset}
690         */
691        @Deprecated
692        public Dataset createGreenDataset(final int dtype) {
693                return createColourChannelDataset(1, DTypeUtils.getInterface(dtype), "green");
694        }
695
696        /**
697         * Extract blue colour channel
698         * @param dtype dataset type
699         * @return a dataset of given type
700         * @deprecated Use {@link #createBlueDataset}
701         */
702        @Deprecated
703        public Dataset createBlueDataset(final int dtype) {
704                return createColourChannelDataset(2, DTypeUtils.getInterface(dtype), "blue");
705        }
706
707        private <T extends Dataset> T createColourChannelDataset(int channelOffset, Class<T> clazz, String cName) {
708                final T channel = DatasetFactory.zeros(clazz, shape);
709
710                final StringBuilder cname = name == null ? new StringBuilder() : new StringBuilder(name);
711                if (cname.length() > 0) {
712                        cname.append('.');
713                }
714                cname.append(cName);
715                channel.setName(cname.toString());
716
717                final IndexIterator it = getIterator();
718
719                int i = 0;
720                while (it.hasNext()) {
721                        channel.setObjectAbs(i++, data[it.index + channelOffset]);
722                }
723
724                return channel;
725        }
726
727
728        /**
729         * @return red view
730         */
731        public ShortDataset getRedView() {
732                return getColourChannelView(0, "red");
733        }
734
735        /**
736         * @return green view
737         */
738        public ShortDataset getGreenView() {
739                return getColourChannelView(1, "green");
740        }
741
742        /**
743         * @return blue view
744         */
745        public ShortDataset getBlueView() {
746                return getColourChannelView(2, "blue");
747        }
748
749        private ShortDataset getColourChannelView(final int channelOffset, final String cName) {
750                ShortDataset view = getElements(channelOffset);
751                view.setName(cName);
752                return view;
753        }
754
755        @Override
756        public Number max(boolean... ignored) {
757                short max = Short.MIN_VALUE;
758                final IndexIterator it = getIterator();
759
760                while (it.hasNext()) {
761                        for (int i = 0; i < ISIZE; i++) {
762                                final short value = data[it.index + i];
763                                if (value > max)
764                                        max = value;
765                        }
766                }
767                return max;
768        }
769
770        @Override
771        public Number min(boolean... ignored) {
772                short min = Short.MAX_VALUE;
773                final IndexIterator it = getIterator();
774
775                while (it.hasNext()) {
776                        for (int i = 0; i < ISIZE; i++) {
777                                final short value = data[it.index + i];
778                                if (value < min)
779                                        min = value;
780                        }
781                }
782                return min;
783        }
784}