// Copyright (C) 2008 VZLU Prague a.s., Czech Republic // // Author: Jaroslav Hajek // // This file is part of Octave. // // Octave is free software; you can redistribute it and/or modify it // under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 3 of the License, or (at // your option) any later version. // // Octave is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Octave; see the file COPYING. If not, see // . // #include // use this to select an appropriate output index type #ifdef USE_64_BIT_IDX_T typedef octave_uint64 ret_idx_type; typedef uint64NDArray ret_idx_array; #else typedef octave_uint32 ret_idx_type; typedef uint32NDArray ret_idx_array; #endif // simple function templates & specialization is used // to avoid writing two versions of the algorithm // generic comparison template inline bool dcomp(const double& a,const double& b); // normal comparison template<> inline bool dcomp(const double& a,const double& b) { return a < b; } // reversed comparison template<> inline bool dcomp(const double& a,const double& b) { return a > b; } template static void do_seq_lookup(const double *begin,const double *end, const double *vals,size_t nvals,ret_idx_type *idx) { if (!nvals) return; const double *first, *last; size_t cur; // the trivial case of empty array if (begin == end) { for(;nvals;--nvals) *(idx++) = 0; return; } // initialize last = end; first = begin; bin_search: { size_t dist = last - first, half; while (dist > 0) { half = dist >> 1; last = first + half; if (dcomp(*last,*vals)) { first = last + 1; dist -= (half + 1); } else dist = half; } last = first; } store_cur: cur = last - begin; if (last == begin) { // leftmost interval while (nvals) { if (dcomp(*last,*vals)) goto search_forwards; *(idx++) = cur; nvals--; vals++; } return; } else if (last == end) { // rightmost interval first = last - 1; while (nvals) { if (dcomp(*vals,*first)) goto search_backwards; *(idx++) = cur; nvals--; vals++; } return; } else { // inner interval first = last - 1; while (nvals) { if (dcomp(*last,*vals)) goto search_forwards; if (dcomp(*vals,*first)) goto search_backwards; *(idx++) = cur; nvals--; vals++; } return; } search_forwards: { size_t dist = 1, max = end - last; while(true) { first = last; if (dist < max) last += dist; else { last = end; break; } if (!dcomp(*last,*vals)) break; max -= dist; dist <<= 1; } if (!(--dist)) goto store_cur; // a shortcut else goto bin_search; } search_backwards: { size_t dist = 1, max = first - begin; while(true) { last = first; if (dist < max) first -= dist; else { first = begin; break; } if (!dcomp(*vals,*first)) break; max -= dist; dist <<= 1; } if (!(--dist)) goto store_cur; // a shortcut else goto bin_search; } } static void seq_lookup(const double *table,size_t ntable, const double *vals,size_t nvals,ret_idx_type *idx) { if (ntable > 1 && table[0] > table[ntable-1]) do_seq_lookup(table,table+ntable,vals,nvals,idx); else do_seq_lookup(table,table+ntable,vals,nvals,idx); } DEFUN_DLD(lookup,args,nargout,"\ -*- texinfo -*- \n\ @deftypefn {Function File} address@hidden =} lookup (@var{table}, @var{y}) \n\ Lookup values in a sorted table. Usually used as a prelude to \n\ interpolation. \n\ \n\ If table is strictly increasing and @code{idx = lookup (table, y)}, then \n\ @code{table(idx(i)) <= y(i) < table(idx(i+1))} for all @code{y(i)} \n\ within the table. If @code{y(i)} is before the table, then \n\ @code{idx(i)} is 0. If @code{y(i)} is after the table then \n\ @code{idx(i)} is @code{table(n)}. \n\ \n\ If the table is strictly decreasing, then the tests are reversed. \n\ There are no guarantees for tables which are non-monotonic or are not \n\ strictly monotonic. \n\ \n\ To get an index value which lies within an interval of the table, \n\ use: \n\ \n\ @example \n\ idx = lookup (table(2:length(table)-1), y) + 1 \n\ @end example \n\ \n\ @noindent \n\ This expression puts values before the table into the first \n\ interval, and values after the table into the last interval. \n\ \n\ If complex values are supplied, their magnitudes are used. \n\ @end deftypefn \n") { int nargin = args.length(); if (nargin != 2 || nargout > 1) { print_usage(); return octave_value_list(); } octave_value table = args(0), y = args(1); NDArray atable; // in the case of complex array, absolute values will be used // (though it's not too meaningful) if (table.is_real_type()) atable = table.array_value(); else if (table.is_complex_type()) atable = table.complex_array_value().abs(); else { error("table is not numeric type"); } if (atable.ndims() > 2 || (atable.rows() > 1 && atable.columns() > 1)) { error("table should be a vector"); return octave_value_list(); } NDArray ay; if (y.is_real_type()) ay = y.array_value(); else if (y.is_complex_type()) ay = y.complex_array_value().abs(); else { error("y is not numeric type"); } ret_idx_array idx(y.dims()); seq_lookup(atable.data(),(size_t)atable.numel(),ay.data(), (size_t)ay.numel(),idx.fortran_vec()); return octave_value_list(octave_value(idx)); } /* %!assert (real(lookup(1:3, 0.5)), 0) # value before table %!assert (real(lookup(1:3, 3.5)), 3) # value after table error %!assert (real(lookup(1:3, 1.5)), 1) # value within table error %!assert (real(lookup(1:3, [3,2,1])), [3,2,1]) %!assert (real(lookup([1:4]', [1.2, 3.5]')), [1, 3]'); %!assert (real(lookup([1:4], [1.2, 3.5]')), [1, 3]'); %!assert (real(lookup([1:4]', [1.2, 3.5])), [1, 3]); %!assert (real(lookup([1:4], [1.2, 3.5])), [1, 3]); %!assert (real(lookup(1:3, [3, 2, 1])), [3, 2, 1]); %!assert (real(lookup([3:-1:1], [3.5, 3, 1.2, 2.5, 2.5])), [0, 1, 2, 1, 1]) %!assert (isempty(lookup([1:3], []))) %!assert (isempty(lookup([1:3]', []))) %!assert (real(lookup(1:3, [1, 2; 3, 0.5])), [1, 2; 3, 0]); */