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                int rank = 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.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                if (shape.length == 0) {
267                        index = -istep;
268                } else {
269                        // work out index of first position
270                        for (int i = 0; i < shape.length; i++) {
271                                pos[i] = start[i];
272                        }
273                        pos[endrank] -= step[endrank];
274
275                        index = pos[0];
276                        for (int j = 1; j <= endrank; j++)
277                                index = index * shape[j] + pos[j];
278                        index *= isize;
279                }
280        }
281
282        /**
283         * Returns {@code true} if there is an other element after the current
284         * Slice.
285         * 
286         * @return Returns {@code true} if the iteration has more Slice, {@code false} in
287         *         the other case
288         */
289        @Override
290        public boolean hasNext() {
291                // now move on one position in slice
292                int j = endrank;
293                for (; j >= 0; j--) {
294                        pos[j] += step[j];
295
296                        if ((pos[j] >= stop[j]) == (step[j] > 0)) { 
297                                pos[j] = start[j]; // stop index has been adjusted in code for -ve steps
298                                index += gap[j];
299                        } else {
300                                break;
301                        }
302                }
303                if (j == -1 && endrank >= 0) {
304                        return false;
305                }
306
307                index += istep;
308                return index < imax;
309        }
310
311        /**
312         * Returns an array of starts indexes.
313         * 
314         * @return Array of starts indexes
315         */
316        public int[] getStart() {
317                return start;
318        }
319
320        /**
321         * Returns the current position of the iterator.
322         * 
323         * @return Iterator current position
324         */
325        @Override
326        public int[] getPos() {
327                return pos;
328        }
329
330        /**
331         * Returns an array of steps
332         * 
333         * @return Array of steps
334         */
335        public int[] getStep() {
336                return step;
337        }
338
339        /**
340         * Returns an array of the Slices shapes.
341         * 
342         * @return Array of shapes.
343         */
344        @Override
345        public int[] getShape() {
346                return sshape;
347        }
348}