/*
*	icalc - complex-expression parser
*
*	Special functions for icalc, such as sum, prod etc.
*
*	(C) Martin W Scott, 1991.
*/

#include <stdio.h>
#include "complex.h"
#include "constant.h"
#include "complex.tab.h"

extern int silent;

#define SUMMATION	1
#define PRODUCT		2
#define LOOP		3
#define VLOOP		4	/* (verbose loop) */

/*
*	General iteration function.
*
*	Used for sums, products and general iteration.
*
*/
static Complex iterate(al,how)
	ArgList *al;		/* argument list */
	int how;		/* sum, product or loop */
{
	Node *expr;		/* expression to iterate */
	Symbol *index;		/* index to iterate over */
	double *idxval, to;	/* real part of index, what to iterate upto */
	char *mode;		/* mode as string, for error message */
	Complex cplx;		/* general purpose complex variable */


	if (al)			/* agruments were passed */
	{
		expr = al->node;	/* get expression to iterate */

		if (al = al->next)	/* get upper limit */
		{
			cplx = cinteger(eval_tree(al->node));
			to = cplx.real;

			if ((al = al->next) && !al->next) /* get index... */
			{				  /* no more args? */
				(void)eval_tree(al->node);
				if (al->node->type == '=' || al->node->type == VAR)
				{
					index = al->node->contents.sym;

					if (index->type != VAR)	/* undefined */
						index->type = VAR;

					switch (how)	/* initial */
					{
						case SUMMATION:
							cplx = zero; break;
						case PRODUCT:
							cplx = one; break;
						case VLOOP:
							if (!silent)
								fprintf(stdout, "%5.5s =", index->name);
							break;
					}

					idxval = &index->u.val.real;
					index->u.val.imag = 0.0;

						for (; *idxval <= to; (*idxval)++) 
						switch (how)
						{
						case SUMMATION:
							cplx = cadd(cplx,eval_tree(expr));
							break;
						case PRODUCT:
							cplx = cmul(cplx,eval_tree(expr));
							break;
						case VLOOP:
							cplx = eval_tree(expr);
							if (!silent) {
								fprintf(stdout, "\t%5lg  ", *idxval);
								cprin(stdout, NULL, "\n", cplx);
							}
							break;
						case LOOP:
							cplx = eval_tree(expr);

						}

					return cplx;
					
				}
			}
		}
	}

	switch (how)	/* got here, so a mistake somewhere */
	{
		case SUMMATION:	mode = "Sum"; break;
		case PRODUCT:	mode = "Prod"; break;
		case LOOP:	mode = "every"; break;
		case VLOOP:	mode = "vevery"; break;
	}

	execerror("invalid usage of special function", mode);
}


Complex spec_sum(al)		/* do summation */
	ArgList *al;
{
	return iterate(al,SUMMATION);
}


Complex spec_prod(al)		/* do product */
	ArgList *al;
{
	return iterate(al,PRODUCT);
}


Complex spec_every(al)		/* do loop */
	ArgList *al;
{
	return iterate(al,LOOP);
}

Complex spec_vevery(al)		/* do verbose loop */
	ArgList *al;
{
	return iterate(al,VLOOP);
}


/*
*	multi() is a special function that takes any number of expressions
*	as arguments and evaluates them from left to right. It was added
*	to make certain operations possible from user-functions.
*/
Complex spec_multi(al)
	ArgList *al;
{
	while (al)	/* uses recursion to step through expressions */
	{
		(void)spec_multi(al->next);
		return eval_tree(al->node);
	}
}


static Complex spec_extreme(al,cmp,what)	/* maximum of Re(arg) in arg-list */
	ArgList *al;
	double (*cmp)();
	char *what;		/* ascii string containing name of op */
{
	Complex mval, tval;

	if (al)		/* called with arguments */
	{
		mval = eval_tree(al->node);
		mval.imag = 0.0;

		al = al->next;

		while (al)
		{
			tval = eval_tree(al->node);
			mval.real = cmp(mval.real, tval.real);
			al = al->next;
		}

		return mval;
	}

	execerror("invalid usage of special function", what);
}

static double fnmax(a, b)
	double a, b;
{ return max(a,b); }

Complex spec_max(al)		/* return max of Re(arg) */
	ArgList *al;
{ return spec_extreme(al, fnmax, "max"); }


static double fnmin(a, b)
	double a, b;
{ return min(a,b); }

Complex spec_min(al)		/* return min of Re(arg) */
	ArgList *al;
{ return spec_extreme(al, fnmin, "min"); }

