    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
    015
    016import java.util.Arrays;
    017
    018/**
    019 * The {@code SliceIterator} class is use to run over a Slice of a Dataset.
    020 *
    021 * This is an Iterator thats allows the programmer to traverse the elements of a sliced Dataset and obtain the current position, the starts, steps,
    022 * shapes.
    023 * Moreover, there is possibilities to set the start point to begin at the wanted position.
    024 */
    025public class SliceIterator extends IndexIterator {
    026        int[] shape;
    027        int isize;
    028        int endrank; // last shape index
    029        int[] gap; // gaps in dataset
    030        int imax; // maximum index in array
    031        int[] start;
    032        int[] stop;
    033        int[] step;
    034        int[] sshape; // slice shape
    035        int[] pos; // position in dataset
    036        int istep; // step in last index
    037
    038        SliceIterator() {
    039        }
    040
    041        /**
    042         * Constructs an SliceIterator Object, which can iterate over sliced
    043         * Datasets elements, by default the start set to 0 and with a step of 1.
    044         *
    045         * @param shape
    046         *            Array of shapes of the Dataset
    047         * @param length
    048         *            Length of entire data array
    049         * @param sshape
    050         *            Shape of the new dataset, i.e. slice
    051         */
    052        public SliceIterator(final int[] shape, final int length, final int[] sshape) {
    053                this(shape, length, null, null, sshape, 1);
    054        }
    055
    056        /**
    057         * Constructs an SliceIterator Object, which can iterate over sliced
    058         * Datasets elements, by default the start set to 0 and with a step of 1.
    059         *
    060         * @param shape
    061         *            Array of shapes of the Dataset
    062         * @param length
    063         *            Length of entire data array
    064         * @param start
    065         *            Array of starts indexes, may be {@code null}
    066         * @param sshape
    067         *            Shape of the new dataset, i.e. slice
    068         */
    069        public SliceIterator(final int[] shape, final int length, final int[] start, final int[] sshape) {
    070                this(shape, length, start, null, sshape, 1);
    071        }
    072
    073        /**
    074         * Constructs an SliceIterator Object, which can iterate over sliced
    075         * Datasets elements, by default the start set to 0 and with a step of 1.
    076         *
    077         * @param shape
    078         *            Array of shapes of the Dataset
    079         * @param length
    080         *            Length of entire data array
    081         * @param sshape
    082         *            Shape of the new dataset, i.e. slice
    083         * @param isize
    084         *            Number of elements in an item
    085         */
    086        public SliceIterator(final int[] shape, final int length, final int[] sshape, final int isize) {
    087                this(shape, length, null, null, sshape, isize);
    088        }
    089
    090        /**
    091         * Constructs an SliceIterator Object, which can iterate over sliced
    092         * Datasets elements, by default the start set to 0 and with a step of 1.
    093         *
    094         * @param shape
    095         *            Array of shapes of the Dataset
    096         * @param length
    097         *            Length of entire data array
    098         * @param start
    099         *            Array of starts indexes, may be {@code null}
    100         * @param sshape
    101         *            Shape of the new dataset, i.e. slice
    102         * @param isize
    103         *            Number of elements in an item
    104         */
    105        public SliceIterator(final int[] shape, final int length, final int[] start, final int[] sshape, final int isize) {
    106                this(shape, length, start, null, sshape, isize);
    107        }
    108
    109        /**
    110         * Constructs an SliceIterator Object, which can iterate over sliced
    111         * Datasets elements, by default the start set to 0 and with a step of 1.
    112         *
    113         * @param shape
    114         *            Array of shapes of the Dataset
    115         * @param length
    116         *            Length of entire data array
    117         * @param start
    118         *            Array of starts indexes, may be {@code null}
    119         * @param step
    120         *            Array of steps, may be {@code null}, but can't be 0
    121         * @param sshape
    122         *            shape of new dataset, i.e. slice
    123         */
    124        public SliceIterator(final int[] shape, final int length, final int[] start, final int[] step, final int[] sshape) {
    125                this(shape, length, start, step, sshape, 1);
    126        }
    127
    128        /**
    129         * Constructs an SliceIterator Object, which can iterate over sliced
    130         * Datasets elements, by default the start set to 0 and with a step of 1.
    131         *
    132         * @param shape
    133         *            Array of shapes of the Dataset
    134         * @param length
    135         *            Length of entire data array
    136         * @param slice
    137         *            SliceND to iterate on
    138         */
    139        public SliceIterator(final int[] shape, final int length, final SliceND slice) {
    140                this(shape, length, slice.getStart(), slice.getStep(), slice.getShape(), 1);
    141        }
    142
    143        /**
    144         * Constructs an SliceIterator Object, which can iterate over sliced
    145         * Datasets elements, by default the start set to 0 and with a step of 1.
    146         *
    147         * @param shape
    148         *            Array of shapes of the Dataset
    149         * @param length
    150         *            Length of entire data array
    151         * @param isize
    152         *            Number of elements in an item
    153         * @param slice
    154         *            SliceND to iterate on
    155         */
    156        public SliceIterator(final int[] shape, final int length, final int isize, final SliceND slice) {
    157                this(shape, length, slice.getStart(), slice.getStep(), slice.getShape(), isize);
    158        }
    159
    160        /**
    161         * Constructs an SliceIterator Object, which can iterate over sliced
    162         * Datasets elements, by default the start set to 0 and with a step of 1.
    163         *
    164         * @param shape
    165         *            Array of shapes of the Dataset
    166         * @param length
    167         *            Length of entire data array
    168         * @param start
    169         *            Array of starts indexes, may be {@code null}
    170         * @param step
    171         *            Array of steps, may be {@code null}, but can't be 0
    172         * @param sshape
    173         *            Shape of the new dataset, i.e. slice
    174         * @param isize
    175         *            Number of elements in an item
    176         */
    177        public SliceIterator(final int[] shape, final int length, final int[] start, final int[] step, final int[] sshape,
    178                        final int isize) {
    179                this.isize = isize;
    180                final int rank = shape == null ? 0 : shape.length;
    181                endrank = rank - 1;
    182                this.shape = shape;
    183                this.start = new int[rank];
    184                this.sshape = sshape;
    185                if (step == null) {
    186                        this.step = new int[rank];
    187                        Arrays.fill(this.step, 1);
    188                } else {
    189                        this.step = step;
    190                }
    191
    192                if (rank == 0) {
    193                        istep = isize;
    194                        imax = length * isize;
    195                        stop = new int[0];
    196                        pos = new int[0];
    197                        gap = null;
    198                } else {
    199                        istep = this.step[endrank] * isize;
    200                        imax = length * isize;
    201                        stop = new int[rank];
    202                        gap = new int[endrank + 1];
    203                        pos = new int[rank];
    204                        calcGap();
    205                }
    206
    207                setStart(start);
    208        }
    209
    210        void calcGap() {
    211                int chunk = isize;
    212                for (int i = endrank; i >= 0; i--) {
    213                        stop[i] = start[i] + sshape[i] * step[i];
    214
    215                        if (step[i] < 0) {
    216                                stop[i]++; // adjust for -ve steps so later code has more succinct test
    217                        }
    218
    219                        if (i > 0) {
    220                                gap[i] = (shape[i] * step[i - 1] - sshape[i] * step[i]) * chunk;
    221                                chunk *= shape[i];
    222                        }
    223                }
    224        }
    225
    226        /**
    227         * Set the starts indexes to new positions, {@code if null} the start index
    228         * is set by default to 0
    229         *
    230         * @param newStart
    231         *            Array of new starts indexes (prefix with zeros if necessary),
    232         *            may be {@code null}
    233         */
    234        public void setStart(int... newStart) {
    235                final int rank = shape == null ? 0 : shape.length;
    236                if (rank == 0) {
    237                        index = -istep;
    238                        return;
    239                }
    240
    241                if (newStart == null) {
    242                        for (int i = 0; i < rank; i++) {
    243                                start[i] = 0;
    244                        }
    245                } else if (newStart.length > rank) {
    246                        throw new IllegalArgumentException("Length of start array greater than rank");
    247                } else {
    248                        int extra = rank - newStart.length;
    249                        for (int i = 0; i < extra; i++) {
    250                                start[i] = 0;
    251                        }
    252                        for (int i = 0; i < newStart.length; i++) {
    253                                start[i + extra] = newStart[i];
    254                        }
    255                }
    256
    257                reset();
    258                calcGap();
    259        }
    260
    261        /**
    262         * Reset the Iterator to the first Slice.
    263         */
    264        @Override
    265        public void reset() {
    266                final int rank = shape == null ? 0 : shape.length;
    267                if (rank == 0) {
    268                        index = -istep;
    269                } else {
    270                        // work out index of first position
    271                        for (int i = 0; i < shape.length; i++) {
    272                                pos[i] = start[i];
    273                        }
    274                        pos[endrank] -= step[endrank];
    275
    276                        index = pos[0];
    277                        for (int j = 1; j <= endrank; j++)
    278                                index = index * shape[j] + pos[j];
    279                        index *= isize;
    280                }
    281        }
    282
    283        /**
    284         * Returns {@code true} if there is an other element after the current
    285         * Slice.
    286         *
    287         * @return Returns {@code true} if the iteration has more Slice, {@code false} in
    288         *         the other case
    289         */
    290        @Override
    291        public boolean hasNext() {
    292                // now move on one position in slice
    293                int j = endrank;
    294                for (; j >= 0; j--) {
    295                        pos[j] += step[j];
    296
    297                        if ((pos[j] >= stop[j]) == (step[j] > 0)) {
    298                                pos[j] = start[j]; // stop index has been adjusted in code for -ve steps
    299                                index += gap[j];
    300                        } else {
    301                                break;
    302                        }
    303                }
    304                if (j == -1 && endrank >= 0) {
    305                        return false;
    306                }
    307
    308                index += istep;
    309                return index < imax;
    310        }
    311
    312        /**
    313         * Returns an array of starts indexes.
    314         *
    315         * @return Array of starts indexes
    316         */
    317        public int[] getStart() {
    318                return start;
    319        }
    320
    321        /**
    322         * Returns the current position of the iterator.
    323         *
    324         * @return Iterator current position
    325         */
    326        @Override
    327        public int[] getPos() {
    328                return pos;
    329        }
    330
    331        /**
    332         * Returns an array of steps
    333         *
    334         * @return Array of steps
    335         */
    336        public int[] getStep() {
    337                return step;
    338        }
    339
    340        /**
    341         * Returns an array of the Slices shapes.
    342         *
    343         * @return Array of shapes.
    344         */
    345        @Override
    346        public int[] getShape() {
    347                return sshape;
    348        }
    349}