blob: 31c1c37f271ef05b14da4af998aa73dafc9c7d3f [file] [log] [blame]
/*
* Copyright (C) 2012-2019 The R Core Team
* Copyright (C) 2003 ff. The R Foundation
* Copyright (C) 2000-2 Martin Maechler <maechler@stat.math.ethz.ch>
* Copyright (C) 1995 Berwin A. Turlach <berwin@alphasun.anu.edu.au>
*
* 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
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* These routines implement a running median smoother according to the
* algorithm described in Haerdle und Steiger (1995),
* DOI:10.2307/2986349 , see ../man/runmed.Rd
*
* The current implementation does not use any global variables!
*/
/* Changes for R port by Martin Maechler ((C) above):
*
* s/long/int/ R uses int, not long (as S does)
* s/void/static void/ most routines are internal
*
* - Added print_level and end_rule arguments
* - Allow to deal with NA / NaN {via ISNAN()}
*/
/* Variable name descri- | Identities from paper
* name here paper ption | (1-indexing)
* --------- ----- -----------------------------------
* window[] H the array containing the double heap
* data[] X the data (left intact)
* ... i 1st permuter H[i[m]] == X[i + m]
* ... j 2nd permuter X[i +j[m]] == H[m]
*/
#include <math.h>
#include <R_ext/RS.h> /* for Memcpy */
static void
swap(int l, int r, double *window, int *outlist, int *nrlist, int print_level)
{
/* swap positions `l' and `r' in window[] and nrlist[]
*
* ---- Used in R_heapsort() and many other routines
*/
if(print_level >= 3) Rprintf(" SW(%d,%d) ", l,r);
double tmp = window[l]; window[l] = window[r]; window[r] = tmp;
int nl = nrlist[l],
nr = nrlist[r];
nrlist[l] = nr; outlist[nr] = l;
nrlist[r] = nl; outlist[nl] = r;
}
//------------------------ 1. inittree() and auxiliaries ----------------------
static void
siftup(int l, int r, double *window, int *outlist, int *nrlist, int print_level)
{
/* Used only in R_heapsort() */
int i = l, j,
nrold = nrlist[i];
double x = window[i];
if(print_level >= 2) Rprintf("siftup(%d,%d): x=%g: ", l,r, x);
while ((j = 2*i) <= r) {
if (j < r)
if (window[j] < window[j+1])
j++;
if (x >= window[j])
break;
window[i] = window[j];
outlist[nrlist[j]] = i;
nrlist[i] = nrlist[j];
i = j;
}
window[i] = x;
outlist[nrold] = i;
nrlist[i] = nrold;
if(print_level >= 2) Rprintf("-> nrlist[i=%d] := %d\n", i, nrold);
}
static void
R_heapsort(int low, int up, double *window, int *outlist, int *nrlist,
int print_level)
{
int l = (up/2) + 1,
u = up;
if(print_level)
Rprintf("R_heapsort(%d, %d,..): l=%d:\n", low, up, l);
while(l > low) {
if(print_level >= 2) Rprintf(" l > low: ");
l--; // l >= low :
siftup(l, u, window, outlist, nrlist, print_level);
} // => l <= low
while(u > low) {
if(print_level >= 2) Rprintf(" u > low: ");
swap(l, u, window, outlist, nrlist, print_level);
u--; // u >= low :
siftup(l, u, window, outlist, nrlist, print_level);
}
}
static void
inittree(R_xlen_t n, int k, int k2,
const double *data,
// --> initialize these three vectors:
double *window, int *outlist, int *nrlist,
int print_level)
{
// use 1-indexing for our 3 arrays {window, nrlist, outlist}
for(int i=1; i <= k; i++) {
window[i] = data[i-1];
nrlist[i] = (outlist[i] = i);
}
// MM: not all 2k+1 entries of nrlist[] are used, it seems
if(print_level >= 1) {
int n0 = -12345; // to be recognized easily ...
double w0 = -80.08; // (ditto)
nrlist[0] = n0;
window[0] = w0;
for(int j=k+1; j <= 2*k; j++) {
nrlist[j] = n0;
window[j] = w0;
}
}
// sort window[1:k] = data["1:k"] [*only* called here] :
R_heapsort(1, k, window, outlist, nrlist, print_level);
double big = fabs(window[k]);
if (big < fabs(window[1]))
big = fabs(window[1]);
// now big := max |X[1..k]| or +BIG if(first_NA <= k), i.e., data had NA|NaN
for(int i=k; i > 0; i--) {
window[i+k2] = window[i];
nrlist[i+k2] = nrlist[i]-1;
}
// outlist[0:(k-1)] := (shift down by 1 and offset by k2)
for(int i=0; i < k; i++)
outlist[i] = outlist[i+1] + k2;
// maybe increase 'big'
for(R_xlen_t i=k; i < n; i++)
if (big < fabs(data[i]))
big = fabs(data[i]);
/* big == max(|data_i|, i = 1,..,n) */
big = 1 + 2. * big;/* such that -big < data[] < +big
(strictly, only if no +/-Inf in data! */
int k2p1 = k2+1;
// window[0] := ..
for(int i=0; i < k2p1; i++) {
window[i] = -big;
window[k+k2p1+i] = big;
}
/* For Printing `diagnostics' : */
#define Rm_PR(_F_,_A_, _k_) for(int j = 0; j <= _k_; j++) Rprintf(_F_, _A_)
#define RgPRINT_j(A_J, _k_) Rm_PR("% 6.4g", A_J, _k_); Rprintf("\n")
#define RdPRINT_j(A_J, _k_) Rm_PR("% 6d" , A_J, _k_); Rprintf("\n")
//--- smart printing of "Big" / "2B" [BIG_dbl := .... -> ./Srunmed.c ]
#define RwwPRINTj(A_J, _k_) \
for(int j = 0; j <= _k_; j++) { \
if (A_J == BIG_dbl ) Rprintf("%6s", "+BIG"); \
else if(A_J == -BIG_dbl ) Rprintf("%6s", "-BIG"); \
else if(A_J == 2*BIG_dbl) Rprintf("%6s", "+2B"); \
else if(A_J == -2*BIG_dbl) Rprintf("%6s", "-2B"); \
else Rprintf("% 6.4g", A_J); \
} \
Rprintf("\n")
#define R_PRINT_4vec() \
Rprintf(" %9s: ","j"); RdPRINT_j( j, 2*k); \
Rprintf(" %9s: ","window []"); RwwPRINTj(window[j], 2*k); \
Rprintf(" %9s: "," nrlist[]"); RdPRINT_j(nrlist[j], 2*k); \
Rprintf(" %9s: ","outlist[]"); RdPRINT_j(outlist[j], k )
/* window[], outlist[], and nrlist[] are all 1-based (indices) */
if(print_level) {
R_PRINT_4vec();
}
} /* inittree*/
//------------------------ 2. runmedint() and auxiliaries -------------------
static void
toroot(int outvirt, int k, R_xlen_t nrnew, int outnext,
const double *data, double *window, int *outlist, int *nrlist,
int print_level)
{
if(print_level >= 2)
Rprintf(" toroot(%d,%d, nn=%d, outn=%d) ", outvirt, k, (int) nrnew, outnext);
int father;
do {
father = outvirt/2;
window[outvirt+k] = window[father+k];
outlist[nrlist[father+k]] = outvirt+k;
if(print_level >= 3)
Rprintf(" nrl[%d] := nrl[%d] = %d;", outvirt+k, father+k, nrlist[father+k]);
nrlist[outvirt+k] = nrlist[father+k];
outvirt = father;
} while (father != 0);
if(print_level >= 2) Rprintf("\n ");
window[k] = data[nrnew];
outlist[outnext] = k;
nrlist[k] = outnext;
}
static void
downtoleave(int outvirt, int k,
double *window, int *outlist, int *nrlist, int print_level)
{
if(print_level >= 2) Rprintf(" downtoleave(%d, %d) ", outvirt,k);
for(;;) {
int childl = outvirt*2,
childr = childl -1;
if (window[childl+k] < window[childr+k])
childl = childr;
if (window[outvirt+k] >= window[childl+k])
break;
/* seg.fault may happen here: invalid outvirt+k/ childl+k ? */
swap(outvirt+k, childl+k, window, outlist, nrlist, print_level);
outvirt = childl;
}
if(print_level >= 2) Rprintf("\n ");
}
static void
uptoleave(int outvirt, int k,
double *window, int *outlist, int *nrlist, int print_level)
{
if(print_level >= 2) Rprintf(" uptoleave(%d, %d) ", outvirt,k);
for(;;) {
int childl = outvirt*2,
childr = childl +1;
if (window[childl+k] > window[childr+k])
childl = childr;
if (window[outvirt+k] <= window[childl+k])
break;
/* seg.fault may happen here: invalid outvirt+k/ childl+k ? */
swap(outvirt+k, childl+k, window, outlist, nrlist, print_level);
outvirt = childl;
}
if(print_level >= 2) Rprintf("\n ");
}
static void
upperoutupperin(int outvirt, int k,
double *window, int *outlist, int *nrlist, int print_level)
{
if(print_level >= 2) Rprintf("UpperoutUPPERin(%d, %d)\n ", outvirt,k);
uptoleave(outvirt, k, window, outlist, nrlist, print_level);
int father = outvirt/2;
while (window[outvirt+k] < window[father+k]) {
if(print_level >= 2) Rprintf(" UpperoutUP(win[%d]): ", outvirt+k);
swap(outvirt+k, father+k, window, outlist, nrlist, print_level);
outvirt = father;
father = outvirt/2;
}
if(print_level >= 2) Rprintf("\n");
}
static void
upperoutdownin(int outvirt, int k, R_xlen_t nrnew, int outnext,
const double *data, double *window, int *outlist, int *nrlist,
int print_level)
{
if(print_level >= 2) Rprintf("__upperoutDOWNin(%d, %d, ..)\n ", outvirt,k);
toroot(outvirt, k, nrnew, outnext, data, window, outlist, nrlist, print_level);
if(window[k] < window[k-1]) {
if(print_level >= 2) Rprintf(" upperoutDN(win[%d]): ", k);
swap(k, k-1, window, outlist, nrlist, print_level);
downtoleave(/*outvirt = */ -1, k, window, outlist, nrlist, print_level);
}
}
static void
downoutdownin(int outvirt, int k,
double *window, int *outlist, int *nrlist, int print_level)
{
if(print_level >= 2) Rprintf("DownoutDOWNin(%d, %d)\n ", outvirt,k);
downtoleave(outvirt, k, window, outlist, nrlist, print_level);
int father = outvirt/2;
while (window[outvirt+k] > window[father+k]) {
swap(outvirt+k, father+k, window, outlist, nrlist, print_level);
outvirt = father;
father = outvirt/2;
}
// if(print_level >= 2) Rprintf("\n");
}
static void
downoutupperin(int outvirt, int k, R_xlen_t nrnew, int outnext,
const double *data, double *window, int *outlist, int *nrlist,
int print_level)
{
if(print_level >= 2) Rprintf("__downoutUPPERin(%d, %d, ..)\n ", outvirt,k);
toroot(outvirt, k, nrnew, outnext, data, window, outlist, nrlist, print_level);
if(window[k] > window[k+1]) {
swap(k, k+1, window, outlist, nrlist, print_level);
uptoleave(/*outvirt = */ +1, k, window, outlist, nrlist, print_level);
}
}
static void
wentoutone(int k, double *window, int *outlist, int *nrlist, int print_level)
{
if(print_level >= 2) Rprintf(" wentOUT_1(%d)\n ", k);
swap(k, k+1, window, outlist, nrlist, print_level);
uptoleave(/*outvirt = */ +1, k, window, outlist, nrlist, print_level);
}
static void
wentouttwo(int k, double *window, int *outlist, int *nrlist, int print_level)
{
if(print_level >= 2) Rprintf(" wentOUT_2(%d)\n ", k);
swap(k, k-1, window, outlist, nrlist, print_level);
downtoleave(/*outvirt = */ -1, k, window, outlist, nrlist, print_level);
}
static void
runmedint(R_xlen_t n, int k, int k2, const double *data, double *median,
double *window, int *outlist, int *nrlist,
int end_rule, int print_level)
{
/* Running Median of `k' , k == 2*k2 + 1 *
* end_rule == 0: leave values at the end,
* otherwise: "constant" end values
*/
int outnext, out, outvirt;
if(end_rule)
for(int i = 0; i <= k2; median[i++] = window[k]);
else {
for(int i = 0; i < k2; median[i] = data[i], i++);
median[k2] = window[k];
}
R_xlen_t every_i;
if(print_level >= 2)
every_i = (n > 100) ? n/10 : 10;
outnext = 0;
for(R_xlen_t i = k2+1; i < n-k2; i++) {/* compute (0-index) median[i] == X*_{i+1} */
out = outlist[outnext];
R_xlen_t nrnew = i+k2;
window[out] = data[nrnew];
outvirt = out-k;
if (out > k)
if(data[nrnew] >= window[k])
upperoutupperin(outvirt, k, window, outlist, nrlist, print_level);
else
upperoutdownin(outvirt, k, nrnew, outnext,
data, window, outlist, nrlist, print_level);
else if(out < k)
if(data[nrnew] < window[k])
downoutdownin(outvirt, k, window, outlist, nrlist, print_level);
else
downoutupperin(outvirt, k, nrnew, outnext,
data, window, outlist, nrlist, print_level);
else if(window[k] > window[k+1])
wentoutone(k, window, outlist, nrlist, print_level);
else if(window[k] < window[k-1])
wentouttwo(k, window, outlist, nrlist, print_level);
median[i] = window[k];
outnext = (outnext+1) % k;
if(print_level >= 2) {
Rprintf("i=%2d (out=%2d, *virt=%2d): med[i] := window[k]=%11g, outnext=%3d\n",
i, out, outvirt, median[i], outnext);
if(print_level >= 3 || (i % every_i) == 0) {
R_PRINT_4vec();
}
}
}
if(print_level >= 2) Rprintf("\n");
if(end_rule)
for(R_xlen_t i = n-k2; i < n; median[i++] = window[k]);
else
for(R_xlen_t i = n-k2; i < n; median[i] = data[i], i++);
}/* runmedint() */
//------------------------ 3. Trunmed() --------------------------------------
// Main function called from runmed() in ./Srunmed.c :
static void Trunmed(const double *x,// (n)
double *median, // (n)
R_xlen_t n,/* = length(x) */
int k, /* is odd <= n */
int end_rule, int print_level)
{
int k2 = (k - 1)/2; // always odd k == 2 * k2 + 1
double *window = (double *) R_alloc(2*k + 1, sizeof(double));
int *nrlist = (int *) R_alloc(2*k + 1, sizeof(int)),
*outlist = (int *) R_alloc( k + 1, sizeof(int));
inittree(n, k, k2, x,
/* initialize the 3 vectors: */ window, outlist, nrlist,
print_level);
runmedint(n, k, k2, x, median, window, outlist, nrlist,
end_rule, print_level);
}