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

/*
 * Various post-processor commands having to do with vectors.
 */

#include "ngspice/ngspice.h"
#include "ngspice/cpdefs.h"
#include "ngspice/ftedefs.h"
#include "ngspice/dvec.h"
#include "ngspice/sim.h"
#include "ngspice/plot.h"
#include "ngspice/graph.h"
#include "ngspice/ftedbgra.h"
#include "com_display.h"

#include "completion.h"
#include "postcoms.h"
#include "variable.h"
#include "parser/complete.h" /* va: throwaway */
#include "plotting/plotting.h"

#include "ngspice/compatmode.h"
#include "ngspice/dstring.h"
#include "numparam/general.h"

static void killplot(struct plot *pl);
static void DelPlotWindows(struct plot *pl);

/* check if the user want's to delete the scale vector of the current plot.
   This should not happen, because then redrawing the graph crashes ngspice */
static bool
is_scale_vec_of_current_plot(const char *v_name)
{
    if (!plot_cur) { /* no current plot */
        return FALSE;
    }

    const struct dvec * const pl_scale = plot_cur->pl_scale;
    if (!pl_scale) { /* no scale vector */
        return FALSE;
    }

    /* Test if this vector's name matches the scale vector's name */
    return cieq(v_name, pl_scale->v_name);
} /* end of function is_scale_vec_of_current_plot */


/* Remove vectors in the wordlist from the current plot */
void
com_unlet(wordlist *wl)
{
    for ( ; wl != (wordlist *) NULL; wl = wl->wl_next) {
        /* Don't delete the scale vector of the current plot */
        const char * const vector_name = wl->wl_word;

        /* A regular-expression argument deletes every matching vector. */
        bool is_regex;
        wordlist *names = vec_regex_expand(vector_name, &is_regex), *w;
        if (is_regex) {
            for (w = names; w; w = w->wl_next) {
                if (is_scale_vec_of_current_plot(w->wl_word))
                    fprintf(cp_err,
                            "\nWarning: Scale vector '%s' of the current plot "
                            "cannot be deleted!\n"
                            "Command 'unlet %s' is ignored.\n\n",
                            plot_cur->pl_scale->v_name, w->wl_word);
                else
                    vec_remove(w->wl_word);
            }
            wl_free(names);
            continue;
        }

        if (is_scale_vec_of_current_plot(vector_name)) {
            /* If it is the scale vector of the current plot, print a
             * warning. Note that if it is true,  the scale vector name must
             * exist, so no part of plot_cur->pl_scale->v_name can be null. */
            fprintf(cp_err,
                    "\nWarning: Scale vector '%s' of the current plot "
                    "cannot be deleted!\n"
                    "Command 'unlet %s' is ignored.\n\n",
                    plot_cur->pl_scale->v_name, vector_name);
        }
        else {
            vec_remove(vector_name);
        }
    } /* end of loop over vectors to delete */
} /* end of function com_unlet */


/* Remove zero length vectors from the current plot */
void
com_remzerovec(wordlist* wl)
{
    NG_IGNORE(wl);
    
    struct dvec* ov;

    for (ov = plot_cur->pl_dvecs; ov; ov = ov->v_next) {
        if (ov->v_length == 0) {
            ov->v_flags &= ~VF_PERMANENT;
            /* Remove from the keyword list. */
            cp_remkword(CT_VECTOR, ov->v_name);
        }
    } /* end of loop over vectors to delete */
} /* end of function com_remzerovec */


/* Load in a file. */
void
com_load(wordlist *wl)
{
    char *copypath;
    if (!wl)
        ft_loadfile(ft_rawfile);
    else
        while (wl) {
            /*ft_loadfile(cp_unquote(wl->wl_word)); DG: bad memory leak*/
            copypath = cp_unquote(wl->wl_word);/*DG*/
            ft_loadfile(copypath);
            tfree(copypath);
            wl = wl->wl_next;
        }

    /* note: default is to display the vectors in the last (current) plot */
    com_display(NULL);
}


/* Print out the value of an expression. When we are figuring out what to
 * print, link the vectors we want with v_link2... This has to be done
 * because of the way temporary vectors are linked together with permanent
 * ones under the plot.
 */

void
com_print(wordlist *wl)
{
    struct dvec *v, *lv = NULL, *bv, *nv, *vecs = NULL;
    int i, j, ll, width = DEF_WIDTH, height = DEF_HEIGHT, npoints, lineno, npages = 0;
    struct pnode *pn, *names;
    struct plot *p;
    bool col = TRUE, nobreak = FALSE, noprintscale, plotnames = FALSE;
    bool optgiven = FALSE;
    char *s, *buf, *buf2; /*, buf[BSIZE_SP], buf2[BSIZE_SP];*/
    char numbuf[BSIZE_SP], numbuf2[BSIZE_SP]; /* Printnum buffers */
    int ngood;

    if (wl == NULL)
        return;

    buf = TMALLOC(char, BSIZE_SP);
    buf2 = TMALLOC(char, BSIZE_SP);

    if (eq(wl->wl_word, "col")) {
        col = TRUE;
        optgiven = TRUE;
        wl = wl->wl_next;
    } else if (eq(wl->wl_word, "line")) {
        col = FALSE;
        optgiven = TRUE;
        wl = wl->wl_next;
    }

    ngood = 0;

    {
        /* Expand any quoted-regexp arguments to matching vector names. */
        wordlist *rwl = vec_regex_wl(wl);
        names = ft_getpnames_quotes(rwl, TRUE);
        wl_free(rwl);
    }

    for (pn = names; pn; pn = pn->pn_next) {
        if ((v = ft_evaluate(pn)) == NULL)
            continue;
        if (!vecs)
            vecs = lv = v;
        else
            lv->v_link2 = v;
        for (lv = v; lv->v_link2; lv = lv->v_link2)
            ;
        ngood += 1;
    }

    if (!ngood)
        goto done;

    /* See whether we really have to print plot names. */
    for (v = vecs; v; v = v->v_link2)
        if (vecs->v_plot != v->v_plot) {
            plotnames = TRUE;
            break;
        }

    if (!optgiven) {
        /* Figure out whether col or line should be used... */
        col = FALSE;
        for (v = vecs; v; v = v->v_link2)
            if (v->v_length > 1) {
                col = TRUE;
                /* Improvement made to print cases @[sin] = (0 12 13 100K) */
                if ((v->v_plot->pl_scale && v->v_length != v->v_plot->pl_scale->v_length) && (*(v->v_name) == '@'))
                {
                    col = FALSE;
                }
                break;
            }
        /* With this I have found that the vector has less elements than the SCALE vector
         * in the linked PLOT. But now I must make sure in case of a print @vin[sin] or
         * @vin[pulse]
         * for it appear that the v->v_name begins with '@'
         * And then be in this case.
         */
    }

    out_init();
    if (!col) {
        if (cp_getvar("width", CP_NUM, &i, 0))
            width = i;
        if (width < 60)
            width = 60;
        if (width > BSIZE_SP - 2)
            buf = TREALLOC(char, buf, (size_t) width + 1);
        for (v = vecs; v; v = v->v_link2) {
            char *basename = vec_basename(v);
            if (plotnames)
                (void) sprintf(buf, "%s.%s", v->v_plot->pl_typename, basename);
            else
                (void) strcpy(buf, basename);
            tfree(basename);

            for (s = buf; *s; s++)
                ;
            s--;
            while (isspace_c(*s)) {
                *s = '\0';
                s--;
            }
            ll = 10;

            /* v->v_rlength = 1 when it comes to make a print @ M1 and does not want to come out on screen
             * Multiplier factor [m]=1
             *  @M1 = 0,00e+00
             * In any other case rlength not used for anything and only applies in the copy of the vectors.
             */
            if (v->v_rlength == 0) {
                if (v->v_length == 1) {
                    if (isreal(v)) {
                        printnum(numbuf, *v->v_realdata);
                        out_printf("%s = %s\n", buf, numbuf);
                    } else {
                        printnum(numbuf, realpart(v->v_compdata[0]));
                        printnum(numbuf2, imagpart(v->v_compdata[0]));
                        out_printf("%s = %s,%s\n", buf, numbuf, numbuf2);
                    }
                } else {
                    out_printf("%s = (  ", buf);
                    for (i = 0; i < v->v_length; i++)
                        if (isreal(v)) {

                            printnum(numbuf, v->v_realdata[i]);
                            (void) strcpy(buf, numbuf);
                            out_send(buf);
                            ll += (int) strlen(buf);
                            ll = (ll + 7) / 8;
                            ll = ll * 8 + 1;
                            if (ll > width) {
                                out_send("\n\t");
                                ll = 9;
                            } else {
                                out_send("\t");
                            }
                        } else {
                            /*DG*/
                            printnum(numbuf, realpart(v->v_compdata[i]));
                            printnum(numbuf2, imagpart(v->v_compdata[i]));
                            (void) sprintf(buf, "%s,%s", numbuf, numbuf2);
                            out_send(buf);
                            ll += (int) strlen(buf);
                            ll = (ll + 7) / 8;
                            ll = ll * 8 + 1;
                            if (ll > width) {
                                out_send("\n\t");
                                ll = 9;
                            } else {
                                out_send("\t");
                            }
                        }
                    out_send(")\n");
                } //end if (v->v_length == 1)
            }  //end  if (v->v_rlength == 1)
        }  // end for loop
    } else {    /* Print in columns. */
        if (cp_getvar("width", CP_NUM, &i, 0))
            width = i;
        if (width < 40)
            width = 40;
        if (width > BSIZE_SP - 2) {
            buf = TREALLOC(char, buf, (size_t) width + 1);
            buf2 = TREALLOC(char, buf2, (size_t) width + 1);
        }
        if (cp_getvar("height", CP_NUM, &i, 0))
            height = i;
        if (height < 20)
            height = 20;
        nobreak = cp_getvar("nobreak", CP_BOOL, NULL, 0);
        if (!nobreak && !ft_nopage)
            nobreak = FALSE;
        else
            nobreak = TRUE;
        noprintscale = cp_getvar("noprintscale", CP_BOOL, NULL, 0);
        bv = vecs;
    nextpage:
        npages++;
        /* Make the first vector of every page be the scale... */
        /* XXX But what if there is no scale?  e.g. op, pz */
        if (!noprintscale && bv->v_plot->pl_ndims)
            if (bv->v_plot->pl_scale && !vec_eq(bv, bv->v_plot->pl_scale)) {
                nv = vec_copy(bv->v_plot->pl_scale);
                vec_new(nv);
                nv->v_link2 = bv;
                bv = nv;
            }

        ll = 8;
        for (lv = bv; lv; lv = lv->v_link2) {
            if (isreal(lv))
                ll += 16;   /* Two tabs for real, */
            else
                ll += 32;   /* 4 for complex. */
            /* Make sure we have at least 2 vectors per page... */
            if ((ll > width) && (lv != bv) && (lv != bv->v_link2))
                break;
        }

        /* Print the header on the first page only, if 'option nopage'. */
        if (!ft_nopage || npages == 1) {
            /* print the header */
            p = bv->v_plot;
            j = (width - (int)strlen(p->pl_title)) / 2;    /* Yes, keep "(int)" */
            if (j < 0)
                j = 0;
            for (i = 0; i < j; i++)
                buf2[i] = ' ';
            buf2[j] = '\0';
            out_send(buf2);
            out_send(p->pl_title);
            out_send("\n");
            out_send(buf2);
            (void)sprintf(buf, "%s  %s", p->pl_name, p->pl_date);
            out_send(buf);
            out_send("\n");
        }
        for (i = 0; i < width; i++)
            buf2[i] = '-';
        buf2[width] = '\n';
        buf2[width+1] = '\0';
        out_send(buf2);
        (void) sprintf(buf, "Index   ");
        for (v = bv; v && (v != lv); v = v->v_link2) {
            if (isreal(v)) {
                (void) sprintf(buf2, "%-16.15s", v->v_name);
            } else {
                /* The frequency vector is complex but often with imaginary part = 0,
                 * this prevents to print two columns.
                 */
                if (eq(v->v_name, "frequency")) {
                    if (imagpart(v->v_compdata[0]) == 0.0)
                        (void) sprintf(buf2, "%-16.15s", v->v_name);
                    else
                        (void) sprintf(buf2, "%-32.31s", v->v_name);
                } else {
                    (void) sprintf(buf2, "%-32.31s", v->v_name);
                }
            }
            (void) strcat(buf, buf2);
        }
        lineno = 3;
        j = 0;
        npoints = 0;
        for (v = bv; (v && (v != lv)); v = v->v_link2)
            if (v->v_length > npoints)
                npoints = v->v_length;
    pbreak:     /* New page. */
        out_send(buf);
        out_send("\n");
        for (i = 0; i < width; i++)
            buf2[i] = '-';
        buf2[width] = '\n';
        buf2[width+1] = '\0';
        out_send(buf2);
        lineno += 2;
    loop:
        while ((j < npoints) && (lineno < height)) {
            out_printf("%d\t", j);
            for (v = bv; (v && (v != lv)); v = v->v_link2) {
                if (v->v_length <= j) {
                    if (isreal(v))
                        out_send("\t\t");
                    else
                        out_send("\t\t\t\t");
                } else {
                    if (isreal(v)) {
                        printnum(numbuf, v->v_realdata[j]);
                        out_printf("%s\t", numbuf);
                    } else {
                        /* In case of a single frequency and have a real part avoids print imaginary part equals 0. */
                        if (eq(v->v_name, "frequency") &&
                            imagpart(v->v_compdata[j]) == 0.0)
                        {
                            printnum(numbuf, realpart(v->v_compdata[j]));
                            out_printf("%s\t", numbuf);
                        } else {
                            printnum(numbuf, realpart(v->v_compdata[j]));
                            printnum(numbuf2, imagpart(v->v_compdata[j]));
                            out_printf("%s,\t%s\t", numbuf, numbuf2);
                        }
                    }
                }
            }
            out_send("\n");
            j++;
            lineno++;
        }
        if ((j == npoints) && (lv == NULL)) /* No more to print. */
            goto done;
        if (j == npoints) { /* More vectors to print. */
            bv = lv;
            if(nobreak)
                out_send("\n");   /* return without form feed. */
            else
                out_send("\f\n");   /* Form feed. */
            goto nextpage;
        }

        /* Otherwise go to a new page. */
        lineno = 0;
        if (nobreak)
            goto loop;
        else
            out_send("\f\n");   /* Form feed. */
        goto pbreak;
    }
done:
    /* Get rid of the vectors. */
    free_pnode(names);
    tfree(buf);
    tfree(buf2);
}


/* Write out some data into a ngspice raw file with 'write filename expr'.
 * If vectors (expr) from various plots are selected, they are written
 * out as seperate plots.  In any case, we have to be sure to write out
 * the scales for everything we write. If expr is omitted, all vectors
 * of the current plot are written.
 */
void
com_write(wordlist *wl)
{
    char *file, buf[BSIZE_SP];
    struct pnode *pn;
    struct dvec *d, *vecs = NULL, *lv = NULL, *end, *vv;
    static wordlist all = { "all", NULL, NULL };
    struct pnode *names = NULL;
    bool ascii = AsciiRawFile;
    bool scalefound, appendwrite, plainwrite = FALSE;
    struct plot *tpl, newplot;

    if (wl) {
        file = wl->wl_word;
        wl = wl->wl_next;
    } else {
        file = ft_rawfile;
    }

    if (cp_getvar("filetype", CP_STRING, buf, sizeof(buf))) {
        if (eq(buf, "binary"))
            ascii = FALSE;
        else if (eq(buf, "ascii"))
            ascii = TRUE;
        else
            fprintf(cp_err, "Warning: strange file type %s\n", buf);
    }
    appendwrite = cp_getvar("appendwrite", CP_BOOL, NULL, 0);

    plainwrite = cp_getvar("plainwrite", CP_BOOL, NULL, 0);

    /* If variable plainwrite is set, we do not expand equations, serve v vs vs etc.
       We offer plain writing of the vectors. This enables node names containing +, -, / etc. */
    if (!plainwrite) {
        if (wl) {
            /* Expand any quoted-regexp arguments to matching vector names. */
            wordlist *rwl = vec_regex_wl(wl);
            names = ft_getpnames_quotes(rwl, TRUE);
            wl_free(rwl);
        }
        else
            names = ft_getpnames_quotes(&all, TRUE);

        if (names == NULL) {
            fprintf(stderr, "Error during 'write': no writable vector found.\n");
            return;
        }

        for (pn = names; pn; pn = pn->pn_next) {
            d = ft_evaluate(pn);
            if (!d)
                goto done;
            if (vecs)
                lv->v_link2 = d;
            else
                vecs = d;
            for (lv = d; lv->v_link2; lv = lv->v_link2)
                ;
        }
    }
    else {
        wordlist* wli;
        if (!wl)
            wl = &all;
        for (wli = wl; wli; wli = wli->wl_next) {
            d = vec_get(wli->wl_word);
            if (!d) {
                fprintf(stderr, "Error during 'write': vector %s not found\n", wli->wl_word);
                goto done;
            }
            if (vecs)
                lv->v_link2 = d;
            else
                vecs = d;
            for (lv = d; lv->v_link2; lv = lv->v_link2)
                ;
        }
    }

    /* Now we have to write them out plot by plot. */

    while (vecs) {
        tpl = vecs->v_plot;
        tpl->pl_written = TRUE;
        end = NULL;
        memcpy(&newplot, tpl, sizeof(struct plot));
        scalefound = FALSE;

        /* Figure out how many vectors are in this plot. Also look
         * for the scale, or a copy of it, which may have a different
         * name.
         */
        for (d = vecs; d; d = d->v_link2) {
            if (d->v_plot == tpl) {
                char *basename = vec_basename(d);
                vv = vec_copy(d);
                /* Note that since we are building a new plot
                 * we don't want to vec_new this one...
                 */
                txfree(vv->v_name);
                vv->v_name = basename;

                if (end)
                    end->v_next = vv;
                else
                    end = newplot.pl_dvecs = vv;
                end = vv;

                if (vec_eq(d, tpl->pl_scale)) {
                    newplot.pl_scale = vv;
                    scalefound = TRUE;
                }
            }
        }
        end->v_next = NULL;

        /* Maybe we shouldn't make sure that the default scale is
         * present if nobody uses it.
         */
        if (!scalefound) {
            newplot.pl_scale = vec_copy(tpl->pl_scale);
            newplot.pl_scale->v_next = newplot.pl_dvecs;
            newplot.pl_dvecs = newplot.pl_scale;
        }

        /* Now let's go through and make sure that everything that
         * has its own scale has it in the plot.
         */
        for (;;) {
            scalefound = FALSE;
            for (d = newplot.pl_dvecs; d; d = d->v_next) {
                if (d->v_scale) {
                    for (vv = newplot.pl_dvecs; vv; vv = vv->v_next)
                        if (vec_eq(vv, d->v_scale))
                            break;
                    if (!vv) {
                        /* We have to grab it... */
                        vv = vec_copy(d->v_scale);
                        vv->v_next = newplot.pl_dvecs;
                        newplot.pl_dvecs = vv;
                        scalefound = TRUE;
                    }
                }
            }

            if (!scalefound)
                break;
            /* Otherwise loop through again... */
        }

        raw_write(file, &newplot, appendwrite, !ascii);

        for (vv = newplot.pl_dvecs; vv;) {
            struct dvec *next_vv = vv->v_next;
            vv->v_plot = NULL;
            vec_free(vv);
            vv = next_vv;
        }

        /* Now throw out the vectors we have written already... */
        for (d = vecs, lv = NULL;  d; d = d->v_link2)
            if (d->v_plot == tpl) {
                if (lv) {
                    lv->v_link2 = d->v_link2;
                    d = lv;
                } else {
                    vecs = d->v_link2;
                }
            } else {
                lv = d;
            }
        /* If there are more plots we want them appended... */
        appendwrite = TRUE;
    }

done:
    free_pnode(names);
}


/* Write scattering parameters into a file with Touchstone File Format Version 1
   with command wrs2p file .
   Format info from http://www.eda.org/ibis/touchstone_ver2.0/touchstone_ver2_0.pdf
   See example 13 on page 15: Two port, ASCII, real-imaginary
   Check if S_1_1, S_2_1, S_1_2, S_2_2 and frequency vectors are available
   Check if vector Rbase is available
   Call spar_write()
*/

void
com_write_sparam(wordlist *wl)
{
    char *file;
    char *sbuf[6];
    wordlist *wl_sparam;
    struct pnode *pn;
    struct dvec *d, *vecs = NULL, *lv = NULL, *end, *vv, *Rbasevec = NULL;
    struct pnode *names;
    bool scalefound;
    struct plot *tpl, newplot;
    double Rbaseval;

    if (wl)
        file = wl->wl_word;
    else
        file = "s_param.s2p";

    fprintf(stderr, "Note: only 2 ports 1 and 2 are supported by wrs2p\n");

    /* generate wordlist with all vectors required*/
    sbuf[0] = "frequency";
    sbuf[1] = "S_1_1";
    sbuf[2] = "S_2_1";
    sbuf[3] = "S_1_2";
    sbuf[4] = "S_2_2";
    sbuf[5] = NULL;
    wl_sparam = wl_build((const char * const *) sbuf);

    names = ft_getpnames(wl_sparam, TRUE);
    if (names == NULL)
        goto done;

    for (pn = names; pn; pn = pn->pn_next) {
        d = ft_evaluate(pn);
        if (!d)
            goto done;

        if (vecs)
            lv->v_link2 = d;
        else
            vecs = d;

        for (lv = d; lv->v_link2; lv = lv->v_link2)
            ;
    }

    Rbasevec = vec_get("Rbase");
    if (Rbasevec) {
        Rbaseval = Rbasevec->v_realdata[0];
    } else {
        fprintf(stderr, "Error: No Rbase vector given\n");
        goto done;
    }

    /* Now we have to write them out plot by plot. */

    while (vecs) {
        tpl = vecs->v_plot;
        tpl->pl_written = TRUE;
        end = NULL;
        memcpy(&newplot, tpl, sizeof(struct plot));
        scalefound = FALSE;

        /* Figure out how many vectors are in this plot. Also look
         * for the scale, or a copy of it, which may have a different
         * name.
         */
        for (d = vecs; d; d = d->v_link2) {
            if (d->v_plot == tpl) {
                char *basename = vec_basename(d);
                vv = vec_copy(d);
                /* Note that since we are building a new plot
                 * we don't want to vec_new this one...
                 */
                tfree(vv->v_name);
                vv->v_name = basename;

                if (end)
                    end->v_next = vv;
                else
                    end = newplot.pl_dvecs = vv;
                end = vv;

                if (vec_eq(d, tpl->pl_scale)) {
                    newplot.pl_scale = vv;
                    scalefound = TRUE;
                }
            }
        }
        end->v_next = NULL;

        /* Maybe we shouldn't make sure that the default scale is
         * present if nobody uses it.
         */
        if (!scalefound) {
            newplot.pl_scale = vec_copy(tpl->pl_scale);
            newplot.pl_scale->v_next = newplot.pl_dvecs;
            newplot.pl_dvecs = newplot.pl_scale;
        }

        /* Now let's go through and make sure that everything that
         * has its own scale has it in the plot.
         */
        for (;;) {
            scalefound = FALSE;
            for (d = newplot.pl_dvecs; d; d = d->v_next) {
                if (d->v_scale) {
                    for (vv = newplot.pl_dvecs; vv; vv = vv->v_next)
                        if (vec_eq(vv, d->v_scale))
                            break;
                    if (!vv) {
                        /* We have to grab it... */
                        vv = vec_copy(d->v_scale);
                        vv->v_next = newplot.pl_dvecs;
                        newplot.pl_dvecs = vv;
                        scalefound = TRUE;
                    }
                }
            }
            if (!scalefound)
                break;
            /* Otherwise loop through again... */
        }

        spar_write(file, &newplot, Rbaseval);

        for (vv = newplot.pl_dvecs; vv;) {
            struct dvec *next_vv = vv->v_next;
            vv->v_plot = NULL;
            vec_free(vv);
            vv = next_vv;
        }

        /* Now throw out the vectors we have written already... */
        for (d = vecs, lv = NULL;  d; d = d->v_link2)
            if (d->v_plot == tpl) {
                if (lv) {
                    lv->v_link2 = d->v_link2;
                    d = lv;
                } else {
                    vecs = d->v_link2;
                }
            } else {
                lv = d;
            }
    }

done:
    free_pnode(names);
    wl_free(wl_sparam);
}


/* If the named vectors have more than 1 dimension, then consider
 * to be a collection of one or more matrices.  This command transposes
 * each named matrix.
 */
void
com_transpose(wordlist *wl)
{
    struct dvec *d;
    char *s;

    /* For each vector named in the wordlist, perform the transform to
     * it and the vectors associated with it through v_link2 */
    for ( ; wl != (wordlist *) NULL; wl = wl->wl_next) {
        s = cp_unquote(wl->wl_word);
        d = vec_get(s);
        tfree(s); /*DG: Avoid Memory Leak */
        if (d == NULL) {
            /* Print error message, but continue with other vectors */
            fprintf(cp_err, "Error: no such vector as %s.\n", wl->wl_word);
       }
        else {
            /* Transpose the named vector and vectors tied to it
             * through v_link2 */
            while (d) {
                vec_transpose(d);
                d = d->v_link2;
            }
        }
    } /* end of loop over words in wordlist */
} /* end of function com_transpose */



/* Take a set of vectors and form a new vector of the nth elements of each. */
void
com_cross(wordlist *wl)
{
    char *newvec, *s;
    struct dvec *n, *v, *vecs = NULL, *lv = NULL;
    struct pnode *pn, *names;
    int i, ind;
    bool comp = FALSE;

    newvec = wl->wl_word;
    wl = wl->wl_next;
    s = wl->wl_word;

    {
        double val;
        if (ft_numparse(&s, FALSE, &val) <= 0) {
            fprintf(cp_err, "Error: bad index value %s\n", wl->wl_word);
            return;
        }
        if ((ind = (int) val) < 0) {
            fprintf(cp_err, "Error: badstrchr %d\n", ind);
            return;
        }
    }

    wl = wl->wl_next;
    names = ft_getpnames(wl, TRUE);
    for (pn = names; pn; pn = pn->pn_next) {
        if ((n = ft_evaluate(pn)) == NULL)
            goto done;

        if (!vecs)
            vecs = lv = n;
        else
            lv->v_link2 = n;

        for (lv = n; lv->v_link2; lv = lv->v_link2)
            ;
    }

    for (n = vecs, i = 0; n; n = n->v_link2) {
        if (iscomplex(n))
            comp = TRUE;
        i++;
    }

    vec_remove(newvec);
    v = dvec_alloc(copy(newvec),
            (int) (vecs ? vecs->v_type : SV_NOTYPE),
            comp ? (VF_COMPLEX | VF_PERMANENT) : (VF_REAL | VF_PERMANENT),
            i, NULL);

    /* Now copy the ind'ths elements into this one. */
    for (n = vecs, i = 0; n; n = n->v_link2, i++)
        if (n->v_length > ind) {
            if (comp) {
                v->v_compdata[i] = n->v_compdata[ind];
            } else {
                v->v_realdata[i] = n->v_realdata[ind];
            }
        } else {
            if (comp) {
                realpart(v->v_compdata[i]) = 0.0;
                imagpart(v->v_compdata[i]) = 0.0;
            } else {
                v->v_realdata[i] = 0.0;
            }
        }
    vec_new(v);
    cp_addkword(CT_VECTOR, v->v_name);

done:
    free_pnode(names);
}

/* Free resources associated with "plot" datasets. The wordlist contains
 * the names of the plots to delete or the word "all" to delete all but the
 * default "const" plot, which cannot be deleted, even by name. If there are
 * no names given, the current plot is deleted */
void com_destroy(wordlist *wl)
{
    /* If no name given, delete the current output data */
    if (!wl) {
        DelPlotWindows(plot_cur);
        killplot(plot_cur);
    }
    else if (eq(wl->wl_word, "all")) { /* "all" -> all plots deleted */
        struct plot *pl, *npl = NULL;
        for (pl = plot_list; pl; pl = npl) {
            npl = pl->pl_next;
            if (!eq(pl->pl_typename, "const")) {
                DelPlotWindows(pl);
                killplot(pl);
            }
            else {
                plot_num = 1;
            }
        }
    }
    else { /* list of plots by name */
        while (wl) {
            struct plot *pl;
            for (pl = plot_list; pl; pl = pl->pl_next) {
                if (eq(pl->pl_typename, wl->wl_word)) {
                    break;
                }
            }
            if (pl) {
                DelPlotWindows(pl);
                killplot(pl);
            }
            else {
                fprintf(cp_err, "Error: no such plot %s\n", wl->wl_word);
            }
            wl = wl->wl_next;
        }
    }
} /* end of function com_destroy */



static void killplot(struct plot *pl)
{
    if (eq(pl->pl_typename, "const")) {
        fprintf(cp_err, "Error: can't destroy the constant plot\n");
        return;
    }
    /*  pl_dvecs, pl_scale */
    {
        struct dvec *v;
        struct dvec *nv;
        for (v = pl->pl_dvecs; v; v = nv) {
            nv = v->v_next;
            vec_free(v);
        }
    }

    /* unlink from plot_list (linked via pl_next) */
    if (pl == plot_list) { /* First in list */
        plot_list = pl->pl_next;
        if (pl == plot_cur) {
            plot_cur = plot_list;
        }
    }
    else { /* inside list */
        struct plot *op;
        for (op = plot_list; op; op = op->pl_next) {
            if (op->pl_next == pl) {
                break;
            }
        }
        if (!op) {
            fprintf(cp_err,
                    "Internal Error: kill plot -- not in list\n");
            return;
        }
        op->pl_next = pl->pl_next;
        if (pl == plot_cur) {
            plot_cur = op;
        }
    }
    /* delete the hash table entry for this plot */
    if (pl->pl_lookup_table) {
        nghash_free(pl->pl_lookup_table, NULL, NULL);
        pl->pl_lookup_table = NULL;
    }
    txfree(pl->pl_title);
    txfree(pl->pl_name);
    txfree(pl->pl_typename);
    wl_free(pl->pl_commands);
    txfree(pl->pl_date); /* va: also tfree (memory leak) */
    if (pl->pl_ccom)  { /* va: also tfree (memory leak) */
        throwaway(pl->pl_ccom);
    }

    if (pl->pl_env) { /* The 'environment' for this plot. */
        /* va: HOW to do? */
        printf("va: killplot should tfree pl->pl_env=(%p)\n", pl->pl_env);
        fflush(stdout);
    }
    txfree(pl); /* va: also tfree pl itself (memory leak) */
}

/* delete the const plot (called from com_quit) */
void
destroy_const_plot(void)
{
    struct dvec *v, *nv = NULL;
    struct plot *pl = &constantplot;

    /*  pl_dvecs, pl_scale */
    for (v = pl->pl_dvecs; v; v = nv) {
        nv = v->v_next;
        vec_free(v);
    }
    /* delete the hash table entry for the const plot */
    if (pl->pl_lookup_table) {
        nghash_free(pl->pl_lookup_table, NULL, NULL);
        pl->pl_lookup_table = NULL;
    }
    wl_free(pl->pl_commands);
    if (pl->pl_ccom)    /* va: also tfree (memory leak) */
        throwaway(pl->pl_ccom);

    if (pl->pl_env) { /* The 'environment' for this plot. */
        /* va: HOW to do? */
        printf("va: killplot should tfree pl->pl_env=(%p)\n", pl->pl_env);
        fflush(stdout);
    }
}


/* delete all windows with graphs dedrived from a given plot */
static void
DelPlotWindows(struct plot *pl)
{
    /* do this only if windows or X11 is defined */
#if defined(HAS_WINGUI) || !defined(X_DISPLAY_MISSING)
    GRAPH *dgraph;
    int n;
    /* find and remove all graph structures derived from a given plot */
    for (n = 1; n < 100; n++) { /* should be no more than 100 */
        dgraph = FindGraph(n);
        if (dgraph) {
            if (ciprefix(pl->pl_typename, dgraph->plotname))
                RemoveWindow(dgraph);
        }
        /* We have to run through all potential graph ids. If some numbers are
           already missing, 'else break;' might miss the plotwindow to be removed. */
        /* else
           break;
        */
    }
#else
    NG_IGNORE(pl);
#endif
}


/*
 * command 'setplot'
 *   print a list of plots available
 * command 'setplot <plotname>'
 *   make <plotname> the current plot
 * command 'setplot new'
 *   create a new plot
 */

void
com_splot(wordlist *wl)
{
    struct plot *pl;

    if (wl) {
        plot_setcur(wl->wl_word);
        return;
    }

    fprintf(cp_out, "List of plots available:\n\n");
    for (pl = plot_list; pl; pl = pl->pl_next)
        fprintf(cp_out, "%s%s\t%s (%s)\n",
                (pl == plot_cur) ? "Current " : "\t",
                pl->pl_typename, pl->pl_title, pl->pl_name);
}
