blob: 7ed6c9870c32dc7e5608731a13dcbcd46d8aa141 [file] [log] [blame]
/*
* R : A Computer Language for Statistical Data Analysis
* Copyright (C) 2002--2016 The R Core Team
*
* This program 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 2 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, a copy is available at
* https://www.R-project.org/Licenses/
*
* The interv() used to be Fortran in ../library/modreg/src/bvalue.f
* and part of Hastie and Tibshirani's public domain GAMFIT package.
*
* Translated by f2c (version 20010821), cleaned up and extended by
* Martin Maechler.
*/
#include <R_ext/Applic.h>
#include <R_ext/Boolean.h>
#include <R_ext/Utils.h>
/* This is called from stats/src/bvalue.f, 3 x stats/src/s*.f for smooth.spline()
and packages gam and mda */
int F77_SUB(interv)(double *xt, int *n, double *x,
Rboolean *rightmost_closed, Rboolean *all_inside,
int *ilo, int *mflag)
{
return findInterval(xt, *n, *x, *rightmost_closed, *all_inside, *ilo, mflag);
}
int findInterval2(double *xt, int n, double x,
Rboolean rightmost_closed, Rboolean all_inside,
Rboolean left_open, // <- new in findInterval2()
int ilo, int *mflag)
{
int istep, middle, ihi;
/* computes `left' := max( i ; 1 <= i <= n && xt[i] <= x ) .
****** i n p u t ******
xt numeric vector of length n , assumed to be nondecreasing
n length(xt)
x the point whose location with respect to the sequence xt is
to be determined.
rightmost_closed {logical} indicating if the rightmost xt[] interval
should be closed, i.e. result := n-1 if x == x[n]
(when left_open, the *leftmost* interval should be closed.)
all_inside {logical} indicating if result should be coerced
to lie inside {1, n-1}
left_open {logical} use intervals (s, t] instead of [s, t)
ilo typically the result of the last call to findInterval(.)
`ilo' used to be a static variable (in Fortran) which is not
desirable in R anymore (threads!).
Instead, you *should* use a reasonable value, in the first call.
****** o u t p u t ******
left, mflag both integers, whose value is
0 -1 if x < xt[1]
i 0 if xt[i] <= x < xt[i+1]
n 1 if xt[n] <= x
in particular, mflag = 0 is the 'usual' case. mflag != 0
indicates that x lies outside the halfopen interval
xt[1] <= y < xt[n] . the asymmetric treatment of the
interval is due to the decision to make all pp functions cont-
inuous from the right.
Note that if all_inside, left is 1 instead of 0 and n-1 instead of n;
and if rightmost_closed and x == xt[n], left is n-1 instead of n.
****** m e t h o d ******
the program is designed to be efficient in the common situation that
it is called repeatedly, with x taken from an increasing or decreasing
sequence. this will happen, e.g., when a pp function is to be graphed.
The first guess for left is therefore taken to be the value returned at
the previous call and stored in the l o c a l variable ilo .
a first check ascertains that ilo < n (this is necessary since the
present call may have nothing to do with the previous call).
then, if xt[ilo] <= x < xt[ilo+1], we set left = ilo
and are done after just three comparisons.
otherwise, we repeatedly double the difference istep = ihi - ilo
while also moving ilo and ihi in the direction of x , until
xt[ilo] <= x < xt[ihi] ,
after which we use bisection to get, in addition, ilo+1 = ihi .
left = ilo is then returned.
*/
#define left_boundary { *mflag = -1; \
return((all_inside || (rightmost_closed && x == xt[1])) ? 1 : 0); }
#define right_boundary { *mflag = +1; \
return((all_inside || (rightmost_closed && x == xt[n])) \
? (n - 1) : n); }
#define X_grtr(XT_v) x > XT_v || (!left_open && x >= XT_v)
#define X_smlr(XT_v) x < XT_v || (left_open && x <= XT_v)
if(n == 0) { *mflag = 0 ; return 0; }
/* 1-indexing : */
--xt;
if(ilo <= 0) {
if (X_smlr(xt[1])) left_boundary;
ilo = 1;
}
ihi = ilo + 1;
if (ihi >= n) {
if (X_grtr(xt[n])) right_boundary;
if (n <= 1) /* x < xt[1] */ left_boundary;
ilo = n - 1;
ihi = n;
}
if (X_smlr(xt[ihi])) {
if (X_grtr(xt[ilo])) {
/* `lucky': same interval as last time */
*mflag = 0; return ilo;
}
/* **** now x < xt[ilo] . decrease ilo to capture x */
if(!left_open) for(istep = 1; ; istep *= 2) {
ihi = ilo;
ilo = ihi - istep;
if (ilo <= 1)
break;
if (x >= xt[ilo]) goto L50;
} else for(istep = 1; ; istep *= 2) {
ihi = ilo;
ilo = ihi - istep;
if (ilo <= 1)
break;
if (x > xt[ilo]) goto L51;
}
ilo = 1;
if (X_smlr(xt[1])) left_boundary;
}
else {
/* **** now x >= xt[ihi] . increase ihi to capture x */
if(!left_open) for(istep = 1; ; istep *= 2) {
ilo = ihi;
ihi = ilo + istep;
if (ihi >= n)
break;
if (x < xt[ihi]) goto L50;
}
else for(istep = 1; ; istep *= 2) {
ilo = ihi;
ihi = ilo + istep;
if (ihi >= n)
break;
if (x <= xt[ihi]) goto L51;
}
if (X_grtr(xt[n])) right_boundary;
ihi = n;
}
if (left_open) goto L51; /* There _is_ a path to here, avoiding return and goto */
L50: // ! left_open
/* **** now xt[ilo] <= x < xt[ihi] . narrow the interval. */
for(;;) {
middle = (ilo + ihi) / 2;
if (middle == ilo) {
*mflag = 0; return ilo;
}
/* note. it is assumed that middle = ilo in case ihi = ilo+1 . */
if (x >= xt[middle])
ilo = middle;
else
ihi = middle;
}
L51: // left_open
/* **** now xt[ilo] < x <= xt[ihi] . narrow the interval. */
for(;;) {
middle = (ilo + ihi) / 2;
if (middle == ilo) {
*mflag = 0; return ilo;
}
/* note. it is assumed that middle = ilo in case ihi = ilo+1 . */
if (x > xt[middle])
ilo = middle;
else
ihi = middle;
}
} /* findInterval2 */
// has been in API -- keep for compatibility:
int findInterval(double *xt, int n, double x,
Rboolean rightmost_closed, Rboolean all_inside, int ilo,
int *mflag)
{
return findInterval2(xt, n, x, rightmost_closed, all_inside, FALSE, ilo, mflag);
}