/*
* Miscellaneous routines for Mathomatic.
*
* Copyright (C) 1987-2007 George Gesslein II.
*/
#include "includes.h"
/*
* This is called when the maximum expression size has been exceeded.
*
* There is no return.
*/
void
error_huge()
{
longjmp(jmp_save, 14);
}
/*
* Check if a floating point math function flagged an error.
*/
void
check_err()
{
switch (errno) {
case EDOM:
errno = 0;
if (domain_check) {
domain_check = false;
} else {
error(_("Domain error in constant."));
longjmp(jmp_save, 2);
}
break;
case ERANGE:
errno = 0;
error(_("Overflow error in constant."));
longjmp(jmp_save, 2);
break;
}
}
/*
* Allocate the needed temp storage.
*
* Return true if successful.
*/
int
init_mem()
{
if ((scratch = (token_type *) malloc(((n_tokens * 3) / 2) * sizeof(token_type))) == NULL
|| (tes = (token_type *) malloc(n_tokens * sizeof(token_type))) == NULL
|| (tlhs = (token_type *) malloc(n_tokens * sizeof(token_type))) == NULL
|| (trhs = (token_type *) malloc(n_tokens * sizeof(token_type))) == NULL) {
return false;
}
if (alloc_next_espace() < 0) { /* make sure there is at least 1 equation space */
return false;
}
return true;
}
/*
* Initialize the global variables.
*/
void
init_gvars()
{
domain_check = false;
high_prec = false;
partial_flag = true;
symb_flag = false;
sign_flag = false;
approximate_roots = false;
zero_token.level = 1;
zero_token.kind = CONSTANT;
zero_token.token.constant = 0.0;
one_token.level = 1;
one_token.kind = CONSTANT;
one_token.token.constant = 1.0;
}
/*
* Clean up when processing is unexpectedly
* interrupted or terminated.
*/
void
clean_up()
{
init_gvars();
if (gfp != stdout) {
#if !SECURE
fclose(gfp);
#endif
gfp = stdout;
}
}
/*
* Restart sign array.
*/
void
clear_sign_array()
{
int i;
for (i = 0; i < ARR_CNT(sign_array); i++) {
sign_array[i] = false;
}
}
/*
* Register all sign variables.
*/
void
set_sign_array()
{
int i, j;
clear_sign_array();
for (i = 0; i < n_equations; i++) {
if (n_lhs[i] > 0) {
for (j = 0; j < n_lhs[i]; j += 2) {
if (lhs[i][j].kind == VARIABLE
&& (lhs[i][j].token.variable & VAR_MASK) == SIGN) {
sign_array[(lhs[i][j].token.variable >> VAR_SHIFT) & SUBSCRIPT_MASK] = true;
}
}
for (j = 0; j < n_rhs[i]; j += 2) {
if (rhs[i][j].kind == VARIABLE
&& (rhs[i][j].token.variable & VAR_MASK) == SIGN) {
sign_array[(rhs[i][j].token.variable >> VAR_SHIFT) & SUBSCRIPT_MASK] = true;
}
}
}
}
}
/*
* Return next unused sign variable in "*vp".
* Mark it used.
*/
int
next_sign(vp)
long *vp;
{
int i;
for (i = 2;; i++) {
if (i >= ARR_CNT(sign_array)) {
/* out of unique sign variables */
*vp = SIGN;
return false;
}
if (!sign_array[i]) {
*vp = SIGN + (((long) i) << VAR_SHIFT);
sign_array[i] = true;
break;
}
}
return true;
}
/*
* Erase all equation spaces.
* Similar to a restart.
*/
void
clear_all()
{
int i;
/* set to equation space number 1 */
cur_equation = 0;
/* erase all equation spaces */
for (i = 0; i < n_equations; i++) {
n_lhs[i] = 0;
n_rhs[i] = 0;
}
/* forget all variables names */
for (i = 0; var_names[i]; i++) {
free(var_names[i]);
var_names[i] = NULL;
}
/* reset everything to be in a known state */
clear_sign_array();
init_gvars();
}
/*
* Allocate or reuse an empty equation space.
*
* Returns new equation space number or -1 on error.
*/
int
alloc_next_espace()
{
int i, n;
for (n = cur_equation, i = 1;; n = (n + 1) % n_equations, i++) {
if (i > n_equations) {
i = n_equations;
if (i < N_EQUATIONS) {
lhs[i] = (token_type *) malloc(n_tokens * sizeof(token_type));
if (lhs[i]) {
rhs[i] = (token_type *) malloc(n_tokens * sizeof(token_type));
if (rhs[i] == NULL) {
free(lhs[i]);
lhs[i] = NULL;
}
}
}
if (i >= N_EQUATIONS || lhs[i] == NULL) {
return -1;
}
n_equations++;
return i;
}
if (n_lhs[n] == 0)
break;
}
n_rhs[n] = 0;
return n;
}
/*
* Return the number of the next empty equation space.
*/
int
next_espace()
{
int i;
i = alloc_next_espace();
if (i < 0) {
error(_("Out of free equation spaces."));
#if !SILENT
printf(_("Use the clear command on unnecessary equations or\n"));
printf(_("restart with \"clear all\" and try again.\n"));
#endif
longjmp(jmp_save, 3);
}
return i;
}
/*
* Copy equation number "src" to equation number "dest".
*/
void
copy_espace(src, dest)
int src, dest;
{
blt(lhs[dest], lhs[src], n_lhs[src] * sizeof(token_type));
n_lhs[dest] = n_lhs[src];
blt(rhs[dest], rhs[src], n_rhs[src] * sizeof(token_type));
n_rhs[dest] = n_rhs[src];
}
/*
* Return true if equation number "i" is solved for a normal variable.
*/
int
solved_equation(i)
int i;
{
if (n_rhs[i] <= 0)
return false;
if (n_lhs[i] != 1 || lhs[i][0].kind != VARIABLE || (lhs[i][0].token.variable & VAR_MASK) <= SIGN)
return false;
if (found_var(rhs[i], n_rhs[i], lhs[i][0].token.variable))
return false;
return true;
}
/*
* Return true if variable "v" exists in expression.
*/
int
found_var(p1, n, v)
token_type *p1;
int n;
long v;
{
int j;
for (j = 0; j < n; j += 2) {
if (p1[j].kind == VARIABLE && p1[j].token.variable == v) {
return true;
}
}
return false;
}
/*
* Return true if variable "v" exists in equation number "i".
*/
int
var_in_equation(i, v)
int i;
long v;
{
if (n_lhs[i] <= 0)
return false;
if (found_var(lhs[i], n_lhs[i], v))
return true;
if (found_var(rhs[i], n_rhs[i], v))
return true;
return false;
}
/*
* Substitute every instance of "v" in "equation" with "expression".
*/
void
subst_var_with_exp(equation, np, expression, len, v)
token_type *equation; /* equation side pointer */
int *np; /* pointer to equation side length */
token_type *expression; /* expression pointer */
int len; /* expression length */
long v; /* variable to substitute with expression */
{
int j, k;
int level;
for (j = *np - 1; j >= 0; j--) {
if (equation[j].kind == VARIABLE && equation[j].token.variable == v) {
level = equation[j].level;
if (*np + len - 1 > n_tokens) {
error_huge();
}
blt(&equation[j+len], &equation[j+1], (*np - (j + 1)) * sizeof(token_type));
*np += len - 1;
blt(&equation[j], expression, len * sizeof(token_type));
for (k = j; k < j + len; k++)
equation[k].level += level;
}
}
}
/*
* Return the minimum level encountered in a Mathomatic "expression".
*/
int
min_level(expression, n)
token_type *expression; /* expression pointer */
int n; /* expression length */
{
int min1;
token_type *p1, *ep;
if (n <= 1) {
if (n <= 0) {
return 1;
} else {
return expression[0].level;
}
}
min1 = expression[1].level;
ep = &expression[n];
for (p1 = &expression[3]; p1 < ep; p1 += 2) {
if (p1->level < min1)
min1 = p1->level;
}
return min1;
}
/*
* Get default equation number from a command parameter string.
* The equation number must be the only parameter.
*
* Return -1 on error.
*/
int
get_default_en(cp)
char *cp;
{
int i;
if (*cp == '\0') {
i = cur_equation;
} else {
i = decstrtol(cp, &cp) - 1;
if (extra_characters(cp))
return -1;
}
if (not_defined(i)) {
return -1;
}
return i;
}
/*
* Parse an expression with equation space pull if the line starts with "#".
*
* Returns the new string position or NULL on error.
*/
char *
parse_expr(equation, np, cp)
token_type *equation; /* where the parsed expression is stored (equation side) */
int *np; /* pointer to the returned parsed expression length */
char *cp; /* string to parse */
{
int i;
char *cp1, *cp2;
cp1 = skip_space(cp);
if (*cp1 == '#') {
cp1++;
switch (*cp1) {
case '+':
case '-':
i = decstrtol(cp1, &cp2);
i = cur_equation + i;
break;
default:
i = decstrtol(cp1, &cp2) - 1;
break;
}
if (cp1 == cp2 || *cp2) {
error(_("Error parsing equation space number."));
return NULL;
}
if (i < 0 || i >= n_equations || n_lhs[i] <= 0) {
error(_("Equation not defined."));
return NULL;
}
if (n_rhs[i]) {
blt(equation, rhs[i], n_rhs[i] * sizeof(token_type));
*np = n_rhs[i];
} else {
blt(equation, lhs[i], n_lhs[i] * sizeof(token_type));
*np = n_lhs[i];
}
list_proc(equation, *np, false);
fprintf(gfp, "\n");
return cp2;
}
return parse_section(equation, np, cp);
}
/*
* Get an expression from the user.
* The prompt must be previously copied into the global prompt_str[].
*
* Return true if successful.
*/
int
get_expr(equation, np)
token_type *equation; /* where the parsed expression is stored (equation side) */
int *np; /* pointer to the returned parsed expression length */
{
char buf[DEFAULT_N_TOKENS];
char *cp;
for (;;) {
if ((cp = get_string(buf, sizeof(buf))) == NULL) {
return false;
}
if (!case_sensitive_flag) {
str_tolower(cp);
}
cp = parse_expr(equation, np, cp);
if (cp && !extra_characters(cp)) {
break;
}
}
return(*np > 0);
}
/*
* Prompt for a variable name from the user.
*
* Return true if successful.
*/
int
prompt_var(vp)
long *vp; /* pointer to the returned variable */
{
char buf[MAX_CMD_LEN];
char *cp;
for (;;) {
my_strlcpy(prompt_str, _("Enter variable: "), sizeof(prompt_str));
if ((cp = get_string(buf, sizeof(buf))) == NULL) {
return false;
}
if (*cp == '\0') {
return false;
}
cp = parse_var2(vp, cp);
if (cp == NULL || extra_characters(cp)) {
continue;
}
return true;
}
}
/*
* Return true and display a message if equation "i" is undefined.
*/
int
not_defined(i)
int i; /* equation space number */
{
if (i < 0 || i >= n_equations) {
error(_("Invalid equation number."));
return true;
}
if (n_lhs[i] <= 0) {
error(_("Equation space is empty."));
return true;
}
return false;
}
/*
* Verify that a current equation or expression is loaded.
*
* Return true and display a message if current equation space is empty.
*/
int
current_not_defined()
{
int i;
i = cur_equation;
if (i < 0 || i >= n_equations || n_lhs[i] <= 0) {
error(_("No current equation or expression."));
return true;
}
return false;
}
/*
* Common routine to output the prompt in prompt_str[] and get a line of input from stdin.
* All Mathomatic input comes from this routine, except for file reading.
* If there is no more input (EOF), exit this program with no error.
*
* Returns "string" if successful or NULL on error.
*/
char *
get_string(string, n)
char *string; /* storage for input string */
int n; /* maximum size of "string" in bytes */
{
#if LIBRARY
error(_("Missing command line argument."));
return NULL;
#else
int i;
#if READLINE
char *cp;
#endif
if (quiet_mode) {
prompt_str[0] = '\0'; /* don't display a prompt */
}
input_column = strlen(prompt_str);
#if READLINE
if (readline_enabled) {
cp = readline(prompt_str);
if (cp == NULL) {
fprintf(gfp, _("\nEnd of input.\n"));
exit_program(0);
}
my_strlcpy(string, cp, n);
if (string[0]) {
add_history(cp);
} else {
free(cp);
}
} else {
printf("%s", prompt_str);
if (fgets(string, n, stdin) == NULL) {
fprintf(gfp, _("\nEnd of input.\n"));
exit_program(0);
}
}
#else
printf("%s", prompt_str);
if (fgets(string, n, stdin) == NULL) {
fprintf(gfp, _("\nEnd of input.\n"));
exit_program(0);
}
#endif
i = strlen(string) - 1;
if (i >= 0 && string[i] == '\n') {
string[i] = '\0';
}
if (gfp != stdout) {
fprintf(gfp, "%s%s\n", prompt_str, string);
}
set_error_level(string);
return string;
#endif
}
/*
* Display the prompt in prompt_str[] and wait for "y" or "n" followed by Enter.
*
* Return true if "y".
*/
int
get_yes_no()
{
char *cp;
char buf[MAX_CMD_LEN];
for (;;) {
if ((cp = get_string(buf, sizeof(buf))) == NULL) {
return false;
}
str_tolower(cp);
switch (*cp) {
case 'n':
return false;
case 'y':
return true;
}
}
}
/*
* Store or display the result of a command.
*
* Return true if successful.
*/
int
return_result(en)
int en; /* equation space number */
{
#if LIBRARY
if (gfp == stdout) {
if (display2d) {
make_fractions_and_group(en);
}
if (factor_int_flag) {
factor_int_sub(en);
}
free(result_str);
result_str = list_equation(en, false);
return(result_str != NULL);
}
#endif
return(list_sub(en) != 0);
}
/*
* Return true if the first word in the passed string is "all".
*/
int
is_all(cp)
char *cp;
{
return(strcmp_tospace(cp, "all") == 0);
}
/*
* Process an equation number range given in text string "*cpp".
* Skip past all spaces and update "*cpp" to point to the next argument.
* If no equation number or range is given, assume the current equation is wanted.
*
* Return true if successful,
* with the starting equation number in "*ip"
* and ending equation number in "*jp".
*/
int
get_range(cpp, ip, jp)
char **cpp;
int *ip, *jp;
{
int i;
*cpp = skip_space(*cpp);
#if LIBRARY /* only allow a single equation number when using as a library */
if (isdigit(**cpp)) {
i = decstrtol(*cpp, cpp) - 1;
} else {
i = cur_equation;
}
if (not_defined(i)) {
return false;
}
*ip = i;
*jp = i;
return true;
#else
if (is_all(*cpp)) {
*cpp = skip_param(*cpp);
*ip = 0;
*jp = n_equations - 1;
} else {
if (isdigit(**cpp)) {
*ip = strtol(*cpp, cpp, 10) - 1;
} else {
*ip = cur_equation;
}
if (*ip < 0 || *ip >= n_equations) {
error(_("Invalid equation number."));
return false;
}
if (**cpp != '-') {
if (not_defined(*ip)) {
return false;
}
*jp = *ip;
*cpp = skip_space(*cpp);
return true;
}
(*cpp)++;
if (isdigit(**cpp)) {
*jp = strtol(*cpp, cpp, 10) - 1;
} else {
*jp = cur_equation;
}
if (*jp < 0 || *jp >= n_equations) {
error(_("Invalid equation number."));
return false;
}
if (*jp < *ip) {
i = *ip;
*ip = *jp;
*jp = i;
}
}
*cpp = skip_space(*cpp);
for (i = *ip; i <= *jp; i++) {
if (n_lhs[i] > 0) {
return true;
}
}
error(_("No equations defined in specified range."));
return false;
#endif
}
/*
* This function is provided to make sure there is nothing else
* on a command line.
*
* Returns false if OK.
* Returns true if any non-space characters were encountered
* and an error message was printed.
*/
int
extra_characters(cp)
char *cp; /* command line */
{
if (cp) {
cp = skip_space(cp);
if (*cp) {
error(_("Extra characters encountered."));
return true;
}
}
return false;
}
/*
* get_range() if it is the last possible option on the command line.
*/
int
get_range_eol(cpp, ip, jp)
char **cpp;
int *ip, *jp;
{
if (!get_range(cpp, ip, jp)) {
return false;
}
if (extra_characters(*cpp)) {
return false;
}
return true;
}
/*
* Skip over space characters.
*/
char *
skip_space(cp)
char *cp; /* character pointer */
{
while (*cp && isspace(*cp))
cp++;
return cp;
}
/*
* Enhanced decimal strtol().
* Skips trailing spaces.
*/
long
decstrtol(cp, cpp)
char *cp, **cpp;
{
long l;
l = strtol(cp, cpp, 10);
if (cp != *cpp) {
*cpp = skip_space(*cpp);
}
return l;
}
/*
* Skip over a parameter (command line argument) in a character string.
* Parameters are separated with spaces.
*
* Returns a string pointer to the next parameter.
*/
char *
skip_param(cp)
char *cp; /* character pointer */
{
while (*cp && (!isascii(*cp) || (!isspace(*cp) && *cp != '='))) {
cp++;
}
cp = skip_space(cp);
if (*cp == '=') {
cp = skip_space(cp + 1);
}
return(cp);
}
/*
* Compare strings up to the first space.
* Must be an exact match.
* Compare is alphabetic case insensitive.
*
* Returns zero on match, non-zero if different.
*/
int
strcmp_tospace(cp1, cp2)
char *cp1, *cp2;
{
char *cp1a, *cp2a;
for (cp1a = cp1; *cp1a && !isspace(*cp1a); cp1a++)
;
for (cp2a = cp2; *cp2a && !isspace(*cp2a); cp2a++)
;
return strncasecmp(cp1, cp2, max(cp1a - cp1, cp2a - cp2));
}
/*
* Return the number of "level" additive type operators.
*/
int
level_plus_count(p1, n1, level)
token_type *p1; /* expression pointer */
int n1; /* expression length */
int level; /* parentheses level number */
{
int i;
int count;
count = 0;
for (i = 1; i < n1; i += 2) {
if (p1[i].level == level) {
switch (p1[i].token.operatr) {
case PLUS:
case MINUS:
count++;
}
}
}
return count;
}
/*
* Return the number of level 1 additive type operators.
*/
int
level1_plus_count(p1, n1)
token_type *p1; /* expression pointer */
int n1; /* expression length */
{
return level_plus_count(p1, n1, min_level(p1, n1));
}
/*
* Return the count of variables in an expression.
*/
int
var_count(p1, n1)
token_type *p1; /* expression pointer */
int n1; /* expression length */
{
int i;
int count;
count = 0;
for (i = 0; i < n1; i += 2) {
if (p1[i].kind == VARIABLE) {
count++;
}
}
return count;
}
/*
* Set "*vp" if single variable expression.
*
* Return true if expression contains no variables.
*/
int
no_vars(source, n, vp)
token_type *source; /* expression pointer */
int n; /* expression length */
long *vp; /* variable pointer */
{
int j;
int found = false;
if (*vp) {
return(var_count(source, n) == 0);
}
for (j = 0; j < n; j += 2) {
if (source[j].kind == VARIABLE) {
if ((source[j].token.variable & VAR_MASK) <= SIGN)
continue;
if (*vp) {
if (*vp != source[j].token.variable) {
*vp = 0;
break;
}
} else {
found = true;
*vp = source[j].token.variable;
}
}
}
return(!found);
}
/*
* Return true if expression contains infinity or NaN (Not a Number).
*/
int
exp_contains_infinity(p1, n1)
token_type *p1; /* expression pointer */
int n1; /* expression length */
{
int i;
for (i = 0; i < n1; i += 2) {
if (p1[i].kind == CONSTANT && !isfinite(p1[i].token.constant)) {
return true;
}
}
return false;
}
/*
* Return true if expression contains NaN (Not a Number).
*/
int
exp_contains_nan(p1, n1)
token_type *p1; /* expression pointer */
int n1; /* expression length */
{
int i;
for (i = 0; i < n1; i += 2) {
if (p1[i].kind == CONSTANT && isnan(p1[i].token.constant)) {
return true;
}
}
return false;
}
/*
* Return true if expression is numeric (not symbolic).
* Pseudo-variables e, pi, i, and sign are considered numeric.
*/
int
exp_is_numeric(p1, n1)
token_type *p1; /* expression pointer */
int n1; /* expression length */
{
int i;
for (i = 0; i < n1; i += 2) {
if (p1[i].kind == VARIABLE && (p1[i].token.variable & VAR_MASK) > SIGN) {
return false; /* not numerical (contains a variable) */
}
}
return true;
}
syntax highlighted by Code2HTML, v. 0.9.1