/*
 * jMax
 * Copyright (C) 1994, 1995, 1998, 1999 by IRCAM-Centre Georges Pompidou, Paris, France.
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * See file LICENSE for further informations on licensing terms.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 * 
 * Based on Max/ISPW by Miller Puckette.
 *
 * Authors: Maurizio De Cecco, Francois Dechelle, Enzo Maggi, Norbert Schnell.
 *
 */

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

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


#include "vexp.h"
#ifndef MSP
#ifndef MACOSX
/*
 * the compiler on mac seems not to like this, perhaps we could get away with
 * not having it at all.
 */
#include "stdlib.h"
#endif
#endif
#include "string.h"

static char *exp_version = "0.3";

extern struct ex_ex *ex_eval(struct expr *exp, 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 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 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;
	
	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;
	}
	if (x->exp_stack)
		fts_free(x->exp_stack);
/*
 * SDY free all the allocated buffers here for expr~ and fexpr~
 */
}

static void
expr_bang(t_expr *x)
{

#ifdef EXPR_DEBUG
	{
		int i;
		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;

	if (!ex_eval(x, x->exp_stack, &x->exp_res, 0))
	{
		/*       fprintf(stderr,"expr_bang(error evaluation)\n"); */
		return;
	}


	switch(x->exp_res.ex_type)
	{
	case ET_INT:
		outlet_float(x->exp_outlet,  (t_float) x->exp_res.ex_int);
		break;

	case ET_FLT:
		outlet_float(x->exp_outlet,  x->exp_res.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.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'");
		/* 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_stack = (struct ex_ex *)0;
	x->exp_proxy = 0;
	x->exp_nivec = 0;
	x->exp_error = 0;
	x->exp_outlet = (t_outlet *)0;
	x->exp_res.ex_type = 0;
	x->exp_res.ex_int = 0;
	x->exp_p_res = (t_float *)0;
	x->exp_tmpres = (t_float *)0;
	for (i = 0; i < MAX_VARS; i++) {
		x->exp_var[i].ex_type = 0;
		x->exp_var[i].ex_int = 0;
		x->exp_p_var[i] = (t_float *)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");
		default:
			pd_error(x, "expr: bad type (%lx) inlet = %d\n",
					    eptr->ex_type, i + 1, 0, 0, 0);
			break;
		}
	}
	if (IS_EXPR(x)) {
		x->exp_outlet = outlet_new(&x->exp_ob, 0);
	} else {
#ifdef PD
		x->exp_outlet = outlet_new(&x->exp_ob,  gensym("signal"));
#else /* MSP */
		x->exp_outlet = outlet_new(&x->exp_ob,  "signal");
#endif
		x->exp_nivec = dsp_index;
	}

	return (x);
}

t_int *
expr_perform(t_int *w)
{
	int i;
	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) {
		memset(x->exp_res.ex_vec, 0, x->exp_vsize * sizeof (float));
		return (w + 2);
	}

	if (IS_EXPR_TILDE(x)) {
		ex_eval(x, x->exp_stack, &x->exp_res, 0);
		return (w + 2);
	}

	if (!IS_FEXPR_TILDE(x)) {
		post("expr_perform: bad x->exp_flags = %d - expecting fexpr",
								x->exp_flags);
		abort();
	}
	/*
	 * 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++) {
		res.ex_type = 0;
		res.ex_int = 0;
		ex_eval(x, x->exp_stack, &res, i);
		switch (res.ex_type) {
		case ET_INT:
			x->exp_tmpres[i] = (t_float) res.ex_int;
			break;
		case ET_FLT:
			x->exp_tmpres[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);
	memcpy(x->exp_p_res, x->exp_tmpres, n);
	memcpy(x->exp_res.ex_vec, x->exp_tmpres, 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 */
	x->exp_res.ex_type = ET_VEC;
	x->exp_res.ex_vec =  sp[x->exp_nivec]->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);

	if (!IS_FEXPR_TILDE(x))
		return;
	if (x->exp_p_res) {
		if (!newsize)
			return;
		/*
		 * if new size, reallocate all the previous buffers for fexpr~
		 */
		fts_free(x->exp_p_res);
		fts_free(x->exp_tmpres);
		for (i = 0; i < MAX_VARS; i++)
			fts_free(x->exp_p_var[i]);

	}
	x->exp_p_res = fts_calloc(x->exp_vsize, sizeof (t_float));
	x->exp_tmpres = 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_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;
}

#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);

	/*
	 * 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"), 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_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"), 0);

	post("expr, expr~, fexpr~ version %s under GNU General Public License ", exp_version);

}

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)
{
	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 *exp,fts_symbol_t s,struct ex_ex *arg,struct ex_ex *optr)
{
#ifdef PD
	t_garray *garray;
	int size, indx;
	t_float *vec;

	if (!s || !(garray = (t_garray *)pd_findbyclass(s, garray_class)) ||
	    !garray_getfloatarray(garray, &size, &vec))
	{
		optr->ex_type = ET_FLT;
		optr->ex_int = 0;
		pd_error(exp, "no such table '%s'", s->s_name);
		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(exp, "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 = vec[indx];
#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);
}

#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_getfloatarray(garray, &size, &vec))  {		\
	optr->ex_type = ET_FLT;						\
	optr->ex_int = 0;						\
	error("no such table '%s'", sym->s_name);			\
	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_float *vec;

	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, vec);

	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_float *vec, 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, vec);

	for (indx = 0, sum = 0; indx < size; indx++)
		sum += vec[indx];

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


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

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_float *vec, sum;
	int 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, vec);

	if (argv->ex_type != ET_INT || argv[1].ex_type != ET_INT)
	{
		post("expr: Sum: boundries have to be fix values\n");
		optr->ex_type = ET_INT;
		optr->ex_int = 0;
		return;
	}
	n1 = argv->ex_int;
	n2 = argv[1].ex_int;

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

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

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

void
ex_avg(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: avg: need a table name\n");
		optr->ex_type = ET_INT;
		optr->ex_int = 0;
	}

	s = (fts_symbol_t ) argv->ex_ptr;

	tw = table_integer_vector_get_by_name(s);

	if (tw)
	{
		optr->ex_type = ET_INT;

		if (! fts_integer_vector_get_size(tw))
			optr->ex_int = 0;
		else
			optr->ex_int = fts_integer_vector_get_sum(tw) / fts_integer_vector_get_size(tw);
	}
	else
	{
		optr->ex_type = ET_INT;
		optr->ex_int = 0;
		post("expr: avg: no such table %s\n", fts_symbol_name(s));
	}
#endif
}


/*
 * ex_Avg -- calculate the avarage of table with the given boundries
 */

void
ex_Avg(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: Avg: need a table name\n");
		optr->ex_type = ET_INT;
		optr->ex_int = 0;
	}

	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: Avg: no such table %s\n", fts_symbol_name(s));
		return;
	}

	if (argv->ex_type != ET_INT || argv[1].ex_type != ET_INT)
	{
		post("expr: Avg: boundries have to be fix values\n");
		optr->ex_type = ET_INT;
		optr->ex_int = 0;
		return;
	}

	optr->ex_type = ET_INT;

	if (argv[1].ex_int - argv->ex_int <= 0)
		optr->ex_int = 0;
	else
		optr->ex_int = (fts_integer_vector_get_sub_sum(tw, argv->ex_int, argv[1].ex_int) /
		    (argv[1].ex_int - argv->ex_int));
#endif
}

/*
 * 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