blob: ae3060106c7cad34363cce4d0fcfd9a036b64a05 [file] [log] [blame]
/* R : A Computer Language for Statistical Data Analysis
* Copyright (C) 2010 The R Foundation
* Copyright (C) 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/
*/
#include "modreg.h"
#include <math.h>
/* To be "exported" (as part of R's C API): */
/**
* Modify the slopes m_k := s'(x_k) using Fritsch & Carlson (1980)'s algorithm
*
* @param m numeric vector of length n, the preliminary desired slopes s'(x_i), i = 1:n
* @param S the divided differences (y_{i+1} - y_i) / (x_{i+1} - x_i); i = 1:(n-1)
* @param n == length(m) == 1 + length(S)
* @return m*: the modified m[]'s: Note that m[] is modified in place
* @author Martin Maechler, Date: 19 Apr 2010
*/
void monoFC_mod(double *m, double S[], int n)
{
if(n < 2)
error(_("n must be at least two"));
for(int k = 0; k < n - 1; k++) {
/* modify both (m[k] & m[k+1]) if needed : */
double Sk = S[k];
int k1 = k + 1;
if(Sk == 0.) { /* or |S| < eps ?? FIXME ?? */
m[k] = m[k1] = 0.;
} else {
double
alpha = m[k ] / Sk,
beta = m[k1] / Sk, a2b3, ab23;
if((a2b3 = 2*alpha + beta - 3) > 0 &&
(ab23 = alpha + 2*beta - 3) > 0 &&
alpha * (a2b3 + ab23) < a2b3*a2b3) {
/* we are outside the monotonocity region ==> fix slopes */
double tauS = 3*Sk / sqrt(alpha*alpha + beta*beta);
m[k ] = tauS * alpha;
m[k1] = tauS * beta;
}
}
} /* end for */
}
SEXP monoFC_m(SEXP m, SEXP Sx)
{
SEXP val;
int n = LENGTH(m);
if (isInteger(m))
val = PROTECT(coerceVector(m, REALSXP));
else {
if (!isReal(m))
error(_("Argument m must be numeric"));
val = PROTECT(duplicate(m));
}
if(n < 2) error(_("length(m) must be at least two"));
if(!isReal(Sx) || LENGTH(Sx) != n-1)
error(_("Argument Sx must be numeric vector one shorter than m[]"));
/* Fix up the slopes m[] := val[]: */
monoFC_mod(REAL(val), REAL(Sx), n);
UNPROTECT(1);
return(val);
}