slice.cpp
1 /*
2  * This file is part of CasADi.
3  *
4  * CasADi -- A symbolic framework for dynamic optimization.
5  * Copyright (C) 2010-2023 Joel Andersson, Joris Gillis, Moritz Diehl,
6  * KU Leuven. All rights reserved.
7  * Copyright (C) 2011-2014 Greg Horn
8  *
9  * CasADi is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 3 of the License, or (at your option) any later version.
13  *
14  * CasADi is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with CasADi; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22  *
23  */
24 
25 
26 #include "slice.hpp"
27 #include "casadi_misc.hpp"
28 #include "serializing_stream.hpp"
29 
30 namespace casadi {
31 
32  Slice::Slice() : start(0), stop(std::numeric_limits<casadi_int>::max()), step(1) {
33  }
34 
35  Slice::Slice(casadi_int i, bool ind1) : start(i-ind1), stop(i-ind1+1), step(1) {
36  casadi_assert(!(ind1 && i<=0),
37  "Matlab is 1-based, but requested index " +
38  str(i) + ". Note that negative slices are"
39  " disabled in the Matlab interface. "
40  "Possibly you may want to use 'end'.");
41  if (i==-1) stop = std::numeric_limits<casadi_int>::max();
42  }
43 
44  Slice::Slice(casadi_int start, casadi_int stop, casadi_int step) :
45  start(start), stop(stop), step(step) { }
46 
47  Slice::Slice(int start, int stop, int step) : start(start), stop(stop), step(step) {
48  }
49  Slice::Slice(int start, casadi_int stop, int step) : start(start), stop(stop), step(step) {
50  }
51  Slice::Slice(casadi_int start, int stop, int step) : start(start), stop(stop), step(step) {
52  }
53 
54  Slice Slice::operator-(casadi_int i) const {
55  return Slice(start==std::numeric_limits<casadi_int>::min() ? start : start-i,
56  stop==std::numeric_limits<casadi_int>::max() ? stop : stop-i,
57  step);
58  }
59 
60  Slice Slice::operator*(casadi_int i) const {
61  return Slice(start==std::numeric_limits<casadi_int>::min() ? start : start*i,
62  stop==std::numeric_limits<casadi_int>::max() ? stop : stop*i,
63  step*i);
64  }
65 
66  Slice Slice::apply(casadi_int len, bool ind1) const {
67  casadi_int start = this->start;
68  if (start==std::numeric_limits<casadi_int>::min()) {
69  start = (step < 0) ? len - 1 : 0;
70  } else if (start<0) {
71  start+=len;
72  }
73  casadi_int stop = this->stop;
74  if (stop==std::numeric_limits<casadi_int>::max()) {
75  stop = (step < 0) ? -1 : len;
76  } else if (stop<0) {
77  stop+=len;
78  }
79 
80  casadi_assert(stop<=len,
81  "Slice (start=" + str(start) + ", stop=" + str(stop) + ", step=" + str(step)
82  + ") out of bounds with supplied length of " + str(len));
83  casadi_assert(start>=0,
84  "Slice (start=" + str(start) + ", stop=" + str(stop) + ", step=" + str(step)
85  + ") out of bounds with start<0.");
86 
87  return Slice(start+ind1, stop+ind1, step);
88  }
89 
90  std::vector<casadi_int> Slice::all() const {
91  casadi_assert(start!=std::numeric_limits<casadi_int>::min(), "Need a length");
92  casadi_assert(stop!=std::numeric_limits<casadi_int>::max(), "Need a length");
93 
94  if ((stop>=start && step<0) ||
95  (stop<=start && step>0)) return std::vector<casadi_int>();
96 
97  return range(start, stop, step);
98  }
99 
100  std::vector<casadi_int> Slice::all(casadi_int len, bool ind1) const {
101  return apply(len, ind1).all();
102  }
103 
104  size_t Slice::size() const {
105  casadi_assert(start!=std::numeric_limits<casadi_int>::min() &&
106  stop!=std::numeric_limits<casadi_int>::max(),
107  "Cannot determine numel of slice.");
108  return all(std::numeric_limits<casadi_int>::max()).size();
109  }
110 
111  bool Slice::is_empty() const {
112  return size()==0;
113  }
114 
115  void Slice::disp(std::ostream& stream, bool more) const {
116  bool from_beginning = start == 0;
117  bool till_end = stop == std::numeric_limits<casadi_int>::max();
118  bool skip_none = step==1;
119  if (stop==start+1) {
120  stream << start;
121  } else {
122  if (!from_beginning) stream << start;
123  stream << ":";
124  if (!till_end) stream << stop;
125  if (!skip_none) stream << ":" << step;
126  }
127  }
128 
129  std::vector<casadi_int> Slice::all(const Slice& outer, casadi_int len) const {
130  std::vector<casadi_int> ret;
131  for (casadi_int i=outer.start; i!=outer.stop; i+=outer.step) {
132  for (casadi_int j=i+start; j!=i+stop; j+=step) {
133  ret.push_back(j);
134  }
135  }
136  return ret;
137  }
138 
139  bool Slice::is_scalar(casadi_int len) const {
140  casadi_int start = std::min(this->start, len);
141  casadi_int stop = std::min(this->stop, len);
142  casadi_int nret = (stop-start)/step + ((stop-start)%step!=0);
143  return nret==1;
144  }
145 
146  casadi_int Slice::scalar(casadi_int len) const {
147  casadi_assert_dev(is_scalar(len));
148  casadi_assert(start >= -len && start < len, "Slice::getScalar: out of bounds");
149  return start >= 0 ? start : start+len;
150  }
151 
152  Slice CASADI_EXPORT to_slice(const std::vector<casadi_int>& v, bool ind1) {
153  Slice r;
154  casadi_assert(is_slice(v, ind1), "Cannot be represented as a Slice");
155  if (v.empty()) {
156  r.start=r.stop=0;
157  r.step = 1;
158  } else if (v.size()==1) {
159  r.start = v.front()-ind1;
160  r.stop = r.start + 1;
161  r.step = 1;
162  } else {
163  r.start = v[0]-ind1;
164  r.step = v[1]-v[0];
165  r.stop = r.start + r.step*v.size();
166  }
167  return r;
168  }
169 
170  bool CASADI_EXPORT is_slice(const std::vector<casadi_int>& v, bool ind1) {
171  // Always false if negative numbers or non-increasing
172  casadi_int last_v = -1;
173  for (casadi_int i=0; i<v.size(); ++i) {
174  casadi_assert(!(ind1 && v[i]<=0),
175  "Matlab is 1-based, but requested index " + str(v[i]) + ". "
176  "Note that negative slices are disabled in the Matlab interface. "
177  "Possibly you may want to use 'end'.");
178  if (v[i]-ind1<=last_v) return false;
179  last_v = v[i]-ind1;
180  }
181 
182  // Always true if less than 2 elements
183  if (v.size()<2) return true;
184 
185  // If two elements, true if they are different
186  if (v.size()==2) return v[0]!=v[1];
187 
188  // We can now get the beginning, end and step
189  casadi_int start = v[0]-ind1;
190  casadi_int step = v[1]-v[0];
191  //casadi_int stop = start + step*v.size();
192 
193  // Consistency check
194  for (casadi_int i=2; i<v.size(); ++i) {
195  if (v[i]-ind1!=start+i*step) return false;
196  }
197 
198  // True if reached this point
199  return true;
200  }
201 
202  bool CASADI_EXPORT is_slice2(const std::vector<casadi_int>& v) {
203  // Always true if 1D slice
204  if (is_slice(v)) return true;
205 
206  // Always false if negative numbers or non-increasing
207  casadi_int last_v = -1;
208  for (casadi_int i=0; i<v.size(); ++i) {
209  if (v[i]<=last_v) return false;
210  last_v = v[i];
211  }
212 
213  // Get the slices
214  casadi_int start_outer = 0;
215  casadi_int step_outer = -1;
216  casadi_int start_inner = v.front();
217  casadi_int step_inner = v[1]-v[0];
218  casadi_int stop_inner = -1;
219  for (casadi_int i=2; i<v.size(); ++i) {
220  casadi_int predicted_v = start_inner+i*step_inner;
221  if (v[i]!=predicted_v) {
222  stop_inner = predicted_v;
223  step_outer = v[i] - start_inner;
224  break;
225  }
226  }
227  casadi_assert_dev(stop_inner>=0);
228 
229  // Get the end of the outer slice
230  casadi_int stop_outer = v.back();
231  do {
232  if (step_outer>0) stop_outer++;
233  else stop_outer--;
234  } while (stop_outer % step_outer!=0);
235 
236  // Check consistency
237  std::vector<casadi_int>::const_iterator it=v.begin();
238  for (casadi_int i=start_outer; i!=stop_outer; i+=step_outer) {
239  for (casadi_int j=i+start_inner; j!=i+stop_inner; j+=step_inner) {
240  // False if we've reached the end
241  if (it==v.end()) return false;
242 
243  // Check if value matches
244  if (*it++ != j) return false;
245  }
246  }
247 
248  // False if there are still elements not accounted for
249  if (it!=v.end()) return false; // NOLINT
250 
251  // True if reached this point
252  return true;
253  }
254 
255  std::pair<Slice, Slice> CASADI_EXPORT to_slice2(const std::vector<casadi_int>& v) {
256  casadi_assert(is_slice2(v), "Cannot be represented as a nested Slice");
257  Slice inner, outer;
258 
259  // If simple slice
260  if (is_slice(v)) {
261  inner = to_slice(v);
262  outer.start = 0;
263  outer.step = outer.stop = inner.stop;
264  return std::make_pair(inner, outer);
265  }
266 
267  // Get the slices
268  outer.start = 0;
269  outer.step = -1;
270  inner.start = v.front();
271  inner.step = v[1]-v[0];
272  inner.stop = -1;
273  for (casadi_int i=2; i<v.size(); ++i) {
274  casadi_int predicted_v = inner.start+i*inner.step;
275  if (v[i]!=predicted_v) {
276  inner.stop = predicted_v;
277  outer.step = v[i] - inner.start;
278  break;
279  }
280  }
281 
282  // Get the end of the outer slice
283  outer.stop = v.back();
284  do {
285  if (outer.step>0) outer.stop++;
286  else outer.stop--;
287  } while (outer.stop % outer.step!=0);
288  return std::make_pair(inner, outer);
289  }
290 
291 
293  s.pack("Slice::start", start);
294  s.pack("Slice::stop", stop);
295  s.pack("Slice::step", step);
296  }
297 
299  casadi_int start, stop, step;
300  s.unpack("Slice::start", start);
301  s.unpack("Slice::stop", stop);
302  s.unpack("Slice::step", step);
303  return Slice(start, stop, step);
304  }
305 
306 } // namespace casadi
Helper class for Serialization.
void unpack(Sparsity &e)
Reconstruct an object from the input stream.
Helper class for Serialization.
void pack(const Sparsity &e)
Serializes an object to the output stream.
Class representing a Slice.
Definition: slice.hpp:48
void serialize(SerializingStream &s) const
Serialize an object.
Definition: slice.cpp:292
Slice operator-(casadi_int i) const
Substract.
Definition: slice.cpp:54
size_t size() const
Get number of elements.
Definition: slice.cpp:104
casadi_int scalar(casadi_int len) const
Get scalar (if is_scalar)
Definition: slice.cpp:146
bool is_empty() const
Check if slice is empty.
Definition: slice.cpp:111
casadi_int step
Definition: slice.hpp:54
Slice apply(casadi_int len, bool ind1=false) const
Apply concrete length.
Definition: slice.cpp:66
casadi_int stop
stop value: use std::numeric_limits<casadi_int>::max() to indicate unboundedness
Definition: slice.hpp:53
Slice()
Default constructor - all elements.
Definition: slice.cpp:32
void disp(std::ostream &stream, bool more=false) const
Print a description of the object.
Definition: slice.cpp:115
static Slice deserialize(DeserializingStream &s)
Deserialize without type information.
Definition: slice.cpp:298
Slice operator*(casadi_int i) const
Definition: slice.cpp:60
std::vector< casadi_int > all() const
Get a vector of indices.
Definition: slice.cpp:90
bool is_scalar(casadi_int len) const
Is the slice a scalar.
Definition: slice.cpp:139
casadi_int start
start value: negative values will get added to length
Definition: slice.hpp:51
The casadi namespace.
Definition: archiver.cpp:28
std::vector< casadi_int > range(casadi_int start, casadi_int stop, casadi_int step, casadi_int len)
Range function.
bool CASADI_EXPORT is_slice(const IM &x, bool ind1=false)
Is the IM a Slice.
bool CASADI_EXPORT is_slice2(const std::vector< casadi_int > &v)
Check if an index vector can be represented more efficiently as two nested slices.
Definition: slice.cpp:202
std::string str(const T &v)
String representation, any type.
std::pair< Slice, Slice > CASADI_EXPORT to_slice2(const std::vector< casadi_int > &v)
Construct nested slices from an index vector (requires is_slice2(v) to be true)
Definition: slice.cpp:255
Slice CASADI_EXPORT to_slice(const IM &x, bool ind1=false)
Convert IM to Slice.