/* ENO interpolation in 3-D */
/*
  Copyright (C) 2004 University of Texas at Austin

  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, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#include "eno2.h"
#include "eno3.h"

#include "alloc.h"
#include "ngspice/cm.h"


struct Eno3 {
    int order, ng, n1, n2, n3;
    sf_eno **ent;
    sf_eno2 jnt;
    double **f, **f1;
};
/* concrete data type */

sf_eno3
sf_eno3_init(int order,             /* interpolation order */
             int n1, int n2, int n3 /* data dimensions */)
/*< Initialize interpolation object >*/
{
    int xrc = 0;
    sf_eno3 pnt = (sf_eno3) NULL;

    /* Allocate base structrue */
    if ((pnt = (sf_eno3) sf_alloc (1, sizeof *pnt)) == (sf_eno3) NULL) {
        cm_message_printf("Unable to allocate sf_eno3 structure "
                "in sf_eno3_init");
        xrc = -1;
        goto EXITPOINT;
    }

    pnt->order = order;
    pnt->n1 = n1;
    pnt->n2 = n2;
    pnt->n3 = n3;
    pnt->ng = 2 * order - 2;

    if (pnt->ng > n2 || pnt->ng > n3) {
        cm_message_printf("%s: ng=%d is too big", __FILE__, pnt->ng);
        xrc = -1;
        goto EXITPOINT;
    }

    if ((pnt->jnt = sf_eno2_init(
            order, pnt->ng, pnt->ng)) == (sf_eno2) NULL) {
        cm_message_printf("Unable to initialize field jnt "
                "in sf_eno3_init");
        xrc = -1;
        goto EXITPOINT;
    }

    if ((pnt->f = sf_doublealloc2(pnt->ng, pnt->ng)) == (double **) NULL) {
        cm_message_printf("Unable to allocate field f in sf_eno3_init()");
        xrc = -1;
        goto EXITPOINT;
    }

    if ((pnt->f1 = sf_doublealloc2(pnt->ng, pnt->ng)) == (double **) NULL) {
        cm_message_printf("Unable to allocate field f1 in sf_eno3_init()");
        xrc = -1;
        goto EXITPOINT;
    }

    if ((pnt->ent = (sf_eno **) sf_alloc(
            n3, sizeof(sf_eno*))) == (sf_eno **) NULL) {
        cm_message_printf("Unable to allocate field ent in sf_eno3_init()");
        xrc = -1;
        goto EXITPOINT;
    }

    {
        int i3;
        for (i3 = 0; i3 < n3; i3++) {
            if ((pnt->ent[i3] = (sf_eno*) sf_alloc(
                    n2, sizeof(sf_eno))) == (sf_eno *) NULL) {
                cm_message_printf("Unable to allocate field ent[%d] "
                        "in sf_eno3_init()", i3);
                xrc = -1;
                goto EXITPOINT;
            }
            int i2;
            for (i2 = 0; i2 < n2; i2++) {
                if ((pnt->ent[i3][i2] = sf_eno_init(
                        order, n1)) == (sf_eno) NULL) {
                    cm_message_printf("Unable to initialize field "
                            "ent[%d][%d] in sf_eno3_init()",
                            i2, i3);
                    xrc = -1;
                    goto EXITPOINT;
                }
            }
        }
    }

EXITPOINT:
    if (xrc != 0) {
        if (pnt != (sf_eno3) NULL) {
            sf_eno3_close(pnt);
            free(pnt);
            pnt = (sf_eno3) NULL;
        }
    }
    return pnt;
}

void
sf_eno3_set(sf_eno3 pnt, double ***c /* data [n3][n2][n1] */)
/*< Set the interpolation table. c can be changed or freed afterwords. >*/
{
    int i2, i3;

    for (i3 = 0; i3 < pnt->n3; i3++)
        for (i2 = 0; i2 < pnt->n2; i2++)
            sf_eno_set (pnt->ent[i3][i2], c[i3][i2]);
}

void
sf_eno3_close(sf_eno3 pnt)
/*< Free internal storage. >*/
{
    int i2, i3;

    if (!pnt)
        return;

    sf_eno2_close (pnt->jnt);
    for (i3 = 0; i3 < pnt->n3; i3++) {
        for (i2 = 0; i2 < pnt->n2; i2++)
            sf_eno_close (pnt->ent[i3][i2]);
        free (pnt->ent[i3]);
    }
    free (pnt->ent);
    free (pnt->f[0]);
    free (pnt->f);
    free (pnt->f1[0]);
    free (pnt->f1);
    free (pnt);
}

void
sf_eno3_apply(sf_eno3 pnt,
              int i, int j, int k,          /* grid location */
              double x, double y, double z, /* offsets from grid */
              double *f,                    /* output data */
              double *f1,                   /* output derivative [3] */
              der what                      /* to compute [FUNC|DER|BOTH] */)
/*< Apply interpolation. >*/
{
    int i2, i3, b2, b3;
    double g;

    if (j - pnt->order + 2 < 0)
        b2 = 0;
    else if (j + pnt->order - 1 > pnt->n2 - 1)
        b2 = pnt->n2 - pnt->ng;
    else
        b2 = j - pnt->order + 2;

    j -= b2;


    if (k - pnt->order + 2 < 0)
        b3 = 0;
    else if (k + pnt->order - 1 > pnt->n3 - 1)
        b3 = pnt->n3 - pnt->ng;
    else
        b3 = k - pnt->order + 2;

    k -= b3;

    for (i3 = 0; i3 < pnt->ng; i3++)
        for (i2 = 0; i2 < pnt->ng; i2++)
            sf_eno_apply (pnt->ent[b3 + i3][b2 + i2], i, x,
                          &(pnt->f[i3][i2]),
                          &(pnt->f1[i3][i2]),
                          (what==FUNC ? FUNC : BOTH));

    sf_eno2_set (pnt->jnt, pnt->f);
    sf_eno2_apply (pnt->jnt, j, k, y, z, f, f1 + 1, what);

    if (what != FUNC) {
        sf_eno2_set (pnt->jnt, pnt->f1);
        sf_eno2_apply (pnt->jnt, j, k, y, z, f1, &g, FUNC);
    }
}

/*      $Id: eno3.c 4148 2009-02-09 03:55:32Z sfomel $   */
