GNU Octave  4.4.1
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
lookup.cc
Go to the documentation of this file.
1 /*
2 
3 Copyright (C) 2008-2018 VZLU Prague a.s., Czech Republic
4 
5 This file is part of Octave.
6 
7 Octave is free software: you can redistribute it and/or modify it
8 under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
11 
12 Octave is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with Octave; see the file COPYING. If not, see
19 <https://www.gnu.org/licenses/>.
20 
21 */
22 
23 // Author: Jaroslav Hajek <highegg@gmail.com>
24 
25 #if defined (HAVE_CONFIG_H)
26 # include "config.h"
27 #endif
28 
29 #include <cctype>
30 #include <functional>
31 #include <algorithm>
32 
33 #include "dNDArray.h"
34 #include "CNDArray.h"
35 
36 #include "Cell.h"
37 #include "defun.h"
38 #include "error.h"
39 #include "errwarn.h"
40 #include "ovl.h"
41 #include "ov.h"
42 
43 static
44 bool
46 {
47  return (str.find (c) != std::string::npos
48  || str.find (std::toupper (c)) != std::string::npos);
49 }
50 
51 template <typename T>
52 inline sortmode
54  typename octave_sort<T>::compare_fcn_type desc_comp
56 {
57  octave_idx_type n = array.numel ();
58  if (n > 1 && desc_comp (array (0), array (n-1)))
59  return DESCENDING;
60  else
61  return ASCENDING;
62 }
63 
64 // FIXME: perhaps there should be octave_value::lookup?
65 // The question is, how should it behave w.r.t. the second argument's type.
66 // We'd need a dispatch on two arguments. Hmmm...
67 
68 #define INT_ARRAY_LOOKUP(TYPE) \
69  (table.is_ ## TYPE ## _type () && y.is_ ## TYPE ## _type ()) \
70  retval = do_numeric_lookup (table.TYPE ## _array_value (), \
71  y.TYPE ## _array_value (), \
72  left_inf, right_inf, \
73  match_idx, match_bool);
74 template <typename ArrayT>
75 static octave_value
76 do_numeric_lookup (const ArrayT& array, const ArrayT& values,
77  bool left_inf, bool right_inf,
78  bool match_idx, bool match_bool)
79 {
81 
82  Array<octave_idx_type> idx = array.lookup (values);
83  octave_idx_type n = array.numel ();
84  octave_idx_type nval = values.numel ();
85 
86  // Post-process.
87  if (match_bool)
88  {
89  boolNDArray match (idx.dims ());
90  for (octave_idx_type i = 0; i < nval; i++)
91  {
92  octave_idx_type j = idx.xelem (i);
93  match.xelem (i) = j != 0 && values(i) == array(j-1);
94  }
95 
96  retval = match;
97  }
98  else if (match_idx || left_inf || right_inf)
99  {
100  if (match_idx)
101  {
102  NDArray ridx (idx.dims ());
103 
104  for (octave_idx_type i = 0; i < nval; i++)
105  {
106  octave_idx_type j = idx.xelem (i);
107  ridx.xelem (i) = (j != 0 && values(i) == array(j-1)) ? j : 0;
108  }
109 
110  retval = ridx;
111  }
112  else if (left_inf && right_inf)
113  {
114  // Results in valid indices. Optimize using lazy index.
115  octave_idx_type zero = 0;
116  for (octave_idx_type i = 0; i < nval; i++)
117  {
118  octave_idx_type j = idx.xelem (i) - 1;
119  idx.xelem (i) = std::max (zero, std::min (j, n-2));
120  }
121 
122  retval = idx_vector (idx);
123  }
124  else if (left_inf)
125  {
126  // Results in valid indices. Optimize using lazy index.
127  octave_idx_type zero = 0;
128  for (octave_idx_type i = 0; i < nval; i++)
129  {
130  octave_idx_type j = idx.xelem (i) - 1;
131  idx.xelem (i) = std::max (zero, j);
132  }
133 
134  retval = idx_vector (idx);
135  }
136  else if (right_inf)
137  {
138  NDArray ridx (idx.dims ());
139 
140  for (octave_idx_type i = 0; i < nval; i++)
141  {
142  octave_idx_type j = idx.xelem (i);
143  ridx.xelem (i) = std::min (j, n-1);
144  }
145 
146  retval = ridx;
147  }
148  }
149  else
150  retval = idx;
151 
152  return retval;
153 }
154 
155 DEFUN (lookup, args, ,
156  doc: /* -*- texinfo -*-
157 @deftypefn {} {@var{idx} =} lookup (@var{table}, @var{y})
158 @deftypefnx {} {@var{idx} =} lookup (@var{table}, @var{y}, @var{opt})
159 Lookup values in a @strong{sorted} table.
160 
161 This function is usually used as a prelude to interpolation.
162 
163 If table is increasing, of length N and @code{idx = lookup (table, y)}, then
164 @code{table(idx(i)) <= y(i) < table(idx(i+1))} for all @code{y(i)} within the
165 table. If @code{y(i) < table(1)} then @code{idx(i)} is 0. If
166 @code{y(i) >= table(end)} or @code{isnan (y(i))} then @code{idx(i)} is N.
167 
168 If the table is decreasing, then the tests are reversed. For non-strictly
169 monotonic tables, empty intervals are always skipped. The result is undefined
170 if @var{table} is not monotonic, or if @var{table} contains a NaN.
171 
172 The complexity of the lookup is O(M*log(N)) where M is the size of @var{y}.
173 In the special case when @var{y} is also sorted, the complexity is
174 O(min (M*log(N), M+N)).
175 
176 @var{table} and @var{y} can also be cell arrays of strings (or @var{y} can be a
177 single string). In this case, string lookup is performed using lexicographical
178 comparison.
179 
180 If @var{opts} is specified, it must be a string with letters indicating
181 additional options.
182 
183 @table @code
184 @item m
185 Match. @code{table(idx(i)) == y(i)} if @code{y(i)} occurs in table;
186 otherwise, @code{idx(i)} is zero.
187 
188 @item b
189 Boolean. @code{idx(i)} is a logical 1 or 0, indicating whether @code{y(i)}
190 is contained in table or not.
191 
192 @item l
193 Left. For numeric lookups the leftmost subinterval shall be extended to
194 minus infinity (i.e., all indices at least 1).
195 
196 @item r
197 Right. For numeric lookups the rightmost subinterval shall be extended to
198 infinity (i.e., all indices at most N-1).
199 @end table
200 
201 @strong{Note}: If @var{table} is not sorted the results from @code{lookup}
202 will be unpredictable.
203 @end deftypefn */)
204 {
205  int nargin = args.length ();
206 
208  print_usage ();
209 
210  octave_value table = args(0);
211  octave_value y = args(1);
212  if (table.ndims () > 2 || (table.columns () > 1 && table.rows () > 1))
213  warning ("lookup: table is not a vector");
214 
216 
217  bool num_case = ((table.isnumeric () && y.isnumeric ())
218  || (table.is_char_matrix () && y.is_char_matrix ()));
219  bool str_case = table.iscellstr () && (y.is_string () || y.iscellstr ());
220  bool left_inf = false;
221  bool right_inf = false;
222  bool match_idx = false;
223  bool match_bool = false;
224 
225  if (nargin == 3)
226  {
227  std::string opt = args(2).xstring_value ("lookup: OPT must be a string");
228  left_inf = contains_char (opt, 'l');
229  right_inf = contains_char (opt, 'r');
230  match_idx = contains_char (opt, 'm');
231  match_bool = contains_char (opt, 'b');
232  if (opt.find_first_not_of ("lrmb") != std::string::npos)
233  error ("lookup: unrecognized option: %c",
234  opt[opt.find_first_not_of ("lrmb")]);
235  }
236 
237  if ((match_idx || match_bool) && (left_inf || right_inf))
238  error ("lookup: m, b cannot be specified with l or r");
239  else if (match_idx && match_bool)
240  error ("lookup: only one of m or b can be specified");
241  else if (str_case && (left_inf || right_inf))
242  error ("lookup: l, r are not recognized for string lookups");
243 
244  if (num_case)
245  {
246  // In the case of a complex array, absolute values will be used for
247  // compatibility (though it's not too meaningful).
248  if (table.iscomplex ())
249  table = table.abs ();
250 
251  if (y.iscomplex ())
252  y = y.abs ();
253 
255 
256  // PS: I learned this from data.cc
257  if INT_ARRAY_LOOKUP (int8)
258  else if INT_ARRAY_LOOKUP (int16)
259  else if INT_ARRAY_LOOKUP (int32)
260  else if INT_ARRAY_LOOKUP (int64)
261  else if INT_ARRAY_LOOKUP (uint8)
262  else if INT_ARRAY_LOOKUP (uint16)
263  else if INT_ARRAY_LOOKUP (uint32)
264  else if INT_ARRAY_LOOKUP (uint64)
265  else if (table.is_char_matrix () && y.is_char_matrix ())
267  y.char_array_value (),
268  left_inf, right_inf,
269  match_idx, match_bool);
270  else if (table.is_single_type () || y.is_single_type ())
272  y.float_array_value (),
273  left_inf, right_inf,
274  match_idx, match_bool);
275  else
277  y.array_value (),
278  left_inf, right_inf,
279  match_idx, match_bool);
280  }
281  else if (str_case)
282  {
283  Array<std::string> str_table = table.cellstr_value ();
284  Array<std::string> str_y (dim_vector (1, 1));
285 
286  if (y.iscellstr ())
287  str_y = y.cellstr_value ();
288  else
289  str_y(0) = y.string_value ();
290 
291  Array<octave_idx_type> idx = str_table.lookup (str_y);
292  octave_idx_type nval = str_y.numel ();
293 
294  // Post-process.
295  if (match_bool)
296  {
297  boolNDArray match (idx.dims ());
298  for (octave_idx_type i = 0; i < nval; i++)
299  {
300  octave_idx_type j = idx.xelem (i);
301  match.xelem (i) = j != 0 && str_y(i) == str_table(j-1);
302  }
303 
304  retval = match;
305  }
306  else if (match_idx)
307  {
308  NDArray ridx (idx.dims ());
309  for (octave_idx_type i = 0; i < nval; i++)
310  {
311  octave_idx_type j = idx.xelem (i);
312  ridx.xelem (i) = (j != 0 && str_y(i) == str_table(j-1) ? j : 0);
313  }
314 
315  retval = ridx;
316  }
317  else
318  retval = idx;
319  }
320  else
321  print_usage ();
322 
323  return retval;
324 }
325 
326 /*
327 %!assert (lookup (1:3, 0.5), 0) # value before table
328 %!assert (lookup (1:3, 3.5), 3) # value after table error
329 %!assert (lookup (1:3, 1.5), 1) # value within table error
330 %!assert (lookup (1:3, [3,2,1]), [3,2,1])
331 %!assert (lookup ([1:4]', [1.2, 3.5]'), [1, 3]')
332 %!assert (lookup ([1:4], [1.2, 3.5]'), [1, 3]')
333 %!assert (lookup ([1:4]', [1.2, 3.5]), [1, 3])
334 %!assert (lookup ([1:4], [1.2, 3.5]), [1, 3])
335 %!assert (lookup (1:3, [3, 2, 1]), [3, 2, 1])
336 %!assert (lookup ([3:-1:1], [3.5, 3, 1.2, 2.5, 2.5]), [0, 1, 2, 1, 1])
337 %!assert (isempty (lookup ([1:3], [])))
338 %!assert (isempty (lookup ([1:3]', [])))
339 %!assert (lookup (1:3, [1, 2; 3, 0.5]), [1, 2; 3, 0])
340 %!assert (lookup (1:4, [1, 1.2; 3, 2.5], "m"), [1, 0; 3, 0])
341 %!assert (lookup (4:-1:1, [1, 1.2; 3, 2.5], "m"), [4, 0; 2, 0])
342 %!assert (lookup (1:4, [1, 1.2; 3, 2.5], "b"), logical ([1, 0; 3, 0]))
343 %!assert (lookup (4:-1:1, [1, 1.2; 3, 2.5], "b"), logical ([4, 0; 2, 0]))
344 %!
345 %!assert (lookup ({"apple","lemon","orange"}, {"banana","kiwi"; "ananas","mango"}), [1,1;0,2])
346 %!assert (lookup ({"apple","lemon","orange"}, "potato"), 3)
347 %!assert (lookup ({"orange","lemon","apple"}, "potato"), 0)
348 */
OCTINTERP_API void print_usage(void)
Definition: defun.cc:54
sortmode
Definition: oct-sort.h:105
sortmode get_sort_mode(const Array< T > &array, typename octave_sort< T >::compare_fcn_type desc_comp=octave_sort< T >::descending_compare)
Definition: lookup.cc:53
OCTAVE_EXPORT octave_value_list or N dimensional array whose elements are all equal to the IEEE symbol zero divided by zero($0/0$)
#define DEFUN(name, args_name, nargout_name, doc)
Macro to define a builtin function.
Definition: defun.h:53
void error(const char *fmt,...)
Definition: error.cc:578
const dim_vector & dims(void) const
Return a const-reference so that dims ()(i) works efficiently.
Definition: Array.h:442
octave_idx_type lookup(const T *x, octave_idx_type n, T y)
static octave_value do_numeric_lookup(const ArrayT &array, const ArrayT &values, bool left_inf, bool right_inf, bool match_idx, bool match_bool)
Definition: lookup.cc:76
int ndims(void) const
Definition: ov.h:478
nd example oindent opens the file binary numeric values will be read assuming they are stored in IEEE format with the least significant bit and then converted to the native representation Opening a file that is already open simply opens it again and returns a separate file id It is not an error to open a file several though writing to the same file through several different file ids may produce unexpected results The possible values of text mode reading and writing automatically converts linefeeds to the appropriate line end character for the you may append a you must also open the file in binary mode The parameter conversions are currently only supported for and permissions will be set to and then everything is written in a single operation This is very efficient and improves performance c
Definition: file-io.cc:587
FloatNDArray float_array_value(bool frc_str_conv=false) const
Definition: ov.h:843
cell array If invoked with two or more scalar integer or a vector of integer values
Definition: ov-cell.cc:1241
charNDArray char_array_value(bool frc_str_conv=false) const
Definition: ov.h:878
bool iscellstr(void) const
Definition: ov.h:543
then the function must return scalars which will be concatenated into the return array(s). If code
Definition: cellfun.cc:400
octave_idx_type lookup(const T &value, sortmode mode=UNSORTED) const
Do a binary lookup in a sorted array.
Definition: Array.cc:2147
octave_idx_type columns(void) const
Definition: ov.h:474
bool is_single_type(void) const
Definition: ov.h:651
std::string str
Definition: hash.cc:118
octave_idx_type rows(void) const
Definition: ov.h:472
octave_value retval
Definition: data.cc:6246
bool is_char_matrix(void) const
Definition: ov.h:568
T & xelem(octave_idx_type n)
Definition: Array.h:458
void warning(const char *fmt,...)
Definition: error.cc:801
N Dimensional Array with copy-on-write semantics.
Definition: Array.h:125
charNDArray max(char d, const charNDArray &m)
Definition: chNDArray.cc:227
static bool contains_char(const std::string &str, char c)
Definition: lookup.cc:45
the element is set to zero In other the statement xample y
Definition: data.cc:5264
args.length() nargin
Definition: file-io.cc:589
bool iscomplex(void) const
Definition: ov.h:710
#define INT_ARRAY_LOOKUP(TYPE)
Definition: lookup.cc:68
for i
Definition: data.cc:5264
octave_idx_type numel(void) const
Number of elements in the array.
Definition: Array.h:366
octave_value abs(void) const
Definition: ov.h:1407
Vector representing the dimensions (size) of an Array.
Definition: dim-vector.h:87
Array< std::string > cellstr_value(void) const
Definition: ov.h:967
If this string is the system will ring the terminal sometimes it is useful to be able to print the original representation of the string
Definition: utils.cc:888
NDArray array_value(bool frc_str_conv=false) const
Definition: ov.h:840
bool isnumeric(void) const
Definition: ov.h:723
charNDArray min(char d, const charNDArray &m)
Definition: chNDArray.cc:204