/* Copyright (c) IRCAM.
* For information on usage and redistribution, and for a DISCLAIMER OF ALL
* WARRANTIES, see the file, "LICENSE.txt," in this distribution.  */

/* "expr" was written by Shahrokh Yadegari c. 1989. -msp */
/* "expr~" and "fexpr~" conversion by Shahrokh Yadegari c. 1999,2000 */

/*
 * Feb 2002 - added access to variables
 *            multiple expression support
 *            new short hand forms for fexpr~
 *              now $y or $y1 = $y1[-1] and $y2 = $y2[-1]
 * --sdy
 *
 *
 *  version 0.50 - March 2016
 *  version 0.55 - July 2017
 *  version 0.56 - January 2018
 *  version 0.57 - October 2020
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include "x_vexp.h"

static char *exp_version = "0.57";

extern struct ex_ex *ex_eval(struct expr *expr, struct ex_ex *eptr,
                                                struct ex_ex *optr, int n);

#ifdef PD
static t_class *expr_class;
static t_class *expr_tilde_class;
static t_class *fexpr_tilde_class;
#else /* MSP */
void *expr_tilde_class;
#endif


/*------------------------- expr class -------------------------------------*/

extern int expr_donew(struct expr *expr, int ac, t_atom *av);

/*#define EXPR_DEBUG*/

static void expr_bang(t_expr *x);
t_int *expr_perform(t_int *w);


static void
expr_list(t_expr *x, t_symbol *s, int argc, const fts_atom_t *argv)
{
        int i;

        if (argc > MAX_VARS) argc = MAX_VARS;

        for (i = 0; i < argc; i++)
        {
                if (argv[i].a_type == A_FLOAT)
                {
                        if (x->exp_var[i].ex_type == ET_FI)
                                x->exp_var[i].ex_flt = argv[i].a_w.w_float;
                        else if (x->exp_var[i].ex_type == ET_II)
                                x->exp_var[i].ex_int = argv[i].a_w.w_float;
                        else if (x->exp_var[i].ex_type)
                            pd_error(x, "expr: type mismatch");
                }
                else if (argv[i].a_type == A_SYMBOL)
                {
                        if (x->exp_var[i].ex_type == ET_SI)
                                x->exp_var[i].ex_ptr = (char *)argv[i].a_w.w_symbol;
                        else if (x->exp_var[i].ex_type)
                            pd_error(x, "expr: type mismatch");
                }
        }
        expr_bang(x);
}

static void
expr_flt(t_expr *x, t_float f, int in)
{
        if (in >= MAX_VARS)
                return;

        if (x->exp_var[in].ex_type == ET_FI)
                x->exp_var[in].ex_flt = f;
        else if (x->exp_var[in].ex_type == ET_II)
                x->exp_var[in].ex_int = f;
}

static t_class *exprproxy_class;

typedef struct _exprproxy {
        t_pd p_pd;
        int p_index;
        t_expr *p_owner;
        struct _exprproxy *p_next;
} t_exprproxy;

t_exprproxy *exprproxy_new(t_expr *owner, int indx);
void exprproxy_float(t_exprproxy *p, t_floatarg f);

t_exprproxy *
exprproxy_new(t_expr *owner, int indx)
{
        t_exprproxy *x = (t_exprproxy *)pd_new(exprproxy_class);
        x->p_owner = owner;
        x->p_index = indx;
        x->p_next = owner->exp_proxy;
        owner->exp_proxy = x;
        return (x);
}

void
exprproxy_float(t_exprproxy *p, t_floatarg f)
{
        t_expr *x = p->p_owner;
        int in = p->p_index;

        if (in >= MAX_VARS)
                return;

        if (x->exp_var[in].ex_type == ET_FI)
                x->exp_var[in].ex_flt = f;
        else if (x->exp_var[in].ex_type == ET_II)
                x->exp_var[in].ex_int = f;
}

/* method definitions */
static void
expr_ff(t_expr *x)
{
        t_exprproxy *y;
        int i;

        y = x->exp_proxy;
        while (y)
        {
                x->exp_proxy = y->p_next;
#ifdef PD
                pd_free(&y->p_pd);
#else /*MSP */
        /* SDY find out what needs to be called for MSP */

#endif
                y = x->exp_proxy;
        }
        for (i = 0 ; i < x->exp_nexpr; i++)
                if (x->exp_stack[i])
                        fts_free(x->exp_stack[i]);
/*
 * SDY free all the allocated buffers here for expr~ and fexpr~
 * check to see if there are others
 */
        for (i = 0; i < MAX_VARS; i++) {
                if (x->exp_p_var[i])
                        fts_free(x->exp_p_var[i]);
                if (x->exp_p_res[i])
                        fts_free(x->exp_p_res[i]);
                if (x->exp_tmpres[i])
                        fts_free(x->exp_tmpres[i]);
        }


}

static void
expr_bang(t_expr *x)
{
        int i;

#ifdef EXPR_DEBUG
        {
                struct ex_ex *eptr;

                for (i = 0, eptr = x->exp_var;  ; eptr++, i++)
                {
                        if (!eptr->ex_type)
                                break;
                        switch (eptr->ex_type)
                        {
                        case ET_II:
                                fprintf(stderr,"ET_II: %d \n", eptr->ex_int);
                                break;

                        case ET_FI:
                                fprintf(stderr,"ET_FT: %f \n", eptr->ex_flt);
                                break;

                        default:
                                fprintf(stderr,"oups\n");
                        }
                }
        }
#endif
        /* banging a signal or filter object means nothing */
        if (!IS_EXPR(x))
                return;

        for (i = x->exp_nexpr - 1; i > -1 ; i--) {
                if (!ex_eval(x, x->exp_stack[i], &x->exp_res[i], 0)) {
                        /*fprintf(stderr,"expr_bang(error evaluation)\n"); */
                /*  SDY now that we have multiple ones, on error we should
                 * continue
                        return;
                 */
                }
                switch(x->exp_res[i].ex_type) {
                case ET_INT:
                        outlet_float(x->exp_outlet[i],
                                        (t_float) x->exp_res[i].ex_int);
                        break;

                case ET_FLT:
                        outlet_float(x->exp_outlet[i],  x->exp_res[i].ex_flt);
                        break;

                case ET_SYM:
                        /* CHANGE this will have to be taken care of */

                default:
                        post("expr: bang: unrecognized result %ld\n", x->exp_res[i].ex_type);
                }
        }
}

static t_expr *
#ifdef PD
expr_new(t_symbol *s, int ac, t_atom *av)
#else /* MSP */
Nexpr_new(t_symbol *s, int ac, t_atom *av)
#endif
{
        struct expr *x;
        int i, ninlet;
        struct ex_ex *eptr;
        t_atom fakearg;
        int dsp_index;  /* keeping track of the dsp inlets */


/*
 * SDY - we may need to call dsp_setup() in this function
 */

        if (!ac)
        {
                ac = 1;
                av = &fakearg;
                SETFLOAT(&fakearg, 0);
        }

#ifdef PD
        /*
         * figure out if we are expr, expr~, or fexpr~
         */
        if (!strcmp("expr", s->s_name)) {
                x = (t_expr *)pd_new(expr_class);
                SET_EXPR(x);
        } else if (!strcmp("expr~", s->s_name)) {
                x = (t_expr *)pd_new(expr_tilde_class);
                SET_EXPR_TILDE(x);
        } else if (!strcmp("fexpr~", s->s_name)) {
                x = (t_expr *)pd_new(fexpr_tilde_class);
                SET_FEXPR_TILDE(x);
        } else {
                post("expr_new: bad object name '%s'", s->s_name);
                /* assume expr */
                x = (t_expr *)pd_new(expr_class);
                SET_EXPR(x);
        }
#else /* MSP */
        /* for now assume an expr~ */
        x = (t_expr *)pd_new(expr_tilde_class);
        SET_EXPR_TILDE(x);
#endif

        /*
         * initialize the newly allocated object
         */
        x->exp_proxy = 0;
        x->exp_nivec = 0;
        x->exp_nexpr = 0;
        x->exp_error = 0;
        for (i = 0; i < MAX_VARS; i++) {
                x->exp_stack[i] = (struct ex_ex *)0;
                x->exp_outlet[i] = (t_outlet *)0;
                x->exp_res[i].ex_type = 0;
                x->exp_res[i].ex_int = 0;
                x->exp_p_res[i] = (t_float *)0;
                x->exp_var[i].ex_type = 0;
                x->exp_var[i].ex_int = 0;
                x->exp_p_var[i] = (t_float *)0;
                x->exp_tmpres[i] = (t_float *)0;
                x->exp_vsize = 0;
        }
        x->exp_f = 0; /* save the control value to be transformed to signal */


        if (expr_donew(x, ac, av))
        {
                pd_error(x, "expr: syntax error");
/*
SDY the following coredumps why?
                pd_free(&x->exp_ob.ob_pd);
*/
                return (0);
        }

        ninlet = 1;
        for (i = 0, eptr = x->exp_var; i < MAX_VARS ; i++, eptr++)
                if (eptr->ex_type) {
                        ninlet = i + 1;
                }

        /*
         * create the new inlets
         */
        for (i = 1, eptr = x->exp_var + 1, dsp_index=1; i<ninlet ; i++, eptr++)
        {
                t_exprproxy *p;
                switch (eptr->ex_type)
                {
                case 0:
                        /* nothing is using this inlet */
                        if (i < ninlet)
#ifdef PD
                                floatinlet_new(&x->exp_ob, &eptr->ex_flt);
#else /* MSP */
                                inlet_new(&x->exp_ob, "float");
#endif
                        break;

                case ET_II:
                case ET_FI:
                        p = exprproxy_new(x, i);
#ifdef PD
                        inlet_new(&x->exp_ob, &p->p_pd, &s_float, &s_float);
#else /* MSP */
                        inlet_new(&x->exp_ob, "float");
#endif
                        break;

                case ET_SI:
#ifdef PD
                        symbolinlet_new(&x->exp_ob, (t_symbol **)&eptr->ex_ptr);
#else /* MSP */
                        inlet_new(&x->exp_ob, "symbol");
#endif
                        break;

                case ET_XI:
                case ET_VI:
                        if (!IS_EXPR(x)) {
                                dsp_index++;
#ifdef PD
                                inlet_new(&x->exp_ob, &x->exp_ob.ob_pd,
                                                        &s_signal, &s_signal);
#else /* MSP */
                                inlet_new(&x->exp_ob, "signal");
#endif
                                break;
                        } else
                                post("expr: internal error expr_new");
                                /* falls through */
                default:
                        pd_error(x, "expr: bad type (%lx) inlet = %d\n",
                                            eptr->ex_type, i + 1);
                        break;
                }
        }
        if (IS_EXPR(x)) {
                for (i = 0; i < x->exp_nexpr; i++)
                        x->exp_outlet[i] = outlet_new(&x->exp_ob, 0);
        } else {
                for (i = 0; i < x->exp_nexpr; i++)
                        x->exp_outlet[i] = outlet_new(&x->exp_ob,
                                                        gensym("signal"));
                x->exp_nivec = dsp_index;
        }
        /*
         * for now assume a 64 sample size block but this may change once
         * expr_dsp is called
         */
        x->exp_vsize = 64;
        for (i = 0; i < x->exp_nexpr; i++) {
                x->exp_p_res[i] = fts_calloc(x->exp_vsize, sizeof (t_float));
                x->exp_tmpres[i] = fts_calloc(x->exp_vsize, sizeof (t_float));
        }
        for (i = 0; i < MAX_VARS; i++)
                x->exp_p_var[i] = fts_calloc(x->exp_vsize, sizeof (t_float));

        return (x);
}

t_int *
expr_perform(t_int *w)
{
        int i, j;
        t_expr *x = (t_expr *)w[1];
        struct ex_ex res;
        int n;

        /* sanity check */
        if (IS_EXPR(x)) {
                post("expr_perform: bad x->exp_flags = %d", x->exp_flags);
                abort();
        }

        if (x->exp_flags & EF_STOP) {
                for (i = 0; i < x->exp_nexpr; i++)
                        memset(x->exp_res[i].ex_vec, 0,
                                        x->exp_vsize * sizeof (t_float));
                return (w + 2);
        }

        if (IS_EXPR_TILDE(x)) {
                /*
                 * if we have only one expression, we can right on
                 * on the output directly, otherwise we have to copy
                 * the data because, outputs could be the same buffer as
                 * inputs
                 */
                if ( x->exp_nexpr == 1)
                        ex_eval(x, x->exp_stack[0], &x->exp_res[0], 0);
                else {
                        res.ex_type = ET_VEC;
                        for (i = 0; i < x->exp_nexpr; i++) {
                                res.ex_vec = x->exp_tmpres[i];
                                ex_eval(x, x->exp_stack[i], &res, 0);
                        }
                        n = x->exp_vsize * sizeof(t_float);
                        for (i = 0; i < x->exp_nexpr; i++)
                                memcpy(x->exp_res[i].ex_vec, x->exp_tmpres[i],
                                                                        n);
                }
                return (w + 2);
        }

        if (!IS_FEXPR_TILDE(x)) {
                post("expr_perform: bad x->exp_flags = %d - expecting fexpr",
                                                                x->exp_flags);
                return (w + 2);
        }
        /*
         * since the output buffer could be the same as one of the inputs
         * we need to keep the output in  a different buffer
         */
        for (i = 0; i < x->exp_vsize; i++) for (j = 0; j < x->exp_nexpr; j++) {
                res.ex_type = 0;
                res.ex_int = 0;
                ex_eval(x, x->exp_stack[j], &res, i);
                switch (res.ex_type) {
                case ET_INT:
                        x->exp_tmpres[j][i] = (t_float) res.ex_int;
                        break;
                case ET_FLT:
                        x->exp_tmpres[j][i] = res.ex_flt;
                        break;
                default:
                        post("expr_perform: bad result type %d", res.ex_type);
                }
        }
        /*
         * copy inputs and results to the save buffers
         * inputs need to be copied first as the output buffer can be
         * same as an input buffer
         */
        n = x->exp_vsize * sizeof(t_float);
        for (i = 0; i < MAX_VARS; i++)
                if (x->exp_var[i].ex_type == ET_XI)
                        memcpy(x->exp_p_var[i], x->exp_var[i].ex_vec, n);
        for (i = 0; i < x->exp_nexpr; i++) {
                memcpy(x->exp_p_res[i], x->exp_tmpres[i], n);
                memcpy(x->exp_res[i].ex_vec, x->exp_tmpres[i], n);
        }
        return (w + 2);
}

static void
expr_dsp(t_expr *x, t_signal **sp)
{
        int i, nv;
        int newsize;

        x->exp_error = 0;               /* reset all errors */
        newsize = (x->exp_vsize !=  sp[0]->s_n);
        x->exp_vsize = sp[0]->s_n;      /* record the vector size */
        for (i = 0; i < x->exp_nexpr; i++) {
                x->exp_res[i].ex_type = ET_VEC;
                x->exp_res[i].ex_vec =  sp[x->exp_nivec + i]->s_vec;
        }
        for (i = 0, nv = 0; i < MAX_VARS; i++)
                /*
                 * the first inlet is always a signal
                 *
                 * SDY  We are warning the user till this limitation
                 * is taken away from pd
                 */
                if (!i || x->exp_var[i].ex_type == ET_VI ||
                                        x->exp_var[i].ex_type == ET_XI) {
                        if (nv >= x->exp_nivec) {
                          post("expr_dsp int. err nv = %d, x->exp_nive = %d",
                                                          nv,  x->exp_nivec);
                          abort();
                        }
                        x->exp_var[i].ex_vec  = sp[nv]->s_vec;
                        nv++;
                }
        /* we always have one inlet but we may not use it */
        if (nv != x->exp_nivec && (nv != 0 ||  x->exp_nivec != 1)) {
                post("expr_dsp internal error 2 nv = %d, x->exp_nive = %d",
                                                          nv,  x->exp_nivec);
                abort();
        }

        dsp_add(expr_perform, 1, (t_int *) x);

        /*
         * The buffer are now being allocated for expr~ and fexpr~
         * because if we have more than one expression we need the
         * temporary buffers, The save buffers are not really needed
        if (!IS_FEXPR_TILDE(x))
                return;
         */
        /*
         * if we have already allocated the buffers and we have a
         * new size free all the buffers
         */
        if (x->exp_p_res[0]) {
                if (!newsize)
                        return;
                /*
                 * if new size, reallocate all the previous buffers for fexpr~
                 */
                for (i = 0; i < x->exp_nexpr; i++) {
                        fts_free(x->exp_p_res[i]);
                        fts_free(x->exp_tmpres[i]);
                }
                for (i = 0; i < MAX_VARS; i++)
                        fts_free(x->exp_p_var[i]);

        }
        for (i = 0; i < x->exp_nexpr; i++) {
                x->exp_p_res[i] = fts_calloc(x->exp_vsize, sizeof (t_float));
                x->exp_tmpres[i] = fts_calloc(x->exp_vsize, sizeof (t_float));
        }
        for (i = 0; i < MAX_VARS; i++)
                x->exp_p_var[i] = fts_calloc(x->exp_vsize, sizeof (t_float));
}

/*
 * expr_verbose -- toggle the verbose switch
 */
static void
expr_verbose(t_expr *x)
{
        if (x->exp_flags & EF_VERBOSE) {
                x->exp_flags &= ~EF_VERBOSE;
                post ("verbose off");
        } else {
                x->exp_flags |= EF_VERBOSE;
                post ("verbose on");
        }
}


static void
expr_version(t_expr *x)
{
        post( "expr, expr~, fexpr~ version %s", exp_version);
}

/*
 * expr_start -- turn on expr processing for now only used for fexpr~
 */
static void
expr_start(t_expr *x)
{
        x->exp_flags &= ~EF_STOP;
}

/*
 * expr_stop -- turn on expr processing for now only used for fexpr~
 */
static void
expr_stop(t_expr *x)
{
        x->exp_flags |= EF_STOP;
}
static void
fexpr_set_usage(void)
{
        post("fexpr~: set val ...");
        post("fexpr~: set {xy}[#] val ...");
}

/*
 * fexpr_tilde_set -- set previous values of the buffers
 *              set val val ... - sets the first elements of output buffers
 *              set x val ...   - sets the elements of the first input buffer
 *              set x# val ...  - sets the elements of the #th input buffers
 *              set y val ...   - sets the elements of the first output buffer
 *              set y# val ...  - sets the elements of the #th output buffers
 */
static void
fexpr_tilde_set(t_expr *x, t_symbol *s, int argc, t_atom *argv)
{
        t_symbol *sx;
        int vecno;
        int i, nargs;

        if (!argc)
                return;
        sx = atom_getsymbolarg(0, argc, argv);
        switch(sx->s_name[0]) {
        case 'x':
                if (!sx->s_name[1])
                        vecno = 0;
                else {
                        vecno = atoi(sx->s_name + 1);
                        if (!vecno) {
                                post("fexpr~.set: bad set x vector number");
                                fexpr_set_usage();
                                return;
                        }
                        if (vecno >= MAX_VARS) {
                                post("fexpr~.set: no more than %d inlets",
                                                                      MAX_VARS);
                                return;
                        }
                        vecno--;
                }
                if (x->exp_var[vecno].ex_type != ET_XI) {
                        post("fexpr~-set: no signal at inlet %d", vecno + 1);
                        return;
                }
                nargs = argc - 1;
                if (!nargs) {
                        post("fexpr~-set: no argument to set");
                        return;
                }
                if (nargs > x->exp_vsize) {
                   post("fexpr~.set: %d set values larger than vector size(%d)",
                                                        nargs,  x->exp_vsize);
                   post("fexpr~.set: only the first %d values will be set",
                                                                x->exp_vsize);
                   nargs = x->exp_vsize;
                }
                for (i = 0; i < nargs; i++) {
                        x->exp_p_var[vecno][x->exp_vsize - i - 1] =
                                        atom_getfloatarg(i + 1, argc, argv);
                }
                return;
        case 'y':
                if (!sx->s_name[1])
                        vecno = 0;
                else {
                        vecno = atoi(sx->s_name + 1);
                        if (!vecno) {
                                post("fexpr~.set: bad set y vector number");
                                fexpr_set_usage();
                                return;
                        }
                        vecno--;
                }
                if (vecno >= x->exp_nexpr) {
                        post("fexpr~.set: only %d outlets", x->exp_nexpr);
                        return;
                }
                nargs = argc - 1;
                if (!nargs) {
                        post("fexpr~-set: no argument to set");
                        return;
                }
                if (nargs > x->exp_vsize) {
                   post("fexpr~-set: %d set values larger than vector size(%d)",
                                                        nargs,  x->exp_vsize);
                   post("fexpr~.set: only the first %d values will be set",
                                                                x->exp_vsize);
                   nargs = x->exp_vsize;
                }
                for (i = 0; i < nargs; i++) {
                        x->exp_p_res[vecno][x->exp_vsize - i - 1] =
                                        atom_getfloatarg(i + 1, argc, argv);
                }
                return;
        case 0:
                if (argc >  x->exp_nexpr) {
                        post("fexpr~.set: only %d outlets available",
                                                                x->exp_nexpr);
                        post("fexpr~.set: the extra set values are ignored");
                }
                for (i = 0; i < x->exp_nexpr && i < argc; i++)
                        x->exp_p_res[i][x->exp_vsize - 1] =
                                        atom_getfloatarg(i, argc, argv);
                return;
        default:
                fexpr_set_usage();
                return;
        }
        return;
}

/*
 * fexpr_tilde_clear - clear the past buffers
 */
static void
fexpr_tilde_clear(t_expr *x, t_symbol *s, int argc, t_atom *argv)
{
        t_symbol *sx;
        int vecno;
        int i, nargs;

        /*
         *  if no argument clear all input and output buffers
         */
        if (!argc) {
                for (i = 0; i < x->exp_nexpr; i++)
                        memset(x->exp_p_res[i], 0, x->exp_vsize*sizeof(t_float));
                for (i = 0; i < MAX_VARS; i++)
                        if (x->exp_var[i].ex_type == ET_XI)
                                memset(x->exp_p_var[i], 0,
                                                x->exp_vsize*sizeof(t_float));
                return;
        }
        if (argc > 1) {
                post("fexpr~ usage: 'clear' or 'clear {xy}[#]'");
                return;
        }

        sx = atom_getsymbolarg(0, argc, argv);
        switch(sx->s_name[0]) {
        case 'x':
                if (!sx->s_name[1])
                        vecno = 0;
                else {
                        vecno = atoi(sx->s_name + 1);
                        if (!vecno) {
                                post("fexpr~.clear: bad clear x vector number");
                                return;
                        }
                        if (vecno >= MAX_VARS) {
                                post("fexpr~.clear: no more than %d inlets",
                                                                      MAX_VARS);
                                return;
                        }
                        vecno--;
                }
                if (x->exp_var[vecno].ex_type != ET_XI) {
                        post("fexpr~-clear: no signal at inlet %d", vecno + 1);
                        return;
                }
                memset(x->exp_p_var[vecno], 0, x->exp_vsize*sizeof(t_float));
                return;
        case 'y':
                if (!sx->s_name[1])
                        vecno = 0;
                else {
                        vecno = atoi(sx->s_name + 1);
                        if (!vecno) {
                                post("fexpr~.clear: bad clear y vector number");
                                return;
                        }
                        vecno--;
                }
                if (vecno >= x->exp_nexpr) {
                        post("fexpr~.clear: only %d outlets", x->exp_nexpr);
                        return;
                }
                memset(x->exp_p_res[vecno], 0, x->exp_vsize*sizeof(t_float));
                return;
                return;
        default:
                post("fexpr~ usage: 'clear' or 'clear {xy}[#]'");
                return;
        }
        return;
}

#ifdef PD

void
expr_setup(void)
{
        /*
         * expr initialization
         */
        expr_class = class_new(gensym("expr"), (t_newmethod)expr_new,
            (t_method)expr_ff, sizeof(t_expr), 0, A_GIMME, 0);
        class_addlist(expr_class, expr_list);
        exprproxy_class = class_new(gensym("exprproxy"), 0,
                                        0, sizeof(t_exprproxy), CLASS_PD, 0);
        class_addfloat(exprproxy_class, exprproxy_float);
        class_addmethod(expr_class,(t_method)expr_version,
                                                        gensym("version"), 0);

        /*
         * expr~ initialization
         */
        expr_tilde_class = class_new(gensym("expr~"), (t_newmethod)expr_new,
            (t_method)expr_ff, sizeof(t_expr), 0, A_GIMME, 0);
        class_addmethod(expr_tilde_class, nullfn, gensym("signal"), 0);
        CLASS_MAINSIGNALIN(expr_tilde_class, t_expr, exp_f);
        class_addmethod(expr_tilde_class,(t_method)expr_dsp, gensym("dsp"),
                                                        A_CANT, 0);
        class_sethelpsymbol(expr_tilde_class, gensym("expr"));
        class_addmethod(expr_tilde_class,(t_method)expr_version,
                                                        gensym("version"), 0);
        /*
         * fexpr~ initialization
         */
        fexpr_tilde_class = class_new(gensym("fexpr~"), (t_newmethod)expr_new,
            (t_method)expr_ff, sizeof(t_expr), 0, A_GIMME, 0);
        class_addmethod(fexpr_tilde_class, nullfn, gensym("signal"), 0);
        CLASS_MAINSIGNALIN(fexpr_tilde_class, t_expr, exp_f);
        class_addmethod(fexpr_tilde_class,(t_method)expr_start,
                                                        gensym("start"), 0);
        class_addmethod(fexpr_tilde_class,(t_method)expr_stop,
                                                        gensym("stop"), 0);

        class_addmethod(fexpr_tilde_class,(t_method)expr_dsp,gensym("dsp"),
                                                        A_CANT, 0);
        class_addmethod(fexpr_tilde_class, (t_method)fexpr_tilde_set,
                        gensym("set"), A_GIMME, 0);
        class_addmethod(fexpr_tilde_class, (t_method)fexpr_tilde_clear,
                        gensym("clear"), A_GIMME, 0);
        class_addmethod(fexpr_tilde_class,(t_method)expr_verbose,
                                                        gensym("verbose"), 0);
        class_addmethod(fexpr_tilde_class,(t_method)expr_version,
                                                        gensym("version"), 0);
        class_sethelpsymbol(fexpr_tilde_class, gensym("expr"));

}

void
expr_tilde_setup(void)
{
        expr_setup();
}

void
fexpr_tilde_setup(void)
{
        expr_setup();
}
#else /* MSP */
void
main(void)
{
        setup((t_messlist **)&expr_tilde_class, (method)Nexpr_new,
                (method)expr_ff, (short)sizeof(t_expr), 0L, A_GIMME, 0);
        addmess((method)expr_dsp, "dsp", A_CANT, 0); // dsp method
        dsp_initclass();
}
#endif


/* -- the following functions use Pd internals and so are in the "if" file. */


int
ex_getsym(char *p, fts_symbol_t *s)
{
        *s = gensym(p);
        return (0);
}

const char *
ex_symname(fts_symbol_t s)
{
        if (!s)
            return (0);
        return (fts_symbol_name(s));
}

/*
 * max_ex_tab -- evaluate this table access
 *               eptr is the name of the table and arg is the index we
 *               have to put the result in optr
 *               return 1 on error and 0 otherwise
 *
 * Arguments:
 *  the expr object
 *  table
 *  the argument
 *  the result pointer
 */
int
max_ex_tab(struct expr *expr, fts_symbol_t s, struct ex_ex *arg,
    struct ex_ex *optr)
{
#ifdef PD
        t_garray *garray;
        int size;
        long indx;
        t_word *wvec;

        if (!s || !(garray = (t_garray *)pd_findbyclass(s, garray_class)) ||
            !garray_getfloatwords(garray, &size, &wvec))
        {
                optr->ex_type = ET_FLT;
                optr->ex_flt = 0;
                pd_error(expr, "no such table '%s'", ex_symname(s));
                return (1);
        }
        optr->ex_type = ET_FLT;

        switch (arg->ex_type) {
        case ET_INT:
                indx = arg->ex_int;
                break;
        case ET_FLT:
                /* strange interpolation code deleted here -msp */
                indx = arg->ex_flt;
                break;

        default:        /* do something with strings */
                pd_error(expr, "expr: bad argument for table '%s'\n", fts_symbol_name(s));
                indx = 0;
        }
        if (indx < 0) indx = 0;
        else if (indx >= size) indx = size - 1;
        optr->ex_flt = wvec[indx].w_float;
#else /* MSP */
        /*
         * table lookup not done for MSP yet
         */
        post("max_ex_tab: not complete for MSP yet!");
        optr->ex_type = ET_FLT;
        optr->ex_flt = 0;
#endif
        return (0);
}

/*
 * max_ex_tab_store -- store a value in a table
 *                                              tbl[arg->value] = rval.value
 *               eptr is the name of the table and arg is the index we
 *               have to put the result in optr
 *               return 1 on error and 0 otherwise
 *
 * Arguments:
 *  the expr object
 *  table
 *  the argument
 *  value to be stored
 *  the result pointer
 */
int
max_ex_tab_store(struct expr *expr, t_symbol *s, struct ex_ex *arg,
                                                                        struct ex_ex *rval, struct ex_ex *optr)
{
#ifdef PD
        t_garray *garray;
        int size;
        long indx;
        t_word *wvec;

        if (!s || !(garray = (t_garray *)pd_findbyclass(s, garray_class)) ||
                !garray_getfloatwords(garray, &size, &wvec)) {
                optr->ex_type = ET_FLT;
                optr->ex_flt = 0;
                if (s)
                    pd_error(expr, "no such table to store '%s'", s->s_name);
                else
                    pd_error(expr, "cannot store in unnamed table");
                return (1);
        }
        optr->ex_type = ET_FLT;

        switch (arg->ex_type) {
        case ET_INT:
                indx = arg->ex_int;
                break;
        case ET_FLT:
                /* strange interpolation code deleted here -msp */
                indx = arg->ex_flt;
                break;

        default:        /* do something with strings */
                pd_error(expr, "expr: bad argument for table store '%s'\n",
                        fts_symbol_name(s));
                indx = 0;
        }
        if (indx < 0)
                indx = 0;
        else if (indx >= size)
                indx = size - 1;
        *optr = *rval;
        switch (rval->ex_type) {
        case ET_INT:
                wvec[indx].w_float = rval->ex_int;
                                break;
        case ET_FLT:
                wvec[indx].w_float = rval->ex_flt;
                                break;
        default:
                pd_error(expr, "expr:bad right value type '%ld'", rval->ex_type);
                optr->ex_type = ET_FLT;
                optr->ex_flt = 0;
                return (1);
        }
                garray_redraw(garray);
                return(0);

#else /* MSP */
        /*
         * table lookup not done for MSP yet
         */
        post("max_ex_tab: not complete for MSP yet!");
        optr->ex_type = ET_FLT;
        optr->ex_flt = 0;
#endif
        return (0);
}

int
max_ex_var(struct expr *expr, t_symbol *var, struct ex_ex *optr, int idx)
{
        optr->ex_type = ET_FLT;
                if (!strcmp(var->s_name, "sys_idx")) {
                        optr->ex_flt = idx;
                        return (0);
                }
        if (value_getfloat(var, &(optr->ex_flt))) {
                optr->ex_type = ET_FLT;
                optr->ex_flt = 0;
                pd_error(expr, "no such var '%s'", var->s_name);
                return (1);
        }
        return (0);
}

#ifdef PD /* this goes to the end of this file as the following functions
           * should be defined in the expr object in MSP
           */
#define ISTABLE(sym, garray, size, vec)                               \
if (!sym || !(garray = (t_garray *)pd_findbyclass(sym, garray_class)) || \
                !garray_getfloatwords(garray, &size, &vec))  {          \
        optr->ex_type = ET_FLT;                                         \
        optr->ex_int = 0;                                               \
        pd_error(0, "no such table '%s'", sym?(sym->s_name):"(null)");                       \
        return;                                                         \
}

/*
 * ex_size -- find the size of a table
 */
void
ex_size(t_expr *e, long int argc, struct ex_ex *argv, struct ex_ex *optr)
{
        t_symbol *s;
        t_garray *garray;
        int size;
        t_word *wvec;

        if (argv->ex_type != ET_SYM)
        {
                post("expr: size: need a table name\n");
                optr->ex_type = ET_INT;
                optr->ex_int = 0;
                return;
        }

        s = (fts_symbol_t ) argv->ex_ptr;

        ISTABLE(s, garray, size, wvec);

        optr->ex_type = ET_INT;
        optr->ex_int = size;
}

/*
 * ex_sum -- calculate the sum of all elements of a table
 */

void
ex_sum(t_expr *e, long int argc, struct ex_ex *argv, struct ex_ex *optr)
{
        t_symbol *s;
        t_garray *garray;
        int size;
        t_word *wvec;
        t_float sum;
        int indx;

        if (argv->ex_type != ET_SYM)
        {
                post("expr: sum: need a table name\n");
                optr->ex_type = ET_INT;
                optr->ex_int = 0;
                return;
        }

        s = (fts_symbol_t ) argv->ex_ptr;

        ISTABLE(s, garray, size, wvec);

        for (indx = 0, sum = 0; indx < size; indx++)
                sum += wvec[indx].w_float;

        optr->ex_type = ET_FLT;
        optr->ex_flt = sum;
}


/*
 * ex_Sum -- calculate the sum of table with the given boundaries
 */

void
ex_Sum(t_expr *e, long int argc, struct ex_ex *argv, struct ex_ex *optr)
{
        t_symbol *s;
        t_garray *garray;
        int size;
        t_word *wvec;
        t_float sum;
        long indx, n1, n2;

        if (argv->ex_type != ET_SYM)
        {
                post("expr: sum: need a table name\n");
                optr->ex_type = ET_INT;
                optr->ex_int = 0;
                return;
        }

        s = (fts_symbol_t ) argv->ex_ptr;

        ISTABLE(s, garray, size, wvec);

                switch((++argv)->ex_type) {
                case ET_INT:
                n1 = argv->ex_int;
                        break;
                case ET_FLT:
                n1 = argv->ex_flt;
                        break;
                default:
                        post("expr: Sum: boundaries have to be fix values\n");
                        optr->ex_type = ET_INT;
                        optr->ex_int = 0;
                        return;
                }
                if (n1 < 0)
                        n1 = 0;

                switch((++argv)->ex_type) {
                case ET_INT:
                n2 = argv->ex_int;
                        break;
                case ET_FLT:
                n2 = argv->ex_flt;
                        break;
                default:
                        post("expr: Sum: boundaries have to be fix values\n");
                        optr->ex_type = ET_INT;
                        optr->ex_int = 0;
                        return;
                }
                if (n2 > size)
                        n2 = size;

        for (indx = n1, sum = 0; indx <= n2; indx++)
                        if (indx >= 0 && indx < size)
                                sum += wvec[indx].w_float;

        optr->ex_type = ET_FLT;
        optr->ex_flt = sum;
}

/*
 * ex_avg -- calculate the average of a table
 */

void
ex_avg(t_expr *e, long int argc, struct ex_ex *argv, struct ex_ex *optr)
{
        t_symbol *s;
        t_garray *garray;
        int size;
        t_word *wvec;
        t_float sum;
        int indx;

        if (argv->ex_type != ET_SYM)
        {
                post("expr: avg: need a table name\n");
                optr->ex_type = ET_INT;
                optr->ex_int = 0;
                return;
        }

        s = (fts_symbol_t ) argv->ex_ptr;

        ISTABLE(s, garray, size, wvec);

        for (indx = 0, sum = 0; indx < size; indx++)
                sum += wvec[indx].w_float;

        optr->ex_type = ET_FLT;
        optr->ex_flt = sum / size;
}


/*
 * ex_Avg -- calculate the average of table with the given boundaries
 */

void
ex_Avg(t_expr *e, long int argc, struct ex_ex *argv, struct ex_ex *optr)
{
        t_symbol *s;
        t_garray *garray;
        int size;
        t_word *wvec;
        t_float sum;
        long indx, n1, n2;

        if (argv->ex_type != ET_SYM)
        {
                post("expr: sum: need a table name\n");
                optr->ex_type = ET_INT;
                optr->ex_int = 0;
                return;
        }

        s = (fts_symbol_t ) argv->ex_ptr;

        ISTABLE(s, garray, size, wvec);

                switch((++argv)->ex_type) {
                case ET_INT:
                n1 = argv->ex_int;
                        break;
                case ET_FLT:
                n1 = argv->ex_flt;
                        break;
                default:
                        post("expr: Avg: boundaries have to be fix values\n");
                        optr->ex_type = ET_INT;
                        optr->ex_int = 0;
                        return;
                }
                if (n1 < 0)
                        n1 = 0;

                switch((++argv)->ex_type) {
                case ET_INT:
                n2 = argv->ex_int;
                        break;
                case ET_FLT:
                n2 = argv->ex_flt;
                        break;
                default:
                        post("expr: Avg: boundaries have to be fix values\n");
                        optr->ex_type = ET_INT;
                        optr->ex_int = 0;
                        return;
                }
                if (n2 >= size)
                        n2 = size - 1;

        for (indx = n1, sum = 0; indx <= n2; indx++)
                if (indx >= 0 && indx < size)
                        sum += wvec[indx].w_float;

        optr->ex_type = ET_FLT;
        optr->ex_flt = sum / (n2 - n1 + 1);
}

/*
 * max_ex_store --- store a value in a variable or table
 */
int
max_ex_var_store(struct expr *expr, t_symbol * var, struct ex_ex *eptr, struct ex_ex *optr)
{
                t_float value = 0.;

                *optr = *eptr;
                switch (eptr->ex_type) {
                case ET_INT:
                        value = eptr->ex_int;
                        break;
                case ET_FLT:
                        value = eptr->ex_flt;
                        break;
                default:
                        post("do not know yet\n");
                }

        if (value_setfloat(var, value)) {
                optr->ex_flt = 0;
                pd_error(expr, "no such var '%s'", var->s_name);
                return (1);
        }
        return (0);
}

/*
 * ex_store -- store a value in a table
 *             if the index is greater the size of the table,
 *             we will make a modulo the size of the table
 */

void
ex_store(t_expr *e, long int argc, struct ex_ex *argv, struct ex_ex *optr)
{
/* SDY - look into this function */
#if 0
        fts_symbol_t s;
        fts_integer_vector_t *tw = 0;

        if (argv->ex_type != ET_SYM)
        {
                post("expr: store: need a table name\n");
        }

        s = (fts_symbol_t ) (argv++)->ex_ptr;

        tw = table_integer_vector_get_by_name(s);

        if (! tw)
        {
                optr->ex_type = ET_INT;
                optr->ex_int = 0;
                post("expr: store: no such table %s\n", fts_symbol_name(s));
                return;
        }

        if (argv->ex_type != ET_INT || argv[1].ex_type != ET_INT)
        {
                post("expr: store: arguments have to be integer\n");
                optr->ex_type = ET_INT;
                optr->ex_int = 0;
        }

        fts_integer_vector_set_element(tw, argv->ex_int < 0 ? 0 : argv->ex_int % fts_integer_vector_get_size(tw), argv[1].ex_int);
        *optr = argv[1];
#endif
}

#else /* MSP */

void
pd_error(void *object, char *fmt, ...)
{
    va_list ap;
    t_int arg[8];
    int i;
    static int saidit = 0;
    va_start(ap, fmt);
/* SDY
    vsprintf(error_string, fmt, ap);
*/ post(fmt, ap);
        va_end(ap);
/* SDY
    fprintf(stderr, "error: %s\n", error_string);
    error_object = object;
*/
    if (!saidit)
    {
        post("... you might be able to track this down from the Find menu.");
        saidit = 1;
    }
}
#endif
