/*
 * Mathomatic simplifying and polynomial routines.
 * Includes polynomial and smart division, polynomial factoring, etc.
 * tlhs and trhs are wiped out by most of these routines.
 *
 * The polynomial division and GCD routines here are not recursive, due to static
 * storage areas.  This limits the polynomial GCD to univariate operation.
 *
 * Copyright (C) 1987-2007 George Gesslein II.
 */

#include "includes.h"

#define	REMAINDER_IS_ZERO()	(n_trhs == 1 && trhs[0].kind == CONSTANT && trhs[0].token.constant == 0.0)

/* These are a non-standard size and must only be used for temp storage: */
token_type	divisor[DIVISOR_SIZE];		/* static data areas for polynomial and smart division */
int		n_divisor;			/* length of expression in divisor[] */
token_type	quotient[DIVISOR_SIZE];
int		n_quotient;			/* length of expression in quotient[] */
token_type	gcd_divisor[DIVISOR_SIZE];	/* static data area for polynomial GCD routine */
int		len_d;				/* length of expression in gcd_divisor[] */

static int	do_gcd();
static int	pf_recurse();
static int	pf_sub();
static int	mod_recurse();
static int	polydiv_recurse();
static int	pdiv_recurse();
static int	poly_div_sub();
static void	save_factors();

/*
 * Compare function for qsort(3).
 */
static int
vcmp(p1, p2)
sort_type	*p1, *p2;
{
	return(p2->count - p1->count);
}

/*
 * Return true if passed expression is strictly a single polynomial term in variable v.
 * The exponent may be anything, as long as it doesn't contain the variable.
 */
static inline int
poly_in_v_sub(p1, n, v, allow_divides)
token_type	*p1;		/* expression pointer */
int		n;		/* expression length */
long		v;		/* variable */
int		allow_divides;	/* if true, allow division by variable */
{
	int	i, k;
	int	level, vlevel;
	int	count;

	level = min_level(p1, n);
	for (i = 0, count = 0; i < n; i += 2) {
		if (p1[i].kind == VARIABLE && p1[i].token.variable == v) {
			count++;
			if (count > 1)
				return false;
			vlevel = p1[i].level;
			if (vlevel == level || vlevel == (level + 1)) {
				for (k = 1; k < n; k += 2) {
					if (p1[k].level == level) {
						switch (p1[k].token.operatr) {
						case DIVIDE:
							if (!allow_divides && k == (i - 1))
								return false;
						case TIMES:
							continue;
						case POWER:
							if (k == (i + 1))
								continue;
						default:
							return false;
						}
					}
				}
				if (vlevel == (level + 1)) {
					if ((i + 1) < n && p1[i+1].level == vlevel
					    && p1[i+1].token.operatr == POWER) {
						continue;
					}
				} else {
					continue;
				}
			}
			return false;
		}
	}
	return true;
}

/*
 * Return true if passed expression is a polynomial in variable v.
 * The exponents may be anything, as long as they don't contain the variable.
 */
int
poly_in_v(p1, n, v, allow_divides)
token_type	*p1;		/* expression pointer */
int		n;		/* expression length */
long		v;		/* variable */
int		allow_divides;	/* allow variable to be right of a divide (negative exponents) as a polynomial term */
{
	int	i, j;

	for (i = 1, j = 0;; i += 2) {
		if (i >= n || (p1[i].level == 1
		    && (p1[i].token.operatr == PLUS || p1[i].token.operatr == MINUS))) {
			if (!poly_in_v_sub(&p1[j], i - j, v, allow_divides)) {
				return false;
			}
			j = i + 1;
		}
		if (i >= n)
			break;
	}
	return true;
}

/*
 * Factor polynomials.
 * Factors repeated factor polynomials (like (x+1)^5)
 * and symbolic factor polynomials (like (x+a)*(x+b)).
 *
 * Return true if equation side was modified (factored).
 */
int
poly_factor(equation, np)
token_type	*equation;	/* pointer to the beginning of equation side */
int		*np;		/* pointer to length of equation side */
{
	return pf_recurse(equation, np, 0, 1);
}

static int
pf_recurse(equation, np, loc, level)
token_type	*equation;
int		*np, loc, level;
{
	int	modified = false;
	int	i;
	int	op;
	int	len;

	op = 0;
	for (i = loc + 1; i < *np && equation[i].level >= level; i += 2) {
		if (equation[i].level == level) {
			op = equation[i].token.operatr;
		}
	}
	len = i - loc;
	switch (op) {
	case PLUS:
	case MINUS:
		modified = pf_sub(equation, np, loc, len, level);
		break;
	}
	for (i = loc; i < *np && equation[i].level >= level;) {
		if (equation[i].level > level) {
			modified |= pf_recurse(equation, np, i, level + 1);
			i++;
			for (; i < *np && equation[i].level > level; i += 2)
				;
			continue;
		}
		i++;
	}
	return modified;
}

/*
 * Polynomial factoring subroutine.
 * It can't factor everything, but it usually can factor polynomials
 * if it will make the expression size smaller.
 *
 * Return true if expression was modified (factored).
 */
static int
pf_sub(equation, np, loc, len, level)
token_type	*equation;
int		*np, loc, len, level;
{
	token_type	*p1;
	int		modified = false, symbolic_modified = false;
	int		i, j, k;
	long		v = 0, v1, last_v;
	int		len_first = 0;
	int		loc1;
	int		loc2, len2 = 0;
	int		loct, lent;
	int		count;
	jmp_buf		save_save;
	int		div_flag = 3;
	int		vc, cnt;
	sort_type	va[MAX_VARS];
	double		d;

	debug_string(3, "Entering pf_sub().");
	loc2 = loc1 = loc;
	find_greatest_power(&equation[loc1], len, &v, &d, &j, &k, &div_flag);
	if (v == 0)
		return false;
	blt(save_save, jmp_save, sizeof(jmp_save));
	if (setjmp(jmp_save) != 0) {
		blt(jmp_save, save_save, sizeof(jmp_save));
		return(modified || symbolic_modified);
	}
/* First factor polynomials with repeated factors */
/* using poly_gcd(polynomial, v * differentiate(polynomial, v)) to discover the factors: */
	for (count = 1;; count++) {
		blt(trhs, &equation[loc1], len * sizeof(token_type));
		n_trhs = len;
		uf_simp(trhs, &n_trhs);
		if (level1_plus_count(trhs, n_trhs) < 2) {
/* must be at least 2 level 1 additive operators to be factorable */
			goto skip_factor;
		}
/* create a variable list with counts of the number of times each variable occurs: */
		last_v = 0;
		for (vc = 0; vc < ARR_CNT(va); vc++) {
			for (i = 0, cnt = 0, v1 = -1; i < n_trhs; i += 2) {
				if (trhs[i].kind == VARIABLE && trhs[i].token.variable > last_v) {
					if (v1 == -1 || trhs[i].token.variable < v1) {
						v1 = trhs[i].token.variable;
						cnt = 1;
					} else if (trhs[i].token.variable == v1) {
						cnt++;
					}
				}
			}
			if (v1 == -1)
				break;
			last_v = v1;
			va[vc].v = v1;
			va[vc].count = cnt;
		}
		side_debug(3, &equation[loc1], len);
/* find a valid polynomial base variable "v": */
		cnt = -1;
		if (v) {
			if (!poly_in_v(trhs, n_trhs, v, true)) {
				v = 0;
			}
		}
		for (i = 0; i < vc; i++) {
			if ((va[i].v & VAR_MASK) <= SIGN) {
				continue;
			}
			if (v == 0) {
				if (poly_in_v(trhs, n_trhs, va[i].v, true)) {
					v = va[i].v;
				}
			}
			if (cnt < 0 || va[i].count < cnt) {
				cnt = va[i].count;
			}
		}
		if (cnt <= 1 || v == 0)
			goto skip_factor;
		blt(tlhs, trhs, n_trhs * sizeof(token_type));
		n_tlhs = n_trhs;
		if (!differentiate(tlhs, &n_tlhs, v)) {
			break;
		}
#if	!SILENT
		if (debug_level >= 3) {
			list_var(v, 0);
			printf(_("Differentiation successful using variable (%s).\n"), var_str);
		}
#endif
		simp_loop(tlhs, &n_tlhs);
		if ((n_tlhs + 2) > min(DIVISOR_SIZE, n_tokens))
			break;
		for (i = 0; i < n_tlhs; i++)
			tlhs[i].level++;
		tlhs[n_tlhs].kind = OPERATOR;
		tlhs[n_tlhs].level = 1;
		tlhs[n_tlhs].token.operatr = TIMES;
		n_tlhs++;
		tlhs[n_tlhs].kind = VARIABLE;
		tlhs[n_tlhs].level = 1;
		tlhs[n_tlhs].token.variable = v;
		n_tlhs++;
		uf_simp(tlhs, &n_tlhs);
		if (poly_gcd(&equation[loc1], len, tlhs, n_tlhs, v) == 0)
			break;
		if (!level1_plus_count(tlhs, n_tlhs))
			break;
		save_factors(equation, np, loc1, len, level);
		loc1 += n_tlhs + 1;
		len = n_trhs;
		switch (count) {
		case 1:
			debug_string(1, "Polynomial with repeated factor factored.");
			len_first = n_tlhs;
			loc2 = loc1;
			break;
		case 2:
			len2 = n_tlhs;
			break;
		}
		modified = true;
	}
/* Now factor polynomials with symbolic factors by grouping: */
	if (!modified) {
		last_v = 0;
next_v:
		p1 = &equation[loc];
		blt(trhs, p1, len * sizeof(token_type));
		n_trhs = len;
		uf_simp(trhs, &n_trhs);
		for (;;) {
			v = -1;
			for (i = 0; i < len; i += 2) {
				if (p1[i].kind == VARIABLE && p1[i].token.variable > last_v) {
					if (v == -1 || p1[i].token.variable < v) {
						v = p1[i].token.variable;
					}
				}
			}
			if (v == -1) {
				break;
			}
			last_v = v;
			/* make sure there is more than one "v" raised to the highest power: */
			if (find_greatest_power(trhs, n_trhs, &v, &d, &j, &k, &div_flag) <= 1) {
				continue;
			}
			blt(tlhs, trhs, n_trhs * sizeof(token_type));
			n_tlhs = n_trhs;
			/* do the grouping: */
			while (factor_plus(tlhs, &n_tlhs, v, 0.0)) {
				simp_loop(tlhs, &n_tlhs);
			}
			/* extract the highest power group: */
			if (find_greatest_power(tlhs, n_tlhs, &v, &d, &j, &k, &div_flag) != 1) {
				continue;
			}
			blt(tlhs, &tlhs[j], k * sizeof(token_type));
			n_tlhs = k;
#if	!SILENT
			if (debug_level >= 3) {
				printf(_("Trying factor: "));
				list_proc(tlhs, n_tlhs, false);
				printf("\n");
			}
#endif
			if (poly_gcd(&equation[loc1], len, tlhs, n_tlhs, 0L) == 0)
				goto next_v;
			if (!level1_plus_count(tlhs, n_tlhs))
				goto next_v;
			debug_string(1, "Symbolic polynomial factored.");
			save_factors(equation, np, loc1, len, level);
			loc1 += n_tlhs + 1;
			len = n_trhs;
			len_first = n_tlhs;
			loc2 = loc1;
			symbolic_modified = true;
			break;
		}
	}
skip_factor:
	blt(jmp_save, save_save, sizeof(jmp_save));
	if (modified) {
/* Repeated factor was factored out. */
/* See if we can factor out more of the repeated factor. */
		if (len2) {
			loct = loc2;
			lent = len2;
		} else {
			loct = loc;
			lent = len_first;
		}
		if (poly_gcd(&equation[loc1], len, &equation[loct], lent, v)) {
			save_factors(equation, np, loc1, len, level);
			loc1 += n_tlhs + 1;
			len = n_trhs;
		}
		if (len2) {
			loc1 = loc2;
			len = len2;
		}
		if (poly_gcd(&equation[loc], len_first, &equation[loc1], len, 0L)) {
			save_factors(equation, np, loc, len_first, level);
		}
	}
	return(modified || symbolic_modified);
}

static void
save_factors(equation, np, loc1, len, level)
token_type	*equation;
int		*np, loc1, len, level;
{
	int	i, j;

	i = n_tlhs + 1 + n_trhs;
	if ((*np + (i - len)) > n_tokens)
		error_huge();
	blt(&equation[loc1+i], &equation[loc1+len], (*np - (loc1 + len)) * sizeof(token_type));
	*np += i - len;
	blt(&equation[loc1], tlhs, n_tlhs * sizeof(token_type));
	i = loc1 + n_tlhs;
	equation[i].level = 0;
	equation[i].kind = OPERATOR;
	equation[i].token.operatr = TIMES;
	i++;
	blt(&equation[i], trhs, n_trhs * sizeof(token_type));
	i += n_trhs;
	for (j = loc1; j < i; j++)
		equation[j].level += level;
}

/*
 * Remove level 1 trivial factors and divides from "tlhs".
 *
 * Return true if result is a level 1 plus type expression.
 * If this returns false, nothing is removed.
 */
int
remove_factors()
{
	int	i, j, k;
	int	plus_flag = false, divide_flag = false;
	int	op;

	debug_string(3, "Entering remove_factors() with:");
	side_debug(3, tlhs, n_tlhs);
	do {
		simp_ssub(tlhs, &n_tlhs, 0L, 1.0, false, true, 0);
	} while (uf_power(tlhs, &n_tlhs));
	for (i = 1, j = 0, k = 0;; i += 2) {
		if (i >= n_tlhs) {
			if (plus_flag && !divide_flag) {
				if (k > 0)
					j--;
				blt(&scratch[k], &tlhs[j], (i - j) * sizeof(token_type));
				k += i - j;
			}
			if (k <= 0)
				return false;
			blt(tlhs, scratch, k * sizeof(token_type));
			n_tlhs = k;
			side_debug(3, tlhs, n_tlhs);
			return true;
		}
		op = tlhs[i].token.operatr;
		switch (tlhs[i].level) {
		case 1:
			switch (op) {
			case PLUS:
			case MINUS:
				plus_flag = true;
				continue;
			case TIMES:
			case DIVIDE:
				break;
			default:
				return false;
			}
			if (plus_flag && !divide_flag) {
				if (k > 0)
					j--;
				blt(&scratch[k], &tlhs[j], (i - j) * sizeof(token_type));
				k += i - j;
			}
			plus_flag = false;
			divide_flag = (op == DIVIDE);
			j = i + 1;
			break;
		case 2:
			switch (op) {
			case PLUS:
			case MINUS:
				plus_flag = true;
			}
			break;
		}
	}
}

/*
 * This is the Euclidean GCD algorithm applied to polynomials.
 * It needs to be made multivariate.
 *
 * Return the number of iterations (divisions), if successful.
 * Return 0 on failure.
 */
static int
do_gcd(vp)
long		*vp;
{
	int	i;
	int	count;

	for (count = 1;; count++) {
		switch (poly_div(trhs, n_trhs, gcd_divisor, len_d, vp)) {
		case 0:
			/* divide failed */
			return 0;
		case 2:
			/* Total success!  Remainder is zero. */
			return count;
		}
		/* swap trhs[] (remainder) and gcd_divisor[] */
		if (len_d > n_tokens || n_trhs > DIVISOR_SIZE)
			return 0;
		blt(scratch, trhs, n_trhs * sizeof(token_type));
		blt(trhs, gcd_divisor, len_d * sizeof(token_type));
		blt(gcd_divisor, scratch, n_trhs * sizeof(token_type));
		i = n_trhs;
		n_trhs = len_d;
		len_d = i;
	}
}

/*
 * Compute polynomial Greatest Common Divisor of "larger" and "smaller".
 *
 * Return true if successful.
 * Return the GCD in "trhs".
 * Return "larger"/GCD in "tlhs".
 */
int
poly_gcd(larger, llen, smaller, slen, v)
token_type	*larger;	/* larger polynomial */
token_type	*smaller;	/* smaller polynomial */
int		llen, slen;
long		v;
{
	int		count;

	if (llen > n_tokens || slen > min(ARR_CNT(gcd_divisor), n_tokens))
		return 0;
	blt(trhs, larger, llen * sizeof(token_type));
	n_trhs = llen;
	blt(tlhs, smaller, slen * sizeof(token_type));
	n_tlhs = slen;
	if (!remove_factors())
		return 0;
	if (n_tlhs > ARR_CNT(gcd_divisor))
		return 0;
	blt(gcd_divisor, tlhs, n_tlhs * sizeof(token_type));
	len_d = n_tlhs;
	count = do_gcd(&v);
	if (count == 0)
		return 0;
	if (count > 1) {
		if (len_d > n_tokens)
			return 0;
		blt(tlhs, gcd_divisor, len_d * sizeof(token_type));
		n_tlhs = len_d;
		if (!remove_factors())
			return 0;
		if (n_tlhs > ARR_CNT(gcd_divisor))
			return 0;
		blt(gcd_divisor, tlhs, n_tlhs * sizeof(token_type));
		len_d = n_tlhs;
		if (poly_div(larger, llen, gcd_divisor, len_d, &v) != 2) {
			debug_string(0, "Polynomial GCD found, but larger divide failed.");
			return 0;
		}
	}
	if (len_d > n_tokens)
		return 0;
	blt(trhs, gcd_divisor, len_d * sizeof(token_type));
	n_trhs = len_d;
	uf_simp(tlhs, &n_tlhs);
	uf_simp(trhs, &n_trhs);
	debug_string(3, "poly_gcd() successful.");
	return(count);
}

/*
 * Compute polynomial Greatest Common Divisor of "larger" and "smaller".
 *
 * Return true if successful.
 * Return "larger"/GCD in "tlhs".
 * Return "smaller"/GCD in "trhs".
 */
int
poly2_gcd(larger, llen, smaller, slen, v)
token_type	*larger;	/* larger polynomial */
token_type	*smaller;	/* smaller polynomial */
int		llen, slen;
long		v;
{
	jmp_buf		save_save;
	int		count;

	debug_string(3, "Entering poly2_gcd():");
	side_debug(3, larger, llen);
	side_debug(3, smaller, slen);
	if (llen > n_tokens || slen > min(ARR_CNT(gcd_divisor), n_tokens))
		return 0;
	blt(trhs, larger, llen * sizeof(token_type));
	n_trhs = llen;
	blt(tlhs, smaller, slen * sizeof(token_type));
	n_tlhs = slen;
	blt(save_save, jmp_save, sizeof(jmp_save));
	if (setjmp(jmp_save) != 0) {
		blt(jmp_save, save_save, sizeof(jmp_save));
		return 0;
	}
	uf_simp(tlhs, &n_tlhs);
	blt(jmp_save, save_save, sizeof(jmp_save));
	if (!level1_plus_count(tlhs, n_tlhs))
		return 0;
	if (n_tlhs > ARR_CNT(gcd_divisor))
		return 0;
	blt(gcd_divisor, tlhs, n_tlhs * sizeof(token_type));
	len_d = n_tlhs;
	count = do_gcd(&v);
	if (count == 0)
		return 0;
	if (count > 1) {
		if (!level1_plus_count(gcd_divisor, len_d))
			return 0;
		if (poly_div(smaller, slen, gcd_divisor, len_d, &v) != 2) {
			debug_string(0, "Polynomial GCD found, but smaller divide failed.");
			return 0;
		}
		blt(trhs, gcd_divisor, len_d * sizeof(token_type));
		n_trhs = len_d;
		if (n_tlhs > ARR_CNT(gcd_divisor))
			return 0;
		blt(gcd_divisor, tlhs, n_tlhs * sizeof(token_type));
		len_d = n_tlhs;
		blt(tlhs, trhs, n_trhs * sizeof(token_type));
		n_tlhs = n_trhs;
		if (poly_div(larger, llen, tlhs, n_tlhs, &v) != 2) {
			debug_string(0, "Polynomial GCD found, but larger divide failed.");
			return 0;
		}
		blt(trhs, gcd_divisor, len_d * sizeof(token_type));
		n_trhs = len_d;
	} else {
		n_trhs = 1;
		trhs[0] = one_token;
	}
	debug_string(3, "poly2_gcd() successful.");
	return count;
}

/*
 * Return true if passed expression is entirely integer.
 *
 * Assuming the variables are integer, the result of evaluating
 * the expression must be integer if this returns true.
 */
int
is_integer_expr(p1, n)
token_type	*p1;	/* expression pointer */
int		n;	/* length of expression */
{
	int	i;

	for (i = 0; i < n; i++) {
		if ((p1[i].kind == OPERATOR && p1[i].token.operatr == DIVIDE)
		    || (p1[i].kind == CONSTANT && fmod(p1[i].token.constant, 1.0))
		    || (p1[i].kind == VARIABLE && p1[i].token.variable <= IMAGINARY)) {
			return false;
		}
	}
	return true;
}

/*
 * This routine is a modulus (%) simplifier for equation sides.
 * It does polynomial division, so tlhs and trhs are wiped out.
 *
 * Return true if expression was modified.
 */
int
mod_simp(equation, np)
token_type	*equation;	/* pointer to the beginning of equation side to simplify */
int		*np;		/* pointer to length of the equation side */
{
	return mod_recurse(equation, np, 0, 1);
}

static int
mod_recurse(equation, np, loc, level)
token_type	*equation;
int		*np, loc, level;
{
	int	modified = false;
	int	i, j, k;
	int	i1, i2, i3, i4, i5;
	int	op, last_op2;
	int	len1, len2, len3;
	long	v;
	int	diff_sign;

	for (i = loc; i < *np && equation[i].level >= level;) {
		if (equation[i].level > level) {
			modified |= mod_recurse(equation, np, i, level + 1);
			i++;
			for (; i < *np && equation[i].level > level; i += 2)
				;
			continue;
		}
		i++;
	}
	if (modified)	/* make sure the deepest levels are completed, first */
		return true;
	for (i = loc + 1; i < *np && equation[i].level >= level; i += 2) {
		if (equation[i].level == level && equation[i].token.operatr == MODULUS) {
			for (k = i + 2;; k += 2) {
				if (k >= *np || equation[k].level <= level)
					break;
			}
			len1 = k - (i + 1);
			last_op2 = 0;
			for (j = loc; j < *np && equation[j].level >= level; j++) {
				if (equation[j].level == level && equation[j].kind == OPERATOR) {
					last_op2 = equation[j].token.operatr;
					continue;
				}
				if (last_op2 == MODULUS) {
					continue;
				}
				last_op2 = MODULUS;
				op = 0;
				for (i1 = k = j + 1; k < *np && equation[k].level > level; k += 2) {
					if (equation[k].level == (level + 1)) {
						op = equation[k].token.operatr;
						i1 = k;
					}
				}
				len2 = k - j;
				switch (op) {
				case MODULUS:
					/* simplify (x%n)%n to x%n */
					len3 = k - (i1 + 1);
					if (se_compare(&equation[i+1], len1, &equation[i1+1], len3, &diff_sign)) {
						blt(&equation[i1], &equation[k], (*np - k) * sizeof(token_type));
						*np -= len3 + 1;
						return true;
					}
					break;
				case TIMES:
					if (!is_integer_expr(&equation[j], len2))
						break;
					/* simplify (i%n*j)%n to (i*j)%n if j is integer */
					for (i2 = i1 = j + 1;; i1 += 2) {
						if (i1 >= k || equation[i1].level == (level + 1)) {
							for (; i2 < i1; i2 += 2) {
								if (equation[i2].level == (level + 2)
								    && equation[i2].token.operatr == MODULUS) {
									len3 = i1 - (i2 + 1);
									if (se_compare(&equation[i+1], len1, &equation[i2+1], len3, &diff_sign)) {
										blt(&equation[i2], &equation[i1], (*np - i1) * sizeof(token_type));
										*np -= len3 + 1;
										return true;
									}
								}
							}
						}
						if (i1 >= k)
							break;
					}
					break;
				case PLUS:
				case MINUS:
					/* simplify (i%n+j)%n to (i+j)%n */
					/* and ((i%n)*j+k)%n to (i*j+k)%n, when j is integer */
					for (i2 = i1 = j + 1, i3 = j - 1;; i1 += 2) {
						if (i1 >= k || equation[i1].level == (level + 1)) {
							for (; i2 < i1; i2 += 2) {
								if (equation[i2].level == (level + 2)) {
									switch (equation[i2].token.operatr) {
									case MODULUS:
										len3 = i1 - (i2 + 1);
										if (se_compare(&equation[i+1], len1, &equation[i2+1], len3, &diff_sign)) {
											blt(&equation[i2], &equation[i1], (*np - i1) * sizeof(token_type));
											*np -= len3 + 1;
											return true;
										}
										break;
									case TIMES:
										i2 = i1 - 2;
										if (!is_integer_expr(&equation[i3+1], i1 - (i3 + 1))) {
											break;
										}
										for (i4 = i3 + 2; i4 < i1; i4 += 2) {
											if (equation[i4].level == (level + 3)
											    && equation[i4].token.operatr == MODULUS) {
												for (i5 = i4 + 2; i5 < i1 && equation[i5].level > (level + 3); i5 += 2)
													;
												len3 = i5 - (i4 + 1);
												if (se_compare(&equation[i+1], len1, &equation[i4+1], len3, &diff_sign)) {
													blt(&equation[i4], &equation[i5], (*np - i5) * sizeof(token_type));
													*np -= len3 + 1;
													return true;
												}
											}
										}
										break;
									}
								}
							}
							i3 = i1;
						}
						if (i1 >= k)
							break;
					}
					break;
				}
				/* remove integer*n multiples in x for x%n by doing */
				/* polynomial division and replacing with remainder%n */
				v = 0;
				if (poly_div(&equation[j], len2, &equation[i+1], len1, &v)) {
					if (is_integer_expr(tlhs, n_tlhs)) {
						if ((*np + (n_trhs - len2)) > n_tokens)
							error_huge();
#if	!SILENT
						if (var_count(tlhs, n_tlhs)) {
							printf(_("Modulus simplification made some variables integer only.\n"));
						}
#endif
						for (k = 0; k < n_trhs; k++)
							trhs[k].level += level;
						blt(&equation[j+n_trhs], &equation[j+len2], (*np - (j + len2)) * sizeof(token_type));
						*np += n_trhs - len2;
						blt(&equation[j], trhs, n_trhs * sizeof(token_type));
						return true;
					}
				}
			}
		}
	}
	return modified;
}

/*
 * This routine is a division simplifier for equation sides.
 *
 * Return true if a Greatest Common Divisor was found and
 * the expression was simplified by dividing the numerator
 * and denominator by the GCD.
 */
int
poly_gcd_simp(equation, np)
token_type	*equation;
int		*np;
{
	return polydiv_recurse(equation, np, 0, 1);
}

static int
polydiv_recurse(equation, np, loc, level)
token_type	*equation;
int		*np, loc, level;
{
	int	modified = false;
	int	i, j, k;
	int	last_op2;
	int	len1, len2;

	for (i = loc; i < *np && equation[i].level >= level;) {
		if (equation[i].level > level) {
			modified |= polydiv_recurse(equation, np, i, level + 1);
			i++;
			for (; i < *np && equation[i].level > level; i += 2)
				;
			continue;
		}
		i++;
	}
start:
	for (i = loc + 1; i < *np && equation[i].level >= level; i += 2) {
		if (equation[i].level == level && equation[i].token.operatr == DIVIDE) {
			for (k = i + 2;; k += 2) {
				if (k >= *np || equation[k].level <= level)
					break;
			}
			len1 = k - (i + 1);
			last_op2 = 0;
			for (j = loc; j < *np && equation[j].level >= level; j++) {
				if (equation[j].level == level && equation[j].kind == OPERATOR) {
					last_op2 = equation[j].token.operatr;
					continue;
				}
				if (last_op2 == DIVIDE) {
					continue;
				}
				last_op2 = DIVIDE;
				for (k = j + 1;; k += 2) {
					if (k >= *np || equation[k].level <= level)
						break;
				}
				len2 = k - j;
				if (poly2_gcd(&equation[i+1], len1, &equation[j], len2, 0L)) {
store_code:
					for (k = 0; k < n_tlhs; k++)
						tlhs[k].level += level;
					for (k = 0; k < n_trhs; k++)
						trhs[k].level += level;
					if (((*np + (n_trhs - len2)) > n_tokens)
					    || ((*np + (n_trhs - len2) + (n_tlhs - len1)) > n_tokens))
						error_huge();
					blt(&equation[j+n_trhs], &equation[j+len2], (*np - (j + len2)) * sizeof(token_type));
					*np += n_trhs - len2;
					if (i > j)
						i += n_trhs - len2;
					blt(&equation[j], trhs, n_trhs * sizeof(token_type));
					blt(&equation[i+n_tlhs+1], &equation[i+1+len1], (*np - (i + 1 + len1)) * sizeof(token_type));
					*np += n_tlhs - len1;
					blt(&equation[i+1], tlhs, n_tlhs * sizeof(token_type));
					debug_string(0, _("Division simplified with polynomial GCD."));
					modified = true;
					goto start;
				}
				if (poly2_gcd(&equation[j], len2, &equation[i+1], len1, 0L)) {
					k = j - 1;
					j = i + 1;
					i = k;
					k = len1;
					len1 = len2;
					len2 = k;
					goto store_code;
				}
			}
		}
	}
	return modified;
}

/*
 * This routine is a division simplifier for equation sides.
 * Check for divides and do polynomial and smart division.
 *
 * Return true if expression was simplified.
 */
int
div_remainder(equation, np, poly_flag, quick_flag)
token_type	*equation;
int		*np;
int		poly_flag;
int		quick_flag;
{
	int	rv;

	if (quick_flag)
		group_proc(equation, np);
	rv = pdiv_recurse(equation, np, 0, 1, poly_flag);
	if (quick_flag)
		organize(equation, np);
	return rv;
}

static int
pdiv_recurse(equation, np, loc, level, code)
token_type	*equation;
int		*np, loc, level, code;
{
	int	modified = false;
	int	i, j, k;
	int	op, op2, last_op2;
	int	len1, len2, real_len1;
	long	v;
	int	rv;
	int	flag, power_flag, zero_remainder;

	for (i = loc + 1; i < *np && equation[i].level >= level; i += 2) {
		if (equation[i].level == level && equation[i].token.operatr == DIVIDE) {
			for (k = i + 2;; k += 2) {
				if (k >= *np || equation[k].level <= level)
					break;
			}
			len1 = real_len1 = k - (i + 1);
			last_op2 = 0;
			for (j = loc; j < *np && equation[j].level >= level; j++) {
				if (equation[j].level == level && equation[j].kind == OPERATOR) {
					last_op2 = equation[j].token.operatr;
					continue;
				}
				if (last_op2 == DIVIDE) {
					continue;
				}
				last_op2 = DIVIDE;
				op = 0;
				for (k = j + 1; k < *np && equation[k].level > level; k += 2) {
					if (equation[k].level == (level + 1)) {
						op = equation[k].token.operatr;
					}
				}
				if (op != PLUS && op != MINUS) {
					continue;
				}
				len2 = k - j;
				flag = code;
				power_flag = false;
				op = 0;
				op2 = 0;
				for (k = i + 2; k < *np && equation[k].level > level; k += 2) {
					if (equation[k].level == level + 3) {
						switch (equation[k].token.operatr) {
						case PLUS:
						case MINUS:
							op2 = PLUS;
						}
					} else if (equation[k].level == level + 2) {
						op = equation[k].token.operatr;
					} else if (equation[k].level == level + 1) {
						if (equation[k].token.operatr == POWER
						    && (op == PLUS || op == MINUS || (op == TIMES && op2 == PLUS))) {
							power_flag = true;
							len1 = k - (i + 1);
						}
						break;
					}
				}
next_thingy:
				if (!power_flag) {
					len1 = real_len1;
				}
				v = 0;
				if (flag || power_flag) {
					rv = poly_div(&equation[j], len2, &equation[i+1], len1, &v);
				} else {
					rv = smart_div(&equation[j], len2, &equation[i+1], len1);
				}
				zero_remainder = (rv > 0 && REMAINDER_IS_ZERO());
				if (power_flag && !zero_remainder) {
					rv = 0;
				}
				if (rv > 0) {
					if ((n_tlhs + 2 + n_trhs + len1) > n_tokens)
						error_huge();
					for (k = 0; k < n_tlhs; k++)
						tlhs[k].level++;
					tlhs[n_tlhs].level = 1;
					tlhs[n_tlhs].kind = OPERATOR;
					tlhs[n_tlhs].token.operatr = PLUS;
					n_tlhs++;
					for (k = 0; k < n_trhs; k++)
						trhs[k].level += 2;
					blt(&tlhs[n_tlhs], trhs, n_trhs * sizeof(token_type));
					n_tlhs += n_trhs;
					tlhs[n_tlhs].level = 2;
					tlhs[n_tlhs].kind = OPERATOR;
					tlhs[n_tlhs].token.operatr = DIVIDE;
					n_tlhs++;
					k = n_tlhs;
					blt(&tlhs[n_tlhs], &equation[i+1], len1 * sizeof(token_type));
					n_tlhs += len1;
					for (; k < n_tlhs; k++)
						tlhs[k].level += 2;
					side_debug(3, &equation[j], len2);
					side_debug(3, &equation[i+1], len1);
					simpb_side(tlhs, &n_tlhs, true, 3);
					side_debug(3, tlhs, n_tlhs);
					if (power_flag) {
						k = (var_count(tlhs, n_tlhs) <= var_count(&equation[j], len2));
					} else {
						k = (var_count(tlhs, n_tlhs) <= (var_count(&equation[j], len2) + var_count(&equation[i+1], len1)));
					}
					if (k) {
						for (k = 0; k < n_tlhs; k++)
							tlhs[k].level += level;
						if (power_flag) {
							if ((*np - len2 + n_tlhs + 2) > n_tokens)
								error_huge();
							for (k = i + 2 + len1; k <= i + real_len1; k++) {
								equation[k].level++;
							}
							blt(&equation[i+real_len1+3], &equation[k], (*np - k) * sizeof(token_type));
							*np += 2;
							equation[k].level = level + 2;
							equation[k].kind = OPERATOR;
							equation[k].token.operatr = MINUS;
							k++;
							equation[k].level = level + 2;
							equation[k].kind = CONSTANT;
							equation[k].token.constant = 1.0;
							if (i < j) {
								j += 2;
							}
						} else {
							if ((*np - (len1 + 1 + len2) + n_tlhs) > n_tokens)
								error_huge();
							blt(&equation[i], &equation[i+1+len1], (*np - (i + 1 + len1)) * sizeof(token_type));
							*np -= len1 + 1;
							if (i < j) {
								j -= len1 + 1;
							}
						}
						blt(&equation[j+n_tlhs], &equation[j+len2], (*np - (j + len2)) * sizeof(token_type));
						*np -= len2 - n_tlhs;
						blt(&equation[j], tlhs, n_tlhs * sizeof(token_type));
						if (flag || power_flag) {
							debug_string(1, _("Polynomial division successful."));
						} else {
							debug_string(1, _("Smart division successful."));
						}
						side_debug(3, equation, *np);
						return true;
					}
				}
				if (power_flag) {
					power_flag = false;
					goto next_thingy;
				}
				if (flag == code) {
					flag = !flag;
					goto next_thingy;
				}
			}
		}
	}
	for (i = loc; i < *np && equation[i].level >= level;) {
		if (equation[i].level > level) {
			modified |= pdiv_recurse(equation, np, i, level + 1, code);
			i++;
			for (; i < *np && equation[i].level > level; i += 2)
				;
			continue;
		}
		i++;
	}
	return modified;
}

/*
 * Do polynomial division.
 *
 * Returns non-zero if successful, 2 if remainder is 0.
 * Quotient is returned in "tlhs" and remainder in "trhs".
 *
 * If "*vp" is 0, automatically select the best base variable,
 * and return it in "*vp".
 */
int
poly_div(d1, len1, d2, len2, vp)
token_type	*d1;		/* pointer to dividend */
int		len1;		/* length of dividend */
token_type	*d2;		/* pointer to divisor */
int		len2;		/* length of divisor */
long		*vp;		/* variable pointer to base variable */
{
	int		j;
	int		old_partial;
	jmp_buf		save_save;

	old_partial = partial_flag;
	partial_flag = false;	/* We want full unfactoring. */
	blt(save_save, jmp_save, sizeof(jmp_save));
	if (setjmp(jmp_save) != 0) {
		blt(jmp_save, save_save, sizeof(jmp_save));
		partial_flag = old_partial;
		return false;
	}
	j = poly_div_sub(d1, len1, d2, len2, vp);
	blt(jmp_save, save_save, sizeof(jmp_save));
	partial_flag = old_partial;
	return j;
}

/*
 * Do the actual polynomial division.
 */
static int
poly_div_sub(d1, len1, d2, len2, vp)
token_type	*d1;
int		len1;
token_type	*d2;
int		len2;
long		*vp;
{
	int		i;
	int		t1, len_t1;
	int		t2, len_t2;
	int		sign;
	int		divide_flag;
	double		last_power, divisor_power, d;
	int		sum_size;
	int		last_count, count;	/* dividend number of terms raised to the highest power */

	if (len1 > n_tokens || len2 > n_tokens)
		return false;
	/* Copy the source polynomials to where we want them (tlhs and trhs). */
	if (trhs != d1) {
		blt(trhs, d1, len1 * sizeof(token_type));
		n_trhs = len1;
	}
	if (tlhs != d2) {
		blt(tlhs, d2, len2 * sizeof(token_type));
		n_tlhs = len2;
	}
	/* Do the basic unfactoring and simplification. */
	uf_simp(trhs, &n_trhs);
	uf_simp(tlhs, &n_tlhs);
	if (*vp == 0) {
		/* Select the best base variable. */
		if (!find_highest_count(trhs, n_trhs, tlhs, n_tlhs, vp))
			return false;
	}
#if	!SILENT
	/* Display debugging info. */
	if (debug_level >= 3) {
		list_var(*vp, 0);
		printf(_("poly_div() starts using variable (%s):\n"), var_str);
		side_debug(3, trhs, n_trhs);
		side_debug(3, tlhs, n_tlhs);
	}
#endif
	/* Determine divide_flag and if the polynomials can be divided. */
	divide_flag = 2;
	last_count = find_greatest_power(trhs, n_trhs, vp, &last_power, &t1, &len_t1, &divide_flag);
	find_greatest_power(tlhs, n_tlhs, vp, &divisor_power, &t2, &len_t2, &divide_flag);
	if (divisor_power <= 0 || last_power < divisor_power) {
		divide_flag = !divide_flag;
		last_count = find_greatest_power(trhs, n_trhs, vp, &last_power, &t1, &len_t1, &divide_flag);
		find_greatest_power(tlhs, n_tlhs, vp, &divisor_power, &t2, &len_t2, &divide_flag);
		if (divisor_power <= 0 || last_power < divisor_power) {
			return false;
		}
	}
	/* Initialize the quotient. */
	n_quotient = 1;
	quotient[0] = zero_token;
	/* Store the divisor. */
	if (n_tlhs > ARR_CNT(divisor))
		return false;
	blt(divisor, tlhs, n_tlhs * sizeof(token_type));
	n_divisor = n_tlhs;
	sum_size = n_trhs + n_quotient;
	/* Loop until polynomial division is finished. */
	for (;;) {
		if (t1 > 0 && trhs[t1-1].token.operatr == MINUS)
			sign = MINUS;
		else
			sign = PLUS;
		if (t2 > 0 && divisor[t2-1].token.operatr == MINUS) {
			if (sign == MINUS)
				sign = PLUS;
			else
				sign = MINUS;
		}
		if ((len_t1 + len_t2 + 1) > n_tokens)
			return false;
		blt(tlhs, &trhs[t1], len_t1 * sizeof(token_type));
		n_tlhs = len_t1;
		for (i = 0; i < n_tlhs; i++)
			tlhs[i].level++;
		tlhs[n_tlhs].level = 1;
		tlhs[n_tlhs].kind = OPERATOR;
		tlhs[n_tlhs].token.operatr = DIVIDE;
		n_tlhs++;
		blt(&tlhs[n_tlhs], &divisor[t2], len_t2 * sizeof(token_type));
		i = n_tlhs;
		n_tlhs += len_t2;
		for (; i < n_tlhs; i++)
			tlhs[i].level++;
		if (!simp_loop(tlhs, &n_tlhs))
			return false;
		if ((n_quotient + 1 + n_tlhs) > min(ARR_CNT(quotient), n_tokens))
			return false;
		for (i = 0; i < n_tlhs; i++)
			tlhs[i].level++;
		quotient[n_quotient].level = 1;
		quotient[n_quotient].kind = OPERATOR;
		quotient[n_quotient].token.operatr = sign;
		n_quotient++;
		blt(&quotient[n_quotient], tlhs, n_tlhs * sizeof(token_type));
		n_quotient += n_tlhs;
		if ((n_trhs + n_tlhs + n_divisor + 2) > n_tokens)
			return false;
		blt(&trhs[t1+1], &trhs[t1+len_t1], (n_trhs - (t1 + len_t1)) * sizeof(token_type));
		n_trhs -= len_t1 - 1;
		trhs[t1] = zero_token;
		for (i = 0; i < n_trhs; i++)
			trhs[i].level++;
		trhs[n_trhs].level = 1;
		trhs[n_trhs].kind = OPERATOR;
		if (sign == PLUS)
			trhs[n_trhs].token.operatr = MINUS;
		else
			trhs[n_trhs].token.operatr = PLUS;
		n_trhs++;
		blt(&trhs[n_trhs], tlhs, n_tlhs * sizeof(token_type));
		i = n_trhs;
		n_trhs += n_tlhs;
		for (; i < n_trhs; i++)
			trhs[i].level++;
		trhs[n_trhs].level = 2;
		trhs[n_trhs].kind = OPERATOR;
		trhs[n_trhs].token.operatr = TIMES;
		n_trhs++;
		i = n_trhs;
		blt(&trhs[n_trhs], divisor, t2 * sizeof(token_type));
		n_trhs += t2;
		trhs[n_trhs] = zero_token;
		n_trhs++;
		blt(&trhs[n_trhs], &divisor[t2+len_t2], (n_divisor - (t2 + len_t2)) * sizeof(token_type));
		n_trhs += (n_divisor - (t2 + len_t2));
		for (; i < n_trhs; i++)
			trhs[i].level += 2;
		side_debug(3, trhs, n_trhs);
		uf_repeat(trhs, &n_trhs);
		uf_tsimp(trhs, &n_trhs);
		side_debug(4, trhs, n_trhs);
		count = find_greatest_power(trhs, n_trhs, vp, &d, &t1, &len_t1, &divide_flag);
		if (d < divisor_power) {
			/* Success!  Polynomial division ends here. */
			blt(tlhs, quotient, n_quotient * sizeof(token_type));
			n_tlhs = n_quotient;
			side_debug(3, tlhs, n_tlhs);
			side_debug(3, trhs, n_trhs);
			if (REMAINDER_IS_ZERO())
				return 2;
			if ((n_trhs + n_quotient) >= (sum_size /* - (sum_size / 10) */)) {
				if ((n_trhs + 1) > sum_size && n_trhs > n_divisor)
					return -2;
				else
					return -1;
			}
			return true;
		} else if (d < last_power) {
			last_power = d;
			last_count = count;
		} else if (d > last_power) {
			return false;
		} else {
			if (count >= last_count) {
				return false;
			}
			last_count = count;
		}
	}
}

/*
 * Do smart division.
 *
 * Smart division is heuristic division much like polynomial division,
 * however instead of basing the division on the highest powers,
 * every term in the dividend is tried, and if a trial makes the
 * expression smaller, we go with that.
 *
 * Only one term in the divisor is tried, to save time.
 * Currently, the divisor term used is the one with the least number
 * of variables, not including any terms with no variables.
 *
 * Returns true if successful.
 * Quotient is returned in "tlhs" and remainder in "trhs".
 */
int
smart_div(d1, len1, d2, len2)
token_type	*d1;		/* pointer to dividend */
int		len1;		/* length of dividend */
token_type	*d2;		/* pointer to divisor */
int		len2;		/* length of divisor */
{
	int		i, j, k;
	int		t1, len_t1;
	int		t2 = 0, len_t2 = 0;
	int		sign;
	int		old_n_quotient;
	int		trhs_size;
	int		term_size = 0, term_count;
	int		term_pos = 0, skip_terms[100];
	int		skip_count;
	token_type	*qp;
	int		q_size;
	int		sum_size;
	int		count;
	int		dcount = 0;		/* divisor term count */
	int		flag;

	blt(trhs, d1, len1 * sizeof(token_type));
	n_trhs = len1;
	blt(tlhs, d2, len2 * sizeof(token_type));
	n_tlhs = len2;
	/* Do the basic unfactoring and simplification of the dividend and divisor. */
	uf_simp_no_repeat(trhs, &n_trhs);
	uf_simp_no_repeat(tlhs, &n_tlhs);
	/* Display debugging info. */
	debug_string(3, "smart_div() starts:");
	side_debug(3, trhs, n_trhs);
	side_debug(3, tlhs, n_tlhs);
	/* Find which divisor term to use. */
	for (i = 0, j = 0, k = 0, flag = false;; i++) {
		if (i >= n_tlhs || (tlhs[i].kind == OPERATOR && tlhs[i].level == 1
		    && (tlhs[i].token.operatr == PLUS || tlhs[i].token.operatr == MINUS))) {
			dcount++;
			if (flag) {
				if (len_t2 == 0 || var_count(&tlhs[j], i - j) < k) {
					len_t2 = i - j;
					t2 = j;
					k = var_count(&tlhs[t2], len_t2);
				}
			}
			flag = false;
			j = i + 1;
		} else if (tlhs[i].kind == VARIABLE && tlhs[i].token.variable != IMAGINARY) {
			flag = true;
		}
		if (i >= n_tlhs)
			break;
	}
	if (len_t2 <= 0)
		return false;
	/* Initialize the quotient. */
	n_quotient = 1;
	quotient[0] = zero_token;
	if (n_tlhs > ARR_CNT(divisor))
		return false;
	blt(divisor, tlhs, n_tlhs * sizeof(token_type));
	n_divisor = n_tlhs;
try_one:
	trhs_size = n_trhs;
	for (skip_count = 0, count = 0;;) {
		sum_size = n_trhs + n_quotient;
		for (term_count = 1, q_size = 0;; term_count++) {
			if (!get_term(trhs, n_trhs, term_count, &t1, &len_t1))
				break;
			flag = false;
			for (i = 0; i < skip_count; i++) {
				if (skip_terms[i] == t1) {
					flag = true;
					break;
				}
			}
			if (flag)
				continue;
			if ((len_t1 + len_t2 + 1) > n_tokens)
				return false;
			blt(tlhs, &trhs[t1], len_t1 * sizeof(token_type));
			n_tlhs = len_t1;
			for (i = 0; i < n_tlhs; i++)
				tlhs[i].level++;
			tlhs[n_tlhs].level = 1;
			tlhs[n_tlhs].kind = OPERATOR;
			tlhs[n_tlhs].token.operatr = DIVIDE;
			n_tlhs++;
			blt(&tlhs[n_tlhs], &divisor[t2], len_t2 * sizeof(token_type));
			i = n_tlhs;
			n_tlhs += len_t2;
			for (; i < n_tlhs; i++)
				tlhs[i].level++;
			if (!simp_loop(tlhs, &n_tlhs))
				continue;
			if (basic_size(tlhs, n_tlhs) <= basic_size(&trhs[t1], len_t1)) {
				q_size = n_tlhs;
				term_pos = t1;
				term_size = len_t1;
				break;
			}
		}
		if (q_size <= 0) {
			if (count <= 0) {
				if (dcount > 1) {
					dcount = 1;
					t2 = 0;
					len_t2 = n_divisor;
					goto try_one;	/* Try using the whole divisor as the divisor term. */
				}
				return false;
			}
end_div:
			if (dcount > 1) {
				if (n_quotient + n_trhs >= trhs_size + 1) {
					return false;
				}
			}
end_div2:
			blt(tlhs, quotient, n_quotient * sizeof(token_type));
			n_tlhs = n_quotient;
			side_debug(3, tlhs, n_tlhs);
			side_debug(3, trhs, n_trhs);
			return true;	/* Success! */
		}
		t1 = term_pos;
		len_t1 = term_size;
		if (t1 > 0 && trhs[t1-1].token.operatr == MINUS)
			sign = MINUS;
		else
			sign = PLUS;
		if (t2 > 0 && divisor[t2-1].token.operatr == MINUS) {
			if (sign == MINUS)
				sign = PLUS;
			else
				sign = MINUS;
		}
		if ((len_t1 + len_t2 + 1) > n_tokens)
			return false;
		blt(tlhs, &trhs[t1], len_t1 * sizeof(token_type));
		n_tlhs = len_t1;
		for (i = 0; i < n_tlhs; i++)
			tlhs[i].level++;
		tlhs[n_tlhs].level = 1;
		tlhs[n_tlhs].kind = OPERATOR;
		tlhs[n_tlhs].token.operatr = DIVIDE;
		n_tlhs++;
		blt(&tlhs[n_tlhs], &divisor[t2], len_t2 * sizeof(token_type));
		i = n_tlhs;
		n_tlhs += len_t2;
		for (; i < n_tlhs; i++)
			tlhs[i].level++;
		simp_loop(tlhs, &n_tlhs);
		if ((n_quotient + 1 + n_tlhs) > min(ARR_CNT(quotient), n_tokens))
			return false;
		for (i = 0; i < n_tlhs; i++)
			tlhs[i].level++;
		old_n_quotient = n_quotient;
		quotient[n_quotient].level = 1;
		quotient[n_quotient].kind = OPERATOR;
		quotient[n_quotient].token.operatr = sign;
		n_quotient++;
		qp = &quotient[n_quotient];
		q_size = n_tlhs;
		blt(&quotient[n_quotient], tlhs, n_tlhs * sizeof(token_type));
		n_quotient += n_tlhs;
		if ((n_trhs + q_size + n_divisor + 2) > n_tokens)
			return false;
		blt(tlhs, trhs, n_trhs * sizeof(token_type));
		n_tlhs = n_trhs;
		blt(&trhs[t1+1], &trhs[t1+len_t1], (n_trhs - (t1 + len_t1)) * sizeof(token_type));
		n_trhs -= len_t1 - 1;
		trhs[t1] = zero_token;
		for (i = 0; i < n_trhs; i++)
			trhs[i].level++;
		trhs[n_trhs].level = 1;
		trhs[n_trhs].kind = OPERATOR;
		if (sign == PLUS)
			trhs[n_trhs].token.operatr = MINUS;
		else
			trhs[n_trhs].token.operatr = PLUS;
		n_trhs++;
		blt(&trhs[n_trhs], qp, q_size * sizeof(token_type));
		i = n_trhs;
		n_trhs += q_size;
		for (; i < n_trhs; i++)
			trhs[i].level++;
		trhs[n_trhs].level = 2;
		trhs[n_trhs].kind = OPERATOR;
		trhs[n_trhs].token.operatr = TIMES;
		n_trhs++;
		i = n_trhs;
		blt(&trhs[n_trhs], divisor, t2 * sizeof(token_type));
		n_trhs += t2;
		trhs[n_trhs] = zero_token;
		n_trhs++;
		blt(&trhs[n_trhs], &divisor[t2+len_t2], (n_divisor - (t2 + len_t2)) * sizeof(token_type));
		n_trhs += (n_divisor - (t2 + len_t2));
		for (; i < n_trhs; i++)
			trhs[i].level += 2;
		side_debug(3, trhs, n_trhs);
		uf_tsimp(trhs, &n_trhs);
		side_debug(4, trhs, n_trhs);
		if (REMAINDER_IS_ZERO())
			goto end_div2;
		if (dcount > 1) {
			if ((n_trhs + n_quotient) >= sum_size) {
				if (skip_count >= ARR_CNT(skip_terms)) {
					if (count == 0) {
						return false;
					} else {
						n_quotient = old_n_quotient;
						blt(trhs, tlhs, n_tlhs * sizeof(token_type));
						n_trhs = n_tlhs;
						goto end_div;
					}
				}
				skip_terms[skip_count] = term_pos;
				skip_count++;
				n_quotient = old_n_quotient;
				blt(trhs, tlhs, n_tlhs * sizeof(token_type));
				n_trhs = n_tlhs;
				debug_string(3, "Skipping last operation.");
				continue;
			}
		}
		if (n_trhs == 1 && trhs[0].kind == CONSTANT)
			goto end_div;
		skip_count = 0;
		count++;
	}
}

/*
 * Return the size of a subexpression,
 * minus any constant multiplier.
 */
int
basic_size(p1, len)
token_type	*p1;
int		len;
{
	int	i, j;
	int	level;
	int	rv;
	int	constant_flag = true;

	rv = len;
	level = min_level(p1, len);
	for (i = 0, j = -1; i < len; i++) {
		if (p1[i].kind == OPERATOR) {
			if (p1[i].level == level
			    && (p1[i].token.operatr == TIMES || p1[i].token.operatr == DIVIDE)) {
				if (constant_flag) {
					rv -= i - j;
				}
				j = i;
				constant_flag = true;
			}
		} else if (p1[i].kind != CONSTANT)
			constant_flag = false;
	}
	if (constant_flag) {
		rv -= i - j;
	}
	return rv;
}

int
get_term(p1, n1, count, tp1, lentp1)
token_type	*p1;
int		n1;
int		count;
int		*tp1, *lentp1;
{
	int	i, j;
	int	no;

	for (no = 0, i = 1, j = 0;; i += 2) {
		if (i >= n1 || (p1[i].level == 1
		    && (p1[i].token.operatr == PLUS
		    || p1[i].token.operatr == MINUS))) {
			no++;
			if (no >= count) {
				*tp1 = j;
				*lentp1 = i - j;
				return true;
			}
			j = i + 1;
		}
		if (i >= n1)
			return false;
	}
}

/*
 * This routine automatically finds the best normal variable to do
 * polynomial division with.
 *
 * Returns 0 if nothing was found, otherwise variable is returned
 * in "*vp1".
 */
int
find_highest_count(p1, n1, p2, n2, vp1)
token_type	*p1;		/* pointer to dividend expression */
int		n1;		/* length of dividend */
token_type	*p2;		/* pointer to divisor expression */
int		n2;		/* length of divisor */
long		*vp1;		/* variable pointer */
{
	int		i;
	int		vc, cnt;
	long		v1, last_v;
	sort_type	va[MAX_VARS];
	int		divide_flag;
	int		t1, len_t1;
	int		t2, len_t2;
	double		d1, d2;
	int		count1, count2;

	last_v = 0;
	for (vc = 0; vc < ARR_CNT(va); vc++) {
		for (i = 0, cnt = 0, v1 = -1; i < n1; i += 2) {
			if (p1[i].kind == VARIABLE && p1[i].token.variable > last_v) {
				if (v1 == -1 || p1[i].token.variable < v1) {
					v1 = p1[i].token.variable;
					cnt = 1;
				} else if (p1[i].token.variable == v1) {
					cnt++;
				}
			}
		}
		if (v1 == -1)
			break;
		last_v = v1;
		va[vc].v = v1;
		va[vc].count = cnt;
	}
	if (vc <= 0)
		return 0;
	qsort((char *) va, vc, sizeof(*va), vcmp);
	for (i = 0; i < vc; i++) {
		if ((va[i].v & VAR_MASK) <= SIGN) {
			continue;
		}
		*vp1 = va[i].v;
		divide_flag = 2;
		count1 = find_greatest_power(p1, n1, vp1, &d1, &t1, &len_t1, &divide_flag);
		count2 = find_greatest_power(p2, n2, vp1, &d2, &t2, &len_t2, &divide_flag);
		if (d2 <= 0 || d1 < d2 || count2 > count1) {
			divide_flag = !divide_flag;
			count1 = find_greatest_power(p1, n1, vp1, &d1, &t1, &len_t1, &divide_flag);
			count2 = find_greatest_power(p2, n2, vp1, &d2, &t2, &len_t2, &divide_flag);
			if (d2 <= 0 || d1 < d2 || count2 > count1) {
				continue;
			}
		}
		return va[i].count;
	}
	return 0;
}

#define	VALUE_CNT	3

void
term_value(dp, p1, n1, loc)
double		*dp;
token_type	*p1;
int		n1, loc;
{
	int	i, j, k;
	int	divide_flag = false;
	int	level, div_level = 0;
	double	d, sub_count, sub_sum;

	for (i = 0; i < VALUE_CNT; i++)
		dp[i] = 0.0;
	for (i = loc; i < n1; i++) {
		level = p1[i].level;
		if (p1[i].kind == VARIABLE) {
			if (divide_flag) {
				dp[0] -= 1.0;
				dp[1] -= p1[i].token.variable;
				dp[2] -= p1[i].token.variable;
			} else {
				dp[0] += 1.0;
				dp[1] += p1[i].token.variable;
				dp[2] += p1[i].token.variable;
			}
		} else if (p1[i].kind == OPERATOR) {
			if (level == 1 && (p1[i].token.operatr == PLUS || p1[i].token.operatr == MINUS))
				break;
			if (p1[i].token.operatr == DIVIDE) {
				if (divide_flag && level >= div_level)
					continue;
				div_level = level;
				divide_flag = true;
			} else if (divide_flag && level <= div_level) {
				divide_flag = false;
			}
		}
	}
	divide_flag = false;
	for (j = loc + 1; j < i; j += 2) {
		level = p1[j].level;
		if (p1[j].token.operatr == DIVIDE) {
			if (divide_flag && level >= div_level)
				continue;
			div_level = level;
			divide_flag = true;
		} else if (divide_flag && level <= div_level) {
			divide_flag = false;
		}
		if (p1[j].token.operatr == POWER && level == p1[j+1].level && p1[j+1].kind == CONSTANT) {
			d = p1[j+1].token.constant - 1.0;
			sub_count = 0.0;
			sub_sum = 0.0;
			for (k = j - 1; k >= loc && p1[k].level >= level; k--) {
				if (p1[k].kind == VARIABLE) {
					sub_count += 1;
					sub_sum += p1[k].token.variable;
				}
			}
			if (divide_flag) {
				dp[0] -= d * sub_count;
				dp[2] -= d * sub_sum;
			} else {
				dp[0] += d * sub_count;
				dp[2] += d * sub_sum;
			}
		}
	}
}

/*
 * This routine finds the additive term raised the highest power,
 * with ordering and much flexibility.
 *
 * The number of terms raised to the highest power is returned.
 */
int
find_greatest_power(p1, n1, vp1, pp1, tp1, lentp1, dcodep)
token_type	*p1;		/* pointer to expression */
int		n1;		/* length of expression */
long		*vp1;		/* variable pointer */
double		*pp1;
int		*tp1, *lentp1;
int		*dcodep;	/* divide flag pointer indicates if term is a denominator */
				/* for example: for x^5 it is false, for 1/x^5 it is true */
				/* if equal to 3, ignore */
{
	int		i, j, k, ii;
	double		d;
	int		flag, divide_flag = false;
	int		level, div_level = 0;
	long		v = 0;
	int		was_power = false;
	double		last_va[VALUE_CNT];
	double		va[VALUE_CNT];
	int		rv;
	int		count = 0;

	*pp1 = 0.0;
	*tp1 = -1;
	rv = *dcodep;
	for (j = 0, i = 1;; i += 2) {
		if (i >= n1 || ((p1[i].token.operatr == PLUS || p1[i].token.operatr == MINUS) && p1[i].level == 1)) {
			divide_flag = false;
			if (!was_power && *pp1 <= 1.0) {
				for (k = j; k < i; k++) {
					if (p1[k].kind == VARIABLE) {
						if (*dcodep <= 1 && *dcodep != divide_flag)
							continue;
						if (*vp1) {
							if (p1[k].token.variable == *vp1) {
								term_value(va, p1, n1, j);
								flag = (*pp1 == 1.0 && (rv > divide_flag));
								if (*pp1 == 1.0 && rv == divide_flag) {
									if (*tp1 != j)
										count++;
									for (ii = 0; ii < ARR_CNT(va); ii++) {
										if (va[ii] == last_va[ii])
											continue;
										if (va[ii] < last_va[ii])
											flag = true;
										break;
									}
								} else if (*pp1 < 1.0 || flag) {
									count = 1;
								}
								if (*pp1 < 1.0 || flag) {
									blt(last_va, va, sizeof(last_va));
									*pp1 = 1.0;
									*tp1 = j;
									rv = divide_flag;
								}
								break;
							}
						} else {
							v = p1[k].token.variable;
							*pp1 = 1.0;
							*tp1 = j;
							rv = divide_flag;
							break;
						}
					} else if (p1[k].kind == OPERATOR) {
						if (p1[k].token.operatr == DIVIDE) {
							if (divide_flag && p1[k].level >= div_level)
								continue;
							div_level = p1[k].level;
							divide_flag = true;
						} else if (divide_flag && p1[k].level <= div_level) {
							divide_flag = false;
						}
						if (p1[k].token.operatr == POWER) {
							level = p1[k].level;
							do {
								k += 2;
							} while (k < i && p1[k].level > level);
							k--;
						}
					}
				}
			}
			if (i >= n1)
				break;
			j = i + 1;
			was_power = false;
			divide_flag = false;
			continue;
		}
		level = p1[i].level;
		if (p1[i].token.operatr == DIVIDE) {
			if (divide_flag && level >= div_level)
				continue;
			div_level = level;
			divide_flag = true;
		} else if (divide_flag && level <= div_level) {
			divide_flag = false;
		}
		if (p1[i].token.operatr == POWER && p1[i+1].kind == CONSTANT
		    && (*vp1 || level == p1[i+1].level)) {
			if (*dcodep <= 1 && *dcodep != divide_flag)
				continue;
			d = p1[i+1].token.constant;
			for (k = i;;) {
				if (p1[k-1].kind == VARIABLE) {
					if (*vp1) {
						if (p1[k-1].token.variable == *vp1) {
							was_power = true;
							term_value(va, p1, n1, j);
							flag = (d == *pp1 && (rv > divide_flag));
							if (d == *pp1 && rv == divide_flag) {
								if (*tp1 != j)
									count++;
								for (ii = 0; ii < ARR_CNT(va); ii++) {
									if (va[ii] == last_va[ii])
										continue;
									if (va[ii] < last_va[ii])
										flag = true;
									break;
								}
							} else if (d > *pp1 || flag) {
								count = 1;
							}
							if (d > *pp1 || flag) {
								blt(last_va, va, sizeof(last_va));
								*pp1 = d;
								*tp1 = j;
								rv = divide_flag;
							}
							break;
						}
					} else {
						was_power = true;
						if (d > *pp1) {
							v = p1[k-1].token.variable;
							*pp1 = d;
							*tp1 = j;
							rv = divide_flag;
						}
						break;
					}
				}
				k -= 2;
				if (k <= j)
					break;
				if (p1[k].level <= level)
					break;
			}
		}
	}
	if (*vp1 == 0)
		*vp1 = v;
	if (*tp1 >= 0) {
		for (i = *tp1 + 1; i < n1; i += 2) {
			if ((p1[i].token.operatr == PLUS || p1[i].token.operatr == MINUS)
			    && p1[i].level == 1) {
				break;
			}
		}
		*lentp1 = i - *tp1;
	}
	if (*dcodep == 2)
		*dcodep = rv;
	return count;
}


syntax highlighted by Code2HTML, v. 0.9.1