The R Project SVN R

Rev

Rev 80166 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

/*
 *  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/
 */

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#include <stdlib.h> // for abs
#include <string.h>

#include <R.h>
#include "ts.h"
#include "statsR.h" // for getListElement

#ifndef max
#define max(a,b) ((a < b)?(b):(a))
#endif
#ifndef min
#define min(a,b) ((a < b)?(a):(b))
#endif


/* 
  KalmanLike, internal to StructTS:
  .Call(C_KalmanLike, y, mod$Z, mod$a, mod$P, mod$T, mod$V, mod$h, mod$Pn,
        nit, FALSE, update)
  KalmanRun:
  .Call(C_KalmanLike, y, mod$Z, mod$a, mod$P, mod$T, mod$V, mod$h, mod$Pn,
        nit, TRUE, update)
*/

/* y vector length n of observations
   Z vector length p for observation equation y_t = Za_t +  eps_t
   a vector length p of initial state
   P p x p matrix for initial state uncertainty (contemparaneous)
   T  p x p transition matrix
   V  p x p = RQR'
   h = var(eps_t)
   anew used for a[t|t-1]
   Pnew used for P[t|t -1]
   M used for M = P[t|t -1]Z

   op is FALSE for KalmanLike, TRUE for KalmanRun.  
   The latter computes residuals and states and has 
   a more elaborate return value.

   Almost no checking here!
 */

SEXP
KalmanLike(SEXP sy, SEXP mod, SEXP sUP, SEXP op, SEXP update)
{
    int lop = asLogical(op);
    mod = PROTECT(duplicate(mod));

    SEXP sZ = getListElement(mod, "Z"), sa = getListElement(mod, "a"), 
    sP = getListElement(mod, "P"), sT = getListElement(mod, "T"), 
    sV = getListElement(mod, "V"), sh = getListElement(mod, "h"),
    sPn = getListElement(mod, "Pn");

    if (TYPEOF(sy) != REALSXP || TYPEOF(sZ) != REALSXP ||
    TYPEOF(sa) != REALSXP || TYPEOF(sP) != REALSXP ||
    TYPEOF(sPn) != REALSXP ||
    TYPEOF(sT) != REALSXP || TYPEOF(sV) != REALSXP)
    error(_("invalid argument type"));

    int n = LENGTH(sy), p = LENGTH(sa);
    double *y = REAL(sy), *Z = REAL(sZ), *T = REAL(sT), *V = REAL(sV),
    *P = REAL(sP), *a = REAL(sa), *Pnew = REAL(sPn), h = asReal(sh);

    double *anew = (double *) R_alloc(p, sizeof(double));
    double *M = (double *) R_alloc(p, sizeof(double));
    double *mm = (double *) R_alloc(p * p, sizeof(double));
    // These are only used if(lop), but avoid -Wall trouble
    SEXP ans = R_NilValue, resid = R_NilValue, states = R_NilValue;
    if(lop) {
    PROTECT(ans = allocVector(VECSXP, 3));
    SET_VECTOR_ELT(ans, 1, resid = allocVector(REALSXP, n));
    SET_VECTOR_ELT(ans, 2, states = allocMatrix(REALSXP, n, p));
    SEXP nm = PROTECT(allocVector(STRSXP, 3));
    SET_STRING_ELT(nm, 0, mkChar("values"));
    SET_STRING_ELT(nm, 1, mkChar("resid"));
    SET_STRING_ELT(nm, 2, mkChar("states"));
    setAttrib(ans, R_NamesSymbol, nm);
    UNPROTECT(1);
    }

    double sumlog = 0.0, ssq = 0.0;
    int nu = 0;
    for (int l = 0; l < n; l++) {
    for (int i = 0; i < p; i++) {
        double tmp = 0.0;
        for (int k = 0; k < p; k++)
        tmp += T[i + p * k] * a[k];
        anew[i] = tmp;
    }
    if (l > asInteger(sUP)) {
        for (int i = 0; i < p; i++)
        for (int j = 0; j < p; j++) {
            double tmp = 0.0;
            for (int k = 0; k < p; k++)
            tmp += T[i + p * k] * P[k + p * j];
            mm[i + p * j] = tmp;
        }
        for (int i = 0; i < p; i++)
        for (int j = 0; j < p; j++) {
            double tmp = V[i + p * j];
            for (int k = 0; k < p; k++)
            tmp += mm[i + p * k] * T[j + p * k];
            Pnew[i + p * j] = tmp;
        }
    }
    if (!ISNAN(y[l])) {
        nu++;
        double *rr = NULL /* -Wall */;
        if(lop) rr = REAL(resid);
        double resid0 = y[l];
        for (int i = 0; i < p; i++)
        resid0 -= Z[i] * anew[i];
        double gain = h;
        for (int i = 0; i < p; i++) {
        double tmp = 0.0;
        for (int j = 0; j < p; j++)
            tmp += Pnew[i + j * p] * Z[j];
        M[i] = tmp;
        gain += Z[i] * M[i];
        }
        ssq += resid0 * resid0 / gain;
        if(lop) rr[l] = resid0 / sqrt(gain);
        sumlog += log(gain);
        for (int i = 0; i < p; i++)
        a[i] = anew[i] + M[i] * resid0 / gain;
        for (int i = 0; i < p; i++)
        for (int j = 0; j < p; j++)
            P[i + j * p] = Pnew[i + j * p] - M[i] * M[j] / gain;
    } else {
        double *rr = NULL /* -Wall */;
        if(lop) rr = REAL(resid);
        for (int i = 0; i < p; i++)
        a[i] = anew[i];
        for (int i = 0; i < p * p; i++)
        P[i] = Pnew[i];
        if(lop) rr[l] = NA_REAL;
    }
    if(lop) {
        double *rs = REAL(states);
        for (int j = 0; j < p; j++) rs[l + n*j] = a[j];
    }
    }

    SEXP res = PROTECT(allocVector(REALSXP, 2));
    REAL(res)[0] = ssq/nu; REAL(res)[1] = sumlog/nu;
    if(lop) {
    SET_VECTOR_ELT(ans, 0, res);
    if(asLogical(update)) setAttrib(ans, install("mod"), mod);
    UNPROTECT(3);
    return ans;
    } else {
    if(asLogical(update)) setAttrib(res, install("mod"), mod);
    UNPROTECT(2);
    return res;
    }
}

SEXP
KalmanSmooth(SEXP sy, SEXP mod, SEXP sUP)
{
    SEXP sZ = getListElement(mod, "Z"), sa = getListElement(mod, "a"), 
    sP = getListElement(mod, "P"), sT = getListElement(mod, "T"), 
    sV = getListElement(mod, "V"), sh = getListElement(mod, "h"),
    sPn = getListElement(mod, "Pn");

    if (TYPEOF(sy) != REALSXP || TYPEOF(sZ) != REALSXP ||
    TYPEOF(sa) != REALSXP || TYPEOF(sP) != REALSXP ||
    TYPEOF(sT) != REALSXP || TYPEOF(sV) != REALSXP)
    error(_("invalid argument type"));

    SEXP ssa, ssP, ssPn, res, states = R_NilValue, sN;
    int n = LENGTH(sy), p = LENGTH(sa);
    double *y = REAL(sy), *Z = REAL(sZ), *a, *P,
    *T = REAL(sT), *V = REAL(sV), h = asReal(sh), *Pnew;
    double *at, *rt, *Pt, *gains, *resids, *Mt, *L, gn, *Nt;
    Rboolean var = TRUE;

    PROTECT(ssa = duplicate(sa)); a = REAL(ssa);
    PROTECT(ssP = duplicate(sP)); P = REAL(ssP);
    PROTECT(ssPn = duplicate(sPn)); Pnew = REAL(ssPn);

    PROTECT(res = allocVector(VECSXP, 2));
    SEXP nm = PROTECT(allocVector(STRSXP, 2));
    SET_STRING_ELT(nm, 0, mkChar("smooth"));
    SET_STRING_ELT(nm, 1, mkChar("var"));
    setAttrib(res, R_NamesSymbol, nm);
    UNPROTECT(1);
    SET_VECTOR_ELT(res, 0, states = allocMatrix(REALSXP, n, p));
    at = REAL(states);
    SET_VECTOR_ELT(res, 1, sN = allocVector(REALSXP, n*p*p));
    Nt = REAL(sN);

    double *anew, *mm, *M;
    anew = (double *) R_alloc(p, sizeof(double));
    M = (double *) R_alloc(p, sizeof(double));
    mm = (double *) R_alloc(p * p, sizeof(double));

    Pt = (double *) R_alloc(n * p * p, sizeof(double));
    gains = (double *) R_alloc(n, sizeof(double));
    resids = (double *) R_alloc(n, sizeof(double));
    Mt = (double *) R_alloc(n * p, sizeof(double));
    L = (double *) R_alloc(p * p, sizeof(double));

    for (int l = 0; l < n; l++) {
    for (int i = 0; i < p; i++) {
        double tmp = 0.0;
        for (int k = 0; k < p; k++)
        tmp += T[i + p * k] * a[k];
        anew[i] = tmp;
    }
    if (l > asInteger(sUP)) {
        for (int i = 0; i < p; i++)
        for (int j = 0; j < p; j++) {
            double tmp = 0.0;
            for (int k = 0; k < p; k++)
            tmp += T[i + p * k] * P[k + p * j];
            mm[i + p * j] = tmp;
        }
        for (int i = 0; i < p; i++)
        for (int j = 0; j < p; j++) {
            double tmp = V[i + p * j];
            for (int k = 0; k < p; k++)
            tmp += mm[i + p * k] * T[j + p * k];
            Pnew[i + p * j] = tmp;
        }
    }
    for (int i = 0; i < p; i++) at[l + n*i] = anew[i];
    for (int i = 0; i < p*p; i++) Pt[l + n*i] = Pnew[i];
    if (!ISNAN(y[l])) {
        double resid0 = y[l];
        for (int i = 0; i < p; i++)
        resid0 -= Z[i] * anew[i];
        double gain = h;
        for (int i = 0; i < p; i++) {
        double tmp = 0.0;
        for (int j = 0; j < p; j++)
            tmp += Pnew[i + j * p] * Z[j];
        Mt[l + n*i] = M[i] = tmp;
        gain += Z[i] * M[i];
        }
        gains[l] = gain;
        resids[l] = resid0;
        for (int i = 0; i < p; i++)
        a[i] = anew[i] + M[i] * resid0 / gain;
        for (int i = 0; i < p; i++)
        for (int j = 0; j < p; j++)
            P[i + j * p] = Pnew[i + j * p] - M[i] * M[j] / gain;
    } else {
        for (int i = 0; i < p; i++) {
        a[i] = anew[i];
        Mt[l + n * i] = 0.0;
        }
        for (int i = 0; i < p * p; i++)
        P[i] = Pnew[i];
        gains[l] = NA_REAL;
        resids[l] = NA_REAL;
    }
    }

    /* rt stores r_{t-1} */
    rt = (double *) R_alloc(n * p, sizeof(double));
    for (int l = n - 1; l >= 0; l--) {
    if (!ISNAN(gains[l])) {
        gn = 1/gains[l];
        for (int i = 0; i < p; i++)
        rt[l + n * i] = Z[i] * resids[l] * gn;
    } else {
        for (int i = 0; i < p; i++) rt[l + n * i] = 0.0;
        gn = 0.0;
    }

    if (var) {
        for (int i = 0; i < p; i++)
        for (int j = 0; j < p; j++)
            Nt[l + n*i + n*p*j] = Z[i] * Z[j] * gn;
    }

    if (l < n - 1) {
        /* compute r_{t-1} */
        for (int i = 0; i < p; i++)
        for (int j = 0; j < p; j++)
            mm[i + p * j] = ((i==j) ? 1:0) - Mt[l + n * i] * Z[j] * gn;
        for (int i = 0; i < p; i++)
        for (int j = 0; j < p; j++) {
            double tmp = 0.0;
            for (int k = 0; k < p; k++)
            tmp += T[i + p * k] * mm[k + p * j];
            L[i + p * j] = tmp;
        }
        for (int i = 0; i < p; i++) {
        double tmp = 0.0;
        for (int j = 0; j < p; j++)
            tmp += L[j + p * i] * rt[l + 1 + n * j];
        rt[l + n * i] += tmp;
        }
        if(var) { /* compute N_{t-1} */
        for (int i = 0; i < p; i++)
            for (int j = 0; j < p; j++) {
            double tmp = 0.0;
            for (int k = 0; k < p; k++)
                tmp += L[k + p * i] * Nt[l + 1 + n*k + n*p*j];
            mm[i + p * j] = tmp;
            }
        for (int i = 0; i < p; i++)
            for (int j = 0; j < p; j++) {
            double tmp = 0.0;
            for (int k = 0; k < p; k++)
                tmp += mm[i + p * k] * L[k + p * j];
            Nt[l + n*i + n*p*j] += tmp;
            }
        }
    }

    for (int i = 0; i < p; i++) {
        double tmp = 0.0;
        for (int j = 0; j < p; j++)
        tmp += Pt[l + n*i + n*p*j] * rt[l + n * j];
        at[l + n*i] += tmp;
    }
    }
    if (var)
    for (int l = 0; l < n; l++) {
        for (int i = 0; i < p; i++)
        for (int j = 0; j < p; j++) {
            double tmp = 0.0;
            for (int k = 0; k < p; k++)
            tmp += Pt[l + n*i + n*p*k] * Nt[l + n*k + n*p*j];
            mm[i + p * j] = tmp;
        }
        for (int i = 0; i < p; i++)
        for (int j = 0; j < p; j++) {
            double tmp = Pt[l + n*i + n*p*j];
            for (int k = 0; k < p; k++)
            tmp -= mm[i + p * k] * Pt[l + n*k + n*p*j];
            Nt[l + n*i + n*p*j] = tmp;
        }
    }
    UNPROTECT(4);
    return res;
}


SEXP
KalmanFore(SEXP nahead, SEXP mod, SEXP update)
{
    mod = PROTECT(duplicate(mod));
    SEXP sZ = getListElement(mod, "Z"), sa = getListElement(mod, "a"), 
    sP = getListElement(mod, "P"), sT = getListElement(mod, "T"), 
    sV = getListElement(mod, "V"), sh = getListElement(mod, "h");

    if (TYPEOF(sZ) != REALSXP ||
    TYPEOF(sa) != REALSXP || TYPEOF(sP) != REALSXP ||
    TYPEOF(sT) != REALSXP || TYPEOF(sV) != REALSXP)
    error(_("invalid argument type"));

    int  n = asInteger(nahead), p = LENGTH(sa);
    double *Z = REAL(sZ), *a = REAL(sa), *P = REAL(sP), *T = REAL(sT),
    *V = REAL(sV), h = asReal(sh);
    double *mm, *anew, *Pnew;

    anew = (double *) R_alloc(p, sizeof(double));
    Pnew = (double *) R_alloc(p * p, sizeof(double));
    mm = (double *) R_alloc(p * p, sizeof(double));
    SEXP res, forecasts, se;
    PROTECT(res = allocVector(VECSXP, 2));
    SET_VECTOR_ELT(res, 0, forecasts = allocVector(REALSXP, n));
    SET_VECTOR_ELT(res, 1, se = allocVector(REALSXP, n));
    {
    SEXP nm = PROTECT(allocVector(STRSXP, 2));
    SET_STRING_ELT(nm, 0, mkChar("pred"));
    SET_STRING_ELT(nm, 1, mkChar("var"));
    setAttrib(res, R_NamesSymbol, nm);
    UNPROTECT(1);
    }
    for (int l = 0; l < n; l++) {
    double fc = 0.0;
    for (int i = 0; i < p; i++) {
        double tmp = 0.0;
        for (int k = 0; k < p; k++)
        tmp += T[i + p * k] * a[k];
        anew[i] = tmp;
        fc += tmp * Z[i];
    }
    for (int i = 0; i < p; i++)
        a[i] = anew[i];
    REAL(forecasts)[l] = fc;

    for (int i = 0; i < p; i++)
        for (int j = 0; j < p; j++) {
        double tmp = 0.0;
        for (int k = 0; k < p; k++)
            tmp += T[i + p * k] * P[k + p * j];
        mm[i + p * j] = tmp;
        }
    for (int i = 0; i < p; i++)
        for (int j = 0; j < p; j++) {
        double tmp = V[i + p * j];
        for (int k = 0; k < p; k++)
            tmp += mm[i + p * k] * T[j + p * k];
        Pnew[i + p * j] = tmp;
        }
    double tmp = h;
    for (int i = 0; i < p; i++)
        for (int j = 0; j < p; j++) {
        P[i + j * p] = Pnew[i + j * p];
        tmp += Z[i] * Z[j] * P[i + j * p];
        }
    REAL(se)[l] = tmp;
    }
    if(asLogical(update)) setAttrib(res, install("mod"), mod);
    UNPROTECT(2);
    return res;
}


static void partrans(int p, double *raw, double *new)
{
    int j, k;
    double a, work[100];

    if(p > 100) error(_("can only transform 100 pars in arima0"));

    /* Step one: map (-Inf, Inf) to (-1, 1) via tanh
       The parameters are now the pacf phi_{kk} */
    for(j = 0; j < p; j++) work[j] = new[j] = tanh(raw[j]);
    /* Step two: run the Durbin-Levinson recursions to find phi_{j.},
       j = 2, ..., p and phi_{p.} are the autoregression coefficients */
    for(j = 1; j < p; j++) {
    a = new[j];
    for(k = 0; k < j; k++)
        work[k] -= a * new[j - k - 1];
    for(k = 0; k < j; k++) new[k] = work[k];
    }
}

SEXP ARIMA_undoPars(SEXP sin, SEXP sarma)
{
    int *arma = INTEGER(sarma), mp = arma[0], mq = arma[1], msp = arma[2],
    v, n = LENGTH(sin);
    double *params, *in = REAL(sin);
    SEXP res = allocVector(REALSXP, n);

    params = REAL(res);
    for (int i = 0; i < n; i++) params[i] = in[i];
    if (mp > 0) partrans(mp, in, params);
    v = mp + mq;
    if (msp > 0) partrans(msp, in + v, params + v);
    return res;
}


SEXP ARIMA_transPars(SEXP sin, SEXP sarma, SEXP strans)
{
    int *arma = INTEGER(sarma), trans = asLogical(strans);
    int mp = arma[0], mq = arma[1], msp = arma[2], msq = arma[3],
    ns = arma[4], i, j, p = mp + ns * msp, q = mq + ns * msq, v;
    double *in = REAL(sin), *params = REAL(sin), *phi, *theta;
    SEXP res, sPhi, sTheta;

    PROTECT(res = allocVector(VECSXP, 2));
    SET_VECTOR_ELT(res, 0, sPhi = allocVector(REALSXP, p));
    SET_VECTOR_ELT(res, 1, sTheta = allocVector(REALSXP, q));
    phi = REAL(sPhi);
    theta = REAL(sTheta);
    if (trans) {
    int n = mp + mq + msp + msq;

    params = (double *) R_alloc(n, sizeof(double));
    for (i = 0; i < n; i++) params[i] = in[i];
    if (mp > 0) partrans(mp, in, params);
    v = mp + mq;
    if (msp > 0) partrans(msp, in + v, params + v);
    }
    if (ns > 0) {
    /* expand out seasonal ARMA models */
    for (i = 0; i < mp; i++) phi[i] = params[i];
    for (i = 0; i < mq; i++) theta[i] = params[i + mp];
    for (i = mp; i < p; i++) phi[i] = 0.0;
    for (i = mq; i < q; i++) theta[i] = 0.0;
    for (j = 0; j < msp; j++) {
        phi[(j + 1) * ns - 1] += params[j + mp + mq];
        for (i = 0; i < mp; i++)
        phi[(j + 1) * ns + i] -= params[i] * params[j + mp + mq];
    }
    for (j = 0; j < msq; j++) {
        theta[(j + 1) * ns - 1] += params[j + mp + mq + msp];
        for (i = 0; i < mq; i++)
        theta[(j + 1) * ns + i] += params[i + mp] *
            params[j + mp + mq + msp];
    }
    } else {
    for (i = 0; i < mp; i++) phi[i] = params[i];
    for (i = 0; i < mq; i++) theta[i] = params[i + mp];
    }
    UNPROTECT(1);
    return res;
}

#if !defined(atanh) && defined(HAVE_DECL_ATANH) && !HAVE_DECL_ATANH
extern double atanh(double x);
#endif
static void invpartrans(int p, double *phi, double *new)
{
    int j, k;
    double a, work[100];

    if(p > 100) error(_("can only transform 100 pars in arima0"));

    for(j = 0; j < p; j++) work[j] = new[j] = phi[j];
    /* Run the Durbin-Levinson recursions backwards
       to find the PACF phi_{j.} from the autoregression coefficients */
    for(j = p - 1; j > 0; j--) {
    a = new[j];
    for(k = 0; k < j; k++)
        work[k]  = (new[k] + a * new[j - k - 1]) / (1 - a * a);
    for(k = 0; k < j; k++) new[k] = work[k];
    }
    for(j = 0; j < p; j++) new[j] = atanh(new[j]);
}

SEXP ARIMA_Invtrans(SEXP in, SEXP sarma)
{
    int *arma = INTEGER(sarma), mp = arma[0], mq = arma[1], msp = arma[2],
    i, v, n = LENGTH(in);
    SEXP y = allocVector(REALSXP, n);
    double *raw = REAL(in), *new = REAL(y);

    for(i = 0; i < n; i++) new[i] = raw[i];
    if (mp > 0) invpartrans(mp, raw, new);
    v = mp + mq;
    if (msp > 0) invpartrans(msp, raw + v, new + v);
    return y;
}

#define eps 1e-3
SEXP ARIMA_Gradtrans(SEXP in, SEXP sarma)
{
    int *arma = INTEGER(sarma), mp = arma[0], mq = arma[1], msp = arma[2],
    n = LENGTH(in);
    SEXP y = allocMatrix(REALSXP, n, n);
    double *raw = REAL(in), *A = REAL(y), w1[100], w2[100], w3[100];

    for (int i = 0; i < n; i++)
    for (int j = 0; j < n; j++)
        A[i + j*n] = (i == j);
    if(mp > 0) {
    for (int i = 0; i < mp; i++) w1[i] = raw[i];
    partrans(mp, w1, w2);
    for (int i = 0; i < mp; i++) {
        w1[i] += eps;
        partrans(mp, w1, w3);
        for (int j = 0; j < mp; j++) A[i + j*n] = (w3[j] - w2[j])/eps;
        w1[i] -= eps;
    }
    }
    if(msp > 0) {
    int v = mp + mq;
    for (int i = 0; i < msp; i++) w1[i] = raw[i + v];
    partrans(msp, w1, w2);
    for(int i = 0; i < msp; i++) {
        w1[i] += eps;
        partrans(msp, w1, w3);
        for(int j = 0; j < msp; j++)
        A[i + v + (j+v)*n] = (w3[j] - w2[j])/eps;
        w1[i] -= eps;
    }
    }
    return y;
}


SEXP
ARIMA_Like(SEXP sy, SEXP mod, SEXP sUP, SEXP giveResid)
{
    SEXP sPhi = getListElement(mod, "phi"), 
    sTheta = getListElement(mod, "theta"), 
    sDelta = getListElement(mod, "Delta"),
    sa = getListElement(mod, "a"),
    sP = getListElement(mod, "P"),
    sPn = getListElement(mod, "Pn");

    if (TYPEOF(sPhi) != REALSXP || TYPEOF(sTheta) != REALSXP ||
    TYPEOF(sDelta) != REALSXP || TYPEOF(sa) != REALSXP ||
    TYPEOF(sP) != REALSXP || TYPEOF(sPn) != REALSXP)
    error(_("invalid argument type"));

    SEXP res, nres, sResid = R_NilValue;
    int n = LENGTH(sy), rd = LENGTH(sa), p = LENGTH(sPhi),
    q = LENGTH(sTheta), d = LENGTH(sDelta), r = rd - d;
    double *y = REAL(sy), *a = REAL(sa), *P = REAL(sP), *Pnew = REAL(sPn);
    double *phi = REAL(sPhi), *theta = REAL(sTheta), *delta = REAL(sDelta);
    double sumlog = 0.0, ssq = 0, *anew, *mm = NULL, *M;
    int nu = 0;
    Rboolean useResid = asLogical(giveResid);
    double *rsResid = NULL /* -Wall */;

    anew = (double *) R_alloc(rd, sizeof(double));
    M = (double *) R_alloc(rd, sizeof(double));
    if (d > 0) mm = (double *) R_alloc(rd * rd, sizeof(double));

    if (useResid) {
    PROTECT(sResid = allocVector(REALSXP, n));
    rsResid = REAL(sResid);
    }

    for (int l = 0; l < n; l++) {
    for (int i = 0; i < r; i++) {
        double tmp = (i < r - 1) ? a[i + 1] : 0.0;
        if (i < p) tmp += phi[i] * a[0];
        anew[i] = tmp;
    }
    if (d > 0) {
        for (int i = r + 1; i < rd; i++) anew[i] = a[i - 1];
        double tmp = a[0];
        for (int i = 0; i < d; i++) tmp += delta[i] * a[r + i];
        anew[r] = tmp;
    }
    if (l > asInteger(sUP)) {
        if (d == 0) {
        for (int i = 0; i < r; i++) {
            double vi = 0.0;
            if (i == 0) vi = 1.0; else if (i - 1 < q) vi = theta[i - 1];
            for (int j = 0; j < r; j++) {
            double tmp = 0.0;
            if (j == 0) tmp = vi; else if (j - 1 < q) tmp = vi * theta[j - 1];
            if (i < p && j < p) tmp += phi[i] * phi[j] * P[0];
            if (i < r - 1 && j < r - 1) tmp += P[i + 1 + r * (j + 1)];
            if (i < p && j < r - 1) tmp += phi[i] * P[j + 1];
            if (j < p && i < r - 1) tmp += phi[j] * P[i + 1];
            Pnew[i + r * j] = tmp;
            }
        }
        } else {
        /* mm = TP */
        for (int i = 0; i < r; i++)
            for (int j = 0; j < rd; j++) {
            double tmp = 0.0;
            if (i < p) tmp += phi[i] * P[rd * j];
            if (i < r - 1) tmp += P[i + 1 + rd * j];
            mm[i + rd * j] = tmp;
            }
        for (int j = 0; j < rd; j++) {
            double tmp = P[rd * j];
            for (int k = 0; k < d; k++)
            tmp += delta[k] * P[r + k + rd * j];
            mm[r + rd * j] = tmp;
        }
        for (int i = 1; i < d; i++)
            for (int j = 0; j < rd; j++)
            mm[r + i + rd * j] = P[r + i - 1 + rd * j];

        /* Pnew = mmT' */
        for (int i = 0; i < r; i++)
            for (int j = 0; j < rd; j++) {
            double tmp = 0.0;
            if (i < p) tmp += phi[i] * mm[j];
            if (i < r - 1) tmp += mm[rd * (i + 1) + j];
            Pnew[j + rd * i] = tmp;
            }
        for (int j = 0; j < rd; j++) {
            double tmp = mm[j];
            for (int k = 0; k < d; k++)
            tmp += delta[k] * mm[rd * (r + k) + j];
            Pnew[rd * r + j] = tmp;
        }
        for (int i = 1; i < d; i++)
            for (int j = 0; j < rd; j++)
            Pnew[rd * (r + i) + j] = mm[rd * (r + i - 1) + j];
        /* Pnew <- Pnew + (1 theta) %o% (1 theta) */
        for (int i = 0; i <= q; i++) {
            double vi = (i == 0) ? 1. : theta[i - 1];
            for (int j = 0; j <= q; j++)
            Pnew[i + rd * j] += vi * ((j == 0) ? 1. : theta[j - 1]);
        }
        }
    }
    if (!ISNAN(y[l])) {
        double resid = y[l] - anew[0];
        for (int i = 0; i < d; i++)
        resid -= delta[i] * anew[r + i];

        for (int i = 0; i < rd; i++) {
        double tmp = Pnew[i];
        for (int j = 0; j < d; j++)
            tmp += Pnew[i + (r + j) * rd] * delta[j];
        M[i] = tmp;
        }

        double gain = M[0];
        for (int j = 0; j < d; j++) gain += delta[j] * M[r + j];
        if(gain < 1e4) {
        nu++;
        ssq += resid * resid / gain;
        sumlog += log(gain);
        }
        if (useResid) rsResid[l] = resid / sqrt(gain);
        for (int i = 0; i < rd; i++)
        a[i] = anew[i] + M[i] * resid / gain;
        for (int i = 0; i < rd; i++)
        for (int j = 0; j < rd; j++)
            P[i + j * rd] = Pnew[i + j * rd] - M[i] * M[j] / gain;
    } else {
        for (int i = 0; i < rd; i++) a[i] = anew[i];
        for (int i = 0; i < rd * rd; i++) P[i] = Pnew[i];
        if (useResid) rsResid[l] = NA_REAL;
    }
    }

    if (useResid) {
    PROTECT(res = allocVector(VECSXP, 3));
    SET_VECTOR_ELT(res, 0, nres = allocVector(REALSXP, 3));
    REAL(nres)[0] = ssq;
    REAL(nres)[1] = sumlog;
    REAL(nres)[2] = (double) nu;
    SET_VECTOR_ELT(res, 1, sResid);
    UNPROTECT(2);
    return res;
    } else {
    nres = allocVector(REALSXP, 3);
    REAL(nres)[0] = ssq;
    REAL(nres)[1] = sumlog;
    REAL(nres)[2] = (double) nu;
    return nres;
    }
}

/* do differencing here */
/* arma is p, q, sp, sq, ns, d, sd */
SEXP
ARIMA_CSS(SEXP sy, SEXP sarma, SEXP sPhi, SEXP sTheta,
      SEXP sncond, SEXP giveResid)
{
    SEXP res, sResid = R_NilValue;
    double ssq = 0.0, *y = REAL(sy), tmp;
    double *phi = REAL(sPhi), *theta = REAL(sTheta), *w, *resid;
    int n = LENGTH(sy), *arma = INTEGER(sarma), p = LENGTH(sPhi),
    q = LENGTH(sTheta), ncond = asInteger(sncond);
    int ns, nu = 0;
    Rboolean useResid = asLogical(giveResid);

    w = (double *) R_alloc(n, sizeof(double));
    for (int l = 0; l < n; l++) w[l] = y[l];
    for (int i = 0; i < arma[5]; i++)
    for (int l = n - 1; l > 0; l--) w[l] -= w[l - 1];
    ns = arma[4];
    for (int i = 0; i < arma[6]; i++)
    for (int l = n - 1; l >= ns; l--) w[l] -= w[l - ns];

    PROTECT(sResid = allocVector(REALSXP, n));
    resid = REAL(sResid);
    if (useResid) for (int l = 0; l < ncond; l++) resid[l] = 0;

    for (int l = ncond; l < n; l++) {
    tmp = w[l];
    for (int j = 0; j < p; j++) tmp -= phi[j] * w[l - j - 1];
    for (int j = 0; j < min(l - ncond, q); j++)
        tmp -= theta[j] * resid[l - j - 1];
    resid[l] = tmp;
    if (!ISNAN(tmp)) {
        nu++;
        ssq += tmp * tmp;
    }
    }
    if (useResid) {
    PROTECT(res = allocVector(VECSXP, 2));
    SET_VECTOR_ELT(res, 0, ScalarReal(ssq / (double) (nu)));
    SET_VECTOR_ELT(res, 1, sResid);
    UNPROTECT(2);
    return res;
    } else {
    UNPROTECT(1);
    return ScalarReal(ssq / (double) (nu));
    }
}

SEXP TSconv(SEXP a, SEXP b)
{
    int na, nb, nab;
    SEXP ab;
    double *ra, *rb, *rab;

    PROTECT(a = coerceVector(a, REALSXP));
    PROTECT(b = coerceVector(b, REALSXP));
    na = LENGTH(a);
    nb = LENGTH(b);
    nab = na + nb - 1;
    PROTECT(ab = allocVector(REALSXP, nab));
    ra = REAL(a); rb = REAL(b); rab = REAL(ab);
    for (int i = 0; i < nab; i++) rab[i] = 0.0;
    for (int i = 0; i < na; i++)
    for (int j = 0; j < nb; j++)
        rab[i + j] += ra[i] * rb[j];
    UNPROTECT(3);
    return (ab);
}

/* based on code from AS154 */

static void
inclu2(size_t np, double *xnext, double *xrow, double ynext,
       double *d, double *rbar, double *thetab)
{
    double cbar, sbar, di, xi, xk, rbthis, dpi;
    size_t i, k, ithisr;

/*   This subroutine updates d, rbar, thetab by the inclusion
     of xnext and ynext. */

    for (i = 0; i < np; i++) xrow[i] = xnext[i];

    for (ithisr = 0, i = 0; i < np; i++) {
    if (xrow[i] != 0.0) {
        xi = xrow[i];
        di = d[i];
        dpi = di + xi * xi;
        d[i] = dpi;
        cbar = di / dpi;
        sbar = xi / dpi;
        for (k = i + 1; k < np; k++) {
        xk = xrow[k];
        rbthis = rbar[ithisr];
        xrow[k] = xk - xi * rbthis;
        rbar[ithisr++] = cbar * rbthis + sbar * xk;
        }
        xk = ynext;
        ynext = xk - xi * thetab[i];
        thetab[i] = cbar * thetab[i] + sbar * xk;
        if (di == 0.0) return;
    } else
        ithisr = ithisr + np - i - 1;
    }
}

#ifdef DEBUG_Q0bis
# include <R_ext/Print.h>
  double chk_V(double v[], char* nm, int jj, int len) { 
    // len = length(<vector>)  <==> index must be in  {0, len-1}
    if(jj < 0 || jj >= len)
    REprintf(" %s[%2d]\n", nm, jj); 
    return(v[jj]); 
  }
#endif

/*
  Matwey V. Kornilov's implementation of algorithm by
  Dr. Raphael Rossignol
  See https://bugs.r-project.org/show_bug.cgi?id=14682 for details.
*/
SEXP getQ0bis(SEXP sPhi, SEXP sTheta, SEXP sTol)
{
    SEXP res;
    int p = LENGTH(sPhi), q = LENGTH(sTheta);
    double *phi = REAL(sPhi), *theta = REAL(sTheta); // tol = REAL(sTol)[0];

    int i,j, r = max(p, q + 1);

    /* Final result is block product 
     *   Q0 = A1 SX A1^T + A1 SXZ A2^T + (A1 SXZ A2^T)^T + A2 A2^T ,
     * where A1 [i,j] = phi[i+j],
     *       A2 [i,j] = ttheta[i+j],  and SX, SXZ are defined below */
    PROTECT(res = allocMatrix(REALSXP, r, r));
    double *P = REAL(res);

    /* Clean P */
    Memzero(P, r*r);

#ifdef DEBUG_Q0bis
#define _ttheta(j) chk_V(ttheta, "ttheta", j, q+1)// was  r
#define _tphi(j)   chk_V(tphi,   "tphi",   j, p+1)
#define _rrz(j)    chk_V(rrz,    "rrz",    j, q)
#else
#define _ttheta(j) ttheta[j]
#define _tphi(j) tphi[j]
#define _rrz(j)  rrz [j]
#endif

    double *ttheta = (double *) R_alloc(q + 1, sizeof(double));
    /* Init ttheta = c(1, theta) */
    ttheta[0] = 1.;
    for (i = 1; i < q + 1; ++i) ttheta[i] = theta[i - 1];

    if( p > 0 ) {
    int r2 = max(p + q, p + 1);
    SEXP sgam = PROTECT(allocMatrix(REALSXP, r2, r2)),
        sg = PROTECT(allocVector(REALSXP, r2));
    double *gam = REAL(sgam);
    double *g = REAL(sg);
    double *tphi = (double *) R_alloc(p + 1, sizeof(double));
    /* Init tphi = c(1, -phi) */
    tphi[0] = 1.;
    for (i = 1; i < p + 1; ++i) tphi[i] = -phi[i - 1];

    /* Compute the autocovariance function of U, the AR part of X */

    /* Gam := C1 + C2 ; initialize */
    Memzero(gam, r2*r2);

    /* C1[E] */
    for (j = 0; j < r2; ++j)
        for (i = j; i < r2 && i - j < p + 1; ++i)
        gam[j*r2 + i] += _tphi(i-j);

    /* C2[E] */
    for (i = 0; i < r2; ++i)
        for (j = 1; j < r2 && i + j < p + 1; ++j)
        gam[j*r2 + i] += _tphi(i+j);

    /* Initialize g = (1 0 0 .... 0) */
    g[0] = 1.;
    for (i = 1; i < r2; ++i)
        g[i] = 0.;

    /* rU = solve(Gam, g)  -> solve.default() -> .Internal(La_solve, .,)
     * --> fiddling with R-objects -> C and then F77_CALL(.) of dgesv, dlange, dgecon
     * FIXME: call these directly here, possibly even use 'info' instead of error(.)
     * e.g., in case of exact singularity.
     */
    SEXP callS = PROTECT(lang4(install("solve.default"), sgam, sg, sTol)),
        su = PROTECT(eval(callS, R_BaseEnv));
    double *u = REAL(su);
    /* SX = A SU A^T */
    /* A[i,j]  = ttheta[j-i] */
    /* SU[i,j] = u[abs(i-j)] */
    /* Q0 += ( A1 SX A1^T == A1 A SU A^T A1^T) */
    // (relying on good compiler optimization here:)
    for (i = 0; i < r; ++i)
        for (j = i; j < r; ++j)
        for (int k = 0; i + k < p; ++k)
            for (int L = k; L - k < q + 1; ++L)
            for (int m = 0; j + m < p; ++m)
                for (int n = m; n - m < q + 1; ++n)
                P[r*i + j] += phi[i + k] * phi[j + m] *
                    _ttheta(L - k) * _ttheta(n - m) * u[abs(L - n)];
    UNPROTECT(4);
    /* Compute correlation matrix between X and Z */
    /* forwardsolve(C1, g) */
    /* C[i,j] = tphi[i-j] */
    /* g[i] = _ttheta(i) */
    double *rrz = (double *) R_alloc(q, sizeof(double));
    if(q > 0) {
        for (i = 0; i < q; ++i) {
        rrz[i] = _ttheta(i);
        for (j = max(0, i - p); j < i; ++j)
            rrz[i] -= _rrz(j) * _tphi(i-j);
        }
    }

    /* Q0 += A1 SXZ A2^T + (A1 SXZ A2^T)^T */
    /* SXZ[i,j] = rrz[j-i-1], j > 0 */
    for (i = 0; i < r; ++i)
        for (j = i; j < r; ++j) {
        int k, L;
        for (k = 0; i + k < p; ++k)
            for (L = k+1; j + L < q + 1; ++L)
            P[r*i + j] += phi[i + k] * _ttheta(j + L) * _rrz(L - k - 1);
        for (k = 0; j + k < p; ++k)
            for (L = k+1; i + L < q + 1; ++L)
            P[r*i + j] += phi[j + k] * _ttheta(i + L) * _rrz(L - k - 1);
        }
    } // end if(p > 0)

    /* Q0 += A2 A2^T */
    for (i = 0; i < r; ++i)
    for (j = i; j < r; ++j)
        for (int k = 0; j + k < q + 1; ++k)
         P[r*i + j] += _ttheta(i + k) * _ttheta(j + k);

    /* Symmetrize result */
    for (i = 0; i < r; ++i)
    for (j = i+1; j < r; ++j)
        P[r*j + i] = P[r*i + j];

    UNPROTECT(1);
    return res;
}

SEXP getQ0(SEXP sPhi, SEXP sTheta)
{
    SEXP res;
    int  p = LENGTH(sPhi), q = LENGTH(sTheta);
    double *phi = REAL(sPhi), *theta = REAL(sTheta);

    /* thetab[np], xnext[np], xrow[np].  rbar[rbar] */
    /* NB: nrbar could overflow */
    int r = max(p, q + 1);
    size_t np = r * (r + 1) / 2, nrbar = np * (np - 1) / 2, npr, npr1;
    size_t indi, indj, indn, i, j, ithisr, ind, ind1, ind2, im, jm;


    /* This is the limit using an int index.  We could use
       size_t and get more on a 64-bit system,
       but there seems no practical need. */
    if(r > 350) error(_("maximum supported lag is 350"));
    double *xnext, *xrow, *rbar, *thetab, *V;
    xnext = (double *) R_alloc(np, sizeof(double));
    xrow = (double *) R_alloc(np, sizeof(double));
    rbar = (double *) R_alloc(nrbar, sizeof(double));
    thetab = (double *) R_alloc(np, sizeof(double));
    V = (double *) R_alloc(np, sizeof(double));
    for (ind = 0, j = 0; j < r; j++) {
    double vj = 0.0;
    if (j == 0) vj = 1.0; else if (j - 1 < q) vj = theta[j - 1];
    for (i = j; i < r; i++) {
        double vi = 0.0;
        if (i == 0) vi = 1.0; else if (i - 1 < q) vi = theta[i - 1];
        V[ind++] = vi * vj;
    }
    }

    PROTECT(res = allocMatrix(REALSXP, r, r));
    double *P = REAL(res);

    if (r == 1) {
    if (p == 0) P[0] = 1.0; // PR#16419
    else P[0] = 1.0 / (1.0 - phi[0] * phi[0]);
    UNPROTECT(1);
    return res;
    }
    if (p > 0) {
/*      The set of equations s * vec(P0) = vec(v) is solved for
    vec(P0).  s is generated row by row in the array xnext.  The
    order of elements in P is changed, so as to bring more leading
    zeros into the rows of s. */

    for (i = 0; i < nrbar; i++) rbar[i] = 0.0;
    for (i = 0; i < np; i++) {
        P[i] = 0.0;
        thetab[i] = 0.0;
        xnext[i] = 0.0;
    }
    ind = 0;
    ind1 = -1;
    npr = np - r;
    npr1 = npr + 1;
    indj = npr;
    ind2 = npr - 1;
    for (j = 0; j < r; j++) {
        double phij = (j < p) ? phi[j] : 0.0;
        xnext[indj++] = 0.0;
        indi = npr1 + j;
        for (i = j; i < r; i++) {
        double ynext = V[ind++];
        double phii = (i < p) ? phi[i] : 0.0;
        if (j != r - 1) {
            xnext[indj] = -phii;
            if (i != r - 1) {
            xnext[indi] -= phij;
            xnext[++ind1] = -1.0;
            }
        }
        xnext[npr] = -phii * phij;
        if (++ind2 >= np) ind2 = 0;
        xnext[ind2] += 1.0;
        inclu2(np, xnext, xrow, ynext, P, rbar, thetab);
        xnext[ind2] = 0.0;
        if (i != r - 1) {
            xnext[indi++] = 0.0;
            xnext[ind1] = 0.0;
        }
        }
    }

    ithisr = nrbar - 1;
    im = np - 1;
    for (i = 0; i < np; i++) {
        double bi = thetab[im];
        for (jm = np - 1, j = 0; j < i; j++)
        bi -= rbar[ithisr--] * P[jm--];
        P[im--] = bi;
    }

/*        now re-order p. */

    ind = npr;
    for (i = 0; i < r; i++) xnext[i] = P[ind++];
    ind = np - 1;
    ind1 = npr - 1;
    for (i = 0; i < npr; i++) P[ind--] = P[ind1--];
    for (i = 0; i < r; i++) P[i] = xnext[i];
    } else {

/* P0 is obtained by backsubstitution for a moving average process. */

    indn = np;
    ind = np;
    for (i = 0; i < r; i++)
        for (j = 0; j <= i; j++) {
        --ind;
        P[ind] = V[ind];
        if (j != 0) P[ind] += P[--indn];
        }
    }
    /* now unpack to a full matrix */
    for (i = r - 1, ind = np; i > 0; i--)
    for (j = r - 1; j >= i; j--)
        P[r * i + j] = P[--ind];
    for (i = 0; i < r - 1; i++)
    for (j = i + 1; j < r; j++)
        P[i + r * j] = P[j + r * i];
    UNPROTECT(1);
    return res;
}