/* Copyright (C) 2008 VZLU Prague a.s., Czech Republic 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 . */ // Author: Jaroslav Hajek #ifdef HAVE_CONFIG_H #include #endif #include "NDArray.h" #include "defun-dld.h" #include "error.h" #include "gripes.h" #include "oct-obj.h" // change these to select appropriate output index type. // TODO: if we want to return integer indices someday, // it should be octave_idx_type. The octave_value class // can not, however, hold an ArrayN yet. // typedef octave_idx_type ret_idx_type; // typedef ???? ret_idx_array; typedef double ret_idx_type; typedef NDArray ret_idx_array; // 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); template inline bool dcompe (const double& a, const double& b); // normal comparison (increasing table) template<> inline bool dcomp(const double& a, const double& b) { return a < b; } // normal comparison with equality template<> inline bool dcompe(const double& a, const double& b) { return a <= b; } // reverse comparison (decreasing table) template<> inline bool dcomp(const double& a, const double& b) { return a > b; } // reverse comparison with equality template<> inline bool dcompe(const double& a, const double& b) { return a >= b; } // the heart of the lookup. The [begin,end) range is assumed // sorted so that dcompe(*iter,*(iter+1)) is true. // // outline of the algorithm: // 1. start with full range // 2. determine interval enclosing the next value // 3. fetch values while they stay inside the interval // if escaped to the left, go to 4. if escaped to the right, // go to 5. If processed all values, return. // 4. enclose the next value by binary bracketing to the left. // if just one step was taken (simple shift), go to 3. // otherwise, go to 2. // 5. enclose the next value by binary bracketing to the right. // if just one step was taken (simple shift), go to 3. // otherwise, go to 2. 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; // these are "global" pointers (i.e. used by all four blocks) const double *first, *last; // the trivial case of empty array. Store all zeros and return. if (begin == end) { for (;nvals;--nvals) *(idx++) = 0; return; } // initialize last = end; first = begin; // perform a binary search in [first,last) 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; } // fetch values as long as they stay in the current interval. // once they get out, jump to the appropriate bracketing block. store_cur: { size_t cur; cur = last - begin; if (last == begin) { // leftmost interval (can't escape backwards) while (nvals) { if (dcompe (*last, *vals)) goto search_forwards; *(idx++) = cur; nvals--; vals++; } return; } else if (last == end) { // rightmost interval (can't escape forwards) first = last - 1; while (nvals) { if (dcomp (*vals, *first)) goto search_backwards; *(idx++) = cur; nvals--; vals++; } return; } else { // inner interval (can escape both ways) first = last - 1; while (nvals) { if (dcompe (*last, *vals)) goto search_forwards; if (dcomp (*vals, *first)) goto search_backwards; *(idx++) = cur; nvals--; vals++; } return; } } // the current value got to the right of current interval. // perform binary bracketing to the right search_forwards: { size_t dist = 1, max = end - last; while (true) { first = last; if (dist < max) last += dist; else { last = end; break; } if (dcomp (*vals, *last)) break; max -= dist; dist <<= 1; } if (!(--dist)) goto store_cur; // a shortcut. If dist was 1, bin_search is not needed. else goto bin_search; } // the current value got to the left of current interval. // perform binary bracketing to the left. search_backwards: { size_t dist = 1, max = first - begin; while (true) { last = first; if (dist < max) first -= dist; else { first = begin; break; } if (dcompe (*first, *vals)) break; max -= dist; dist <<= 1; } if (!(--dist)) goto store_cur; // a shortcut. If dist was 1, bin_search is not needed. else goto bin_search; } } // this determines the orientation of the table and calls the appropriate // instance of do_seq_lookup 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") { int nargin = args.length (); octave_value table = args (0), y = args (1); if (nargin == 2 && nargout <= 1 && (table = args(0), table.is_numeric_type ()) && (y = args(1), y.is_numeric_type ()) ) { if (table.ndims() == 2 && (table.rows () == 1 || table.columns () == 1)) { // in the case of a complex array, absolute values will be used (though it's // not too meaningful). NDArray atable = (table.is_complex_type ()) ? table.complex_array_value ().abs () : table.array_value (); NDArray ay = (y.is_complex_type ()) ? y.complex_array_value ().abs () : y.array_value (); ret_idx_array idx (y.dims ()); seq_lookup (atable.data (), atable.numel (), ay.data (), ay.numel (), idx.fortran_vec ()); return octave_value (idx); } else { error("lookup: table must be a vector"); return octave_value_list (); } } else { print_usage (); return octave_value_list (); } } /* %!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]); */