/* * Mathomatic expression and equation display routines. * Color mode routines, too. * * Copyright (C) 1987-2007 George Gesslein II. */ #include "includes.h" #define EQUATE_STRING " = " /* string displayed between the LHS and RHS of equations */ #define MODULUS_STRING " % " /* string displayed for the modulus binary operator */ #define APPEND(str) { if (string) { strcpy(&string[len], str); } len += strlen(str); } static int flist(); static int flist_recurse(); /* ANSI color code array */ static int carray[] = { 32, /* green (default color) */ 33, /* yellow */ 31, /* red */ 35, /* magenta */ 34, /* blue */ 36, /* cyan */ 37 /* white */ }; /* HTML color array */ static char *html_carray[] = { "#00FF00", /* bright green (default color) */ "#FFFF00", "#FF9000", "#FF0000", "#FF00FF", "#00FFFF", "#0000FF" }; /* global variables for flist() */ int cur_line; /* current line */ int cur_pos; /* current position in the current line on the screen */ /* * Turn color off if color mode is on. */ void reset_attr() { cur_color = -1; if (color_flag) { if (html_flag) { printf(""); } else { printf("\033[0m"); } } fflush(NULL); /* flush all output */ } /* * Set the current color on the display. * Range is 0 to INT_MAX. */ void set_color(color) int color; { if (gfp != stdout) return; if (cur_color == color) return; if (color_flag) { if (html_flag) { if (cur_color >= 0) { printf(""); } printf("", html_carray[color%ARR_CNT(html_carray)]); } else { if (bold_colors) { printf("\033[1;%dm", carray[color%ARR_CNT(carray)]); } else { printf("\033[0;%dm", carray[color%ARR_CNT(carray)]); } } cur_color = color; } } /* * Set the normal text color. */ void default_color() { set_color(0); } /* * Display the expression or equation stored in equation space "n" in single-line format. * The equation space is not modified. * * Return length (number of screen columns) of output line. */ int list1_sub(n, export_flag) int n; /* equation space number */ int export_flag; /* true for exportable format (readable by other math programs) */ /* 1 for Maxima, 2 for other */ { int len = 0; if (n_lhs[n] <= 0) return len; if (!export_flag && !high_prec) { len += fprintf(gfp, "#%d: ", n + 1); } len += list_proc(lhs[n], n_lhs[n], export_flag); if (n_rhs[n]) { len += fprintf(gfp, EQUATE_STRING); len += list_proc(rhs[n], n_rhs[n], export_flag); } if (export_flag == 1) { len += fprintf(gfp, ";"); } if (high_prec) { #if CYGWIN fprintf(gfp, "\r\n"); #else fprintf(gfp, "\n"); #endif } else { fprintf(gfp, "\n\n"); } return len; } /* * Display the expression or equation stored in equation space "n". * * Return the total width of the output (number of screen columns) * or zero on failure. */ int list_sub(n) int n; /* equation space number */ { if (n_lhs[n] <= 0) return 0; if (display2d) { /* display in fraction format */ make_fractions_and_group(n); if (factor_int_flag) { factor_int_sub(n); } return flist_sub(n); } else { /* display in single-line format */ if (factor_int_flag) { factor_int_sub(n); } return list1_sub(n, false); } } #if !SILENT void list_debug(level, p1, n1, p2, n2) int level; token_type *p1; int n1; token_type *p2; int n2; { if (debug_level >= level) { printf(_("level %d: "), level); list_proc(p1, n1, false); if (p2 && n2 > 0) { printf(EQUATE_STRING); list_proc(p2, n2, false); } printf("\n"); } } #endif /* * Convert the passed Mathomatic variable to an ASCII variable name. * The ASCII variable name is stored in the global var_str[]. * * Return length of variable name (number of ASCII characters). * * If (lang_code == 0), use standard Mathomatic format. * If (lang_code == 1), make variable name C language compatible. * If (lang_code == 2), make variable name Java compatible. * If (lang_code == 3), make variable name Python compatible. * If lang_code is negative, create an exportable variable name: -1 for Maxima, -2 for other. */ int list_var(v, lang_code) long v; /* variable to convert */ int lang_code; /* language code */ { int j; char *cp, buf[100]; cp = NULL; switch (v & VAR_MASK) { case V_NULL: cp = "null"; break; case SIGN: cp = "sign"; break; case IMAGINARY: switch (lang_code) { case -2: cp = "i"; break; case -1: cp = "%i"; break; case 0: cp = "i#"; break; case 3: cp = "1j"; break; default: cp = "I"; break; } break; case V_E: switch (lang_code) { case -1: cp = "%e"; break; case 0: cp = "e#"; break; case 1: cp ="M_E"; break; case 2: cp = "Math.E"; break; case 3: cp = "math.e"; break; default: cp = "e"; break; } break; case V_PI: switch (lang_code) { case -1: cp = "%pi"; break; case 0: cp = "pi#"; break; case 1: cp = "M_PI"; break; case 2: cp = "Math.PI"; break; case 3: cp = "math.pi"; break; default: cp = "pi"; break; } break; case MATCH_ANY: cp = "all"; break; default: j = (v & VAR_MASK) - VAR_OFFSET; if (j >= 0) { cp = var_names[j]; } break; } if (cp) { my_strlcpy(var_str, cp, sizeof(var_str)); } else { my_strlcpy(var_str, "bad_variable", sizeof(var_str)); } j = (v >> VAR_SHIFT) & SUBSCRIPT_MASK; if (j) { snprintf(buf, sizeof(buf), "%d", j - 1); strcat(var_str, buf); } return(strlen(var_str)); } /* * Display an expression in single line format. * Use color if available. * The expression is not modified. * * Return number of characters output (excluding escape sequences). */ int list_proc(equation, n, export_flag) token_type *equation; /* expression pointer */ int n; /* length of expression */ int export_flag; /* flag for exportable format (usually false) */ /* 1 for Maxima, 2 for other */ { int i, j, k, i1; int min1; int cur_level; char *cp; int len = 0; cur_level = min1 = min_level(equation, n); for (i = 0; i < n; i++) { j = cur_level - equation[i].level; k = abs(j); for (i1 = 1; i1 <= k; i1++) { if (j > 0) { cur_level--; len += fprintf(gfp, ")"); set_color(cur_level-min1); } else { cur_level++; set_color(cur_level-min1); len += fprintf(gfp, "("); } } switch (equation[i].kind) { case CONSTANT: if (equation[i].token.constant == 0.0) { equation[i].token.constant = 0.0; /* fix -0 */ } if (high_prec || export_flag) { if (equation[i].token.constant < 0.0) { len += fprintf(gfp, "(%.20g)", equation[i].token.constant); } else { len += fprintf(gfp, "%.20g", equation[i].token.constant); } } else if (finance_option) { len += fprintf(gfp, "%.*f", finance_option, equation[i].token.constant); } else { len += fprintf(gfp, "%.*g", precision, equation[i].token.constant); } break; case VARIABLE: list_var(equation[i].token.variable, 0 - export_flag); len += fprintf(gfp, "%s", var_str); break; case OPERATOR: cp = _("(unknown operator)"); switch (equation[i].token.operatr) { case PLUS: cp = " + "; break; case MINUS: cp = " - "; break; case TIMES: cp = "*"; break; case DIVIDE: cp = "/"; break; case MODULUS: cp = MODULUS_STRING; break; case POWER: cp = "^"; break; case FACTORIAL: cp = "!"; i++; break; } len += fprintf(gfp, "%s", cp); break; } } for (j = cur_level - min1; j > 0;) { len += fprintf(gfp, ")"); j--; set_color(j); } return len; } /* * Store the specified equation in a text string. * String should be freed with free() when done. * * Return string or NULL if can't malloc(). */ char * list_equation(n, export_flag) int n; /* equation space number */ int export_flag; /* flag for exportable format (usually false) */ { int len; char *cp; len = list_string_sub(lhs[n], n_lhs[n], NULL, export_flag); if (n_rhs[n]) { len += strlen(EQUATE_STRING); len += list_string_sub(rhs[n], n_rhs[n], NULL, export_flag); } len += 2; cp = (char *) malloc(len); if (cp == NULL) { error(_("Out of memory (can't malloc(3)).")); return NULL; } list_string_sub(lhs[n], n_lhs[n], cp, export_flag); if (n_rhs[n]) { strcat(cp, EQUATE_STRING); list_string_sub(rhs[n], n_rhs[n], &cp[strlen(cp)], export_flag); } if (export_flag == 1) { strcat(cp, ";"); } return cp; } /* * Store an expression in a text string. * String should be freed with free() when done. * * Return string or NULL if can't malloc(). */ char * list_expression(equation, n, export_flag) token_type *equation; /* expression pointer */ int n; /* length of expression */ int export_flag; { int len; char *cp; len = list_string_sub(equation, n, NULL, export_flag); len++; cp = (char *) malloc(len); if (cp == NULL) { error(_("Out of memory (can't malloc(3)).")); return NULL; } list_string_sub(equation, n, cp, export_flag); return cp; } /* * Convert an expression to a text string and store in "string" if * "string" is not NULL. "string" need not be initialized, * but must be long enough to contain the expression. * * Return length (number of characters). */ int list_string_sub(equation, n, string, export_flag) token_type *equation; /* expression pointer */ int n; /* length of expression */ char *string; /* buffer or NULL pointer */ int export_flag; { int i, j, k, i1; int min1; int cur_level; char *cp; int len = 0; char buf[500]; if (string) string[0] = '\0'; cur_level = min1 = min_level(equation, n); for (i = 0; i < n; i++) { j = cur_level - equation[i].level; k = abs(j); for (i1 = 1; i1 <= k; i1++) { if (j > 0) { cur_level--; APPEND(")"); } else { cur_level++; APPEND("("); } } switch (equation[i].kind) { case CONSTANT: if (equation[i].token.constant == 0.0) { equation[i].token.constant = 0.0; /* fix -0 */ } if (high_prec || export_flag) { if (equation[i].token.constant < 0.0) { snprintf(buf, sizeof(buf), "(%.20g)", equation[i].token.constant); } else { snprintf(buf, sizeof(buf), "%.20g", equation[i].token.constant); } } else if (finance_option) { snprintf(buf, sizeof(buf), "%.*f", finance_option, equation[i].token.constant); } else { snprintf(buf, sizeof(buf), "%.*g", precision, equation[i].token.constant); } APPEND(buf); break; case VARIABLE: list_var(equation[i].token.variable, 0 - export_flag); APPEND(var_str); break; case OPERATOR: cp = _("(unknown operator)"); switch (equation[i].token.operatr) { case PLUS: cp = " + "; break; case MINUS: cp = " - "; break; case TIMES: cp = "*"; break; case DIVIDE: cp = "/"; break; case MODULUS: cp = MODULUS_STRING; break; case POWER: cp = "^"; break; case FACTORIAL: cp = "!"; i++; break; } APPEND(cp); break; } } for (j = cur_level - min1; j > 0; j--) { APPEND(")"); } return len; } /* * Return true if expression is a valid integer expression for * list_code(). */ int int_expr(equation, n) token_type *equation; /* expression pointer */ int n; /* length of expression */ { int i; for (i = 0; i < n; i++) { if ((equation[i].kind == CONSTANT && fmod(equation[i].token.constant, 1.0)) || (equation[i].kind == VARIABLE && equation[i].token.variable <= IMAGINARY)) { return false; } } return true; } /* * Display an equation as C, Java, or Python code. */ void list_c_equation(en, language, int_flag) int en; /* equation space number */ int language; int int_flag; /* integer arithmetic flag */ { if (n_lhs[en] <= 0) return; list_code(lhs[en], &n_lhs[en], language, int_flag); if (n_rhs[en]) { fprintf(gfp, " = "); list_code(rhs[en], &n_rhs[en], language, int_flag); } if (language != 2) { fprintf(gfp, ";"); } fprintf(gfp, "\n"); } /* * Output C, Java, or Python code for an expression. * Expression might be modified by this function. * * Return length of output (number of characters). */ int list_code(equation, np, language, int_flag) token_type *equation; /* expression pointer */ int *np; /* pointer to length of expression */ int language; /* if 0, generate C code; if 1, generate Java code; if 2, generate Python code */ int int_flag; /* integer arithmetic flag, should work with any language */ { int i, j, k, i1, i2; int min1; int cur_level; char *cp; char buf[500]; int len = 0; min1 = min_level(equation, *np); if (*np > 1) min1--; cur_level = min1; for (i = 0; i < *np; i++) { j = cur_level - equation[i].level; k = abs(j); for (i1 = 1; i1 <= k; i1++) { if (j > 0) { cur_level--; len += fprintf(gfp, ")"); } else { cur_level++; for (i2 = i + 1; i2 < *np && equation[i2].level >= cur_level; i2 += 2) { if (equation[i2].level == cur_level) { switch (equation[i2].token.operatr) { case POWER: if (equation[i2-1].level == cur_level && equation[i2+1].level == cur_level && equation[i2+1].kind == CONSTANT && equation[i2+1].token.constant == 2.0) { equation[i2].token.operatr = TIMES; equation[i2+1] = equation[i2-1]; } else { if (int_flag || language > 1) break; if (language) { cp = "Math.pow"; } else { cp = "pow"; } len += fprintf(gfp, cp); } break; case FACTORIAL: cp = "fact"; len += fprintf(gfp, cp); break; } break; } } len += fprintf(gfp, "("); } } switch (equation[i].kind) { case CONSTANT: if (equation[i].token.constant == 0.0) { equation[i].token.constant = 0.0; /* fix -0 */ } if (int_flag) { snprintf(buf, sizeof(buf), "%.0f", equation[i].token.constant); } else { snprintf(buf, sizeof(buf), "%#.*g", MAX_PRECISION, equation[i].token.constant); j = strlen(buf) - 1; for (; j >= 0; j--) { if (buf[j] == '0') continue; if (buf[j] == '.') { buf[j+2] = '\0'; } else { break; } } } len += fprintf(gfp, "%s", buf); break; case VARIABLE: len += list_var(equation[i].token.variable, language + 1); fprintf(gfp, "%s", var_str); break; case OPERATOR: cp = _("(unknown operator)"); switch (equation[i].token.operatr) { case PLUS: cp = " + "; break; case MINUS: cp = " - "; break; case TIMES: cp = " * "; break; case DIVIDE: cp = " / "; break; case MODULUS: cp = MODULUS_STRING; break; case POWER: if (int_flag || language > 1) { cp = "**"; } else { cp = ", "; } break; case FACTORIAL: cp = ""; i++; break; } len += fprintf(gfp, "%s", cp); break; } } for (j = cur_level - min1; j > 0; j--) { len += fprintf(gfp, ")"); } return len; } /* * Display an equation space in multi-line fraction format. * Use color if available. * * Return the total width of the output (that is, the required number of screen columns) * or zero on failure. */ int flist_sub(n) int n; /* equation space number */ { int sind; char buf[30]; int len, len2, len3, width; int pos; int high, low; int max_line, min_line; int max2_line, min2_line; if (n_lhs[n] <= 0) return 0; len = snprintf(buf, sizeof(buf), "#%d: ", n + 1); cur_line = 0; cur_pos = 0; sind = n_rhs[n]; len += flist(lhs[n], n_lhs[n], false, 0, &max_line, &min_line); if (n_rhs[n]) { len += strlen(EQUATE_STRING); make_smaller: len2 = flist(rhs[n], sind, false, 0, &high, &low); if (screen_columns && gfp == stdout && (len + len2) >= screen_columns && sind > 0) { for (sind--; sind > 0; sind--) { if (rhs[n][sind].level == 1 && rhs[n][sind].kind == OPERATOR) { switch (rhs[n][sind].token.operatr) { case PLUS: case MINUS: case MODULUS: goto make_smaller; } } } goto make_smaller; } if (high > max_line) max_line = high; if (low < min_line) min_line = low; len3 = flist(&rhs[n][sind], n_rhs[n] - sind, false, 0, &max2_line, &min2_line); } else { len2 = 0; len3 = 0; } width = max(len + len2, len3); if (screen_columns && gfp == stdout && width >= screen_columns) { /* output too wide to fit screen, output in single-line format */ return list1_sub(n, false); } fprintf(gfp, "\n"); for (cur_line = max_line; cur_line >= min_line; cur_line--) { pos = cur_pos = 0; if (cur_line == 0) { cur_pos += fprintf(gfp, "%s", buf); } pos += strlen(buf); pos += flist(lhs[n], n_lhs[n], true, pos, &high, &low); if (n_rhs[n]) { if (cur_line == 0) { cur_pos += fprintf(gfp, "%s", EQUATE_STRING); } pos += strlen(EQUATE_STRING); pos += flist(rhs[n], sind, true, pos, &high, &low); } fprintf(gfp, "\n"); } if (sind < n_rhs[n]) { fprintf(gfp, "\n"); for (cur_line = max2_line; cur_line >= min2_line; cur_line--) { cur_pos = 0; flist(&rhs[n][sind], n_rhs[n] - sind, true, 0, &high, &low); fprintf(gfp, "\n"); } } fprintf(gfp, "\n"); return width; } /* * Display a line of an expression if "out_flag" is true. * Use color if available. * * Return the width of the expression (that is, the required number of screen columns). */ static int flist(equation, n, out_flag, pos, highp, lowp) token_type *equation; /* expression pointer */ int n; /* length of expression */ int out_flag; int pos; int *highp, *lowp; { int i; i = flist_recurse(equation, n, out_flag, 0, pos, 1, highp, lowp); if (out_flag) { default_color(); } return i; } static int flist_recurse(equation, n, out_flag, line, pos, cur_level, highp, lowp) token_type *equation; int n; int out_flag; int line; int pos; int cur_level; int *highp, *lowp; { int i, j, k, i1; int l1, l2; int ii; int stop_at; int div_loc; int len_div; int level; int start_level; int oflag; int len = 0, len1, len2; int high, low; char buf[500]; char *cp; start_level = cur_level; *highp = line; *lowp = line; if (n <= 0) { return 0; } oflag = (out_flag && line == cur_line); if (oflag) { set_color(cur_level-1); for (; cur_pos < pos; cur_pos++) { fprintf(gfp, " "); } } ii = 0; check_again: stop_at = n; div_loc = -1; for (i = ii; i < n; i++) { if (equation[i].kind == OPERATOR && equation[i].token.operatr == DIVIDE) { level = equation[i].level; for (j = i - 2; j > 0; j -= 2) { if (equation[j].level < level) break; } j++; if (div_loc < 0) { div_loc = i; stop_at = j; } else { if (j < stop_at) { div_loc = i; stop_at = j; } else if (j == stop_at) { if (level < equation[div_loc].level) div_loc = i; } } } } for (i = ii; i < n; i++) { if (i == stop_at) { j = cur_level - equation[div_loc].level; k = abs(j) - 1; } else { j = cur_level - equation[i].level; k = abs(j); } for (i1 = 1; i1 <= k; i1++) { if (j > 0) { cur_level--; len++; if (oflag) { fprintf(gfp, ")"); set_color(cur_level-1); } } else { cur_level++; len++; if (oflag) { set_color(cur_level-1); fprintf(gfp, "("); } } } if (i == stop_at) { level = equation[div_loc].level; len1 = flist_recurse(&equation[stop_at], div_loc - stop_at, false, line + 1, pos + len, level, &high, &low); l1 = (2 * (line + 1)) - low; for (j = div_loc + 2; j < n; j += 2) { if (equation[j].level <= level) break; } len2 = flist_recurse(&equation[div_loc+1], j - (div_loc + 1), false, line - 1, pos + len, level, &high, &low); l2 = (2 * (line - 1)) - high; ii = j; len_div = max(len1, len2); j = 0; if (len1 < len_div) { j = (len_div - len1) / 2; } flist_recurse(&equation[stop_at], div_loc - stop_at, out_flag, l1, pos + len + j, level, &high, &low); if (high > *highp) *highp = high; if (low < *lowp) *lowp = low; if (oflag) { set_color(level-1); for (j = 0; j < len_div; j++) { if (html_flag) { fprintf(gfp, "—"); } else { fprintf(gfp, "-"); } } set_color(cur_level-1); } j = 0; if (len2 < len_div) { j = (len_div - len2) / 2; } flist_recurse(&equation[div_loc+1], ii - (div_loc + 1), out_flag, l2, pos + len + j, level, &high, &low); if (high > *highp) *highp = high; if (low < *lowp) *lowp = low; len += len_div; goto check_again; } switch (equation[i].kind) { case CONSTANT: if (equation[i].token.constant == 0.0) { equation[i].token.constant = 0.0; /* fix -0 */ } if (finance_option) { len += snprintf(buf, sizeof(buf), "%.*f", finance_option, equation[i].token.constant); } else { len += snprintf(buf, sizeof(buf), "%.*g", precision, equation[i].token.constant); } if (oflag) fprintf(gfp, "%s", buf); break; case VARIABLE: len += list_var(equation[i].token.variable, 0); if (oflag) fprintf(gfp, "%s", var_str); break; case OPERATOR: /* len must be incremented here by the number of columns the display of the operator takes */ switch (equation[i].token.operatr) { case PLUS: cp = " + "; len += 3; break; case MINUS: if (html_flag) { cp = " − "; } else { cp = " - "; } len += 3; break; case TIMES: if (html_flag) { cp = "·"; } else { cp = "*"; } len++; break; case DIVIDE: cp = "/"; len++; break; case MODULUS: cp = MODULUS_STRING; len += strlen(cp); break; case POWER: cp = "^"; len++; break; case FACTORIAL: cp = "!"; len++; i++; break; default: cp = _("(unknown operator)"); len += strlen(cp); break; } if (oflag) fprintf(gfp, "%s", cp); break; } } for (j = cur_level - start_level; j > 0;) { cur_level--; len++; j--; if (oflag) { fprintf(gfp, ")"); if (j > 0) set_color(cur_level-1); } } if (oflag) cur_pos += len; return len; }