/**********
Copyright 1990 Regents of the University of California.  All rights reserved.
Author: 1985 Wayne A. Christopher, U. C. Berkeley CAD Group
**********/

/*
 * Routines for dealing with the vector database.
 */

#include "ngspice/ngspice.h"
#include "ngspice/cpdefs.h"
#include "ngspice/ftedefs.h"
#include "ngspice/dvec.h"
#include "ngspice/sim.h"
#include "ngspice/stringskip.h"

#include "circuits.h"
#include "completion.h"
#include "variable.h"
#include "dimens.h"
#include "../misc/misc_time.h"
#include "vectors.h"
#include "ngspice/dstring.h"
#include "plotting/plotting.h"

#ifdef XSPICE
/* gtri - begin - add function prototype for EVTfindvec */
struct dvec *EVTfindvec(char *node);
/* gtri - end   - add function prototype for EVTfindvec */
#endif


static void
vec_rebuild_lookup_table(struct plot *pl)
{
    int cnt;                            /* count entries */
    struct dvec *d;                     /* dynamic vector */
    NGHASHPTR lookup_p;                 /* lookup table for speed */
    SPICE_DSTRING dbuf;                 /* dynamic buffer */
    char *lower_name;                   /* lower case name */

    if (pl->pl_lookup_table) {
        nghash_empty(pl->pl_lookup_table, NULL, NULL);
    } else {
        cnt = 0;
        for (d = pl->pl_dvecs; d; d = d->v_next)
            cnt++;
        pl->pl_lookup_table = nghash_init(cnt);
        /* allow multiple entries */
        nghash_unique(pl->pl_lookup_table, FALSE);
    }
    lookup_p = pl->pl_lookup_table;
    spice_dstring_init(&dbuf);
    for (d = pl->pl_dvecs; d; d = d->v_next) {
        spice_dstring_reinit(&dbuf);
        lower_name = spice_dstring_append_lower(&dbuf, d->v_name, -1);
        nghash_insert(lookup_p, lower_name, d);
    }
    spice_dstring_free(&dbuf);
    pl->pl_lookup_valid = TRUE;
}


/* Find a named vector in a plot. We are careful to copy the vector if
 * v_link2 is set, because otherwise we will get screwed up.  */
static struct dvec *
findvec(char *word, struct plot *pl)
{
    SPICE_DSTRING dbuf;                 /* dynamic buffer */
    char *lower_name;                   /* lower case name */
    char *node_name;
    struct dvec *d, *newv = NULL, *end = NULL, *v;

    if (pl == NULL)
        return (NULL);

    if (cieq(word, "all")) {
        for (d = pl->pl_dvecs; d; d = d->v_next) {
            if (d->v_flags & VF_PERMANENT) {
                if (d->v_link2) {
                    v = vec_copy(d);
                    vec_new(v);
                } else {
                    v = d;
                }
                if (end)
                    end->v_link2 = v;
                else
                    newv = v;
                end = v;
            }
        }
        return (newv);
    }

    if (cieq(word, "allv")) {
        for (d = pl->pl_dvecs; d; d = d->v_next) {
            if ((d->v_flags & VF_PERMANENT) && (d->v_type == SV_VOLTAGE)) {
                if (d->v_link2) {
                    v = vec_copy(d);
                    vec_new(v);
                } else {
                    v = d;
                }
                if (end)
                    end->v_link2 = v;
                else
                    newv = v;
                end = v;
            }
        }
        return (newv);
    }

    if (cieq(word, "alli")) {
        for (d = pl->pl_dvecs; d; d = d->v_next) {
            if ((d->v_flags & VF_PERMANENT) && (d->v_type == SV_CURRENT)) {
                if (d->v_link2) {
                    v = vec_copy(d);
                    vec_new(v);
                } else {
                    v = d;
                }
                if (end)
                    end->v_link2 = v;
                else
                    newv = v;
                end = v;
            }
        }
        return (newv);
    }

    if (cieq(word, "ally")) {
        for (d = pl->pl_dvecs; d; d = d->v_next) {
            if ((d->v_flags & VF_PERMANENT) && (!cieq(d->v_name, pl->pl_scale->v_name))) {
                if (d->v_link2) {
                    v = vec_copy(d);
                    vec_new(v);
                } else {
                    v = d;
                }
                if (end)
                    end->v_link2 = v;
                else
                    newv = v;
                end = v;
            }
        }
        return (newv);
    }

    if (!pl->pl_lookup_valid)
        vec_rebuild_lookup_table(pl);

    spice_dstring_init(&dbuf);
    lower_name = spice_dstring_append_lower(&dbuf, word, -1);

    for (d = nghash_find(pl->pl_lookup_table, lower_name);
         d;
         d = nghash_find_again(pl->pl_lookup_table, lower_name))
    {
        if (d->v_flags & VF_PERMANENT)
            break;
    }

    if (!d) {
        spice_dstring_reinit(&dbuf);
        spice_dstring_append(&dbuf, "v(", -1);
        spice_dstring_append_lower(&dbuf, word, -1);
        node_name = spice_dstring_append_char(&dbuf, ')');

        for (d = nghash_find(pl->pl_lookup_table, node_name);
             d;
             d = nghash_find_again(pl->pl_lookup_table, node_name))
        {
            if (d->v_flags & VF_PERMANENT)
                break;
        }
    }

    spice_dstring_free(&dbuf);

#ifdef XSPICE
    /* gtri - begin - Add processing for getting event-driven vector */

    if (!d)
        d = EVTfindvec(word);

    /* gtri - end   - Add processing for getting event-driven vector */
#endif
    if (d && d->v_link2) {
        d = vec_copy(d);
        vec_new(d);
    }

    return (d);
}


/* If there are imbedded numeric strings, compare them numerically, not
 * alphabetically.
 */
static int
namecmp(const void *a, const void *b)
{
    int i, j;

    const char *s = (const char *) a;
    const char *t = (const char *) b;

    for (;;) {
        while ((*s == *t) && !isdigit_c(*s) && *s)
            s++, t++;
        if (!*s)
            return (0);
        if ((*s != *t) && (!isdigit_c(*s) || !isdigit_c(*t)))
            return (*s - *t);

        /* The beginning of a number... Grab the two numbers and then
         * compare them...  */
        for (i = 0; isdigit_c(*s); s++)
            i = i * 10 + *s - '0';
        for (j = 0; isdigit_c(*t); t++)
            j = j * 10 + *t - '0';

        if (i != j)
            return (i - j);
    }
}


static int
veccmp(const void *a, const void *b)
{
    int i;
    struct dvec **d1 = (struct dvec **) a;
    struct dvec **d2 = (struct dvec **) b;

    if ((i = namecmp((*d1)->v_plot->pl_typename,
                     (*d2)->v_plot->pl_typename)) != 0)
        return (i);
    return (namecmp((*d1)->v_name, (*d2)->v_name));
}


/* Sort all the vectors in d, first by plot name and then by vector
 * name.  Do the right thing with numbers.  */
static struct dvec *
sortvecs(struct dvec *d)
{
    struct dvec **array, *t;
    int i, j;

    for (t = d, i = 0; t; t = t->v_link2)
        i++;
    if (i < 2)
        return (d);
    array = TMALLOC(struct dvec *, i);
    for (t = d, i = 0; t; t = t->v_link2)
        array[i++] = t;

    qsort(array, (size_t) i, sizeof(struct dvec *), veccmp);

    /* Now string everything back together... */
    for (j = 0; j < i - 1; j++)
        array[j]->v_link2 = array[j + 1];
    array[j]->v_link2 = NULL;
    d = array[0];
    tfree(array);
    return (d);
}


/* Load in a rawfile. */
void
ft_loadfile(char *file)
{
    struct plot *pl, *np, *pp;

    fprintf(cp_out, "Loading raw data file (\"%s\") . . . ", file);
    pl = raw_read(file);
    if (pl)
        fprintf(cp_out, "done.\n");
    else
        fprintf(cp_out, "no data read.\n");

    /* This is a minor annoyance -- we should reverse the plot list so
     * they get numbered in the correct order.
     */
    for (pp = pl, pl = NULL; pp; pp = np) {
        np = pp->pl_next;
        pp->pl_next = pl;
        pl = pp;
    }
    for (; pl; pl = np) {
        np = pl->pl_next;
        plot_add(pl);
        /* Don't want to get too many "plot not written" messages. */
        pl->pl_written = TRUE;
    }
    plot_num++;
    plotl_changed = TRUE;
}


void
plot_add(struct plot *pl)
{
    struct dvec *v;
    struct plot *tp;
    char *s, buf[BSIZE_SP];

    fprintf(cp_out, "Title:  %s\nName: %s\nDate: %s\n\n", pl->pl_title,
            pl->pl_name, pl->pl_date);

    if (plot_cur)
        plot_cur->pl_ccom = cp_kwswitch(CT_VECTOR, pl->pl_ccom);

    for (v = pl->pl_dvecs; v; v = v->v_next)
        cp_addkword(CT_VECTOR, v->v_name);
    cp_addkword(CT_VECTOR, "all");

    if ((s = ft_plotabbrev(pl->pl_name)) == NULL)
        s = "unknown";
    do {
        (void) sprintf(buf, "%s%d", s, plot_num);
        for (tp = plot_list; tp; tp = tp->pl_next)
            if (cieq(tp->pl_typename, buf)) {
                plot_num++;
                break;
            }
    } while (tp);

    pl->pl_typename = copy(buf);
    plot_new(pl);
    cp_addkword(CT_PLOT, buf);
    pl->pl_ccom = cp_kwswitch(CT_VECTOR, NULL);
    plot_setcur(pl->pl_typename);
}


/* Remove a vector from the database, if it is there. */

void
vec_remove(char *name)
{
    struct dvec *ov;

    for (ov = plot_cur->pl_dvecs; ov; ov = ov->v_next)
        if (cieq(name, ov->v_name) && (ov->v_flags & VF_PERMANENT))
            break;

    if (!ov)
        return;

    ov->v_flags &= ~VF_PERMANENT;

    /* Remove from the keyword list. */
    cp_remkword(CT_VECTOR, name);
}


/* Get a vector by name. This deals with v(1), etc. almost properly. Also,
 * it checks for pre-defined vectors.
 */

struct dvec *
vec_fromplot(char *word, struct plot *plot)
{
    struct dvec *d;
    char buf[BSIZE_SP], buf2[BSIZE_SP], cc, *s;

    d = findvec(word, plot);
    if (!d) {
        (void) strcpy(buf, word);
        strtolower(buf);
        d = findvec(buf, plot);
    }
    if (!d) {
        (void) strcpy(buf, word);
        strtoupper(buf);
        d = findvec(buf, plot);
    }

    /* scanf("%c(%s)" doesn't do what it should do. ) */
    if (!d && (sscanf(word, "%c(%s", &cc, buf) == 2) &&
        ((s = strrchr(buf, ')')) != NULL) &&
        (s[1] == '\0')) {
        *s = '\0';
        if (prefix("i(", word) || prefix("I(", word)) {
            /* Spice dependency... */
            (void) sprintf(buf2, "%s#branch", buf);
            (void) strcpy(buf, buf2);
        }
        d = findvec(buf, plot);
    }

    return (d);
}


/* This is the main lookup routine for names. The possible types of names are:
 *  name        An ordinary vector.
 *  plot.name   A vector from a particular plot.
 *  @device[parm]   A device parameter.
 *  @model[parm]    A model parameter.
 *  @param      A circuit parameter.
 * For the @ cases, we construct a dvec with length 1 to hold the value.
 * In the other two cases, either the plot or the name can be "all", a
 * wildcard.
 * The vector name may have imbedded dots -- if the first component is a plot
 * name, it is considered the plot, otherwise the current plot is used.
 */

#define SPECCHAR '@'

struct dvec *
vec_get(const char *vec_name)
{
    struct dvec *d, *end = NULL, *newv = NULL;
    struct plot *pl;
    char buf[BSIZE_SP], *s, *wd, *word, *whole, *name = NULL, *param;
    int i = 0;
    struct variable *vv;

    wd = word = copy(vec_name);   /* Gets mangled below... */

    if (strchr(word, '.')) {
        /* Snag the plot... */
        for (i = 0, s = word; *s != '.'; i++, s++)
            buf[i] = *s;
        buf[i] = '\0';
        if (cieq(buf, "all")) {
            word = ++s;
            pl = NULL;  /* NULL pl signifies a wildcard. */
        } else {
            for (pl = plot_list;
                 pl && !plot_prefix(buf, pl->pl_typename);
                 pl = pl->pl_next)
                ;
            if (pl) {
                word = ++s;
            } else {
                /* This used to be an error... */
                pl = plot_cur;
            }
        }
    } else {
        pl = plot_cur;
    }

    if (pl) {
        d = vec_fromplot(word, pl);
        if (!d)
            d = vec_fromplot(word, &constantplot);
    } else {
        for (pl = plot_list; pl; pl = pl->pl_next) {
            if (cieq(pl->pl_typename, "const"))
                continue;
            d = vec_fromplot(word, pl);
            if (d) {
                if (end)
                    end->v_link2 = d;
                else
                    newv = d;
                for (end = d; end->v_link2; end = end->v_link2)
                    ;
            }
        }
        d = newv;
        if (!d) {
            fprintf(cp_err,
                    "Error: plot wildcard (name %s) matches nothing\n",
                    word);
            tfree(wd); /* MW. I don't want core leaks here */
            return (NULL);
        }
    }

    if (!d && (*word == SPECCHAR)) {
        /* This is a special quantity... */
        if (ft_nutmeg) {
            fprintf(cp_err,
                    "Error: circuit parameters only available with spice\n");
            tfree(wd);  /* MW. Memory leak fixed again */
            return (NULL); /* va: use NULL */
        }

        whole = copy(word);
        name = ++word;
        for (param = name; *param && (*param != '['); param++)
            ;

        if (*param) {
            *param++ = '\0';
            for (s = param; *s && *s != ']'; s++)
                ;
            *s = '\0';
        } else {
            param = NULL;
        }


        if (ft_curckt) {
            /*
             *  This is what is done in case of "alter r1 resistance = 1234"
             *                                r1    resistance, 0
             * if_setparam(ft_curckt->ci_ckt, &dev, param, dv, do_model);
             */

            /* vv = if_getparam (ft_curckt->ci_ckt, &name, param, 0, 0); */
            vv = if_getparam(ft_curckt->ci_ckt, &name, param, 0, 0);
            if (!vv) {
                tfree(whole);
                tfree(wd);
                return (NULL);
            }
        } else {
            fprintf(cp_err, "Error: No circuit loaded.\n");
            tfree(whole);
            tfree(wd);
            return (NULL);
        }

        d = dvec_alloc(copy(whole), /* MW. The same as word before */
                       SV_NOTYPE,
                       VF_REAL,  /* No complex values yet... */
                       1, NULL);

        /* In case the represented variable is a REAL vector this takes
         * the actual value of the first element of the linked list which
         * does not make sense.
         * This is an error.
         */

        /* This will copy the contents of the structure vv in another structure
         * dvec (FTEDATA.H) that do not have INTEGER so that those parameters
         * defined as IF_INTEGER are not given their value when using
         * print @pot[pos_node]
         * To fix this, it is necessary to define:
         * OPU( "pos_node",    POT_QUEST_POS_NODE, IF_REAL,"Positive node of potenciometer"),
         * int POTnegNode;     // number of negative node of potenciometer (Nodo_3)
         *  case POT_QUEST_POS_NODE:
         *  value->rValue = (double)fast->POTposNode;
         *  return (OK);
         *  Works but with the format 1.00000E0
         */

        /* We must make a change in format between the data that carries a variable to
         * put in a dvec.
         */

        /*
         * #define va_bool    va_V.vV_bool
         * #define va_num     va_V.vV_num
         * #define va_real    va_V.vV_real
         * #define va_string  va_V.vV_string
         * #define va_vlist   va_V.vV_list
         * enum cp_types {
         *   CP_BOOL,
         *   CP_NUM,
         *   CP_REAL,
         *   CP_STRING,
         *   CP_LIST
         ° };
        */

        /* The variable is a vector */
        if (vv->va_type == CP_LIST) {
            /* Compute the length of the vector,
             * used with the parameters of isrc and vsrc
             */
            struct variable *nv;

            i = 0;
            for (nv = vv->va_vlist; nv; nv = nv->va_next)
                i++;

            dvec_realloc(d, i, NULL);

            i = 0;
            for (nv = vv->va_vlist; nv; nv = nv->va_next)
                d->v_realdata[i++] = nv->va_real;

            /* To be able to identify the vector to represent
             * belongs to a special "conunto" and should be printed in a
             * special way.
             */
            d->v_dims[1] = 1;
        } else if (vv->va_type == CP_NUM) { /* Variable is an integer */
            *d->v_realdata = (double) vv->va_num;
        } else if (vv->va_type == CP_REAL) { /* Variable is a real */
            if (!(vv->va_next)) {
                /* Only a real data
                 * usually normal
                 */
                *d->v_realdata = vv->va_real;
            } else {
                /* Real data set
                 * When you print a model @ [all]
                 * Just print numerical values, not the string
                 */
                struct variable *nv;
                /* We go to print the list of values
                 * nv->va_name = Parameter description
                 * nv->va_string = Parameter
                 * nv->va_real= Value
                 */
                nv = vv;
                for (i = 1; ; i++) {
                    switch (nv->va_type) {
                    case  CP_REAL:
                        fprintf(stdout, "%s=%g\n", nv->va_name, nv->va_real);
                        break;
                    case  CP_STRING:
                        fprintf(stdout, "%s=%s\n", nv->va_name, nv->va_string);
                        break;
                    case  CP_NUM:
                        fprintf(stdout, "%s=%d\n", nv->va_name, nv->va_num);
                        break;
                    default: {
                        fprintf(stderr, "ERROR: enumeration value `CP_BOOL' or `CP_LIST' not handled in vec_get\nAborting...\n");
                        controlled_exit(EXIT_FAILURE);
                    }
                    }
                    nv = nv->va_next;

                    if (!nv)
                        break;
                }

                /* To distinguish those does not take anything for print screen to
                 * make a print or M1 @ @ M1 [all] leaving only the correct data
                 * and not the last
                 */
                d->v_rlength = 1;
            }
        }

        free_struct_variable(vv);
        tfree(wd);
        vec_new(d);
        tfree(whole);
        return (d);
    }

    tfree(wd);
    return (sortvecs(d));
}


/* Execute the commands for a plot. This is done whenever a plot becomes
 * the current plot.
 */

void
plot_docoms(wordlist *wl)
{
    bool inter;

    inter = cp_interactive;
    cp_interactive = FALSE;
    while (wl) {
        (void) cp_evloop(wl->wl_word);
        wl = wl->wl_next;
    }
    cp_resetcontrol();
    cp_interactive = inter;
}


/* Create a copy of a vector. */

struct dvec *
vec_copy(struct dvec *v)
{
    struct dvec *nv;
    int i;

    if (!v)
        return (NULL);

    nv = dvec_alloc(copy(v->v_name),
                    v->v_type,
                    v->v_flags & ~VF_PERMANENT,
                    v->v_length, NULL);

    if (isreal(v))
        memcpy(nv->v_realdata, v->v_realdata,
              sizeof(double) * (size_t) v->v_length);
    else
        memcpy(nv->v_compdata, v->v_compdata,
              sizeof(ngcomplex_t) * (size_t) v->v_length);

    nv->v_minsignal = v->v_minsignal;
    nv->v_maxsignal = v->v_maxsignal;
    nv->v_gridtype = v->v_gridtype;
    nv->v_plottype = v->v_plottype;

    /* Modified to copy the rlength of origin to destination vecor
     * instead of always putting it to 0.
     * As when it comes to make a print does not leave M1 @ @ M1 = 0.0,
     * to do so in the event that rlength = 0 not print anything on screen
     * nv-> v_rlength = 0;
     * Default -> v_rlength = 0 and only if you come from a print or M1 @
     * @ M1 [all] rlength = 1, after control is one of
     * if (v-> v_rlength == 0) com_print (wordlist * wl)
     */
    nv->v_rlength = v->v_rlength;

    nv->v_outindex = 0; /*XXX???*/
    nv->v_linestyle = 0; /*XXX???*/
    nv->v_color = 0; /*XXX???*/
    nv->v_defcolor = v->v_defcolor;
    nv->v_numdims = v->v_numdims;
    for (i = 0; i < v->v_numdims; i++)
        nv->v_dims[i] = v->v_dims[i];
    nv->v_plot = v->v_plot;
    nv->v_next = NULL;
    nv->v_link2 = NULL;
    nv->v_scale = v->v_scale;

    return (nv);
}


/* Create a new plot structure. This just fills in the typename and sets up
 * the ccom struct.
 */

struct plot *
plot_alloc(char *name)
{
    struct plot *pl = TMALLOC(struct plot, 1), *tp;
    char *s;
    struct ccom *ccom;
    char buf[BSIZE_SP];

    ZERO(pl, struct plot);
    if ((s = ft_plotabbrev(name)) == NULL)
        s = "unknown";
    do {
        (void) sprintf(buf, "%s%d", s, plot_num);
        for (tp = plot_list; tp; tp = tp->pl_next)
            if (cieq(tp->pl_typename, buf)) {
                plot_num++;
                break;
            }
    } while (tp);
    pl->pl_typename = copy(buf);
    cp_addkword(CT_PLOT, buf);
    /* va: create a new, empty keyword tree for class CT_VECTOR, s=old tree */
    ccom = cp_kwswitch(CT_VECTOR, NULL);
    cp_addkword(CT_VECTOR, "all");
    pl->pl_ccom = cp_kwswitch(CT_VECTOR, ccom);
    /* va: keyword tree is old tree again, new tree is linked to pl->pl_ccom */
    return (pl);
}


/* Stick a new vector in the proper place in the plot list. */

void
vec_new(struct dvec *d)
{
#ifdef FTEDEBUG
    if (ft_vecdb)
        fprintf(cp_err, "new vector %s\n", d->v_name);
#endif
    /* Note that this can't happen. */
    if (plot_cur == NULL) {
        fprintf(cp_err, "vec_new: Internal Error: no cur plot\n");
    }
    plot_cur->pl_lookup_valid = FALSE;
    if ((d->v_flags & VF_PERMANENT) && (plot_cur->pl_scale == NULL))
        plot_cur->pl_scale = d;
    if (!d->v_plot)
        d->v_plot = plot_cur;
    if (d->v_numdims < 1) {
        d->v_numdims = 1;
        d->v_dims[0] = d->v_length;
    }
    d->v_next = d->v_plot->pl_dvecs;
    d->v_plot->pl_dvecs = d;
}


/* Because of the way that all vectors, including temporary vectors,
 * are linked together under the current plot, they can often be
 * left lying around. This gets rid of all vectors that don't have
 * the permanent flag set. Also, for the remaining vectors, it
 * clears the v_link2 pointer.
 */

void
vec_gc(void)
{
    struct dvec *d, *nd;
    struct plot *pl;

    for (pl = plot_list; pl; pl = pl->pl_next)
        for (d = pl->pl_dvecs; d; d = nd) {
            nd = d->v_next;
            if (!(d->v_flags & VF_PERMANENT)) {
                if (ft_vecdb)
                    fprintf(cp_err,
                            "vec_gc: throwing away %s.%s\n",
                            pl->pl_typename, d->v_name);
                vec_free(d);
            }
        }

    for (pl = plot_list; pl; pl = pl->pl_next)
        for (d = pl->pl_dvecs; d; d = d->v_next)
            d->v_link2 = NULL;
}


/* Free a dvector. This is sort of a pain because we also have to make sure
 * that it has been unlinked from its plot structure. If the name of the
 * vector is NULL, then we have already freed it so don't try again. (This
 * situation can happen with user-defined functions.) Note that this depends
 * on our having tfree set its argument to NULL. Note that if all the vectors
 * in a plot are gone it stays around...
 */

void
vec_free_x(struct dvec *v)
{
    struct plot *pl;

    if ((v == NULL) || (v->v_name == NULL))
        return;
    pl = v->v_plot;

    /* Now we have to take this dvec out of the plot list. */
    if (pl != NULL) {
        pl->pl_lookup_valid = FALSE;
        if (pl->pl_dvecs == v) {
            pl->pl_dvecs = v->v_next;
        } else {
            struct dvec *lv = pl->pl_dvecs;
            if (lv)
                for (; lv->v_next; lv = lv->v_next)
                    if (lv->v_next == v)
                        break;
            if (lv && lv->v_next)
                lv->v_next = v->v_next;
            else
                fprintf(cp_err,
                        "vec_free: Internal Error: %s not in plot\n",
                        v->v_name);
        }
        if (pl->pl_scale == v) {
            if (pl->pl_dvecs)
                pl->pl_scale = pl->pl_dvecs;    /* Random one... */
            else
                pl->pl_scale = NULL;
        }
    }

    dvec_free(v);
}


/*
 * return TRUE if every vector element is zero
 */

bool
vec_iszero(struct dvec *v)
{
    int i;

    for (; v; v = v->v_link2)
        if (isreal(v))
            for (i = 0; i < v->v_length; i++) {
                if (v->v_realdata[i] != 0.0)
                    return FALSE;
            }
        else
            for (i = 0; i < v->v_length; i++) {
                if (realpart(v->v_compdata[i]) != 0.0)
                    return FALSE;
                if (imagpart(v->v_compdata[i]) != 0.0)
                    return FALSE;
            }

    return TRUE;
}


/* This is something we do in a few places...  Since vectors get copied a lot,
 * we can't just compare pointers to tell if two vectors are 'really' the same.
 */

bool
vec_eq(struct dvec *v1, struct dvec *v2)
{
    char *s1, *s2;
    bool rtn;

    if (v1->v_plot != v2->v_plot)
        return (FALSE);

    s1 = vec_basename(v1);
    s2 = vec_basename(v2);

    if (cieq(s1, s2))
        rtn = TRUE;
    else
        rtn = FALSE;

    tfree(s1);
    tfree(s2);
    return rtn;
}


/* Return the name of the vector with the plot prefix stripped off.  This
 * is no longer trivial since '.' doesn't always mean 'plot prefix'.
 */

char *
vec_basename(struct dvec *v)
{
    char buf[BSIZE_SP], *t, *s;

    if (strchr(v->v_name, '.')) {
        if (cieq(v->v_plot->pl_typename, v->v_name))
            (void) strcpy(buf, v->v_name + strlen(v->v_name) + 1);
        else
            (void) strcpy(buf, v->v_name);
    } else {
        (void) strcpy(buf, v->v_name);
    }

    strtolower(buf);
    s = skip_ws(buf);
    for (t = s; *t; t++)
        ;
    while ((t > s) && isspace_c(t[-1]))
        *--t = '\0';
    return (copy(s));
}


/* Make a plot the current one.  This gets called by cp_usrset() when one
 * does a 'set curplot = name'.
 * va: ATTENTION: has unlinked old keyword-class-tree from keywords[CT_VECTOR]
 *                (potentially memory leak)
 */

void
plot_setcur(char *name)
{
    struct plot *pl;

    if (cieq(name, "new")) {
        pl = plot_alloc("unknown");
        pl->pl_title = copy("Anonymous");
        pl->pl_name = copy("unknown");
        pl->pl_date = copy(datestring());
        plot_new(pl);
        plot_cur = pl;
        return;
    }
    for (pl = plot_list; pl; pl = pl->pl_next)
        if (plot_prefix(name, pl->pl_typename))
            break;
    if (!pl) {
        fprintf(cp_err, "Error: no such plot named %s\n", name);
        return;
    }
    /* va: we skip cp_kwswitch, because it confuses the keyword-tree management for
     *     repeated op-commands. When however cp_kwswitch is necessary for other
     *     reasons, we should hold the original keyword table pointer in an
     *     permanent variable, since it will lost here, and can never tfree'd.
     if (plot_cur)
     {
     plot_cur->pl_ccom = cp_kwswitch(CT_VECTOR, pl->pl_ccom);
     }
    */
    plot_cur = pl;
}


/* Add a plot to the plot list. This is different from plot_add() in that
 * all this does is update the list and the variable $plots.
 */

void
plot_new(struct plot *pl)
{
    pl->pl_next = plot_list;
    plot_list = pl;
}


/* This routine takes a multi-dimensional vector, treats it as a
 * group of 2-dimensional matrices and transposes each matrix.
 * The data array is replaced with a new one that has the elements
 * in the proper order.  Otherwise the transposition is done in place.
 */

void
vec_transpose(struct dvec *v)
{
    int dim0, dim1, nummatrices;
    int i, j, k, joffset, koffset, blocksize;
    double *newreal, *oldreal;
    ngcomplex_t *newcomp, *oldcomp;

    if (v->v_numdims < 2 || v->v_length <= 1)
        return;

    dim0 = v->v_dims[v->v_numdims-1];
    dim1 = v->v_dims[v->v_numdims-2];
    v->v_dims[v->v_numdims-1] = dim1;
    v->v_dims[v->v_numdims-2] = dim0;
    /* Assume length is a multiple of each dimension size.
     * This may not be safe, in which case a test should be
     * made that the length is the product of all the dimensions.
     */
    blocksize = dim0*dim1;
    nummatrices = v->v_length / blocksize;

    /* Note:
     *   olda[i,j] is at data[i*dim0+j]
     *   newa[j,i] is at data[j*dim1+i]
     *   where j is in [0, dim0-1]  and  i is in [0, dim1-1]
     * Since contiguous data in the old array is scattered in the new array
     * we can't use memcpy :(.  There is probably a BLAS2 function for this
     * though.  The formulation below gathers scattered old data into
     * consecutive new data.
     */

    if (isreal(v)) {
        newreal = TMALLOC(double, v->v_length);
        oldreal = v->v_realdata;
        koffset = 0;
        for (k = 0; k < nummatrices; k++) {
            joffset = 0;
            for (j = 0; j < dim0; j++) {
                for (i = 0; i < dim1; i++) {
                    newreal[ koffset + joffset + i ] =
                        oldreal[ koffset + i*dim0 + j ];
                }
                joffset += dim1;  /* joffset = j*dim0 */
            }
            koffset += blocksize; /* koffset = k*blocksize = k*dim0*dim1 */
        }
        dvec_realloc(v, v->v_length, newreal);
    } else {
        newcomp = TMALLOC(ngcomplex_t, v->v_length);
        oldcomp = v->v_compdata;
        koffset = 0;
        for (k = 0; k < nummatrices; k++) {
            joffset = 0;
            for (j = 0; j < dim0; j++) {
                for (i = 0; i < dim1; i++) {
                    newcomp[ koffset + joffset + i ] =
                        oldcomp[ koffset + i*dim0 + j ];
                }
                joffset += dim1;  /* joffset = j*dim0 */
            }
            koffset += blocksize; /* koffset = k*blocksize = k*dim0*dim1 */
        }
        dvec_realloc(v, v->v_length, newcomp);
    }
}


/* This routine takes a multi-dimensional vector and turns it into a family
 * of 1-d vectors, linked together with v_link2.  It is here so that plot
 * can do intelligent things.
 */

struct dvec *
vec_mkfamily(struct dvec *v)
{
    int size, numvecs, i, count[MAXDIMS];
    struct dvec *vecs, *d, **t;
    char buf2[BSIZE_SP];

    if (v->v_numdims < 2)
        return (v);

    size = v->v_dims[v->v_numdims - 1];
    for (i = 0, numvecs = 1; i < v->v_numdims - 1; i++)
        numvecs *= v->v_dims[i];
    for (i = 0; i < MAXDIMS; i++)
        count[i] = 0;
    for (t = &vecs, i = 0; i < numvecs; i++) {

        indexstring(count, v->v_numdims - 1, buf2);

        d = dvec_alloc(tprintf("%s%s", v->v_name, buf2),
                       v->v_type,
                       v->v_flags,
                       size, NULL);

        d->v_minsignal = v->v_minsignal;
        d->v_maxsignal = v->v_maxsignal;
        d->v_gridtype = v->v_gridtype;
        d->v_plottype = v->v_plottype;
        d->v_scale = v->v_scale;
        /* Don't copy the default color, since there will be many
         * of these things...
         */
        d->v_numdims = 1;
        d->v_dims[0] = size;

        if (isreal(v)) {
            memcpy(d->v_realdata, v->v_realdata + size*i, (size_t) size * sizeof(double));
        } else {
            memcpy(d->v_compdata, v->v_compdata + size*i, (size_t) size * sizeof(ngcomplex_t));
        }
        /* Add one to the counter. */
        (void) incindex(count, v->v_numdims - 1, v->v_dims, v->v_numdims);

        *t = d;
        t = &(d->v_link2);
    }

    for (d = vecs; d; d = d->v_link2)
        vec_new(d);

    return (vecs);
}


/* This function will match "op" with "op1", but not "op1" with "op12". */

bool
plot_prefix(char *pre, char *str)
{
    if (!*pre)
        return (TRUE);

    while (*pre && *str) {
        if (*pre != *str)
            break;
        pre++;
        str++;
    }

    if (*pre || (*str && isdigit_c(pre[-1])))
        return (FALSE);
    else
        return (TRUE);
}
