/**********
Copyright 1992 Regents of the University of California.  All rights reserved.
**********/

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

#include "ngspice.h"
#include "cpdefs.h"
#include "ftedefs.h"
#include "fteparse.h"
#include "dvec.h"

#include "newcoms.h"
#include "quote.h"

/*
 *	reshape v(1) vxx#branch [10]
 *	reshape v(1) vxx#branch [10,4]
 *	reshape v(1) [,4]
 */

void
com_reshape(wordlist *wl)
{
    wordlist	*w, *w2, *wlast, *wsave;
    char	*p;
    struct dvec	*dv, *d;
    int		numdims;
    int		*dims;
    int		local_dims[MAXDIMS];
    int		state;
    int		empty;
    int		err;
    int		missing, nprod, prod;
    char	*vname;
    int		i;

    do {
	if (!wl)
	    return;

	/* find the first '[' */

	p = NULL;
	for (w = wl; w; w = w->wl_next) {
	  if ((p =strchr(w->wl_word, '[')))
		    break;
	}

	if (p && *p) {
	    if (p != w->wl_word)
		w = w->wl_next;
	    wlast = w;
	    *p++ = 0;
	} else
	    wlast = NULL;

	/* get the dimensions */
	dims = local_dims;
	numdims = 0;
	state = 0;
	empty = -1;
	err = 0;
	wsave = NULL;
	do {

	    if (!p || !*p) {
		if (!wlast)
		    break;
		p = wlast->wl_word;
		if (state == 2)
		    wsave = wlast;
		else
		    wsave = NULL;
		wlast = wlast->wl_next;
	    }

	    while (*p && isspace(*p))
		p++;

	    switch (state) {
	    case 0: /* p just at or before a number */

		if (numdims >= MAXDIMS) {
		    if (numdims == MAXDIMS)
		        printf("Maximum of %d dimensions possible\n", MAXDIMS);
		    numdims += 1;
		} else if (!isdigit(*p)) {
		    if (empty > -1) {
			printf("dimensions underspecified at dimension %d\n",
				numdims++);
			err = 1;
		    } else {
			empty = numdims;
			dims[numdims++] = 1;
		    }
		} else {
		    dims[numdims++] = atoi(p);
		    while (isdigit(*p))
			p++;
		}
		state = 1;
		break;

	    case 1: /* p after a number, looking for ',' or ']' */
		if (*p == ']') {
		    p++;
		    state = 2;
		} else if (*p == ',') {
		    p++;
		    state = 0;
		} else if (isdigit(*p)) {
		    state = 0;
		    break;
		} else if (!isspace(*p))
		    /* error */
		    state = 4;
		break;

	    case 2: /* p after a ']', either at the end or looking for '[' */
		if (*p == '[') {
		    p++;
		    state = 0;
		} else {
		    state = 3;
		}
	    }

	    while (*p && isspace(*p))
		p++;

	} while (state < 3);

	if (state == 2) {
	    wlast = wsave;
	} else if ((state == 4 || state < 2) && ((state != 0 || p) && *p)) {
	    printf("syntax error specifying dimensions\n");
	    return;
	}

	if (numdims > MAXDIMS)
	    continue;
	if (err)
	    continue;

	/* Copy dimensions from the first item if none are explicitly given */
	if (!numdims) {
	    /* Copy from the first */
	    vname = cp_unquote(wl->wl_word);
	    dv = vec_get(vname);
	    if (!dv) {
		printf("'%s' dimensions vector not found\n", vname);
		return;
	    }
	    numdims = dv->v_numdims;
	    dims = dv->v_dims;
	    wl = wl->wl_next;
	    empty = -1; /* just in case */
	}

	prod = 1;
	for (i = 0; i < numdims; i++)
	    prod *= dims[i];

	/* resize each vector */
	for (w2 = wl; w2 && w2 != w; w2 = w2->wl_next) {
	    vname = cp_unquote(w2->wl_word);

	    dv = vec_get(vname);
	    if (!dv) {
		printf("'%s' vector not found\n", vname);
		continue;
	    }

	    /* The name may expand to several vectors */
	    for (d = dv; d; d = d->v_link2) {
		nprod = 1;
		for (i = 0; i < d->v_numdims; i++)
		    nprod *= d->v_dims[i];
		if (nprod != d->v_length) {
		    printf("dimensions of \"%s\" were inconsistent\n",
			d->v_name);
		    nprod = d->v_length;
		}

		missing = nprod / prod;
		if (missing * prod != nprod) {
		    printf("dimensions don't fit \"%s\" (total size = %d)\n",
			d->v_name, nprod);
		    continue;
		}

		if (missing > 1 && empty < 0) {
		    /* last dimension unspecified */
		    d->v_numdims = numdims + 1;
		    d->v_dims[numdims] = missing;
		} else
		    d->v_numdims = numdims;

		/* fill in dimensions */
		for (i = 0; i < numdims; i++) {
		    if (i == empty)
			d->v_dims[i] = missing;
		    else
			d->v_dims[i] = dims[i];
		}
	    }
	    if (vname)
	        tfree(vname);
	}
    } while ((wl = wlast));
}
