/*
 * Mathomatic commands that don't belong anywhere else.
 *
 * Copyright (C) 1987-2007 George Gesslein II.
 */

#include "includes.h"

#define	OPT_MIN_SIZE	7	/* Minimum size of repeated expressions to find in optimize command. */

static int	sum_product();
static int	complex_func();
static int	elim_sub();
#if	!LIBRARY
static int	edit_sub();
static int	find_more();
static int	opt_es();

/* Global variables for the optimize command. */
static int	opt_en[N_EQUATIONS];
static int	last_temp_var = 0;
#endif

/*
 * The version command.
 */
int
version_cmd(cp)
char	*cp;	/* the command line argument */
{
#if	LIBRARY
	free(result_str);
	result_str = strdup(VERSION);
#endif
	return version_report();
}

/*
 * Report version number, compile flags, and memory usage.
 */
int
version_report()
{
	fprintf(gfp, _("Mathomatic version %s, written by George Gesslein II.\nCompile flags used: "), VERSION);
#if	__STDC__
	fprintf(gfp, "__STDC__ ");
#endif
#if	UNIX
	fprintf(gfp, "UNIX ");
#endif
#if	CYGWIN
	fprintf(gfp, "CYGWIN ");
#endif
#if	READLINE
	fprintf(gfp, "READLINE ");
#endif
#if	SILENT
	fprintf(gfp, "SILENT ");
#endif
#if	LIBRARY
	fprintf(gfp, "LIBRARY ");
#endif
#if	SECURE
	fprintf(gfp, "SECURE ");
#endif
#if	TIMEOUT_SECONDS
	fprintf(gfp, "TIMEOUT_SECONDS=%d ", TIMEOUT_SECONDS);
#endif
#if	I18N
	fprintf(gfp, "I18N ");
#endif
	fprintf(gfp, _("\nMaximum memory usage: %lld kilobytes\n"), (long long) (N_EQUATIONS + 3LL) * n_tokens * sizeof(token_type) * 2LL / 1000LL);
	return true;
}

/*
 * The solve command.
 */
int
solve_cmd(cp)
char	*cp;
{
	int	i;
	char	buf[MAX_CMD_LEN];

	if (cur_equation < 0 || cur_equation >= n_equations || n_lhs[cur_equation] <= 0 || n_rhs[cur_equation] <= 0) {
		error(_("No current equation."));
		return false;
	}
	i = next_espace();
	if (*cp == '\0') {
		my_strlcpy(prompt_str, _("Enter variable or 0: "), sizeof(prompt_str));
		if ((cp = get_string(buf, sizeof(buf))) == NULL) {
			return false;
		}
		if (!case_sensitive_flag) {
			str_tolower(cp);
		}
	}
	if (parse_equation(i, cp)) {
		if (n_lhs[i] && n_rhs[i]) {
			error(_("Invalid argument."));
		} else {
			if (solve(i, cur_equation)) {
				return return_result(cur_equation);
			}
		}
	}
	n_lhs[i] = 0;
	n_rhs[i] = 0;
	return false;
}

/*
 * The sum command.
 */
int
sum_cmd(cp)
char	*cp;
{
	return sum_product(cp, false);
}

/*
 * The product command.
 */
int
product_cmd(cp)
char	*cp;
{
	return sum_product(cp, true);
}

/*
 * Common function for the sum and product commands.
 */
static int
sum_product(cp, product_flag)
char	*cp;		/* the command line */
int	product_flag;	/* true for product, otherwise sum */
{
	int		i;
	long		v = 0;
	double		start, end, step = 1.0;
	int		result_en;
	int		n, ns;
	token_type	*dest, *source;
	int		count_down;		/* if true, count down, otherwise count up */
	char		*cp1, buf[MAX_CMD_LEN];

	if (current_not_defined()) {
		return false;
	}
	result_en = next_espace();
	if (n_rhs[cur_equation]) {
		ns = n_rhs[cur_equation];
		source = rhs[cur_equation];
		dest = rhs[result_en];
	} else {
		ns = n_lhs[cur_equation];
		source = lhs[cur_equation];
		dest = lhs[result_en];
	}
	if (*cp) {
		cp = parse_var2(&v, cp);
		if (cp == NULL) {
			return false;
		}
	}
	if (no_vars(source, ns, &v)) {
		error(_("Current expression contains no variables."));
		return false;
	}
	if (v == 0) {
		if (!prompt_var(&v)) {
			return false;
		}
	}
	if (!found_var(source, ns, v)) {
		error(_("Variable not found."));
		return false;
	}
	if (*cp) {
		if (*cp == '=') {
			cp1 = skip_space(cp + 1);
		} else {
			cp1 = cp;
		}
	} else {
		list_var(v, 0);
		snprintf(prompt_str, sizeof(prompt_str), "%s = ", var_str);
		if ((cp1 = get_string(buf, sizeof(buf))) == NULL)
			return false;
	}
	start = strtod(cp1, &cp);
	if (cp1 == cp || fabs(start) >= MAX_K_INTEGER) {
		error(_("Invalid number."));
		return false;
	}
	cp = skip_space(cp);
	if (strcmp_tospace(cp, "to") == 0) {
		cp = skip_param(cp);
	}
	if (*cp) {
		cp1 = cp;
	} else {
		my_strlcpy(prompt_str, _("To: "), sizeof(prompt_str));
		if ((cp1 = get_string(buf, sizeof(buf))) == NULL)
			return false;
	}
	end = strtod(cp1, &cp);
	if (cp1 == cp || fabs(end) >= MAX_K_INTEGER) {
		error(_("Invalid number."));
		return false;
	}
	cp = skip_space(cp);
	if (*cp) {
		cp1 = cp;
		step = fabs(strtod(cp1, &cp));
		if (step <= 0.0 || step >= MAX_K_INTEGER || cp1 == cp) {
			error(_("Invalid step."));
			return false;
		}
	}
	if (extra_characters(cp))
		return false;
	count_down = (end < start);
	if (fmod(fabs(start - end) / step, 1.0)) {
		error(_("Warning: end value not reached."));
	}
	if (product_flag) {
		dest[0] = one_token;
	} else {
		dest[0] = zero_token;
	}
	n = 1;
	for (; count_down ? (start >= end) : (start <= end); count_down ? (start -= step) : (start += step)) {
		if (n + 1 + ns > n_tokens) {
			error_huge();
		}
		blt(tlhs, source, ns * sizeof(token_type));
		n_tlhs = ns;
		for (i = 0; i < n_tlhs; i += 2) {
			if (tlhs[i].kind == VARIABLE && tlhs[i].token.variable == v) {
				tlhs[i].kind = CONSTANT;
				tlhs[i].token.constant = start;
			}
		}
		for (i = 0; i < n_tlhs; i++) {
			tlhs[i].level++;
		}
		for (i = 0; i < n; i++) {
			dest[i].level++;
		}
		dest[n].kind = OPERATOR;
		dest[n].level = 1;
		if (product_flag) {
			dest[n].token.operatr = TIMES;
		} else {
			dest[n].token.operatr = PLUS;
		}
		n++;
		blt(&dest[n], tlhs, n_tlhs * sizeof(token_type));
		n += n_tlhs;
		calc_simp(dest, &n);
	}
	if (n_rhs[cur_equation]) {
		n_rhs[result_en] = n;
		blt(lhs[result_en], lhs[cur_equation], n_lhs[cur_equation] * sizeof(token_type));
		n_lhs[result_en] = n_lhs[cur_equation];
	} else {
		n_lhs[result_en] = n;
	}
	return return_result(result_en);
}

#if	!LIBRARY
/*
 * This function is for the "optimize" command.
 * It finds and substitutes all occurrences of the RHS of "en" in "equation".
 * It should be called repeatedly until it returns false.
 */
static int
find_more(equation, np, en)
token_type	*equation;	/* expression to search */
int		*np;		/* pointer to length of expression */
int		en;		/* equation space number */
{
	int	i, j, k;
	int	level;
	int	diff_sign;
	int	found_se;	/* found sub-expression flag */

	if (*np <= 0 || !solved_equation(en)) {
		return false;
	}
	for (level = 1, found_se = true; found_se; level++) {
		for (i = 1, found_se = false; i < *np; i = j + 2) {
			for (j = i; j < *np && equation[j].level >= level; j += 2)
				;
			if (j == i) {
				continue;
			}
			found_se = true;
			k = i - 1;
			if (se_compare(&equation[k], j - k, rhs[en], n_rhs[en], &diff_sign)) {
				if (diff_sign) {
					blt(&equation[i+2], &equation[j], (*np - j) * sizeof(token_type));
					*np -= (j - (i + 2));
					level++;
					equation[k].level = level;
					equation[k].kind = CONSTANT;
					equation[k].token.constant = -1.0;
					k++;
					equation[k].level = level;
					equation[k].kind = OPERATOR;
					equation[k].token.operatr = TIMES;
					k++;
				} else {
					blt(&equation[i], &equation[j], (*np - j) * sizeof(token_type));
					*np -= (j - i);
				}
				equation[k].level = level;
				equation[k].kind = VARIABLE;
				equation[k].token.variable = lhs[en][0].token.variable;
				return true;
			}
		}
	}
	return false;
}

/*
 * This function is for the "optimize" command.
 * It finds and replaces all repeated expressions in "equation" with temporary variables.
 * It also creates a new equation for each temporary variable.
 * It should be called repeatedly until it returns false.
 */
static int
opt_es(equation, np)
token_type	*equation;
int		*np;
{
	int	i, j, k, i1, i2, jj1, k1;
	int	level, level1;
	int	diff_sign;
	int	found_se, found_se1;	/* found sub-expression flags */
	long	v;
	char	temp_buf[50];

	if (*np <= 0) {
		return false;
	}
	for (level = 1, found_se = true; found_se; level++) {
		for (i = 1, found_se = false; i < *np; i = j + 2) {
			for (j = i; j < *np && equation[j].level > level; j += 2)
				;
			if (j == i) {
				continue;
			}
			found_se = true;
			k = i - 1;
			if ((j - k) < OPT_MIN_SIZE) {
				continue;
			}
			found_se1 = true;
			for (level1 = 1; found_se1; level1++) {
				for (i1 = 1, found_se1 = false; i1 < *np; i1 = jj1 + 2) {
					for (jj1 = i1; jj1 < *np && equation[jj1].level > level1; jj1 += 2) {
					}
					if (jj1 == i1) {
						continue;
					}
					found_se1 = true;
					if (i1 <= j)
						continue;
					k1 = i1 - 1;
					if ((jj1 - k1) >= OPT_MIN_SIZE
					    && se_compare(&equation[k], j - k, &equation[k1], jj1 - k1, &diff_sign)) {
						snprintf(temp_buf, sizeof(temp_buf), "temp%d", last_temp_var);
						if (parse_var(&v, temp_buf) == NULL) {
							return false;	/* can't create "temp" variable */
						}
						last_temp_var++;
						if (last_temp_var < 0) {
							last_temp_var = 0;
						}
						i2 = next_espace();
						lhs[i2][0].level = 1;
						lhs[i2][0].kind = VARIABLE;
						lhs[i2][0].token.variable = v;
						n_lhs[i2] = 1;
						blt(rhs[i2], &equation[k], (j - k) * sizeof(token_type));
						n_rhs[i2] = j - k;
						if (diff_sign) {
							blt(&equation[i1+2], &equation[jj1], (*np - jj1) * sizeof(token_type));
							*np -= (jj1 - (i1 + 2));
							level1++;
							equation[k1].level = level1;
							equation[k1].kind = CONSTANT;
							equation[k1].token.constant = -1.0;
							k1++;
							equation[k1].level = level1;
							equation[k1].kind = OPERATOR;
							equation[k1].token.operatr = TIMES;
							k1++;
						} else {
							blt(&equation[i1], &equation[jj1], (*np - jj1) * sizeof(token_type));
							*np -= (jj1 - i1);
						}
						equation[k1].level = level1;
						equation[k1].kind = VARIABLE;
						equation[k1].token.variable = v;
						blt(&equation[i], &equation[j], (*np - j) * sizeof(token_type));
						*np -= j - i;
						equation[k].level = level;
						equation[k].kind = VARIABLE;
						equation[k].token.variable = v;
						while (find_more(equation, np, i2))
							;
						simp_loop(rhs[i2], &n_rhs[i2]);
						simp_loop(equation, np);
						for (i = 0; opt_en[i] >= 0; i++)
							;
						opt_en[i] = i2;
						opt_en[i+1] = -1;
						return true;
					}
				}
			}
		}
	}
	return false;
}

/*
 * The optimize command.
 */
int
optimize_cmd(cp)
char	*cp;
{
	int	i, j, k, i1;
	int	start, stop;
	int	rv = false, flag;
	int	start_en;

	if (!get_range_eol(&cp, &start, &stop)) {
		return false;
	}
	opt_en[0] = -1;
	start_en = 0;
	for (j = i = start; i <= stop; i++) {
		if (n_lhs[i]) {
			j = i;
			simp_sub(i);
		}
	}
	stop = j;
	do {
		flag = false;
		for (i = start; i <= stop; i++) {
			for (j = start; j <= stop; j++) {
				if (i != j) {
					while (find_more(rhs[i], &n_rhs[i], j)) {
						flag = true;
						rv = true;
					}
				}
			}
		}
	} while (flag);
	for (i = start; i <= stop; i++) {
		if (n_lhs[i] == 0)
			continue;
		do {
			flag = false;
			simp_sub(i);
			for (j = 0; opt_en[j] >= 0; j++) {
				simp_sub(opt_en[j]);
				if (i != opt_en[j]) {
					while (find_more(lhs[i], &n_lhs[i], opt_en[j]))
						flag = true;
					while (find_more(rhs[i], &n_rhs[i], opt_en[j]))
						flag = true;
				}
			}
		} while (flag);
		while (opt_es(lhs[i], &n_lhs[i])) {
			rv = true;
		}
		while (opt_es(rhs[i], &n_rhs[i])) {
			rv = true;
		}
		if (rv) {
			for (i1 = start_en; opt_en[i1] >= 0; i1++) {
				for (j = start_en; opt_en[j] >= 0; j++) {
					for (k = j + 1; opt_en[k] >= 0; k++) {
						while (find_more(rhs[opt_en[k]], &n_rhs[opt_en[k]], opt_en[j]))
							;
						while (find_more(rhs[opt_en[j]], &n_rhs[opt_en[j]], opt_en[k]))
							;
					}
				}
				while (opt_es(rhs[opt_en[i1]], &n_rhs[opt_en[i1]]))
					;
			}
			for (; opt_en[start_en] >= 0; start_en++) {
				list_sub(opt_en[start_en]);
			}
			list_sub(i);
		}
	}
	if (!rv) {
		error(_("Unable to find any repeated expressions."));
	}
	return rv;
}
#endif

#if	READLINE
/*
 * The push command.
 */
int
push_cmd(cp)
char	*cp;
{
	int	i, j;

	if (!get_range_eol(&cp, &i, &j)) {
		return false;
	}
	for (; i <= j; i++) {
		if (!push_en(i)) {
			error(_("Push failed."));
			return false;
		}
	}
	debug_string(0, _("Expression(s) pushed.  Press the UP key to access."));
	return true;
}

/*
 * Push an equation space into the readline history.
 *
 * Return true if successful.
 */
int
push_en(en)
int	en;	/* equation space number */
{
	if (readline_enabled) {
		high_prec = true;
		add_history(list_equation(en, false));
		high_prec = false;
		return true;
	} else {
		return false;
	}
}
#endif

/*
 * Display current working directory.
 *
 * Return true if successful.
 */
int
display_current_directory()
{
#if	!SECURE
	char	buf[MAX_CMD_LEN];

	if (getcwd(buf, sizeof(buf))) {
		fprintf(gfp, "directory = %s\n", buf);
		return true;
	}
#endif
	return false;
}

/*
 * Output the current set options in a format suitable for RC_FILE.
 */
void
output_options()
{
	if (!autosolve) {
		fprintf(gfp, "no ");
	}
	fprintf(gfp, "autosolve\n");

	if (!autocalc) {
		fprintf(gfp, "no ");
	}
	fprintf(gfp, "autocalc\n");

#if	!SILENT
	fprintf(gfp, "debug_level = %d\n", debug_level);
#endif

	if (!case_sensitive_flag) {
		fprintf(gfp, "no ");
	}
	fprintf(gfp, "case_sensitive\n");

	if (!color_flag) {
		fprintf(gfp, "no ");
	}
	fprintf(gfp, "color\n");

	if (!bold_colors) {
		fprintf(gfp, "no ");
	}
	fprintf(gfp, "bold_colors\n");

	if (!display2d) {
		fprintf(gfp, "no ");
	}
	fprintf(gfp, "display2d\n");

	if (!preserve_roots) {
		fprintf(gfp, "no ");
	}
	fprintf(gfp, "preserve_roots\n");

	if (!true_modulus) {
		fprintf(gfp, "no ");
	}
	fprintf(gfp, "true_modulus\n");

	fprintf(gfp, "finance = %d\n", finance_option);

	if (!factor_int_flag) {
		fprintf(gfp, "no ");
	}
	fprintf(gfp, "factor_integers\n");

	if (!right_associative_power) {
		fprintf(gfp, "no ");
	}
	fprintf(gfp, "right_associative_power\n");

	fprintf(gfp, "special_variable_character = %c\n", special_variable_character);
}

/*
 * Return true if "cp" points to a negative word.
 */
int
is_no(cp)
char	*cp;
{
	if (strcmp_tospace(cp, "no") == 0
	    || strcmp_tospace(cp, "off") == 0
	    || strcmp_tospace(cp, "false") == 0) {
		return true;
	}
	return false;
}

/*
 * Handle parsing of options for the set command.
 *
 * Return false if error.
 */
int
set_options(cp)
char	*cp;
{
	int	i;
	int	negate;
	char	*cp1;

next_option:
	cp = skip_space(cp);
	if (*cp == '\0') {
		return true;
	}
	negate = is_no(cp);
	if (negate) {
		cp = skip_param(cp);
	}
#if	!SECURE
	if (strncasecmp(cp, "dir", 3) == 0) {
		cp = skip_param(cp);
		if (*cp == '\0') {
			cp1 = getenv("HOME");
			if (cp1) {
				cp = cp1;
			}
		}
		if (chdir(cp)) {
			error(_("Error changing directory."));
			return false;
		}
		display_current_directory();
		return true;
	}
#endif
#if	!SILENT
	if (strncasecmp(cp, "debug", 5) == 0) {
		cp = skip_param(cp);
		if (negate) {
			debug_level = 0;
		} else {
			i = decstrtol(cp, &cp1);
			if (cp == cp1) {
				error(_("Please specify the debug level number."));
				return false;
			}
			cp = cp1;
			debug_level = i;
		}
		goto next_option;
	}
#endif
	if (strncasecmp(cp, "special", 7) == 0) {
		cp = skip_param(cp);
		if (negate) {
			special_variable_character = '\0';
		} else {
			if (*cp == '\0' || cp[1]) {
				error(_("Please specify a single character."));
				return false;
			}
			special_variable_character = *cp;
		}
		return true;
	}
	if (strncasecmp(cp, "columns", 7) == 0) {
		cp = skip_param(cp);
		if (negate) {
			screen_columns = 0;
		} else {
			if ((i = decstrtol(cp, &cp1)) < 0 || cp == cp1) {
				error(_("Please specify how wide the screen is in columns."));
				return false;
			}
			cp = cp1;
			screen_columns = i;
		}
		goto next_option;
	}
	if (strncasecmp(cp, "autosolve", 9) == 0) {
		cp = skip_param(cp);
		autosolve = !negate;
		goto next_option;
	}
	if (strncasecmp(cp, "autocalc", 8) == 0) {
		cp = skip_param(cp);
		autocalc = !negate;
		goto next_option;
	}
	if (strncasecmp(cp, "case", 4) == 0) {
		cp = skip_param(cp);
		case_sensitive_flag = !negate;
		goto next_option;
	}
	if (strncasecmp(cp, "display2d", 7) == 0) {
		cp = skip_param(cp);
		display2d = !negate;
		goto next_option;
	}
	if (strncasecmp(cp, "preserve", 8) == 0) {
		cp = skip_param(cp);
		preserve_roots = !negate;
		goto next_option;
	}
	if (strncasecmp(cp, "true_modulus", 4) == 0) {
		cp = skip_param(cp);
		true_modulus = !negate;
		goto next_option;
	}
	if (strncasecmp(cp, "color", 5) == 0) {
		cp = skip_param(cp);
		color_flag = !negate;
		cur_color = -1;
		goto next_option;
	}
	if (strncasecmp(cp, "bold", 4) == 0) {
		cp = skip_param(cp);
		bold_colors = !negate;
		cur_color = -1;
		goto next_option;
	}
	if (strncasecmp(cp, "finance", 7) == 0) {
		cp = skip_param(cp);
		if (negate) {
			finance_option = 0;
		} else {
			i = decstrtol(cp, &cp1);
			if (cp == cp1) {
				i = 2;
			}
			if (i > 50) {
				error(_("Maximum is 50."));
				return false;
			}
			cp = cp1;
			finance_option = i;
		}
		goto next_option;
	}
	if (strncasecmp(cp, "factor_integers", 6) == 0) {
		cp = skip_param(cp);
		factor_int_flag = !negate;
		goto next_option;
	}
	if (strncasecmp(cp, "right_associative_power", 5) == 0) {
		cp = skip_param(cp);
		right_associative_power = !negate;
		goto next_option;
	}
	error(_("Unknown set option."));
	return false;
}

/*
 * The set command.
 */
int
set_cmd(cp)
char	*cp;
{
	if (*cp == '\0') {
		fprintf(gfp, _("Options are set as follows:\n\n"));

		output_options();

		fprintf(gfp, "columns = %d\n", screen_columns);

		display_current_directory();
		return true;
	}
	return set_options(cp);
}

/*
 * The echo command.
 */
int
echo_cmd(cp)
char	*cp;
{
	fprintf(gfp, "%s\n", cp);
	return true;
}

/*
 * The pause command.
 */
int
pause_cmd(cp)
char	*cp;
{
	char	*cp1;
	char	buf[MAX_CMD_LEN];

	if (test_mode) {
		return true;
	}
	if (*cp == '\0') {
		cp = _("Please press Enter");
	}
	snprintf(prompt_str, sizeof(prompt_str), " ==== %s ==== ", cp);
	if ((cp1 = get_string(buf, sizeof(buf))) == NULL) {
		return false;
	}
	if (strncasecmp(cp1, "quit", 4) == 0) {
		return false;
	}
	if (strncasecmp(cp1, "exit", 4) == 0) {
		return false;
	}
	return true;
}

/*
 * The copy command.
 */
int
copy_cmd(cp)
char	*cp;
{
	int	i, j, k;
	int	i1;
	char	exists[N_EQUATIONS];

	if (!get_range_eol(&cp, &i, &j)) {
		return false;
	}
	for (i1 = 0; i1 < N_EQUATIONS; i1++) {
		exists[i1] = false;
	}
	for (i1 = i; i1 <= j; i1++) {
		if (n_lhs[i1] > 0) {
			exists[i1] = true;
		}
	}
	for (i1 = i; i1 <= j; i1++) {
		if (exists[i1]) {
			k = next_espace();
			copy_espace(i1, k);
			return_result(k);
		}
	}
	return true;
}

/*
 * Common function for the imaginary and real commands.
 */
static int
complex_func(cp, imag_flag)
char	*cp;		/* the command line */
int	imag_flag;	/* if true, copy the imaginary part, otherwise copy the real part */
{
	int		i, j, k;
	int		beg;
	int		found_imag;
	int		has_imag, has_real;
	token_type	*source, *dest;
	int		n1, *nps, *np;
	long		v;

	if (current_not_defined()) {
		return false;
	}
	i = cur_equation;
	j = next_espace();
	if (n_rhs[i]) {
		source = rhs[i];
		nps = &n_rhs[i];
		dest = rhs[j];
		np = &n_rhs[j];
	} else {
		source = lhs[i];
		nps = &n_lhs[i];
		dest = lhs[j];
		np = &n_lhs[j];
	}
	v = IMAGINARY;
	if (*cp) {
		cp = parse_var2(&v, cp);
		if (cp == NULL) {
			return false;
		}
		if (extra_characters(cp))
			return false;
	}
	simp_loop(source, nps);
	uf_simp(source, nps);
	factorv(source, nps, v);
	partial_flag = false;
	uf_simp(source, nps);
	partial_flag = true;
	n1 = 1;
	dest[0] = zero_token;
	has_imag = has_real = false;
	for (beg = k = 0; beg < *nps; beg = k, k++) {
		found_imag = false;
		for (; k < *nps; k++) {
			if (source[k].level == 1 && source[k].kind == OPERATOR
			    && (source[k].token.operatr == PLUS || source[k].token.operatr == MINUS)) {
				break;
			}
			if (source[k].kind == VARIABLE && source[k].token.variable == v) {
				found_imag = true;
			}
		}
		if (found_imag)
			has_imag = true;
		else
			has_real = true;
		if (found_imag == imag_flag) {
			if (beg == 0) {
				n1 = 0;
			}
			blt(&dest[n1], &source[beg], (k - beg) * sizeof(token_type));
			n1 += (k - beg);
		}
	}
	if (!has_imag || !has_real) {
		error(_("Failed, expression is not a mix."));
		return false;
	}
	simp_divide(dest, &n1);
	if (n_rhs[i]) {
		blt(lhs[j], lhs[i], n_lhs[i] * sizeof(token_type));
		n_lhs[j] = n_lhs[i];
	}
	*np = n1;
	cur_equation = j;
	return return_result(cur_equation);
}

/*
 * The real command.
 */
int
real_cmd(cp)
char	*cp;
{
	return complex_func(cp, false);
}

/*
 * The imaginary command.
 */
int
imaginary_cmd(cp)
char	*cp;
{
	return complex_func(cp, true);
}

#if	!LIBRARY
/*
 * The tally command.
 */
int
tally_cmd(cp)
char	*cp;
{
	int	i;
	double	count;
	int	arithmetic_mean = false;

	if (strncasecmp(cp, "average", 4) == 0) {
		arithmetic_mean = true;
		cp = skip_param(cp);
	}
	if (extra_characters(cp)) {
		return false;
	}
	trhs[0] = zero_token;
	n_trhs = 1;
	for (count = 0.0;; count++) {
		fprintf(gfp, _("Running total = "));
		list_proc(trhs, n_trhs, false);
		fprintf(gfp, "\n");
		if (count > 0.0) {
			if (arithmetic_mean) {
				/* calculate and display the average */
				blt(tlhs, trhs, n_trhs * sizeof(token_type));
				n_tlhs = n_trhs;
				if ((n_tlhs + 2) > n_tokens) {
					error_huge();
				}
				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 = DIVIDE;
				n_tlhs++;
				tlhs[n_tlhs].kind = CONSTANT;
				tlhs[n_tlhs].level = 1;
				tlhs[n_tlhs].token.constant = count;
				n_tlhs++;
				calc_simp(tlhs, &n_tlhs);
				fprintf(gfp, _("Average = "));
				list_proc(tlhs, n_tlhs, false);
				fprintf(gfp, "\n");
			}
		}
		fprintf(gfp, "\n");
		my_strlcpy(prompt_str, _("Enter value: "), sizeof(prompt_str));
		if (!get_expr(tlhs, &n_tlhs)) {
			break;
		}
		if ((n_trhs + 1 + n_tlhs) > n_tokens) {
			error_huge();
		}
		for (i = 0; i < n_tlhs; i++) {
			tlhs[i].level++;
		}
		for (i = 0; i < n_trhs; i++) {
			trhs[i].level++;
		}
		trhs[n_trhs].kind = OPERATOR;
		trhs[n_trhs].level = 1;
		trhs[n_trhs].token.operatr = PLUS;
		n_trhs++;
		blt(&trhs[n_trhs], tlhs, n_tlhs * sizeof(token_type));
		n_trhs += n_tlhs;
		calc_simp(trhs, &n_trhs);
	}
	fprintf(gfp, _("End.\n"));
	return true;
}
#endif

#if	!LIBRARY
/*
 * The calculate command.
 *
 * Temporarily plug values into the RHS of the current equation,
 * unless there is no RHS, then use LHS.
 */
int
calculate_cmd(cp)
char	*cp;
{
	int		i, j, k;
	long		v, last_v, it_v = 0;
	long		counter, counter_max;
	sign_array_type	sa_mark;
	sign_array_type	sa_value;
	long		l, iterations = 1;
	token_type	*source;
	int		n;
	int		diff_sign;
	char		buf[MAX_CMD_LEN];

	if (current_not_defined()) {
		return false;
	}
	i = cur_equation;
	if (n_rhs[i]) {
		source = rhs[i];
		n = n_rhs[i];
	} else {
		source = lhs[i];
		n = n_lhs[i];
	}
	if (*cp) {
		cp = parse_var2(&it_v, cp);
		if (cp == NULL) {
			return false;
		}
		if (!found_var(source, n, it_v)) {
			error(_("Specified iteration variable not found."));
			return false;
		}
		if (*cp == '\0') {
			my_strlcpy(prompt_str, _("Enter number of iterations: "), sizeof(prompt_str));
			if ((cp = get_string(buf, sizeof(buf))) == NULL)
				return false;
		}
		iterations = decstrtol(cp, &cp);
		if (*cp || iterations <= 0) {
			error(_("Invalid number of iterations."));
			return false;
		}
	}
	n_trhs = n;
	blt(trhs, source, n_trhs * sizeof(token_type));
	last_v = 0;
	for (;;) {
		v = -1;
		for (j = 0; j < n; j += 2) {
			if (source[j].kind == VARIABLE) {
				if (source[j].token.variable > last_v
				    && (v == -1 || source[j].token.variable < v))
					v = source[j].token.variable;
			}
		}
		if (v == -1)
			break;
		last_v = v;
		if ((v & VAR_MASK) <= SIGN || v == it_v) {
			continue;
		}
		list_var(v, 0);
		snprintf(prompt_str, sizeof(prompt_str), _("Enter %s: "), var_str);
		if (!get_expr(tlhs, &n_tlhs)) {
			continue;
		}
		/* disguise all variables in the entered expression by making them negative: */
		for (j = 0; j < n_tlhs; j += 2)
			if (tlhs[j].kind == VARIABLE)
				tlhs[j].token.variable = -tlhs[j].token.variable;
		subst_var_with_exp(trhs, &n_trhs, tlhs, n_tlhs, v);
	}
	/* restore disguised variables: */
	for (j = 0; j < n_trhs; j += 2)
		if (trhs[j].kind == VARIABLE && trhs[j].token.variable < 0)
			trhs[j].token.variable = -trhs[j].token.variable;
	if (it_v) {
		list_var(it_v, 0);
		snprintf(prompt_str, sizeof(prompt_str), _("Enter initial %s: "), var_str);
		while (!get_expr(tes, &n_tes))
			;
		calc_simp(tes, &n_tes);
		blt(tlhs, trhs, n_trhs * sizeof(token_type));
		n_tlhs = n_trhs;
		for (l = 0; l < iterations; l++) {
			blt(trhs, tlhs, n_tlhs * sizeof(token_type));
			n_trhs = n_tlhs;
			subst_var_with_exp(trhs, &n_trhs, tes, n_tes, it_v);
			calc_simp(trhs, &n_trhs);
			if (se_compare(trhs, n_trhs, tes, n_tes, &diff_sign) && !diff_sign) {
				fprintf(gfp, _("Convergence reached after %ld iterations.\n"), l + 1);
				break;
			}
			blt(tes, trhs, n_trhs * sizeof(token_type));
			n_tes = n_trhs;
		}
	}
	calc_simp(trhs, &n_trhs);
	fprintf(gfp, "\n");
	for (j = 0; j < ARR_CNT(sa_mark); j++)
		sa_mark[j] = false;
	for (j = 0; j < n_trhs; j += 2) {
		if (trhs[j].kind == VARIABLE && (trhs[j].token.variable & VAR_MASK) == SIGN) {
			sa_mark[(trhs[j].token.variable >> VAR_SHIFT) & SUBSCRIPT_MASK] = true;
		}
	}
	for (j = 0, k = 0; j < ARR_CNT(sa_mark); j++) {
		if (sa_mark[j]) {
			k++;
		}
	}
	counter_max = (1L << k) - 1L;
	for (counter = 0; counter <= counter_max; counter++) {
		blt(tlhs, trhs, n_trhs * sizeof(token_type));
		n_tlhs = n_trhs;
		for (j = 0, k = 0; j < ARR_CNT(sa_mark); j++) {
			if (sa_mark[j]) {
				sa_value[j] = (((1L << k) & counter) != 0);
				k++;
			}
		}
		for (j = 0; j < n_tlhs; j += 2) {
			if (tlhs[j].kind == VARIABLE && (tlhs[j].token.variable & VAR_MASK) == SIGN) {
				if (sa_value[(tlhs[j].token.variable >> VAR_SHIFT) & SUBSCRIPT_MASK]) {
					tlhs[j].kind = CONSTANT;
					tlhs[j].token.constant = -1.0;
				} else {
					tlhs[j].kind = CONSTANT;
					tlhs[j].token.constant = 1.0;
				}
			}
		}
		for (j = 0, k = false; j < ARR_CNT(sa_mark); j++) {
			if (sa_mark[j]) {
				if (k) {
					fprintf(gfp, ", ");
				} else {
					fprintf(gfp, _("Solution #%ld with "), counter + 1);
				}
				list_var((long) SIGN + (((long) j) << VAR_SHIFT), 0);
				fprintf(gfp, "%s = ", var_str);
				if (sa_value[j]) {
					fprintf(gfp, "-1");
				} else {
					fprintf(gfp, "1");
				}
				k = true;
			}
		}
		if (k)
			fprintf(gfp, ":\n");
		calc_simp(tlhs, &n_tlhs);
		if (factor_int_flag) {
			factor_int(tlhs, &n_tlhs);
		}
		fprintf(gfp, " ");
		if (n_rhs[i]) {
			list_proc(lhs[i], n_lhs[i], false);
			fprintf(gfp, " = ");
		}
		list_proc(tlhs, n_tlhs, false);
		fprintf(gfp, "\n\n");
	}
	return true;
}
#endif

/*
 * The clear command.
 */
int
clear_cmd(cp)
char	*cp;
{
	int	i, j;
	char	*cp1;

	do {
		cp1 = cp;
		if (is_all(cp)) {
			clear_all();
			return true;
		} else {
			if (!get_range(&cp, &i, &j)) {
				return false;
			}
			if (*cp && cp == cp1) {
				error(_("Invalid argument."));
				return false;
			}
			for (; i <= j; i++) {
				n_lhs[i] = 0;
				n_rhs[i] = 0;
			}
		}
	} while (*cp);
	return true;
}

static int
compare_rhs(i, j, diff_signp)
int	i, j;
int	*diff_signp;
{
	int	rv;

	rv = se_compare(rhs[i], n_rhs[i], rhs[i], n_rhs[i], diff_signp);
	if (!rv || *diff_signp) {
		error(_("Error in compare function or too many terms to compare."));
		return false;
	}
	sign_flag = true;
	rv = se_compare(rhs[i], n_rhs[i], rhs[j], n_rhs[j], diff_signp);
	sign_flag = false;
	return rv;
}

static int
compare_lhs(i, j)
int	i, j;
{
	int	rv;
	int	diff_sign;

	rv = se_compare(lhs[i], n_lhs[i], lhs[i], n_lhs[i], &diff_sign);
	if (!rv || diff_sign) {
		error(_("Error in compare function or too many terms to compare."));
		return false;
	}
	sign_flag = true;
	rv = se_compare(lhs[i], n_lhs[i], lhs[j], n_lhs[j], &diff_sign);
	sign_flag = false;
	return(rv && !diff_sign);
}

/*
 * The compare command.
 */
int
compare_cmd(cp)
char	*cp;
{
	int		i, j;
	int		diff_sign;
	int		already_solved;

	i = decstrtol(cp, &cp) - 1;
	if (not_defined(i)) {
		return false;
	}
	if (strcmp_tospace(cp, "with") == 0) {
		cp = skip_param(cp);
	}
	if ((j = get_default_en(cp)) < 0) {
		return false;
	}
	if (i == j) {
		error(_("Cannot compare an equation with itself."));
		return false;
	}
#if	!SILENT
	fprintf(gfp, _("Comparing #%d with #%d...\n"), i + 1, j + 1);
#endif
	if (n_rhs[i] == 0 || n_rhs[j] == 0) {
		if (n_rhs[i] == 0 && n_rhs[j] == 0) {
			simp_loop(lhs[i], &n_lhs[i]);
			simp_loop(lhs[j], &n_lhs[j]);
			if (compare_lhs(i, j)) {
				fprintf(gfp, _("Expressions are identical.\n"));
				return true;
			}
			debug_string(0, _("Simplifying both expressions..."));
			simpa_side(lhs[i], &n_lhs[i], true, false);
			simpa_side(lhs[j], &n_lhs[j], true, false);
#if	!SILENT
			if (debug_level >= 0) {
				list_sub(i);
				list_sub(j);
			}
#endif
			if (compare_lhs(i, j)) {
				fprintf(gfp, _("Expressions are identical.\n"));
				return true;
			}
			uf_simp(lhs[i], &n_lhs[i]);
			uf_simp(lhs[j], &n_lhs[j]);
			if (compare_lhs(i, j)) {
				fprintf(gfp, _("Expressions are identical.\n"));
				return true;
			}
			fprintf(gfp, _("Expressions differ.\n"));
			return false;
		}
		error(_("Cannot compare an expression with an equation."));
		return false;
	}
	already_solved = (solved_equation(i) && solved_equation(j));
	if (already_solved) {
		simp_loop(rhs[i], &n_rhs[i]);
		simp_loop(rhs[j], &n_rhs[j]);
		if (compare_rhs(i, j, &diff_sign)) {
			goto times_neg1;
		}
		debug_string(0, _("Simplifying both equations..."));
		simpa_side(rhs[i], &n_rhs[i], true, false);
		simpa_side(rhs[j], &n_rhs[j], true, false);
#if	!SILENT
		if (debug_level >= 0) {
			list_sub(i);
			list_sub(j);
		}
#endif
		if (compare_rhs(i, j, &diff_sign)) {
			goto times_neg1;
		}
		uf_simp(rhs[i], &n_rhs[i]);
		uf_simp(rhs[j], &n_rhs[j]);
		if (compare_rhs(i, j, &diff_sign)) {
			goto times_neg1;
		}
	}
	debug_string(0, _("Solving both equations for zero and unfactoring..."));
	if (solve_sub(&zero_token, 1, lhs[i], &n_lhs[i], rhs[i], &n_rhs[i]) <= 0
	    || solve_sub(&zero_token, 1, lhs[j], &n_lhs[j], rhs[j], &n_rhs[j]) <= 0) {
		error(_("Can't solve for zero!"));
		return false;
	}
	uf_simp(rhs[i], &n_rhs[i]);
	uf_simp(rhs[j], &n_rhs[j]);
	if (compare_rhs(i, j, &diff_sign)) {
		fprintf(gfp, _("Equations are identical.\n"));
		return true;
	}
	debug_string(0, _("Simplifying both equations..."));
	simpa_side(rhs[i], &n_rhs[i], true, false);
	simpa_side(rhs[j], &n_rhs[j], true, false);
	if (compare_rhs(i, j, &diff_sign)) {
		fprintf(gfp, _("Equations are identical.\n"));
		return true;
	}
	if (solve_sub(&zero_token, 1, lhs[i], &n_lhs[i], rhs[i], &n_rhs[i]) <= 0
	    || solve_sub(&zero_token, 1, lhs[j], &n_lhs[j], rhs[j], &n_rhs[j]) <= 0) {
		error(_("Can't solve for zero!"));
		return false;
	}
	uf_simp(rhs[i], &n_rhs[i]);
	uf_simp(rhs[j], &n_rhs[j]);
	if (compare_rhs(i, j, &diff_sign)) {
		fprintf(gfp, _("Equations are identical.\n"));
		return true;
	}
	fprintf(gfp, _("Equations differ.\n"));
	return false;
times_neg1:
	if (!diff_sign && lhs[i][0].token.variable == lhs[j][0].token.variable) {
		fprintf(gfp, _("Equations are identical.\n"));
		return true;
	}
	fprintf(gfp, _("Variable ("));
	list_proc(lhs[i], n_lhs[i], false);
	fprintf(gfp, _(") in the first equation is equal to ("));
	if (diff_sign) {
		fprintf(gfp, "-");
	}
	list_proc(lhs[j], n_lhs[j], false);
	fprintf(gfp, _(") in the second equation.\n"));
	return(!diff_sign);
}

#if	!LIBRARY
/*
 * The divide command.
 */
int
divide_cmd(cp)
char	*cp;
{
	long		v, v_tmp;
	int		i, j;
	int		nl, nr;
	double		d1, d2, d3, d4;
	complexs	c1, c2, c3;

	v = 0;
	if (*cp) {
		cp = parse_var2(&v, cp);
		if (cp == NULL) {
			return false;
		}
		if (extra_characters(cp))
			return false;
	}
	i = next_espace();
	my_strlcpy(prompt_str, _("Enter dividend: "), sizeof(prompt_str));
	if (!get_expr(rhs[i], &nr)) {
		return false;
	}
	my_strlcpy(prompt_str, _("Enter divisor: "), sizeof(prompt_str));
	if (!get_expr(lhs[i], &nl)) {
		return false;
	}
	fprintf(gfp, "\n");
	calc_simp(lhs[i], &nl);
	calc_simp(rhs[i], &nr);
	if (get_constant(rhs[i], nr, &d1) && get_constant(lhs[i], nl, &d2)) {
		d4 = modf(d1 / d2, &d3);
		fprintf(gfp, _("Result of numerical division: %.*g/%.*g = %.*g\n"), precision, d1, precision, d2, precision, d1 / d2);
		fprintf(gfp, _("Quotient: %.*g, Remainder: %.*g\n"), precision, d3, precision, d4 * d2);
		d1 = fabs(d1);
		d2 = fabs(d2);
		d3 = gcd_verified(d1, d2);
		if (d3 <= 0.0) {
			fprintf(gfp, _("No GCD found.\n"));
			return true;
		}
		fprintf(gfp, _("Greatest Common Divisor (GCD) = %.*g\n"), precision, d3);
		fprintf(gfp, _("Least Common Multiple (LCM) = %.*g\n"), precision, (d1 * d2) / d3);
		return true;
	}
	if (parse_complex(rhs[i], nr, &c1) && parse_complex(lhs[i], nl, &c2)) {
		c3 = complex_div(c1, c2);
		fprintf(gfp, _("Result of complex number division:\n"));
		fprintf(gfp, "%.*g %+.*g*i\n\n", precision, c3.re, precision, c3.im);
		return true;
	}
	v_tmp = v;
	if (poly_div(rhs[i], nr, lhs[i], nl, &v_tmp)) {
		simp_divide(tlhs, &n_tlhs);
		simp_divide(trhs, &n_trhs);
		list_var(v_tmp, 0);
		fprintf(gfp, _("Polynomial division successful using base variable (%s).\n"), var_str);
		fprintf(gfp, _("The quotient is:\n"));
		list_proc(tlhs, n_tlhs, false);
		fprintf(gfp, _("\n\nThe remainder is:\n"));
		list_proc(trhs, n_trhs, false);
		fprintf(gfp, "\n");
	} else {
		fprintf(gfp, _("Polynomial division failed.\n"));
	}
	fprintf(gfp, "\n");
	j = poly_gcd(rhs[i], nr, lhs[i], nl, v);
	if (!j) {
		j = poly_gcd(lhs[i], nl, rhs[i], nr, v);
	}
	if (j) {
		simp_divide(trhs, &n_trhs);
		fprintf(gfp, _("Polynomial GCD (Euclidean algorithm iterations = %d):\n"), j);
		list_proc(trhs, n_trhs, false);
		fprintf(gfp, "\n");
	} else {
		fprintf(gfp, _("No univariate polynomial GCD found.\n"));
	}
	return true;
}
#endif

/*
 * The eliminate command.
 */
int
eliminate_cmd(cp)
char	*cp;
{
	long	v, last_v, v1, va[MAX_VARS];
	int	i, n;
	int	using_flag, did_something = false;
	char	used[N_EQUATIONS];
	int	vc = 0;

	if (*cp == '\0') {
		error(_("Please specify the variables to eliminate or \"all\" for all variables."));
		return false;
	}
	for (i = 0; i < ARR_CNT(used); i++)
		used[i] = false;
	if (current_not_defined()) {
		return false;
	}
next_var:
	if (vc) {
		v = va[--vc];
	} else if (*cp) {
		if (is_all(cp)) {
			cp = skip_param(cp);
			vc = 0;
			last_v = 0;
			for (;;) {
				v1 = -1;
				for (i = 0; i < n_lhs[cur_equation]; i += 2) {
					if (lhs[cur_equation][i].kind == VARIABLE
					    && lhs[cur_equation][i].token.variable > last_v) {
						if (v1 == -1 || lhs[cur_equation][i].token.variable < v1) {
							v1 = lhs[cur_equation][i].token.variable;
						}
					}
				}
				for (i = 0; i < n_rhs[cur_equation]; i += 2) {
					if (rhs[cur_equation][i].kind == VARIABLE
					    && rhs[cur_equation][i].token.variable > last_v) {
						if (v1 == -1 || rhs[cur_equation][i].token.variable < v1) {
							v1 = rhs[cur_equation][i].token.variable;
						}
					}
				}
				if (v1 == -1)
					break;
				last_v = v1;
				if ((v1 & VAR_MASK) > SIGN) {
					if (vc >= ARR_CNT(va)) {
						break;
					}
					va[vc++] = v1;
				}
			}
			goto next_var;
		}
		cp = parse_var2(&v, cp);
		if (cp == NULL) {
			return false;
		}
	} else {
		if (did_something) {
			did_something = return_result(cur_equation);
		} else {
			error(_("No substitutions made."));
		}
		return did_something;
	}
	if (!var_in_equation(cur_equation, v)) {
#if	!SILENT
		list_var(v, 0);
		printf(_("Variable (%s) not found in current equation.\n"), var_str);
#endif
		goto next_var;
	}
	using_flag = (strcmp_tospace(cp, "using") == 0);
	if (using_flag) {
		cp = skip_param(cp);
		i = decstrtol(cp, &cp) - 1;
		if (not_defined(i)) {
			return false;
		}
		if (i == cur_equation) {
			error(_("Error: source and destination are the same."));
			return false;
		}
		if (!elim_sub(i, v))
			goto next_var;
	} else {
		n = 1;
		i = cur_equation;
		for (;; n++) {
			if (n >= n_equations) {
				goto next_var;
			}
			if (i <= 0)
				i = n_equations - 1;
			else
				i--;
			if (used[i])
				continue;
			if (n_lhs[i] && n_rhs[i] && var_in_equation(i, v)) {
				if (elim_sub(i, v))
					break;
			}
		}
	}
	did_something = true;
	used[i] = true;
	goto next_var;
}

static int
elim_sub(i, v)
int	i;
long	v;
{
	token_type	want;

#if	!SILENT
	if (debug_level >= 0) {
		list_var(v, 0);
		printf(_("Solving equation #%d for (%s) and substituting into the current equation...\n"), i + 1, var_str);
	}
#endif
	want.level = 1;
	want.kind = VARIABLE;
	want.token.variable = v;
	if (solve_sub(&want, 1, lhs[i], &n_lhs[i], rhs[i], &n_rhs[i]) <= 0) {
		debug_string(0, _("Solve failed."));
		return false;
	}
	subst_var_with_exp(rhs[cur_equation], &n_rhs[cur_equation], rhs[i], n_rhs[i], v);
	subst_var_with_exp(lhs[cur_equation], &n_lhs[cur_equation], rhs[i], n_rhs[i], v);
	simp_sub(cur_equation);
	return true;
}

/*
 * The display command.
 *
 * Displays equations in multi-line fraction format.
 */
int
display_cmd(cp)
char	*cp;
{
	int	i, j;
	int	factor_flag;

	factor_flag = (strcmp_tospace(cp, "factor") == 0);
	if (factor_flag) {
		cp = skip_param(cp);
	}
	if (!get_range_eol(&cp, &i, &j)) {
		return false;
	}
	for (; i <= j; i++) {
		if (n_lhs[i] > 0) {
			make_fractions_and_group(i);
			if (factor_flag || factor_int_flag) {
				factor_int_sub(i);
			}
#if	LIBRARY
			if (gfp == stdout) {
				free(result_str);
				result_str = list_equation(i, false);
				return(result_str != NULL);
			}
#endif
			flist_sub(i);
		}
	}
	return true;
}

/*
 * The list command.
 */
int
list_cmd(cp)
char	*cp;
{
	int	i, j;
	char	*cp1;
	int	export_flag = 0;

	if (strncasecmp(cp, "export", 4) == 0) {
		export_flag = 2;
		cp = skip_param(cp);
	} else if (strncasecmp(cp, "maxima", 4) == 0) {
		export_flag = 1;
		cp = skip_param(cp);
	}
	do {
		cp1 = cp;
		if (!get_range(&cp, &i, &j)) {
			return false;
		}
		if (*cp && cp == cp1) {
			error(_("Invalid argument."));
			return false;
		}
		for (; i <= j; i++) {
			if (factor_int_flag) {
				factor_int_sub(i);
			}
#if	LIBRARY
			if (gfp == stdout) {
				free(result_str);
				result_str = list_equation(i, export_flag);
				return(result_str != NULL);
			}
#endif
			list1_sub(i, export_flag);
		}
	} while (*cp);
	return true;
}

#if	!LIBRARY
/*
 * The code command.
 */
int
code_cmd(cp)
char	*cp;
{
	int	i, j;
	int	language = 0;
	int	int_flag = false, displayed = false;
	char	*cp1;

	if (strcmp_tospace(cp, "c") == 0 || strcmp_tospace(cp, "c++") == 0) {
		cp = skip_param(cp);
	} else if (strcmp_tospace(cp, "java") == 0) {
		cp = skip_param(cp);
		language = 1;
	} else if (strcmp_tospace(cp, "python") == 0) {
		cp = skip_param(cp);
		language = 2;
	} else if (strcmp_tospace(cp, "int") == 0 || strcmp_tospace(cp, "integer") == 0) {
		cp = skip_param(cp);
		int_flag = true;
	}
	do {
		cp1 = cp;
		if (!get_range(&cp, &i, &j)) {
			return false;
		}
		if (*cp && cp == cp1) {
			error(_("Invalid argument."));
			return false;
		}
		for (; i <= j; i++) {
			if (n_lhs[i] > 0) {
				if (n_rhs[i] == 0 || n_lhs[i] != 1 || lhs[i][0].kind != VARIABLE) {
					error(_("Not a solved equation."));
					continue;
				}
				approximate_roots = true;
				simp_i(rhs[i], &n_rhs[i]);
				approximate_roots = false;
				if (int_flag) {
					uf_repeat_always(rhs[i], &n_rhs[i]);
				}
				make_fractions_and_group(i);
				if (int_flag) {
					if (!int_expr(rhs[i], n_rhs[i])) {
						error(_("Not an integer equation."));
						continue;
					}
				}
				list_c_equation(i, language, int_flag);
				displayed = true;
			}
		}
	} while (*cp);
	return displayed;
}
#endif

/*
 * The approximate command.
 */
int
approximate_cmd(cp)
char	*cp;
{
	int	i, j;
	char	*cp1;

	do {
		cp1 = cp;
		if (!get_range(&cp, &i, &j)) {
			return false;
		}
		if (*cp && cp == cp1) {
			error(_("Invalid argument."));
			return false;
		}
		for (; i <= j; i++) {
			if (n_lhs[i]) {
				subst_constants(lhs[i], &n_lhs[i]);
				subst_constants(rhs[i], &n_rhs[i]);
				approximate_roots = true;
				simp_sub(i);
				approximate_roots = false;
				return_result(i);
			}
		}
	} while (*cp);
	return true;
}

/*
 * The replace command.
 */
int
replace_cmd(cp)
char	*cp;
{
	int	i, j;
	int	n;
	long	last_v, v;
	char	*cp_start;
	long	va[MAX_VARS];
	int	vc;
	int	found;
	char	*cp1;

	cp_start = cp;
	if (strcmp_tospace(cp, "constants") == 0) {
		cp = skip_param(cp);
		return approximate_cmd(cp);
	}
	if (current_not_defined()) {
		return false;
	}
	i = cur_equation;
	for (vc = 0; *cp; vc++) {
		if (strcmp_tospace(cp, "with") == 0) {
			if (vc)
				break;
			error(_("No variables specified."));
			return false;
		}
		if (vc >= ARR_CNT(va)) {
			error(_("Too many variables specified."));
			return false;
		}
		cp = parse_var2(&va[vc], cp);
		if (cp == NULL) {
			return false;
		}
		if (!var_in_equation(i, va[vc])) {
			error(_("Variable not found."));
			return false;
		}
	}
	n_tlhs = n_lhs[i];
	blt(tlhs, lhs[i], n_tlhs * sizeof(token_type));
	n_trhs = n_rhs[i];
	blt(trhs, rhs[i], n_trhs * sizeof(token_type));
	last_v = 0;
	for (;;) {
		v = -1;
		for (j = 0; j < n_lhs[i]; j += 2) {
			if (lhs[i][j].kind == VARIABLE) {
				if (lhs[i][j].token.variable > last_v
				    && (v == -1 || lhs[i][j].token.variable < v))
					v = lhs[i][j].token.variable;
			}
		}
		for (j = 0; j < n_rhs[i]; j += 2) {
			if (rhs[i][j].kind == VARIABLE) {
				if (rhs[i][j].token.variable > last_v
				    && (v == -1 || rhs[i][j].token.variable < v))
					v = rhs[i][j].token.variable;
			}
		}
		if (v == -1) {
			break;
		}
		last_v = v;
		if (vc) {
			found = false;
			for (j = 0; j < vc; j++) {
				if (v == va[j])
					found = true;
			}
			if (!found)
				continue;
			if (*cp) {
				if (strcmp_tospace(cp, "with") != 0) {
					return false;
				}
				cp1 = skip_param(cp);
				input_column += (cp1 - cp_start);
				if (!case_sensitive_flag) {
					str_tolower(cp1);
				}
				if ((cp1 = parse_section(scratch, &n, cp1)) == NULL || n <= 0) {
					return false;
				}
				if (extra_characters(cp1))
					return false;
				goto do_this;
			}
		}
		list_var(v, 0);
		snprintf(prompt_str, sizeof(prompt_str), _("Enter %s: "), var_str);
		if (!get_expr(scratch, &n)) {
			continue;
		}
do_this:
		/* disguise all variables in the entered expression by making them negative: */
		for (j = 0; j < n; j += 2) {
			if (scratch[j].kind == VARIABLE) {
				scratch[j].token.variable = -scratch[j].token.variable;
			}
		}
		subst_var_with_exp(tlhs, &n_tlhs, scratch, n, v);
		subst_var_with_exp(trhs, &n_trhs, scratch, n, v);
	}
	/* restore disguised variables: */
	for (j = 0; j < n_tlhs; j += 2)
		if (tlhs[j].kind == VARIABLE && tlhs[j].token.variable < 0)
			tlhs[j].token.variable = -tlhs[j].token.variable;
	for (j = 0; j < n_trhs; j += 2)
		if (trhs[j].kind == VARIABLE && trhs[j].token.variable < 0)
			trhs[j].token.variable = -trhs[j].token.variable;
	n_lhs[i] = n_tlhs;
	blt(lhs[i], tlhs, n_tlhs * sizeof(token_type));
	n_rhs[i] = n_trhs;
	blt(rhs[i], trhs, n_trhs * sizeof(token_type));
	simp_sub(i);
	return return_result(i);
}

/*
 * The simplify command.
 */
int
simplify_cmd(cp)
char	*cp;
{
	int	i, j;
	int	quick_flag = false, symb = false, frac_flag = false;

	for (;; cp = skip_param(cp)) {
		if (strncasecmp(cp, "symbolic", 4) == 0) {
			symb = true;
			continue;
		}
		if (strncasecmp(cp, "quick", 4) == 0) {
			quick_flag = true;
			continue;
		}
		if (strncasecmp(cp, "fraction", 4) == 0) {
			frac_flag = true;
			continue;
		}
		break;
	}
	if (!get_range_eol(&cp, &i, &j)) {
		return false;
	}
	symb_flag = symb;
	for (; i <= j; i++) {
		if (n_lhs[i]) {
			simpa_side(lhs[i], &n_lhs[i], quick_flag, frac_flag);
			simpa_side(rhs[i], &n_rhs[i], quick_flag, frac_flag);
			return_result(i);
		}
	}
	symb_flag = false;
	return true;
}

/*
 * The factor command.
 */
int
factor_cmd(cp)
char	*cp;
{
	int	i, j;
	int	i1, count;
	long	v;
	double	d;
	char	*cp_start, buf[MAX_CMD_LEN];
	int	valid_range = false;

	cp_start = cp;
	if (strncasecmp(cp, "number", 6) == 0) {
		cp = skip_param(cp);
	} else {
		valid_range = get_range(&cp, &i, &j);
		if (!valid_range) {
			cp = cp_start;
		}
	}
	if (!valid_range) {
		if (*cp == '\0') {
			my_strlcpy(prompt_str, _("Enter integers to factor: "), sizeof(prompt_str));
			cp = get_string(buf, sizeof(buf));
			if (cp == NULL)
				return false;
		}
		for (count = 1; *cp; count++) {
			d = strtod(cp, &cp);
			cp = skip_space(cp);
			if (!factor_one(d)) {
				error(_("Not a valid integer or number too large."));
				return false;
			}
			display_unique();
		}
		return true;
	}
	v = 0;
	do {
		if (*cp) {
			if ((cp = parse_var2(&v, cp)) == NULL) {
				return false;
			}
		}
		for (i1 = i; i1 <= j; i1++) {
			if (n_lhs[i1]) {
				simpv_side(lhs[i1], &n_lhs[i1], v);
				simpv_side(rhs[i1], &n_rhs[i1], v);
			}
		}
	} while (*cp);
	for (i1 = i; i1 <= j; i1++) {
		return_result(i1);
	}
	return true;
}

/*
 * The unfactor command.
 */
int
unfactor_cmd(cp)
char	*cp;
{
	int	i, j;
	int	fully_flag;

	fully_flag = (strncasecmp(cp, "fully", 4) == 0);
	if (fully_flag) {
		cp = skip_param(cp);
	}
	if (!get_range_eol(&cp, &i, &j)) {
		return false;
	}
	partial_flag = !fully_flag;
	for (; i <= j; i++) {
		if (n_lhs[i]) {
			uf_simp(lhs[i], &n_lhs[i]);
			uf_simp(rhs[i], &n_rhs[i]);
			return_result(i);
		}
	}
	partial_flag = true;
	return true;
}

/*
 * The fraction command.
 */
int
fraction_cmd(cp)
char	*cp;
{
	int	i, j;

	if (!get_range_eol(&cp, &i, &j)) {
		return false;
	}
	for (; i <= j; i++) {
		if (n_lhs[i]) {
			frac_side(lhs[i], &n_lhs[i]);
			frac_side(rhs[i], &n_rhs[i]);
			return_result(i);
		}
	}
	return true;
}

#if	!LIBRARY
/*
 * The quit command.
 */
int
quit_cmd(cp)
char	*cp;
{
	if (extra_characters(cp))
		return false;
	exit_program(0);
	return false;
}
#endif

#if	!SECURE
/*
 * The read command.
 */
int
read_cmd(cp)
char	*cp;
{
	int	rv;
	FILE	*fp;
	char	buf[MAX_CMD_LEN];

	if (*cp == '\0') {
		error(_("No file specified."));
		return false;
	}
	fp = NULL;
	snprintf(buf, sizeof(buf), "%s.in", cp);
	fp = fopen(buf, "r");
	if (fp == NULL) {
		fp = fopen(cp, "r");
		if (fp == NULL) {
			error(_("Can't open input file."));
			return false;
		}
	} else {
		cp = buf;
	}
	rv = read_sub(fp);
	if (rv) {
		printf(_("Read operation aborted.\n"));
	} else {
		printf(_("Finished reading file \"%s\".\n"), cp);
	}
	fclose(fp);
	return(!rv);
}

/*
 * Read and process Mathomatic input from a file pointer.
 *
 * Return zero if no error, non-zero if aborted.
 */
int
read_sub(fp)
FILE	*fp;
{
	int	rv;
	jmp_buf	save_save;
	char	*cp;

	blt(save_save, jmp_save, sizeof(jmp_save));
	if ((rv = setjmp(jmp_save)) != 0) {
		clean_up();
		if (rv == 14) {
			error(_("Expression too large."));
		}
	} else {
		while ((cp = fgets((char *) tlhs, n_tokens * sizeof(token_type), fp)) != NULL) {
			default_color();
			input_column = printf("%d%s", cur_equation + 1, html_flag ? HTML_PROMPT : PROMPT);
			printf("%s", cp);
			if (gfp != stdout) {
				fprintf(gfp, "%d%s%s", cur_equation + 1, html_flag ? HTML_PROMPT : PROMPT, cp);
			}
			set_error_level(cp);
			if (!process(cp)) {
				longjmp(jmp_save, 3);
			}
		}
	}
	blt(jmp_save, save_save, sizeof(jmp_save));
	return rv;
}
#endif

#if	(UNIX || CYGWIN) && !SECURE && !LIBRARY
/*
 * The edit command.
 */
int
edit_cmd(cp)
char	*cp;
{
	FILE	*fp;
	int	fd;
	int	rv;
	char	tmp_file[MAX_CMD_LEN];

	if (*cp == '\0') {
#if	CYGWIN
		my_strlcpy(tmp_file, "mathomatic.tmp", sizeof(tmp_file));
		fp = fopen(tmp_file, "w+");
		if (fp == NULL) {
			error(_("Can't create temporary file."));
			return false;
		}
#else
		my_strlcpy(tmp_file, TMP_FILE, sizeof(tmp_file));
		fd = mkstemp(tmp_file);
		if (fd < 0 || (fp = fdopen(fd, "w+")) == NULL) {
			error(_("Can't create temporary file."));
			return false;
		}
#endif
		gfp = fp;
		high_prec = true;
		list_cmd("all");
		high_prec = false;
		gfp = stdout;
		fclose(fp);
		rv = edit_sub(tmp_file);
		unlink(tmp_file);
		return rv;
	} else {
		if (access(cp, 6)) {
			error(_("You can only edit existing/writable files or all equations."));
			return false;
		}
		return edit_sub(cp);
	}
}

static int
edit_sub(cp)
char	*cp;
{
	char	cl[MAX_CMD_LEN];	/* command line */
	char	*cp1;

edit_again:
	cp1 = getenv("EDITOR");
	if (cp1 == NULL) {
#if	CYGWIN
		cp1 = "notepad";
#else
		error("EDITOR environment variable not set.");
		return false;
#endif
	}
	snprintf(cl, sizeof(cl), "%s %s", cp1, cp);
	if (shell_out(cl)) {
		error("Error executing editor, check EDITOR environment variable.");
		printf(_("Command line = \"%s\".\n"), cl);
		return false;
	}
	clear_all();
	if (!read_cmd(cp)) {
		if (pause_cmd(_("Prepare to run the editor"))) {
			goto edit_again;
		}
	}
	return true;
}
#endif

#if	!SECURE
/*
 * The save command.
 */
int
save_cmd(cp)
char	*cp;
{
	FILE	*fp;
	int	rv;

	if (*cp == '\0') {
		error(_("No file specified."));
		return false;
	}
#if	!SILENT
	if (access(cp, 0) == 0) {
		snprintf(prompt_str, sizeof(prompt_str), _("\"%s\" exists, overwrite (y/n)? "), cp);
		if (!get_yes_no()) {
			printf(_("Command aborted.\n"));
			return false;
		}
	}
#endif
	fp = fopen(cp, "w");
	if (fp == NULL) {
		error(_("Can't create specified file."));
		return false;
	}
	gfp = fp;
	high_prec = true;
	rv = list_cmd("all");
	high_prec = false;
	gfp = stdout;
	if (fclose(fp))
		rv = false;
	if (rv) {
#if	!SILENT
		printf(_("All equations saved in file: \"%s\".\n"), cp);
#endif
	} else {
		error(_("Error encountered while saving equations."));
	}
	return rv;
}
#endif


syntax highlighted by Code2HTML, v. 0.9.1