001// Copyright (c) FIRST and other WPILib contributors.
002// Open Source Software; you can modify and/or share it under the terms of
003// the WPILib BSD license file in the root directory of this project.
004
005package org.wpilib.math.autodiff;
006
007import java.util.OptionalInt;
008
009/** Represents a sequence of elements in an iterable object. */
010public class Slice {
011  /** Type tag used to designate an omitted argument of the slice. */
012  public static class None {
013    /** Default constructor. */
014    public None() {}
015  }
016
017  /** Designates an omitted argument of the slice. */
018  public static final None __ = null;
019
020  /** Start index (inclusive). */
021  public int start = 0;
022
023  /** Stop index (exclusive). */
024  public int stop = 0;
025
026  /** Step. */
027  public int step = 1;
028
029  /** Constructs a Slice. */
030  public Slice() {}
031
032  /**
033   * Constructs a slice.
034   *
035   * @param start Slice start index (inclusive).
036   */
037  public Slice(None start) {
038    this(OptionalInt.of(0), OptionalInt.of(Integer.MAX_VALUE), OptionalInt.of(1));
039  }
040
041  /**
042   * Constructs a slice.
043   *
044   * @param start Slice start index (inclusive).
045   */
046  public Slice(int start) {
047    this.start = start;
048    this.stop = (start == -1) ? Integer.MAX_VALUE : start + 1;
049    this.step = 1;
050  }
051
052  /**
053   * Constructs a slice.
054   *
055   * @param start Slice start index (inclusive).
056   * @param stop Slice stop index (exclusive).
057   */
058  public Slice(None start, None stop) {
059    this(OptionalInt.empty(), OptionalInt.empty(), OptionalInt.of(1));
060  }
061
062  /**
063   * Constructs a slice.
064   *
065   * @param start Slice start index (inclusive).
066   * @param stop Slice stop index (exclusive).
067   */
068  public Slice(None start, int stop) {
069    this(OptionalInt.empty(), OptionalInt.of(stop), OptionalInt.of(1));
070  }
071
072  /**
073   * Constructs a slice.
074   *
075   * @param start Slice start index (inclusive).
076   * @param stop Slice stop index (exclusive).
077   */
078  public Slice(int start, None stop) {
079    this(OptionalInt.of(start), OptionalInt.empty(), OptionalInt.of(1));
080  }
081
082  /**
083   * Constructs a slice.
084   *
085   * @param start Slice start index (inclusive).
086   * @param stop Slice stop index (exclusive).
087   */
088  public Slice(int start, int stop) {
089    this(OptionalInt.of(start), OptionalInt.of(stop), OptionalInt.of(1));
090  }
091
092  /**
093   * Constructs a slice.
094   *
095   * @param start Slice start index (inclusive).
096   * @param stop Slice stop index (exclusive).
097   * @param step Slice step.
098   */
099  public Slice(None start, None stop, None step) {
100    this(OptionalInt.empty(), OptionalInt.empty(), OptionalInt.empty());
101  }
102
103  /**
104   * Constructs a slice.
105   *
106   * @param start Slice start index (inclusive).
107   * @param stop Slice stop index (exclusive).
108   * @param step Slice step.
109   */
110  public Slice(None start, None stop, int step) {
111    this(OptionalInt.empty(), OptionalInt.empty(), OptionalInt.of(step));
112  }
113
114  /**
115   * Constructs a slice.
116   *
117   * @param start Slice start index (inclusive).
118   * @param stop Slice stop index (exclusive).
119   * @param step Slice step.
120   */
121  public Slice(None start, int stop, None step) {
122    this(OptionalInt.empty(), OptionalInt.of(stop), OptionalInt.empty());
123  }
124
125  /**
126   * Constructs a slice.
127   *
128   * @param start Slice start index (inclusive).
129   * @param stop Slice stop index (exclusive).
130   * @param step Slice step.
131   */
132  public Slice(None start, int stop, int step) {
133    this(OptionalInt.empty(), OptionalInt.of(stop), OptionalInt.of(step));
134  }
135
136  /**
137   * Constructs a slice.
138   *
139   * @param start Slice start index (inclusive).
140   * @param stop Slice stop index (exclusive).
141   * @param step Slice step.
142   */
143  public Slice(int start, None stop, None step) {
144    this(OptionalInt.of(start), OptionalInt.empty(), OptionalInt.empty());
145  }
146
147  /**
148   * Constructs a slice.
149   *
150   * @param start Slice start index (inclusive).
151   * @param stop Slice stop index (exclusive).
152   * @param step Slice step.
153   */
154  public Slice(int start, None stop, int step) {
155    this(OptionalInt.of(start), OptionalInt.empty(), OptionalInt.of(step));
156  }
157
158  /**
159   * Constructs a slice.
160   *
161   * @param start Slice start index (inclusive).
162   * @param stop Slice stop index (exclusive).
163   * @param step Slice step.
164   */
165  public Slice(int start, int stop, None step) {
166    this(OptionalInt.of(start), OptionalInt.of(stop), OptionalInt.empty());
167  }
168
169  /**
170   * Constructs a slice.
171   *
172   * @param start Slice start index (inclusive).
173   * @param stop Slice stop index (exclusive).
174   * @param step Slice step.
175   */
176  public Slice(int start, int stop, int step) {
177    this(OptionalInt.of(start), OptionalInt.of(stop), OptionalInt.of(step));
178  }
179
180  /**
181   * Constructs a slice.
182   *
183   * @param start Slice start index (inclusive).
184   * @param stop Slice stop index (exclusive).
185   * @param step Slice step.
186   */
187  public Slice(OptionalInt start, OptionalInt stop, OptionalInt step) {
188    if (!step.isPresent()) {
189      this.step = 1;
190    } else {
191      assert step.getAsInt() != 0;
192
193      this.step = step.getAsInt();
194    }
195
196    // Avoid UB for step = -step if step is INT_MIN
197    if (this.step == Integer.MIN_VALUE) {
198      this.step = -Integer.MAX_VALUE;
199    }
200
201    if (!start.isPresent()) {
202      if (this.step < 0) {
203        this.start = Integer.MAX_VALUE;
204      } else {
205        this.start = 0;
206      }
207    } else {
208      this.start = start.getAsInt();
209    }
210
211    if (!stop.isPresent()) {
212      if (this.step < 0) {
213        this.stop = Integer.MIN_VALUE;
214      } else {
215        this.stop = Integer.MAX_VALUE;
216      }
217    } else {
218      this.stop = stop.getAsInt();
219    }
220  }
221
222  /**
223   * Adjusts start and end slice indices assuming a sequence of the specified length.
224   *
225   * @param length The sequence length.
226   * @return The slice length.
227   */
228  public int adjust(int length) {
229    assert step != 0;
230    assert step >= -Integer.MAX_VALUE;
231
232    if (start < 0) {
233      start += length;
234
235      if (start < 0) {
236        start = (step < 0) ? -1 : 0;
237      }
238    } else if (start >= length) {
239      start = (step < 0) ? length - 1 : length;
240    }
241
242    if (stop < 0) {
243      stop += length;
244
245      if (stop < 0) {
246        stop = (step < 0) ? -1 : 0;
247      }
248    } else if (stop >= length) {
249      stop = (step < 0) ? length - 1 : length;
250    }
251
252    if (step < 0) {
253      if (stop < start) {
254        return (start - stop - 1) / -step + 1;
255      } else {
256        return 0;
257      }
258    } else {
259      if (start < stop) {
260        return (stop - start - 1) / step + 1;
261      } else {
262        return 0;
263      }
264    }
265  }
266}