1 /*
2 * Mathomatic commands that don't belong anywhere else.
3 *
4 * Copyright (C) 1987-2012 George Gesslein II.
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public
17 License along with this library; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19
20 The chief copyright holder can be contacted at gesslein@mathomatic.org, or
21 George Gesslein II, P.O. Box 224, Lansing, NY 14882-0224 USA.
22
23 */
24
25 #include "includes.h"
26
27 #define OPT_MIN_SIZE 7 /* Minimum size (in tokens) of repeated expressions to find in optimize command. */
28
29 enum spf_function {
30 SUM_COMMAND,
31 PRODUCT_COMMAND,
32 FOR_COMMAND
33 };
34
35 static int sum_product(char *cp, enum spf_function current_function);
36 static int complex_func(char *cp, int imag_flag);
37 static int elim_sub(int i, long v);
38
39 /* Global variables for the optimize command. */
40 static int opt_en[N_EQUATIONS+1];
41 static int last_temp_var = 0;
42
43 #if SHELL_OUT
44 /*
45 * The plot command.
46 *
47 * All command functions like this return true if successful, or false for failure.
48 */
49 int
plot_cmd(cp)50 plot_cmd(cp)
51 char *cp; /* the command-line argument */
52 {
53 #define APPEND(str) { if (strlen(str) + cl1_len < sizeof(cl1)) { strcpy(&cl1[cl1_len], str); cl1_len += strlen(str); } else warning(_("Expression too large to plot; omitted.")); }
54
55 int i1, i2;
56 int start, stop;
57 int first_time = true;
58 int cl1_len = 0, cl2_len = 0, len;
59 char *cp1, cl[16384+MAX_CMD_LEN], cl1[16384], cl2[MAX_CMD_LEN], *exp_str;
60 long v, vx; /* Mathomatic variables */
61 token_type *equation;
62 int *np;
63 int ev; /* exit value */
64 int cur_equation_flag = false;
65
66 if (security_level > 0) {
67 show_usage = false;
68 error(_("Command disabled by security level."));
69 return false;
70 }
71 if (!parse_var(&vx, "x")) {
72 return false;
73 }
74 do {
75 cp1 = cp;
76 if (!get_range(&cp, &start, &stop)) {
77 reset_error();
78 break;
79 }
80 if (cp != cp1 || first_time) {
81 if (cp == cp1)
82 cur_equation_flag = (!empty_equation_space(cur_equation));
83 for (i1 = start; i1 <= stop; i1++) {
84 if (i1 != cur_equation) {
85 cur_equation_flag = false;
86 }
87 if (n_lhs[i1] > 0) {
88 v = 0;
89 if (n_rhs[i1]) {
90 equation = rhs[i1];
91 np = &n_rhs[i1];
92 } else {
93 equation = lhs[i1];
94 np = &n_lhs[i1];
95 }
96 if (!no_vars(equation, *np, &v) && v) {
97 if (strcmp(var_name(v), "x") && strcmp(var_name(v), "t")) {
98 list_var(v, 0);
99 fprintf(gfp, _("#%d: Renaming variable %s to x for gnuplot.\n"), i1 + 1, var_str);
100 rename_var_in_es(i1, v, vx);
101 }
102 }
103 if (n_rhs[i1] && !solved_equation(i1)) {
104 warning(_("Not a normally solved equation, plotting the RHS only."));
105 }
106 for (i2 = 0; i2 < *np; i2 += 2) {
107 if (equation[i2].kind == VARIABLE && (equation[i2].token.variable & VAR_MASK) == SIGN) {
108 warning(_("Plot expression contains sign variables; try \"simplify sign\" before plotting."));
109 break;
110 }
111 }
112 exp_str = list_expression(equation, *np, 3);
113 if (exp_str == NULL)
114 return false;
115 if (cl1_len) {
116 APPEND(", ");
117 }
118 APPEND(exp_str);
119 free(exp_str);
120 }
121 }
122 }
123 first_time = false;
124 } while (*cp && cp != cp1);
125 cl1[cl1_len] = '\0';
126 if (cl1_len == 0 && *cp == '\0') {
127 error(_("No plot expression specified."));
128 return false;
129 }
130 for (cl2_len = 0, i2 = 0; cp[i2]; i2++) {
131 if ((cl2_len + 2) >= sizeof(cl2)) {
132 error(_("Command-line too long."));
133 return false;
134 }
135 switch (cp[i2]) {
136 case '^':
137 cl2[cl2_len] = '*';
138 cl2_len++;
139 cl2[cl2_len] = '*';
140 cl2_len++;
141 break;
142 default:
143 cl2[cl2_len] = cp[i2];
144 cl2_len++;
145 break;
146 }
147 }
148 cl2[cl2_len] = '\0';
149 if (cl1_len && cl2_len && cl2[cl2_len-1] != ',') {
150 if (cur_equation_flag) {
151 snprintf(prompt_str, sizeof(prompt_str), _("Do you want to plot the current equation, too (y/n)? "));
152 if (!get_yes_no()) {
153 cl1_len = 0;
154 cl1[cl1_len] = '\0';
155 }
156 }
157 if (cl1_len) {
158 snprintf(prompt_str, sizeof(prompt_str), _("Does the plot command-line consist of any expressions (y/n)? "));
159 if (get_yes_no()) {
160 fprintf(gfp, _("Appending a comma to the command-line.\n"));
161 if ((cl2_len + 2) >= sizeof(cl2)) {
162 error(_("Command-line too long."));
163 return false;
164 }
165 cl2[cl2_len++] = ',';
166 cl2[cl2_len] = '\0';
167 }
168 }
169 }
170 if (strchr(cl2, 'y') || strchr(cl1, 'y')) {
171 fprintf(gfp, _("Performing 3D surface plot...\n"));
172 #if MINGW
173 len = snprintf(cl, sizeof(cl), "gnuplot -persist -e \"%s; splot %s %s\"", plot_prefix, cl2, cl1);
174 #else
175 len = snprintf(cl, sizeof(cl), "echo '%s; splot %s %s'|gnuplot -persist", plot_prefix, cl2, cl1);
176 #endif
177 } else {
178 fprintf(gfp, _("Performing 2D plot...\n"));
179 #if MINGW
180 len = snprintf(cl, sizeof(cl), "gnuplot -persist -e \"%s; plot %s %s\"", plot_prefix, cl2, cl1);
181 #else
182 len = snprintf(cl, sizeof(cl), "echo '%s; plot %s %s'|gnuplot -persist", plot_prefix, cl2, cl1);
183 #endif
184 }
185 if (len >= sizeof(cl)) {
186 error(_("gnuplot command-line too long."));
187 return false;
188 }
189 ev = shell_out(cl);
190 if (ev) {
191 error(_("Possible error running gnuplot."));
192 printf(_("Decimal exit value = %d\n"), ev);
193 if (ev == 256 || ev == 1) {
194 printf(_("Try separating each expression with a comma.\n"));
195 }
196 printf(_("Shell command-line = %s\n"), cl);
197 }
198 return true;
199 }
200 #endif
201
202 /*
203 * The version command.
204 *
205 * All commands return true if successful.
206 */
207 int
version_cmd(cp)208 version_cmd(cp)
209 char *cp; /* the command-line argument */
210 {
211 int rv = true; /* return value */
212 int status_flag = false;
213
214 if (strncasecmp(cp, "status", 4) == 0) {
215 status_flag = true;
216 cp = skip_param(cp);
217 }
218 if (extra_characters(cp)) /* Make sure nothing else is on the command-line. */
219 return false;
220 #if LIBRARY
221 free_result_str();
222 result_str = strdup(VERSION);
223 #endif
224 if (status_flag) {
225 rv = version_report();
226 } else {
227 #if !SILENT || !LIBRARY
228 fprintf(gfp, _("Mathomatic version %s\n"), VERSION);
229 #endif
230 }
231
232 if (status_flag) {
233 debug_string(0, "\nMathomatic is GNU LGPL version 2.1 licensed software,\n"
234 "meaning it is free software that comes with no warranty.\n"
235 "Type \"help license\" for the copyright and license.");
236
237 EP(_("\nFor all new stuff, visit the Mathomatic website: www.mathomatic.org"));
238 }
239 #if LIBRARY
240 return(rv && result_str != NULL);
241 #else
242 return rv;
243 #endif
244 }
245
246 /*
247 * Return the maximum amount of memory (in bytes) that this program will use.
248 */
249 long
max_memory_usage(void)250 max_memory_usage(void)
251 {
252 return((long) (N_EQUATIONS + 3L) * (long) n_tokens * sizeof(token_type) * 2L);
253 }
254
255 /*
256 * Try the function getrusage(2).
257 *
258 * Return true if successful.
259 */
260 int
show_status(ofp)261 show_status(ofp)
262 FILE *ofp;
263 {
264 #if SHOW_RESOURCES
265 struct rusage usage_local;
266
267 if (getrusage(RUSAGE_SELF, &usage_local) == 0) {
268 fprintf(ofp, _("Total CPU usage, user time: %g seconds, system time: %g seconds.\n"),
269 (double) usage_local.ru_utime.tv_sec + ((double) usage_local.ru_utime.tv_usec / 1000000.0),
270 (double) usage_local.ru_stime.tv_sec + ((double) usage_local.ru_stime.tv_usec / 1000000.0));
271 if (usage_local.ru_ixrss == 0 && usage_local.ru_idrss == 0 && usage_local.ru_isrss == 0) {
272 if (usage_local.ru_maxrss)
273 fprintf(ofp, _("Total RSS size: %ld kilobytes.\n"), usage_local.ru_maxrss);
274 } else {
275 fprintf(ofp, _("Total RSS size: %ld kbytes; shared text memory size: %ld kbytes*ticks;\n"), usage_local.ru_maxrss, usage_local.ru_ixrss);
276 fprintf(ofp, _("Unshared data size: %ld kbytes*ticks; unshared stack size: %ld kbytes*ticks.\n"), usage_local.ru_idrss, usage_local.ru_isrss);
277 fprintf(ofp, _("Number of times Mathomatic was swapped out: %ld; signals received: %ld.\n"), usage_local.ru_nswap, usage_local.ru_nsignals);
278 }
279 return true;
280 }
281 #endif
282 return false;
283 }
284
285 /*
286 * Display version and status info.
287 */
288 int
version_report(void)289 version_report(void)
290 {
291 long l;
292
293 fprintf(gfp, _("Mathomatic version %s\n"), VERSION);
294 fprintf(gfp, _("The last main prompt return value is %d (meaning "), previous_return_value);
295 switch (previous_return_value) {
296 case 0:
297 fprintf(gfp, _("failure).\n"));
298 break;
299 default:
300 fprintf(gfp, _("success).\n"));
301 break;
302 }
303 show_status(gfp);
304 fprintf(gfp, _("\nCompile-time defines used: "));
305 #if linux
306 fprintf(gfp, "linux ");
307 #endif
308 #if sun
309 fprintf(gfp, "sun ");
310 #endif
311 #if UNIX
312 fprintf(gfp, "UNIX ");
313 #endif
314 #if CYGWIN
315 fprintf(gfp, "CYGWIN ");
316 #endif
317 #if MINGW
318 fprintf(gfp, "MINGW ");
319 #endif
320 #if HANDHELD
321 fprintf(gfp, "HANDHELD ");
322 #endif
323 #if EDITLINE
324 fprintf(gfp, "EDITLINE ");
325 #endif
326 #if READLINE
327 fprintf(gfp, "READLINE ");
328 #endif
329 #if SILENT
330 fprintf(gfp, "SILENT ");
331 #endif
332 #if LIBRARY
333 fprintf(gfp, "LIBRARY ");
334 #endif
335 #if SECURE
336 fprintf(gfp, "SECURE ");
337 #endif
338 #if TIMEOUT_SECONDS
339 fprintf(gfp, "TIMEOUT_SECONDS=%d ", TIMEOUT_SECONDS);
340 #endif
341 #if I18N
342 fprintf(gfp, "I18N ");
343 #endif
344 #if NO_COLOR
345 fprintf(gfp, "NO_COLOR ");
346 #endif
347 #if BOLD_COLOR
348 fprintf(gfp, "BOLD_COLOR ");
349 #endif
350 #if WIN32_CONSOLE_COLORS
351 fprintf(gfp, "WIN32_CONSOLE_COLORS ");
352 #endif
353 #if NOGAMMA
354 fprintf(gfp, "NOGAMMA ");
355 #endif
356 #if USE_TGAMMA
357 fprintf(gfp, "USE_TGAMMA ");
358 #endif
359 #if DEBUG
360 fprintf(gfp, "DEBUG ");
361 #endif
362 #if VALGRIND
363 fprintf(gfp, "VALGRIND ");
364 #endif
365 #if SHOW_RESOURCES
366 fprintf(gfp, "SHOW_RESOURCES ");
367 #endif
368
369 fprintf(gfp, "\nsizeof(int) = %u bytes, sizeof(long) = %u bytes.\n", (unsigned) sizeof(int), (unsigned) sizeof(long));
370 fprintf(gfp, "sizeof(double) = %u bytes, maximum double precision = %d decimal digits.\n", (unsigned) sizeof(double), DBL_DIG);
371 #ifdef __VERSION__
372 #ifdef __GNUC__
373 fprintf(gfp, "GNU ");
374 #endif
375 fprintf(gfp, _("C Compiler version: %s\n"), __VERSION__);
376 #endif
377
378 fprintf(gfp, _("\n%d equation spaces currently allocated.\n"), n_equations);
379 fprintf(gfp, _("The current expression array size is %d tokens,\n"), n_tokens);
380 l = max_memory_usage() / 1000L;
381 if (l >= 10000L) {
382 fprintf(gfp, _("making the maximum memory usage approximately %ld megabytes.\n"), l / 1000L);
383 } else {
384 fprintf(gfp, _("making the maximum memory usage approximately %ld kilobytes.\n"), l);
385 }
386 #if SECURE
387 fprintf(gfp, _("Compiled for maximum security.\n"));
388 #else
389 fprintf(gfp, _("The current security level is %d"), security_level);
390 switch (security_level) {
391 case -1:
392 fprintf(gfp, _(", meaning you are running m4 Mathomatic.\n"));
393 break;
394 case 0:
395 fprintf(gfp, _(", no security, meaning users are unrestricted.\n"));
396 break;
397 case 1:
398 case 2:
399 fprintf(gfp, _(", some security.\n"));
400 break;
401 case 3:
402 fprintf(gfp, _(", high security.\n"));
403 break;
404 case 4:
405 fprintf(gfp, _(", maximum security.\n"));
406 break;
407 default:
408 fprintf(gfp, _(", unknown meaning.\n"));
409 break;
410 }
411 #endif
412 #if READLINE || EDITLINE
413 #if READLINE
414 fprintf(gfp, _("\nreadline is compiled in and "));
415 #else
416 fprintf(gfp, _("\neditline is compiled in and "));
417 #endif
418 if (readline_enabled) {
419 fprintf(gfp, _("activated.\n"));
420 } else {
421 fprintf(gfp, _("deactivated.\n"));
422 }
423 #elif !LIBRARY && !HANDHELD
424 #if MINGW
425 SP(_("\nreadline is not compiled in, however some of its functionality"));
426 SP(_("already exists in the Windows console for any"));
427 EP(_("Windows console program (like Mathomatic)."));
428 #else
429 SP(_("\nreadline is not compiled in."));
430 SP(_("Please notify the package maintainer that readline"));
431 EP(_("should be compiled into Mathomatic, with \"make READLINE=1\"."));
432 #endif
433 #endif
434 return true;
435 }
436
437 /*
438 * The solve command.
439 *
440 * Return 0 on solve failure for any equation which a solve was requested, or something was not verifiable,
441 * with the "solve verifiable" option.
442 * Return 1 on total success (all requested solves completed successfully),
443 * or 2 if partial success (all solved, but some solutions didn't verify when doing "solve verify",
444 * or the result contains infinity or NaN).
445 */
446 int
solve_cmd(cp)447 solve_cmd(cp)
448 char *cp;
449 {
450 int i, j, k;
451 int start, stop;
452 char buf[MAX_CMD_LEN];
453 int diff_sign;
454 int verify_flag = 0, plural_flag, once_through, contains_infinity, did_something = false, last_solve_successful = false;
455 char *cp1, *cp_start;
456 token_type want;
457 int rv = 1;
458 long pre_v; /* Mathomatic variable */
459
460 cp_start = cp;
461 if (strcmp_tospace(cp, "verify") == 0) {
462 verify_flag = 1;
463 cp = skip_param(cp);
464 } else if (strcmp_tospace(cp, "verifiable") == 0) {
465 verify_flag = 2;
466 cp = skip_param(cp);
467 }
468 if (!get_range(&cp, &start, &stop)) {
469 warning(_("No equations to solve."));
470 return false;
471 }
472 i = next_espace();
473 repeat_flag = true;
474 if (strcmp_tospace(cp, "verify") == 0) {
475 verify_flag = 1;
476 cp = skip_param(cp);
477 } else if (strcmp_tospace(cp, "verifiable") == 0) {
478 verify_flag = 2;
479 cp = skip_param(cp);
480 }
481 if (strcmp_tospace(cp, "for") == 0) {
482 cp1 = skip_param(cp);
483 if (*cp1) {
484 cp = cp1;
485 }
486 }
487 if (*cp == '\0') {
488 my_strlcpy(prompt_str, _("Enter variable to solve for: "), sizeof(prompt_str));
489 if ((cp = get_string(buf, sizeof(buf))) == NULL) {
490 return false;
491 }
492 cp_start = cp;
493 }
494 input_column += (cp - cp_start);
495 if ((cp = parse_equation(i, cp)) == NULL) {
496 return false;
497 }
498 if (verify_flag) {
499 if (n_lhs[i] != 1 || n_rhs[i] != 0 || (lhs[i][0].kind != VARIABLE
500 && (lhs[i][0].kind != CONSTANT || lhs[i][0].token.constant != 0.0))) {
501 error(_("Can only verify for a single solve variable or identities after solving for 0."));
502 goto fail;
503 }
504 want = lhs[i][0];
505 }
506 show_usage = false;
507 for (k = start; k <= stop; k++) {
508 if (k == i || n_lhs[k] <= 0 || n_rhs[k] <= 0) {
509 continue;
510 }
511 last_solve_successful = false;
512 cur_equation = k;
513
514 did_something = true;
515 if (verify_flag) {
516 pre_v = 0;
517 fprintf(gfp, _("Solving equation #%d for "), cur_equation + 1);
518 list_proc(&want, 1, false);
519 if (verify_flag == 2) {
520 fprintf(gfp, " with required ");
521 } else {
522 fprintf(gfp, " with ");
523 }
524 if (want.kind == VARIABLE) {
525 fprintf(gfp, "verification...\n");
526 if (solved_equation(cur_equation)) {
527 pre_v = lhs[cur_equation][0].token.variable;
528 }
529 } else {
530 fprintf(gfp, "identity verification...\n");
531 }
532 copy_espace(cur_equation, i);
533 if (solve_sub(&want, 1, lhs[cur_equation], &n_lhs[cur_equation], rhs[cur_equation], &n_rhs[cur_equation]) > 0) {
534 simpa_repeat(cur_equation, true, false); /* Solve result should be quick simplified. */
535 last_solve_successful = true;
536 debug_string(0, _("Solve and \"repeat simplify quick\" successful:"));
537 if (!return_result(cur_equation)) { /* Display the simplified solve result. */
538 goto fail;
539 }
540 if (want.kind == VARIABLE) {
541 if (!solved_equation(cur_equation) || lhs[cur_equation][0].token.variable != want.token.variable) {
542 error(_("Result not a properly solved equation, so cannot verify."));
543 continue;
544 }
545 if (pre_v && pre_v == want.token.variable) {
546 warning(_("Equation was already solved, so no need to verify solutions."));
547 continue;
548 }
549 } else {
550 copy_espace(cur_equation, i);
551 }
552 plural_flag = false;
553 for (j = 0; j < n_rhs[cur_equation]; j += 2) {
554 if (rhs[cur_equation][j].kind == VARIABLE && (rhs[cur_equation][j].token.variable & VAR_MASK) == SIGN) {
555 plural_flag = true;
556 break;
557 }
558 }
559 if (want.kind == VARIABLE) {
560 subst_var_with_exp(lhs[i], &n_lhs[i], rhs[cur_equation], n_rhs[cur_equation], want.token.variable);
561 subst_var_with_exp(rhs[i], &n_rhs[i], rhs[cur_equation], n_rhs[cur_equation], want.token.variable);
562 }
563 once_through = false;
564 calc_simp(lhs[i], &n_lhs[i]);
565 calc_simp(rhs[i], &n_rhs[i]);
566 check_result:
567 contains_infinity = (exp_contains_infinity(lhs[i], n_lhs[i])
568 || exp_contains_infinity(rhs[i], n_rhs[i]));
569 if (se_compare(lhs[i], n_lhs[i], rhs[i], n_rhs[i], &diff_sign) && (want.kind != VARIABLE || !diff_sign)) {
570 if (want.kind != VARIABLE) {
571 fprintf(gfp, _("This equation is an identity.\n"));
572 } else if (plural_flag)
573 fprintf(gfp, _("All solutions verified.\n"));
574 else
575 fprintf(gfp, _("Solution verified.\n"));
576 if (contains_infinity) {
577 error(_("Solution might be incorrect because it contains infinity or NaN."));
578 if (rv)
579 rv = 2;
580 }
581 } else {
582 if (!contains_infinity && once_through < 2) {
583 symb_flag = symblify;
584 simpa_repeat(i, once_through ? false : true, once_through ? true : false); /* Simplify to compare equation sides. */
585 symb_flag = false;
586 once_through++;
587 goto check_result;
588 }
589 if (contains_infinity) {
590 error(_("Solution might be incorrect because it contains infinity or NaN."));
591 } else {
592 if (want.kind != VARIABLE) {
593 error(_("This equation is NOT an identity."));
594 } else if (plural_flag)
595 error(_("Unable to verify all solutions."));
596 else
597 error(_("Unable to verify solution."));
598 }
599 if (verify_flag == 2)
600 rv = 0;
601 else if (rv)
602 rv = 2;
603 }
604 } else {
605 printf(_("Solve failed for equation space #%d.\n"), cur_equation + 1);
606 rv = 0;
607 }
608 } else {
609 if (solve_espace(i, cur_equation)) {
610 last_solve_successful = true;
611 if (!return_result(cur_equation)) {
612 goto fail;
613 }
614 } else {
615 rv = 0;
616 }
617 }
618 }
619 if (did_something) {
620 if (last_solve_successful && verify_flag) {
621 debug_string(1, "Verification identity:");
622 list_esdebug(1, i);
623 }
624 } else {
625 printf(_("No work done.\n"));
626 }
627 n_lhs[i] = 0;
628 n_rhs[i] = 0;
629 return rv;
630 fail:
631 n_lhs[i] = 0;
632 n_rhs[i] = 0;
633 return 0;
634 }
635
636 /*
637 * The sum command.
638 */
639 int
sum_cmd(cp)640 sum_cmd(cp)
641 char *cp;
642 {
643 return sum_product(cp, SUM_COMMAND);
644 }
645
646 /*
647 * The product command.
648 */
649 int
product_cmd(cp)650 product_cmd(cp)
651 char *cp;
652 {
653 return sum_product(cp, PRODUCT_COMMAND);
654 }
655
656 /*
657 * The for command.
658 */
659 int
for_cmd(cp)660 for_cmd(cp)
661 char *cp;
662 {
663 return sum_product(cp, FOR_COMMAND);
664 }
665
666 /*
667 * Common function for the sum and product commands.
668 */
669 static int
sum_product(cp,current_function)670 sum_product(cp, current_function)
671 char *cp; /* the command-line */
672 enum spf_function current_function;
673 {
674 int i;
675 long v = 0; /* Mathomatic variable */
676 double start, end, step = 1.0;
677 int result_equation;
678 int n, ns;
679 token_type *dest, *source;
680 int count_down; /* if true, count down, otherwise count up */
681 char *cp1, buf[MAX_CMD_LEN];
682
683 if (current_not_defined()) {
684 return false;
685 }
686 result_equation = next_espace();
687 if (n_rhs[cur_equation]) {
688 ns = n_rhs[cur_equation];
689 source = rhs[cur_equation];
690 dest = rhs[result_equation];
691 } else {
692 ns = n_lhs[cur_equation];
693 source = lhs[cur_equation];
694 dest = lhs[result_equation];
695 }
696 if (*cp) {
697 cp = parse_var2(&v, cp);
698 if (cp == NULL) {
699 return false;
700 }
701 }
702 if (no_vars(source, ns, &v)) {
703 error(_("Current expression contains no variables."));
704 return false;
705 }
706 if (v == 0) {
707 if (!prompt_var(&v)) {
708 return false;
709 }
710 }
711 if (!found_var(source, ns, v)) {
712 error(_("Specified variable not found."));
713 return false;
714 }
715 if (*cp) {
716 if (*cp == '=') {
717 cp++;
718 }
719 cp1 = cp;
720 } else {
721 list_var(v, 0);
722 snprintf(prompt_str, sizeof(prompt_str), "%s = ", var_str);
723 if ((cp1 = get_string(buf, sizeof(buf))) == NULL)
724 return false;
725 }
726 start = strtod(cp1, &cp);
727 if (cp1 == cp || !isfinite(start)) {
728 error(_("Number expected."));
729 return false;
730 }
731 if (fabs(start) >= MAX_K_INTEGER) {
732 error(_("Number too large."));
733 return false;
734 }
735 cp = skip_comma_space(cp);
736 if (strcmp_tospace(cp, "to") == 0) {
737 cp = skip_param(cp);
738 }
739 if (*cp) {
740 cp1 = cp;
741 } else {
742 my_strlcpy(prompt_str, _("To: "), sizeof(prompt_str));
743 if ((cp1 = get_string(buf, sizeof(buf))) == NULL)
744 return false;
745 }
746 end = strtod(cp1, &cp);
747 if (cp1 == cp || !isfinite(end)) {
748 error(_("Number expected."));
749 return false;
750 }
751 if (fabs(end) >= MAX_K_INTEGER) {
752 error(_("Number too large."));
753 return false;
754 }
755 cp = skip_comma_space(cp);
756 if (strcmp_tospace(cp, "step") == 0) {
757 cp = skip_param(cp);
758 }
759 if (*cp) {
760 cp1 = cp;
761 step = fabs(strtod(cp1, &cp));
762 if (cp1 == cp || !isfinite(step) || step <= 0.0 || step >= MAX_K_INTEGER) {
763 error(_("Invalid step."));
764 return false;
765 }
766 }
767 if (extra_characters(cp))
768 return false;
769 count_down = (end < start);
770 if (fmod(fabs(start - end) / step, 1.0) != 0.0) {
771 warning(_("End value not reached."));
772 }
773 if (current_function == PRODUCT_COMMAND) {
774 dest[0] = one_token;
775 } else {
776 dest[0] = zero_token;
777 }
778 n = 1;
779 for (; count_down ? (start >= end) : (start <= end); count_down ? (start -= step) : (start += step)) {
780 if (n + 1 + ns > n_tokens) {
781 error_huge();
782 }
783 blt(tlhs, source, ns * sizeof(token_type));
784 n_tlhs = ns;
785 for (i = 0; i < n_tlhs; i += 2) {
786 if (tlhs[i].kind == VARIABLE && tlhs[i].token.variable == v) {
787 tlhs[i].kind = CONSTANT;
788 tlhs[i].token.constant = start;
789 }
790 }
791 if (current_function != FOR_COMMAND) {
792 for (i = 0; i < n_tlhs; i++) {
793 tlhs[i].level++;
794 }
795 for (i = 0; i < n; i++) {
796 dest[i].level++;
797 }
798 dest[n].kind = OPERATOR;
799 dest[n].level = 1;
800 }
801 switch (current_function) {
802 case PRODUCT_COMMAND:
803 dest[n].token.operatr = TIMES;
804 n++;
805 break;
806 case SUM_COMMAND:
807 dest[n].token.operatr = PLUS;
808 n++;
809 break;
810 case FOR_COMMAND:
811 n = 0;
812 break;
813 }
814 blt(&dest[n], tlhs, n_tlhs * sizeof(token_type));
815 n += n_tlhs;
816 calc_simp(dest, &n);
817 if (current_function == FOR_COMMAND) {
818 list_var(v, 0);
819 fprintf(gfp, "%s = %.*g: ", var_str, precision, start);
820 list_factor(dest, &n, false);
821 fprintf(gfp, "\n");
822 } else {
823 side_debug(1, dest, n);
824 }
825 }
826 if (current_function == FOR_COMMAND) {
827 return true;
828 } else {
829 if (n_rhs[cur_equation]) {
830 n_rhs[result_equation] = n;
831 blt(lhs[result_equation], lhs[cur_equation], n_lhs[cur_equation] * sizeof(token_type));
832 n_lhs[result_equation] = n_lhs[cur_equation];
833 } else {
834 n_lhs[result_equation] = n;
835 }
836 return return_result(result_equation);
837 }
838 }
839
840 /*
841 * This function is for the "optimize" command.
842 * It finds and substitutes all occurrences of the RHS of "en" in "equation".
843 * It should be called repeatedly until it returns false.
844 */
845 static int
find_more(equation,np,en)846 find_more(equation, np, en)
847 token_type *equation; /* expression to search */
848 int *np; /* pointer to length of expression */
849 int en; /* equation space number */
850 {
851 int i, j, k;
852 int level;
853 int diff_sign;
854 int found_se; /* found sub-expression flag */
855
856 if (*np <= 0 || !solved_equation(en)) {
857 return false;
858 }
859 for (level = 1, found_se = true; found_se; level++) {
860 for (i = 1, found_se = false; i < *np; i = j + 2) {
861 for (j = i; j < *np && equation[j].level >= level; j += 2)
862 ;
863 if (j == i) {
864 continue;
865 }
866 found_se = true;
867 k = i - 1;
868 if (se_compare(&equation[k], j - k, rhs[en], n_rhs[en], &diff_sign)) {
869 if (diff_sign) {
870 blt(&equation[i+2], &equation[j], (*np - j) * sizeof(token_type));
871 *np -= (j - (i + 2));
872 level++;
873 equation[k].level = level;
874 equation[k].kind = CONSTANT;
875 equation[k].token.constant = -1.0;
876 k++;
877 equation[k].level = level;
878 equation[k].kind = OPERATOR;
879 equation[k].token.operatr = TIMES;
880 k++;
881 } else {
882 blt(&equation[i], &equation[j], (*np - j) * sizeof(token_type));
883 *np -= (j - i);
884 }
885 equation[k].level = level;
886 equation[k].kind = VARIABLE;
887 equation[k].token.variable = lhs[en][0].token.variable;
888 return true;
889 }
890 }
891 }
892 return false;
893 }
894
895 /*
896 * This function is for the "optimize" command.
897 * It finds and replaces all repeated expressions in "equation" with temporary variables.
898 * It also creates a new equation for each temporary variable.
899 * It should be called repeatedly until it returns false.
900 */
901 static int
opt_es(equation,np)902 opt_es(equation, np)
903 token_type *equation;
904 int *np;
905 {
906 int i, j, k, i1, i2, jj1, k1;
907 int level, level1;
908 int diff_sign;
909 int found_se, found_se1; /* found sub-expression flags */
910 long v; /* Mathomatic variable */
911 char var_name_buf[MAX_VAR_LEN];
912
913 if (*np <= 0) {
914 return false;
915 }
916 for (level = 1, found_se = true; found_se; level++) {
917 for (i = 1, found_se = false; i < *np; i = j + 2) {
918 for (j = i; j < *np && equation[j].level > level; j += 2)
919 ;
920 if (j == i) {
921 continue;
922 }
923 found_se = true;
924 k = i - 1;
925 if ((j - k) < OPT_MIN_SIZE) {
926 continue;
927 }
928 found_se1 = true;
929 for (level1 = 1; found_se1; level1++) {
930 for (i1 = 1, found_se1 = false; i1 < *np; i1 = jj1 + 2) {
931 for (jj1 = i1; jj1 < *np && equation[jj1].level > level1; jj1 += 2) {
932 }
933 if (jj1 == i1) {
934 continue;
935 }
936 found_se1 = true;
937 if (i1 <= j)
938 continue;
939 k1 = i1 - 1;
940 if ((jj1 - k1) >= OPT_MIN_SIZE
941 && se_compare(&equation[k], j - k, &equation[k1], jj1 - k1, &diff_sign)) {
942 snprintf(var_name_buf, sizeof(var_name_buf), "temp%.0d", last_temp_var);
943 if (parse_var(&v, var_name_buf) == NULL) {
944 return false; /* can't create "temp" variable */
945 }
946 last_temp_var++;
947 if (last_temp_var < 0) {
948 last_temp_var = 0;
949 }
950 i2 = next_espace();
951 lhs[i2][0].level = 1;
952 lhs[i2][0].kind = VARIABLE;
953 lhs[i2][0].token.variable = v;
954 n_lhs[i2] = 1;
955 blt(rhs[i2], &equation[k], (j - k) * sizeof(token_type));
956 n_rhs[i2] = j - k;
957 if (diff_sign) {
958 blt(&equation[i1+2], &equation[jj1], (*np - jj1) * sizeof(token_type));
959 *np -= (jj1 - (i1 + 2));
960 level1++;
961 equation[k1].level = level1;
962 equation[k1].kind = CONSTANT;
963 equation[k1].token.constant = -1.0;
964 k1++;
965 equation[k1].level = level1;
966 equation[k1].kind = OPERATOR;
967 equation[k1].token.operatr = TIMES;
968 k1++;
969 } else {
970 blt(&equation[i1], &equation[jj1], (*np - jj1) * sizeof(token_type));
971 *np -= (jj1 - i1);
972 }
973 equation[k1].level = level1;
974 equation[k1].kind = VARIABLE;
975 equation[k1].token.variable = v;
976 blt(&equation[i], &equation[j], (*np - j) * sizeof(token_type));
977 *np -= j - i;
978 equation[k].level = level;
979 equation[k].kind = VARIABLE;
980 equation[k].token.variable = v;
981 while (find_more(equation, np, i2))
982 ;
983 simp_loop(rhs[i2], &n_rhs[i2]);
984 simp_loop(equation, np);
985 for (i = 0;; i++) {
986 if (i >= N_EQUATIONS) {
987 error_bug("Too many optimized equations.");
988 }
989 if (opt_en[i] < 0)
990 break;
991 }
992 opt_en[i] = i2;
993 opt_en[i+1] = -1;
994 return true;
995 }
996 }
997 }
998 }
999 }
1000 return false;
1001 }
1002
1003 /*
1004 * The optimize command.
1005 */
1006 int
optimize_cmd(cp)1007 optimize_cmd(cp)
1008 char *cp;
1009 {
1010 int i, j, k, i1;
1011 int start, stop;
1012 int rv = false, flag, skip_flag;
1013 int start_en;
1014 int diff_sign;
1015
1016 if (!get_range_eol(&cp, &start, &stop)) {
1017 return false;
1018 }
1019 opt_en[0] = -1;
1020 start_en = 0;
1021 for (j = i = start; i <= stop; i++) {
1022 if (n_lhs[i]) {
1023 j = i;
1024 simp_equation(i);
1025 }
1026 }
1027 stop = j;
1028 do {
1029 flag = false;
1030 for (i = start; i <= stop; i++) {
1031 for (j = start; j <= stop; j++) {
1032 if (i != j) {
1033 while (find_more(rhs[i], &n_rhs[i], j)) {
1034 flag = true;
1035 rv = true;
1036 }
1037 }
1038 }
1039 }
1040 } while (flag);
1041 for (i = start; i <= stop; i++) {
1042 if (n_lhs[i] == 0)
1043 continue;
1044 do {
1045 flag = false;
1046 simp_equation(i);
1047 for (j = 0; opt_en[j] >= 0; j++) {
1048 if (i != opt_en[j]) {
1049 simp_equation(opt_en[j]);
1050 while (find_more(lhs[i], &n_lhs[i], opt_en[j]))
1051 flag = true;
1052 while (find_more(rhs[i], &n_rhs[i], opt_en[j]))
1053 flag = true;
1054 }
1055 }
1056 } while (flag);
1057 while (opt_es(lhs[i], &n_lhs[i])) {
1058 rv = true;
1059 }
1060 while (opt_es(rhs[i], &n_rhs[i])) {
1061 rv = true;
1062 }
1063 if (rv) {
1064 for (i1 = start_en; opt_en[i1] >= 0; i1++) {
1065 for (j = start_en; opt_en[j] >= 0; j++) {
1066 for (k = j + 1; opt_en[k] >= 0; k++) {
1067 while (find_more(rhs[opt_en[k]], &n_rhs[opt_en[k]], opt_en[j]))
1068 ;
1069 while (find_more(rhs[opt_en[j]], &n_rhs[opt_en[j]], opt_en[k]))
1070 ;
1071 }
1072 }
1073 while (opt_es(rhs[opt_en[i1]], &n_rhs[opt_en[i1]]))
1074 ;
1075 }
1076 /* Remove equation if identity, otherwise display. */
1077 for (; opt_en[start_en] >= 0; start_en++) {
1078 k = opt_en[start_en];
1079 if (se_compare(lhs[k], n_lhs[k], rhs[k], n_rhs[k], &diff_sign) && !diff_sign) {
1080 n_lhs[k] = 0;
1081 n_rhs[k] = 0;
1082 } else
1083 list_sub(k);
1084 }
1085 if (se_compare(lhs[i], n_lhs[i], rhs[i], n_rhs[i], &diff_sign) && !diff_sign) {
1086 n_lhs[i] = 0;
1087 n_rhs[i] = 0;
1088 }
1089 }
1090 }
1091 if (rv) {
1092 for (i = start; i <= stop; i++) {
1093 if (n_lhs[i] == 0)
1094 continue;
1095 skip_flag = false;
1096 do {
1097 flag = false;
1098 simp_equation(i);
1099 for (j = 0; opt_en[j] >= 0; j++) {
1100 if (i != opt_en[j]) {
1101 simp_equation(opt_en[j]);
1102 while (find_more(lhs[i], &n_lhs[i], opt_en[j]))
1103 flag = true;
1104 while (find_more(rhs[i], &n_rhs[i], opt_en[j]))
1105 flag = true;
1106 } else
1107 skip_flag = true;
1108 }
1109 } while (flag);
1110 if (!skip_flag)
1111 list_sub(i);
1112 }
1113 }
1114 if (!rv) {
1115 error(_("Unable to find any repeated expressions."));
1116 }
1117 return rv;
1118 }
1119
1120 #if READLINE || EDITLINE
1121 /*
1122 * The push command.
1123 */
1124 int
push_cmd(cp)1125 push_cmd(cp)
1126 char *cp;
1127 {
1128 int start, stop;
1129 int k;
1130 char *cp1, *cp_start;
1131
1132 cp_start = cp;
1133 if (!readline_enabled) {
1134 error(_("Readline is currently turned off."));
1135 return false;
1136 }
1137 do {
1138 cp1 = cp;
1139 if (!get_range(&cp, &start, &stop)) {
1140 if (*cp_start) {
1141 reset_error();
1142 }
1143 goto push_text;
1144 }
1145 if (*cp && cp == cp1) {
1146 goto push_text;
1147 }
1148 for (k = start; k <= stop; k++) {
1149 if (n_lhs[k]) {
1150 if (push_en(k)) {
1151 debug_string(0, _("Expression pushed. Press the UP key to access."));
1152 } else {
1153 error(_("Expression push failed."));
1154 return false;
1155 }
1156 }
1157 }
1158 } while (*cp);
1159 return true;
1160
1161 push_text:
1162 if (*cp_start) {
1163 add_history(cp_start);
1164 last_history_string = NULL;
1165 debug_string(0, _("Text string pushed. Press the UP key to access."));
1166 return true;
1167 }
1168 return false;
1169 }
1170
1171 /*
1172 * Push an equation space into the readline history.
1173 *
1174 * Return true if successful.
1175 */
1176 int
push_en(en)1177 push_en(en)
1178 int en; /* equation space number to push */
1179 {
1180 char *cp;
1181
1182 if (!readline_enabled)
1183 return false;
1184 high_prec = true;
1185 cp = list_equation(en, false);
1186 high_prec = false;
1187 if (cp == NULL)
1188 return false;
1189 add_history(cp);
1190 last_history_string = cp;
1191 return true;
1192 }
1193 #endif
1194
1195 /*
1196 * Output the current working directory.
1197 *
1198 * Return true if successful.
1199 */
1200 int
output_current_directory(ofp)1201 output_current_directory(ofp)
1202 FILE *ofp; /* output file pointer */
1203 {
1204 #if !SECURE
1205 char buf[MAX_CMD_LEN];
1206
1207 if (security_level < 3 && ofp) {
1208 if (getcwd(buf, sizeof(buf))) {
1209 fprintf(ofp, "directory %s\n", buf);
1210 return true;
1211 } else {
1212 perror(NULL);
1213 }
1214 }
1215 #endif
1216 return false;
1217 }
1218
1219 int
fprintf_escaped(ofp,cp)1220 fprintf_escaped(ofp, cp)
1221 FILE *ofp;
1222 char *cp;
1223 {
1224 int len = 0;
1225
1226 while (*cp) {
1227 if (*cp == ';') {
1228 len += fprintf(ofp, "\\");
1229 }
1230 len += fprintf(ofp, "%c", *cp);
1231 cp++;
1232 }
1233 return len;
1234 }
1235
1236 /*
1237 * Output the current set options in a format suitable for reading back in.
1238 * If all_set_options, include options you don't want to save.
1239 */
1240 void
output_options(ofp,all_set_options)1241 output_options(ofp, all_set_options)
1242 FILE *ofp; /* output file pointer */
1243 int all_set_options;
1244 {
1245 if (ofp == NULL)
1246 return;
1247
1248 fprintf(ofp, "precision = %d digits\n", precision);
1249
1250 if (!autosolve) {
1251 fprintf(ofp, "no ");
1252 }
1253 fprintf(ofp, "autosolve\n");
1254
1255 if (!autocalc) {
1256 fprintf(ofp, "no ");
1257 }
1258 fprintf(ofp, "autocalc\n");
1259
1260 if (!autodelete) {
1261 fprintf(ofp, "no ");
1262 }
1263 fprintf(ofp, "autodelete\n");
1264
1265 if (!autoselect) {
1266 fprintf(ofp, "no ");
1267 }
1268 fprintf(ofp, "autoselect\n");
1269
1270 #if !SILENT
1271 fprintf(ofp, "debug_level = %d\n", debug_level);
1272 #endif
1273
1274 if (!case_sensitive_flag) {
1275 fprintf(ofp, "no ");
1276 }
1277 fprintf(ofp, "case_sensitive\n");
1278
1279 if (all_set_options && html_flag) {
1280 if (html_flag == 2) {
1281 fprintf(ofp, "all html ");
1282 } else {
1283 fprintf(ofp, "html ");
1284 }
1285 }
1286 if (color_flag == 2) {
1287 fprintf(ofp, "alternative ");
1288 }
1289 if (bold_colors && color_flag) {
1290 fprintf(ofp, "bold color");
1291 } else {
1292 if (!color_flag) {
1293 fprintf(ofp, "no color");
1294 } else {
1295 fprintf(ofp, "no bold color");
1296 }
1297 }
1298 if (text_color >= 0) {
1299 fprintf(ofp, " %d", text_color);
1300 }
1301 fprintf(ofp, "\n");
1302
1303 if (!display2d) {
1304 fprintf(ofp, "no ");
1305 }
1306 fprintf(ofp, "display2d\n");
1307
1308 if (all_set_options) {
1309 fprintf(ofp, "columns = %d, ", screen_columns);
1310 fprintf(ofp, "rows = %d\n", screen_rows);
1311 }
1312
1313 fprintf(ofp, "fractions_display_mode = ");
1314 switch (fractions_display) {
1315 case 0:
1316 fprintf(ofp, "none\n");
1317 break;
1318 case 2:
1319 fprintf(ofp, "mixed\n");
1320 break;
1321 default:
1322 fprintf(ofp, "simple\n");
1323 break;
1324 }
1325
1326 if (quiet_mode) {
1327 fprintf(ofp, "no ");
1328 }
1329 fprintf(ofp, "prompt\n");
1330
1331 #if 0
1332 if (!preserve_surds) {
1333 fprintf(ofp, "no ");
1334 }
1335 fprintf(ofp, "preserve_surds\n");
1336 #endif
1337
1338 if (!rationalize_denominators) {
1339 fprintf(ofp, "no ");
1340 }
1341 fprintf(ofp, "rationalize_denominators\n");
1342
1343 fprintf(ofp, "modulus_mode = ");
1344 switch (modulus_mode) {
1345 case 0:
1346 fprintf(ofp, "C\n");
1347 break;
1348 case 1:
1349 fprintf(ofp, "Python\n");
1350 break;
1351 case 2:
1352 fprintf(ofp, "normal\n");
1353 break;
1354 default:
1355 fprintf(ofp, "unknown\n");
1356 break;
1357 }
1358
1359 if (finance_option < 0) {
1360 fprintf(ofp, "no fixed_point\n");
1361 } else {
1362 fprintf(ofp, "fixed_point = %d\n", finance_option);
1363 }
1364
1365 if (!factor_int_flag) {
1366 fprintf(ofp, "no ");
1367 }
1368 fprintf(ofp, "factor_integers\n");
1369
1370 if (right_associative_power) { /* option is hardly ever used */
1371 fprintf(ofp, "right_associative_power\n");
1372 }
1373
1374 #if SHELL_OUT
1375 fprintf(ofp, "plot_prefix = ");
1376 fprintf_escaped(ofp, plot_prefix);
1377 fprintf(ofp, "\n");
1378 #endif
1379
1380 fprintf(ofp, "special_variable_characters = %s\n", special_variable_characters);
1381 }
1382
1383 /*
1384 * Skip over a yes/no indicator and return true if *cpp pointed to a negative word.
1385 */
1386 int
skip_no(cpp)1387 skip_no(cpp)
1388 char **cpp;
1389 {
1390 if (strcmp_tospace(*cpp, "no") == 0
1391 || strcmp_tospace(*cpp, "not") == 0
1392 || strcmp_tospace(*cpp, "off") == 0
1393 || strcmp_tospace(*cpp, "false") == 0) {
1394 *cpp = skip_param(*cpp);
1395 return true;
1396 }
1397 if (strcmp_tospace(*cpp, "yes") == 0
1398 || strcmp_tospace(*cpp, "on") == 0
1399 || strcmp_tospace(*cpp, "true") == 0) {
1400 *cpp = skip_param(*cpp);
1401 }
1402 return false;
1403 }
1404
1405 #if !SECURE
1406 /*
1407 * Save set options in the startup file, displaying a confirmation message.
1408 * If a string is passed, then just save the string.
1409 *
1410 * Return true if successful.
1411 */
1412 int
save_set_options(cp)1413 save_set_options(cp)
1414 char *cp;
1415 {
1416 FILE *fp;
1417 int pre_existing;
1418
1419 if (rc_file[0] == '\0') {
1420 error(_("Set options startup file name not set; contact the developer."));
1421 return false;
1422 }
1423 pre_existing = (access(rc_file, F_OK) == 0);
1424 if ((fp = fopen(rc_file, "w")) == NULL) {
1425 perror(rc_file);
1426 error(_("Unable to write to set options startup file."));
1427 return false;
1428 }
1429 fprintf(fp, "; Mathomatic set options loaded at startup,\n");
1430 fprintf(fp, "; created by the \"set save\" command.\n");
1431 fprintf(fp, "; This file can be edited or deleted.\n\n");
1432 if (cp && *cp) {
1433 fprintf(fp, "%s\n", cp);
1434 } else {
1435 output_options(fp, false);
1436 }
1437 if (fclose(fp) == 0) {
1438 if (pre_existing)
1439 printf(_("Startup file \"%s\" overwritten with set options.\n"), rc_file);
1440 else
1441 printf(_("Set options saved in startup file \"%s\".\n"), rc_file);
1442 } else {
1443 perror(rc_file);
1444 error(_("Error saving set options."));
1445 return false;
1446 }
1447 return true;
1448 }
1449 #endif
1450
1451 /*
1452 * Handle parsing of options for the set command.
1453 *
1454 * Return false if error.
1455 */
1456 int
set_options(cp,loading_startup_file)1457 set_options(cp, loading_startup_file)
1458 char *cp;
1459 int loading_startup_file;
1460 {
1461 int i;
1462 int negate;
1463 char *cp1 = NULL, *option_string;
1464
1465 show_usage = false; /* this command gives enough usage information */
1466 try_next_param:
1467 cp = skip_comma_space(cp);
1468 if (*cp == '\0') {
1469 return true;
1470 }
1471 if (strncasecmp(cp, "directory", 3) == 0) {
1472 cp = skip_param(cp);
1473 #if !SECURE
1474 if (security_level < 3) {
1475 if (*cp == '\0') {
1476 cp1 = getenv("HOME");
1477 if (cp1 == NULL) {
1478 error(_("HOME environment variable not set."));
1479 return false;
1480 }
1481 cp = cp1;
1482 }
1483 if (chdir(cp)) {
1484 perror(cp);
1485 error(_("Error changing directory."));
1486 return false;
1487 }
1488 printf(_("Current working directory changed to "));
1489 return output_current_directory(stdout);
1490 }
1491 #endif
1492 error(_("Option disabled by security level."));
1493 return false;
1494 }
1495 negate = skip_no(&cp);
1496 option_string = cp;
1497 cp = skip_param(cp);
1498 #if !SILENT
1499 if (strncasecmp(option_string, "debug", 5) == 0) {
1500 if (negate) {
1501 debug_level = 0;
1502 } else {
1503 i = decstrtol(cp, &cp1);
1504 if (cp1 == NULL || cp == cp1) {
1505 error(_("Please specify the debug level number from -2 to 6."));
1506 return false;
1507 }
1508 cp = cp1;
1509 debug_level = i;
1510 }
1511 goto try_next_param;
1512 }
1513 #endif
1514 if (strncasecmp(option_string, "special", 7) == 0) {
1515 if (negate) {
1516 special_variable_characters[0] = '\0';
1517 } else {
1518 for (i = 0; cp[i]; i++) {
1519 if (is_mathomatic_operator(cp[i])) {
1520 error(_("Invalid character in list, character is a Mathomatic operator."));
1521 return false;
1522 }
1523 }
1524 my_strlcpy(special_variable_characters, cp, sizeof(special_variable_characters));
1525 }
1526 return true;
1527 }
1528 #if SHELL_OUT
1529 if (strncasecmp(option_string, "plot_prefix", 4) == 0) {
1530 if (negate) {
1531 plot_prefix[0] = '\0';
1532 } else {
1533 my_strlcpy(plot_prefix, cp, sizeof(plot_prefix));
1534 }
1535 return true;
1536 }
1537 #endif
1538 if (strncasecmp(option_string, "rows", 3) == 0) {
1539 if (negate) {
1540 screen_rows = 0;
1541 } else {
1542 if (*cp == '\0') {
1543 printf(_("Current screen rows is %d.\n"), screen_rows);
1544 goto check_return;
1545 }
1546 i = decstrtol(cp, &cp1);
1547 if (i < 0 || cp1 == NULL || cp == cp1) {
1548 error(_("Please specify how tall the screen is; 0 = no pagination."));
1549 return false;
1550 }
1551 cp = cp1;
1552 screen_rows = i;
1553 }
1554 goto try_next_param;
1555 }
1556 if (strncasecmp(option_string, "columns", 6) == 0) {
1557 if (negate) {
1558 screen_columns = 0;
1559 } else {
1560 if (*cp == '\0') {
1561 if (!get_screen_size()) {
1562 error(_("OS failed to return screen size."));
1563 return false;
1564 }
1565 goto check_return;
1566 }
1567 i = decstrtol(cp, &cp1);
1568 if (i < 0 || cp1 == NULL || cp == cp1) {
1569 error(_("Please specify how wide the screen is; 0 = no limit."));
1570 return false;
1571 }
1572 cp = cp1;
1573 screen_columns = i;
1574 }
1575 goto try_next_param;
1576 }
1577 if (strncasecmp(option_string, "wide", 4) == 0) {
1578 if (negate) {
1579 if (!get_screen_size() || screen_columns == 0) {
1580 error(_("OS failed to return screen size."));
1581 return false;
1582 }
1583 } else {
1584 screen_columns = 0;
1585 screen_rows = 0;
1586 }
1587 goto try_next_param;
1588 }
1589 if (strncasecmp(option_string, "precision", 4) == 0) {
1590 i = decstrtol(cp, &cp1);
1591 if (i < 0 || i > 15 || cp1 == NULL || cp == cp1) {
1592 error(_("Please specify a display precision between 0 and 15 digits."));
1593 return false;
1594 }
1595 precision = i;
1596 return true;
1597 }
1598 if (strcmp_tospace(option_string, "auto") == 0) {
1599 autosolve = autocalc = autoselect = !negate;
1600 goto try_next_param;
1601 }
1602 if (strncasecmp(option_string, "autosolve", 9) == 0) {
1603 autosolve = !negate;
1604 goto try_next_param;
1605 }
1606 if (strncasecmp(option_string, "autocalc", 8) == 0) {
1607 autocalc = !negate;
1608 goto try_next_param;
1609 }
1610 if (strncasecmp(option_string, "autodelete", 7) == 0) {
1611 autodelete = !negate;
1612 goto try_next_param;
1613 }
1614 if (strncasecmp(option_string, "autoselect", 10) == 0) {
1615 autoselect = !negate;
1616 goto try_next_param;
1617 }
1618 if (strncasecmp(option_string, "case", 4) == 0) {
1619 case_sensitive_flag = !negate;
1620 goto try_next_param;
1621 }
1622 if (strncasecmp(option_string, "display2d", 7) == 0) {
1623 display2d = !negate;
1624 goto try_next_param;
1625 }
1626 if (strncasecmp(option_string, "fractions", 4) == 0) {
1627 if (negate) {
1628 fractions_display = 0;
1629 } else {
1630 i = decstrtol(cp, &cp1);
1631 if (cp == cp1) {
1632 if (strcmp_tospace(cp, "none") == 0) {
1633 cp1 = skip_param(cp);
1634 i = 0;
1635 } else if (strcmp_tospace(cp, "simple") == 0) {
1636 cp1 = skip_param(cp);
1637 i = 1;
1638 } else if (strcmp_tospace(cp, "mixed") == 0) {
1639 cp1 = skip_param(cp);
1640 i = 2;
1641 }
1642 }
1643 if (cp1 == NULL || cp == cp1 || i < 0 || i > 2) {
1644 error(_("Please specify the fractions display mode number (0, 1, or 2)."));
1645 printf(_("0 means do not display any constants as fractions,\n"));
1646 printf(_("1 means display some constants as \"simple\" fractions,\n"));
1647 printf(_("2 means display some constants as \"mixed\" or simple fractions.\n"));
1648 printf(_("Current value is %d.\n"), fractions_display);
1649 return false;
1650 }
1651 cp = cp1;
1652 fractions_display = i;
1653 }
1654 goto try_next_param;
1655 }
1656 if (strncasecmp(option_string, "prompt", 6) == 0) {
1657 quiet_mode = negate;
1658 goto try_next_param;
1659 }
1660 if (strncasecmp(option_string, "demo", 4) == 0) {
1661 demo_mode = !negate;
1662 goto try_next_param;
1663 }
1664 if (strncasecmp(option_string, "html", 4) == 0) {
1665 #if !SECURE
1666 if (security_level > 0) {
1667 #endif
1668 error(_("Option disabled by security level."));
1669 return false;
1670 #if !SECURE
1671 }
1672 #endif
1673 reset_attr();
1674 if (is_all(cp)) {
1675 cp = skip_param(cp);
1676 if (negate)
1677 html_flag = 0;
1678 else
1679 html_flag = 2;
1680 } else {
1681 html_flag = !negate;
1682 }
1683 goto try_next_param;
1684 }
1685 if (strncasecmp(option_string, "preserve_surds", 13) == 0) {
1686 preserve_surds = !negate;
1687 goto try_next_param;
1688 }
1689 if (strncasecmp(option_string, "rationalize", 11) == 0) {
1690 rationalize_denominators = !negate;
1691 goto try_next_param;
1692 }
1693 if (strncasecmp(option_string, "modulus_mode", 3) == 0) {
1694 if (negate) {
1695 error(_("Modulus mode cannot be turned off."));
1696 return false;
1697 } else {
1698 i = decstrtol(cp, &cp1);
1699 if (cp == cp1) {
1700 if (strcmp_tospace(cp, "C") == 0 || strcmp_tospace(cp, "java") == 0) {
1701 cp1 = skip_param(cp);
1702 i = 0;
1703 } else if (strcmp_tospace(cp, "python") == 0) {
1704 cp1 = skip_param(cp);
1705 i = 1;
1706 } else if (strcmp_tospace(cp, "positive") == 0 || strcmp_tospace(cp, "normal") == 0) {
1707 cp1 = skip_param(cp);
1708 i = 2;
1709 }
1710 }
1711 if (cp1 == NULL || cp == cp1 || i < 0 || i > 2) {
1712 error(_("Please specify the modulus mode number (0, 1, or 2)."));
1713 printf(_("* \"C\" and \"Java\" programming language mode 0:\n"));
1714 printf(_(" 0 means modulus operator (dividend %% divisor) result has same sign as dividend;\n"));
1715 printf(_("* \"Python\" programming language mode 1:\n"));
1716 printf(_(" 1 means computed result always has same sign as the divisor;\n"));
1717 printf(_("* Mathematically correct mode 2 for perfect simplification:\n"));
1718 printf(_(" 2 means the result is always \"positive\" or zero (\"normal\" mode).\n\n"));
1719 printf(_("The current value is %d ("), modulus_mode);
1720 switch (modulus_mode) {
1721 case 0:
1722 printf("C");
1723 break;
1724 case 1:
1725 printf("Python");
1726 break;
1727 case 2:
1728 printf("normal");
1729 break;
1730 default:
1731 printf("unknown");
1732 break;
1733 }
1734 printf(_(" mode).\n"));
1735 return false;
1736 }
1737 cp = cp1;
1738 modulus_mode = i;
1739 }
1740 goto try_next_param;
1741 }
1742 if (strncasecmp(option_string, "color", 5) == 0) {
1743 reset_attr();
1744 if (color_flag != 2 || negate) {
1745 color_flag = !negate;
1746 }
1747 i = decstrtol(cp, &cp1);
1748 if (cp1 && cp != cp1) {
1749 text_color = i;
1750 cp = cp1;
1751 } else {
1752 text_color = -1;
1753 }
1754 goto try_next_param;
1755 }
1756 if (strncasecmp(option_string, "alternative", 3) == 0) {
1757 reset_attr();
1758 color_flag = (!negate) + 1;
1759 goto try_next_param;
1760 }
1761 if (strncasecmp(option_string, "bold", 4) == 0) {
1762 reset_attr();
1763 bold_colors = !negate;
1764 goto try_next_param;
1765 }
1766 if (strncasecmp(option_string, "fixed", 3) == 0) {
1767 if (negate) {
1768 finance_option = -1;
1769 } else {
1770 i = decstrtol(cp, &cp1);
1771 if (cp1 == NULL) {
1772 return false;
1773 }
1774 if (cp == cp1) {
1775 if (*cp1 == '\0') {
1776 i = 2;
1777 } else {
1778 error(_("Please specify the number of digits to display after the decimal point."));
1779 return false;
1780 }
1781 }
1782 if (i < -1 || i > 100) {
1783 error(_("Range is -1 to 100; Sets rounded display with fixed number of trailing digits."));
1784 return false;
1785 }
1786 if (i == 0) {
1787 warning("Setting rounded, integer-only display.");
1788 }
1789 cp = cp1;
1790 finance_option = i;
1791 }
1792 goto try_next_param;
1793 }
1794 if (strncasecmp(option_string, "factor_integers", 6) == 0) {
1795 factor_int_flag = !negate;
1796 goto try_next_param;
1797 }
1798 if (strncasecmp(option_string, "right_associative_power", 5) == 0) {
1799 right_associative_power = !negate;
1800 goto try_next_param;
1801 }
1802 if (strcmp_tospace(option_string, "load") == 0) {
1803 #if !SECURE
1804 if (negate) {
1805 printf(_("Doing nothing.\n"));
1806 return true;
1807 }
1808 if (loading_startup_file) {
1809 printf(_("Ignoring recursive \"set load\".\n"));
1810 return true;
1811 }
1812 if (extra_characters(cp))
1813 return false;
1814 if (security_level <= 3) {
1815 if (load_rc(false, gfp)) {
1816 fprintf(gfp, _("\nEnd of file.\n"));
1817 return true;
1818 } else {
1819 error(_("Error loading startup set options."));
1820 return false;
1821 }
1822 }
1823 #endif
1824 error(_("Option disabled by security level."));
1825 return false;
1826 }
1827 if (strcmp_tospace(option_string, "save") == 0) {
1828 #if !SECURE
1829 if (security_level < 2) {
1830 if (rc_file[0] == '\0') {
1831 error(_("Set options startup file name not set; contact the developer."));
1832 return false;
1833 }
1834 if (loading_startup_file) {
1835 printf(_("Got \"set save\" while loading startup options, quitting.\n"));
1836 return false;
1837 }
1838 if (negate) {
1839 if (extra_characters(cp))
1840 return false;
1841 if (unlink(rc_file) == 0) {
1842 printf(_("Set options startup file \"%s\" removed.\n"), rc_file);
1843 printf(_("Factory default options will be used on next startup of Mathomatic.\n"));
1844 return true;
1845 } else {
1846 perror(rc_file);
1847 error(_("Set options startup file cannot be removed."));
1848 return false;
1849 }
1850 } else {
1851 if (save_set_options(cp)) {
1852 if (load_rc(false, gfp)) {
1853 fprintf(gfp, _("\nNew startup set options loaded.\n"));
1854 return true;
1855 } else {
1856 error(_("Error loading new startup set options."));
1857 fprintf(gfp, _("Correct or type \"set no save\" to remove.\n"));
1858 }
1859 }
1860 return false;
1861 }
1862 }
1863 #endif
1864 error(_("Option disabled by security level."));
1865 return false;
1866 }
1867 if (strcmp_tospace(option_string, "set") == 0) {
1868 if (!negate)
1869 goto try_next_param;
1870 }
1871 printf(_("\nCannot process set string \"%s\".\n"), option_string);
1872 error(_("Unknown set option."));
1873 return false;
1874
1875 check_return:
1876 extra_characters(cp);
1877 return true;
1878 }
1879
1880 /*
1881 * The set command.
1882 */
1883 int
set_cmd(cp)1884 set_cmd(cp)
1885 char *cp;
1886 {
1887 int rv;
1888
1889 if (*cp == '\0') {
1890 fprintf(gfp, _("Options are set as follows:\n\n"));
1891
1892 output_options(gfp, true);
1893
1894 output_current_directory(gfp);
1895 return true;
1896 }
1897 rv = set_options(cp, false);
1898 if (rv) {
1899 debug_string(0, _("Success."));
1900 }
1901 return rv;
1902 }
1903
1904 /*
1905 * The echo command.
1906 */
1907 int
echo_cmd(cp)1908 echo_cmd(cp)
1909 char *cp;
1910 {
1911 int i;
1912 int len = 0;
1913 int width, height;
1914
1915 if (repeat_flag) {
1916 if (*cp) {
1917 if (screen_columns)
1918 width = screen_columns;
1919 else
1920 width = TEXT_COLUMNS;
1921 while ((len + strlen(cp)) < width) {
1922 fprintf(gfp, "%s", cp);
1923 len += strlen(cp);
1924 }
1925 fprintf(gfp, "\n");
1926 } else {
1927 if (screen_rows)
1928 height = screen_rows;
1929 else
1930 height = TEXT_ROWS;
1931 for (i = 0; i < height; i++) {
1932 fprintf(gfp, "\n");
1933 }
1934 }
1935 } else {
1936 fprintf(gfp, "%s\n", cp);
1937 }
1938 return true;
1939 }
1940
1941 /*
1942 * The pause command.
1943 */
1944 int
pause_cmd(cp)1945 pause_cmd(cp)
1946 char *cp;
1947 {
1948 #if LIBRARY
1949 return true;
1950 #else
1951 char *cp1;
1952 char buf[MAX_CMD_LEN];
1953
1954 if (test_mode || demo_mode) {
1955 return true;
1956 }
1957 show_usage = false;
1958 if (*cp == '\0') {
1959 cp = _("Please press the Enter key");
1960 }
1961 set_color(3); /* make color blue, to show that this is not part of the surrounding text */
1962 snprintf(prompt_str, sizeof(prompt_str), "==== %s: ", cp);
1963 cp1 = get_string(buf, sizeof(buf));
1964 default_color(false);
1965 if (cp1 == NULL) {
1966 return false;
1967 }
1968 if (strncasecmp(cp1, "quit", 4) == 0) {
1969 return false;
1970 }
1971 if (strncasecmp(cp1, "exit", 4) == 0) {
1972 return false;
1973 }
1974 return true;
1975 #endif
1976 }
1977
1978 /*
1979 * The copy command.
1980 */
1981 int
copy_cmd(cp)1982 copy_cmd(cp)
1983 char *cp;
1984 {
1985 int i, j, k;
1986 int i1;
1987 char exists[N_EQUATIONS];
1988 char *cp1;
1989 int select_flag = false; /* select the first equation space copied to */
1990
1991 CLEAR_ARRAY(exists);
1992 for (i1 = 0; i1 < n_equations; i1++) {
1993 if (n_lhs[i1] > 0) {
1994 exists[i1] = true;
1995 }
1996 }
1997 if (strncasecmp(cp, "select", 3) == 0) {
1998 select_flag = true;
1999 cp = skip_param(cp);
2000 }
2001 do {
2002 cp1 = cp;
2003 if (!get_range(&cp, &i, &j)) {
2004 return false;
2005 }
2006 if (*cp && cp == cp1) {
2007 error(_("Invalid equation number range."));
2008 return false;
2009 }
2010 for (i1 = i; i1 <= j; i1++) {
2011 if (exists[i1]) {
2012 k = next_espace();
2013 copy_espace(i1, k);
2014 if (!return_result(k)) {
2015 return false;
2016 }
2017 if (select_flag) {
2018 cur_equation = k;
2019 select_flag = false;
2020 }
2021 }
2022 }
2023 } while (*cp);
2024 return true;
2025 }
2026
2027 /*
2028 * Common function for the imaginary and real commands.
2029 */
2030 static int
complex_func(cp,imag_flag)2031 complex_func(cp, imag_flag)
2032 char *cp; /* the command-line */
2033 int imag_flag; /* if true, copy the imaginary part, otherwise copy the real part */
2034 {
2035 int i, j, k;
2036 int beg;
2037 int found_imag, has_imag, has_real, solved;
2038 token_type *source, *dest;
2039 int n1, *nps, *np;
2040 long v = IMAGINARY; /* separation variable */
2041
2042 if (current_not_defined()) {
2043 return false;
2044 }
2045 solved = solved_equation(cur_equation);
2046 i = cur_equation;
2047 j = next_espace();
2048 if (n_rhs[i]) {
2049 source = rhs[i];
2050 nps = &n_rhs[i];
2051 dest = rhs[j];
2052 np = &n_rhs[j];
2053 } else {
2054 source = lhs[i];
2055 nps = &n_lhs[i];
2056 dest = lhs[j];
2057 np = &n_lhs[j];
2058 }
2059 if (*cp) {
2060 cp = parse_var2(&v, cp);
2061 if (cp == NULL) {
2062 return false;
2063 }
2064 if (extra_characters(cp))
2065 return false;
2066 }
2067 simp_loop(source, nps);
2068 uf_simp(source, nps);
2069 factorv(source, nps, v);
2070 partial_flag = false;
2071 uf_simp(source, nps);
2072 partial_flag = true;
2073 n1 = 1;
2074 dest[0] = zero_token;
2075 has_imag = has_real = false;
2076 for (beg = k = 0; beg < *nps; beg = k, k++) {
2077 for (found_imag = false; k < *nps; k++) {
2078 if (source[k].level == 1 && source[k].kind == OPERATOR
2079 && (source[k].token.operatr == PLUS || source[k].token.operatr == MINUS)) {
2080 break;
2081 }
2082 if (source[k].kind == VARIABLE && source[k].token.variable == v) {
2083 found_imag = true;
2084 }
2085 }
2086 if (found_imag)
2087 has_imag = true;
2088 else
2089 has_real = true;
2090 if (found_imag == imag_flag) {
2091 if (beg == 0) {
2092 n1 = 0;
2093 }
2094 blt(&dest[n1], &source[beg], (k - beg) * sizeof(token_type));
2095 n1 += (k - beg);
2096 }
2097 }
2098 if (!has_imag || !has_real) {
2099 warning(_("Expression was not a mix."));
2100 }
2101 do {
2102 simp_loop(dest, &n1);
2103 } while (factor_plus(dest, &n1, v, 0.0));
2104 simp_divide(dest, &n1);
2105 if (n_rhs[i]) {
2106 blt(lhs[j], lhs[i], n_lhs[i] * sizeof(token_type));
2107 n_lhs[j] = n_lhs[i];
2108 if (solved) {
2109 if (list_var(lhs[j][0].token.variable, 0) < (MAX_VAR_LEN - 5)) {
2110 if (imag_flag)
2111 strcat(var_str, "_imag");
2112 else
2113 strcat(var_str, "_real");
2114 parse_var(&lhs[j][0].token.variable, var_str);
2115 }
2116 }
2117 }
2118 *np = n1;
2119 cur_equation = j;
2120 return return_result(cur_equation);
2121 }
2122
2123 /*
2124 * The real command.
2125 */
2126 int
real_cmd(cp)2127 real_cmd(cp)
2128 char *cp;
2129 {
2130 return complex_func(cp, false);
2131 }
2132
2133 /*
2134 * The imaginary command.
2135 */
2136 int
imaginary_cmd(cp)2137 imaginary_cmd(cp)
2138 char *cp;
2139 {
2140 return complex_func(cp, true);
2141 }
2142
2143 #if !LIBRARY
2144 /*
2145 * The tally command.
2146 */
2147 int
tally_cmd(cp)2148 tally_cmd(cp)
2149 char *cp;
2150 {
2151 int i, k, first, last;
2152 double count = 0.0;
2153 int arithmetic_mean = false;
2154 long v;
2155 char *cp1;
2156
2157 if (!parse_var(&v, "total")) {
2158 return false;
2159 }
2160 if (strcmp_tospace(cp, "average") == 0) {
2161 arithmetic_mean = true;
2162 cp = skip_param(cp);
2163 }
2164 trhs[0] = zero_token;
2165 n_trhs = 1;
2166 if (*cp) {
2167 do {
2168 cp1 = cp;
2169 if (!get_range(&cp, &first, &last)) {
2170 return false;
2171 }
2172 if (*cp && cp == cp1) {
2173 error(_("Invalid argument. Expecting equation number or range."));
2174 return false;
2175 }
2176 for (k = first; k <= last; k++) {
2177 if (n_lhs[k] <= 0)
2178 continue;
2179 if (n_rhs[k] > 0) {
2180 if ((n_trhs + 1 + n_rhs[k]) > n_tokens) {
2181 error_huge();
2182 }
2183 for (i = 0; i < n_trhs; i++) {
2184 trhs[i].level++;
2185 }
2186 trhs[n_trhs].kind = OPERATOR;
2187 trhs[n_trhs].level = 1;
2188 trhs[n_trhs].token.operatr = PLUS;
2189 n_trhs++;
2190 blt(&trhs[n_trhs], rhs[k], n_rhs[k] * sizeof(token_type));
2191 n_trhs += n_rhs[k];
2192 } else {
2193 if ((n_trhs + 1 + n_lhs[k]) > n_tokens) {
2194 error_huge();
2195 }
2196 for (i = 0; i < n_trhs; i++) {
2197 trhs[i].level++;
2198 }
2199 trhs[n_trhs].kind = OPERATOR;
2200 trhs[n_trhs].level = 1;
2201 trhs[n_trhs].token.operatr = PLUS;
2202 n_trhs++;
2203 blt(&trhs[n_trhs], lhs[k], n_lhs[k] * sizeof(token_type));
2204 n_trhs += n_lhs[k];
2205 }
2206 for (i++; i < n_trhs; i++) {
2207 trhs[i].level++;
2208 }
2209 calc_simp(trhs, &n_trhs);
2210 count++;
2211 }
2212 } while (*cp);
2213 }
2214 if (extra_characters(cp)) {
2215 return false;
2216 }
2217 for (;; count++) {
2218 fprintf(gfp, _("total = "));
2219 list_proc(trhs, n_trhs, false);
2220 fprintf(gfp, "\n");
2221 if (count > 0) {
2222 if (arithmetic_mean) {
2223 /* calculate and display the average */
2224 blt(tlhs, trhs, n_trhs * sizeof(token_type));
2225 n_tlhs = n_trhs;
2226 if ((n_tlhs + 2) > n_tokens) {
2227 error_huge();
2228 }
2229 for (i = 0; i < n_tlhs; i++) {
2230 tlhs[i].level++;
2231 }
2232 tlhs[n_tlhs].kind = OPERATOR;
2233 tlhs[n_tlhs].level = 1;
2234 tlhs[n_tlhs].token.operatr = DIVIDE;
2235 n_tlhs++;
2236 tlhs[n_tlhs].kind = CONSTANT;
2237 tlhs[n_tlhs].level = 1;
2238 tlhs[n_tlhs].token.constant = count;
2239 n_tlhs++;
2240 calc_simp(tlhs, &n_tlhs);
2241 fprintf(gfp, _("count = %.0f, average = "), count);
2242 list_proc(tlhs, n_tlhs, false);
2243 fprintf(gfp, "\n");
2244 }
2245 }
2246 fprintf(gfp, "\n");
2247 my_strlcpy(prompt_str, _("Enter value to add: "), sizeof(prompt_str));
2248 if (!get_expr(tlhs, &n_tlhs)) {
2249 break;
2250 }
2251 if ((n_trhs + 1 + n_tlhs) > n_tokens) {
2252 error_huge();
2253 }
2254 for (i = 0; i < n_tlhs; i++) {
2255 tlhs[i].level++;
2256 }
2257 for (i = 0; i < n_trhs; i++) {
2258 trhs[i].level++;
2259 }
2260 trhs[n_trhs].kind = OPERATOR;
2261 trhs[n_trhs].level = 1;
2262 trhs[n_trhs].token.operatr = PLUS;
2263 n_trhs++;
2264 blt(&trhs[n_trhs], tlhs, n_tlhs * sizeof(token_type));
2265 n_trhs += n_tlhs;
2266 calc_simp(trhs, &n_trhs);
2267 }
2268 fprintf(gfp, _("End.\n"));
2269 if (count > 0) {
2270 i = next_espace();
2271 lhs[i][0].level = 1;
2272 lhs[i][0].kind = VARIABLE;
2273 lhs[i][0].token.variable = v;
2274 n_lhs[i] = 1;
2275 blt(rhs[i], trhs, n_trhs * sizeof(token_type));
2276 n_rhs[i] = n_trhs;
2277 cur_equation = i;
2278 return return_result(cur_equation);
2279 }
2280 return true;
2281 }
2282 #endif
2283
2284 #if !LIBRARY
2285 /*
2286 * The calculate command.
2287 */
2288 int
calculate_cmd(cp)2289 calculate_cmd(cp)
2290 char *cp;
2291 {
2292 int i, k1, k;
2293 int first, last;
2294 long v, last_v, it_v = 0; /* Mathomatic variables */
2295 long counter, counter_max;
2296 sign_array_type sa_mark, sa_value;
2297 long l, iterations = 1;
2298 token_type *source;
2299 int n;
2300 int diff_sign;
2301 char buf[MAX_CMD_LEN];
2302 int factor_flag = false, value_entered;
2303
2304 for (;; cp = skip_param(cp)) {
2305 if (strcmp_tospace(cp, "factor") == 0) {
2306 factor_flag = true;
2307 continue;
2308 }
2309 break;
2310 }
2311 if (!get_range(&cp, &first, &last)) {
2312 return false;
2313 }
2314 if (*cp) {
2315 cp = parse_var2(&it_v, cp);
2316 if (cp == NULL) {
2317 return false;
2318 }
2319 if (*cp == '\0') {
2320 my_strlcpy(prompt_str, _("Enter maximum number of iterations: "), sizeof(prompt_str));
2321 if ((cp = get_string(buf, sizeof(buf))) == NULL)
2322 return false;
2323 }
2324 iterations = decstrtol(cp, &cp);
2325 if (*cp || iterations < 0) {
2326 error(_("Positive integer required."));
2327 return false;
2328 }
2329 if (iterations == 0) {
2330 warning(_("Feedback calculation will be iterated until convergence."));
2331 iterations = LONG_MAX - 1L;
2332 }
2333 }
2334 if (extra_characters(cp)) {
2335 return false;
2336 }
2337 calc_again:
2338 value_entered = false;
2339 for (i = first; i <= last; i++) {
2340 if (n_rhs[i]) {
2341 source = rhs[i];
2342 n = n_rhs[i];
2343 } else {
2344 source = lhs[i];
2345 n = n_lhs[i];
2346 }
2347 if (it_v) {
2348 if (!found_var(source, n, it_v)) {
2349 debug_string((first == last) ? 0 : 1, _("Specified feedback variable not found."));
2350 continue;
2351 }
2352 }
2353 n_trhs = n;
2354 blt(trhs, source, n_trhs * sizeof(token_type));
2355 last_v = 0;
2356 for (;;) {
2357 v = -1;
2358 for (k1 = 0; k1 < n; k1 += 2) {
2359 if (source[k1].kind == VARIABLE) {
2360 if (source[k1].token.variable > last_v
2361 && (v == -1 || source[k1].token.variable < v))
2362 v = source[k1].token.variable;
2363 }
2364 }
2365 if (v == -1)
2366 break;
2367 last_v = v;
2368 if ((v & VAR_MASK) <= SIGN || v == it_v) {
2369 continue;
2370 }
2371 if (test_mode || demo_mode)
2372 continue;
2373 list_var(v, 0);
2374 snprintf(prompt_str, sizeof(prompt_str), _("Enter %s: "), var_str);
2375 if (!get_expr(tlhs, &n_tlhs)) {
2376 continue;
2377 }
2378 value_entered = true;
2379 /* Disguise all variables in the entered expression by making them negative. */
2380 /* That way they won't be improperly substituted in the future. */
2381 for (k1 = 0; k1 < n_tlhs; k1 += 2)
2382 if (tlhs[k1].kind == VARIABLE)
2383 tlhs[k1].token.variable = -tlhs[k1].token.variable;
2384 subst_var_with_exp(trhs, &n_trhs, tlhs, n_tlhs, v);
2385 }
2386 /* Restore disguised variables: */
2387 for (k1 = 0; k1 < n_trhs; k1 += 2)
2388 if (trhs[k1].kind == VARIABLE && trhs[k1].token.variable < 0)
2389 trhs[k1].token.variable = -trhs[k1].token.variable;
2390 if (it_v) {
2391 /* Handle the iteration option, where the simplified result is repeatedly plugged into variable it_v. */
2392 list_var(it_v, 0);
2393 snprintf(prompt_str, sizeof(prompt_str), _("Enter initial %s: "), var_str);
2394 while (!get_expr(tes, &n_tes)) {
2395 printf("Aborted.\n");
2396 return repeat_flag;
2397 }
2398 value_entered = true;
2399 calc_simp(tes, &n_tes);
2400 blt(tlhs, trhs, n_trhs * sizeof(token_type));
2401 n_tlhs = n_trhs;
2402 for (l = 0;; l++) {
2403 if (l >= iterations) {
2404 fprintf(gfp, _("%ld feedback iterations performed.\n"), l);
2405 break;
2406 }
2407 side_debug(1, tes, n_tes);
2408 blt(trhs, tlhs, n_tlhs * sizeof(token_type));
2409 n_trhs = n_tlhs;
2410 subst_var_with_exp(trhs, &n_trhs, tes, n_tes, it_v);
2411 calc_simp(trhs, &n_trhs);
2412 if (se_compare(trhs, n_trhs, tes, n_tes, &diff_sign) && !diff_sign) {
2413 fprintf(gfp, _("Convergence reached after %ld iterations.\n"), l + 1);
2414 break;
2415 }
2416 blt(tes, trhs, n_trhs * sizeof(token_type));
2417 n_tes = n_trhs;
2418 }
2419 }
2420 calc_simp(trhs, &n_trhs);
2421
2422 /* Now substitute all sign variables with +1 and -1. */
2423 CLEAR_ARRAY(sa_mark);
2424 for (k1 = 0; k1 < n_trhs; k1 += 2) {
2425 if (trhs[k1].kind == VARIABLE && (trhs[k1].token.variable & VAR_MASK) == SIGN) {
2426 sa_mark[(trhs[k1].token.variable >> VAR_SHIFT) & SUBSCRIPT_MASK] = true;
2427 }
2428 }
2429 for (k1 = 0, k = 0; k1 < ARR_CNT(sa_mark); k1++) {
2430 if (sa_mark[k1]) {
2431 k++;
2432 }
2433 }
2434 counter_max = (1L << k) - 1L;
2435 if (counter_max) {
2436 fprintf(gfp, _("There are %ld solutions.\n"), counter_max + 1);
2437 }
2438 for (counter = 0; counter <= counter_max; counter++) {
2439 blt(tlhs, trhs, n_trhs * sizeof(token_type));
2440 n_tlhs = n_trhs;
2441 for (k1 = 0, k = 0; k1 < ARR_CNT(sa_mark); k1++) {
2442 if (sa_mark[k1]) {
2443 sa_value[k1] = (((1L << k) & counter) != 0);
2444 k++;
2445 }
2446 }
2447 for (k1 = 0; k1 < n_tlhs; k1 += 2) {
2448 if (tlhs[k1].kind == VARIABLE && (tlhs[k1].token.variable & VAR_MASK) == SIGN) {
2449 if (sa_value[(tlhs[k1].token.variable >> VAR_SHIFT) & SUBSCRIPT_MASK]) {
2450 tlhs[k1].kind = CONSTANT;
2451 tlhs[k1].token.constant = -1.0;
2452 } else {
2453 tlhs[k1].kind = CONSTANT;
2454 tlhs[k1].token.constant = 1.0;
2455 }
2456 }
2457 }
2458 for (k1 = 0, k = false; k1 < ARR_CNT(sa_mark); k1++) {
2459 if (sa_mark[k1]) {
2460 if (k) {
2461 fprintf(gfp, ", ");
2462 } else {
2463 fprintf(gfp, _("\nSolution number %ld with "), counter + 1);
2464 }
2465 list_var((long) SIGN + (((long) k1) << VAR_SHIFT), 0);
2466 fprintf(gfp, "%s = ", var_str);
2467 if (sa_value[k1]) {
2468 fprintf(gfp, "-1");
2469 } else {
2470 fprintf(gfp, "1");
2471 }
2472 k = true;
2473 }
2474 }
2475 if (k)
2476 fprintf(gfp, ":\n");
2477 calc_simp(tlhs, &n_tlhs);
2478 if (factor_flag) {
2479 mid_simp_side(tlhs, &n_tlhs);
2480 }
2481 fprintf(gfp, " ");
2482 if (n_rhs[i]) {
2483 list_proc(lhs[i], n_lhs[i], false);
2484 fprintf(gfp, " = ");
2485 }
2486 list_factor(tlhs, &n_tlhs, factor_flag);
2487 if (fractions_display && n_tlhs <= 9 && make_fractions(tlhs, &n_tlhs)) {
2488 group_proc(tlhs, &n_tlhs);
2489 fprintf(gfp, ", with fractions it is: ");
2490 list_factor(tlhs, &n_tlhs, factor_flag);
2491 }
2492 fprintf(gfp, "\n");
2493 }
2494 }
2495 if (value_entered && repeat_flag) {
2496 fprintf(gfp, "Repeating:\n");
2497 goto calc_again;
2498 }
2499 return true;
2500 }
2501 #endif
2502
2503 /*
2504 * The clear command.
2505 */
2506 int
clear_cmd(cp)2507 clear_cmd(cp)
2508 char *cp;
2509 {
2510 int i, j;
2511 char *cp1;
2512
2513 do {
2514 cp1 = cp;
2515 if (is_all(cp)) {
2516 clear_all();
2517 return true;
2518 } else {
2519 if (!get_range(&cp, &i, &j)) {
2520 return false;
2521 }
2522 if (*cp && cp == cp1) {
2523 error(_("Invalid argument. Expecting equation number or range."));
2524 return false;
2525 }
2526 for (; i <= j; i++) {
2527 n_lhs[i] = 0;
2528 n_rhs[i] = 0;
2529 }
2530 }
2531 } while (*cp);
2532 return true;
2533 }
2534
2535 /*
2536 * Compare the Right Hand Sides of two equation spaces.
2537 */
2538 static int
compare_rhs(i,j,diff_signp)2539 compare_rhs(i, j, diff_signp)
2540 int i, j;
2541 int *diff_signp;
2542 {
2543 int rv;
2544
2545 /* First, test the compare function by comparing with self: */
2546 rv = se_compare(rhs[i], n_rhs[i], rhs[i], n_rhs[i], diff_signp);
2547 if (!rv || *diff_signp) {
2548 error(_("Too many terms to compare."));
2549 return false;
2550 }
2551 /* Now do the requested compare: */
2552 sign_cmp_flag = true;
2553 rv = se_compare(rhs[i], n_rhs[i], rhs[j], n_rhs[j], diff_signp);
2554 sign_cmp_flag = false;
2555 return rv;
2556 }
2557
2558 /*
2559 * Compare two equation spaces.
2560 *
2561 * Return 0 if they differ.
2562 * Return 1 if identical. Return -1 if they are expressions that differ only in sign.
2563 */
2564 int
compare_es(i,j)2565 compare_es(i, j)
2566 int i, j; /* equation space numbers */
2567 {
2568 int rv;
2569 int diff_sign_lhs, diff_sign_rhs;
2570
2571 if (n_lhs[i] == 0 || n_lhs[j] == 0)
2572 return false; /* empty equation space */
2573 if ((n_rhs[i] == 0) != (n_rhs[j] == 0))
2574 return false; /* mix of expression and equation */
2575 /* Compare the two left hand sides: */
2576 sign_cmp_flag = true;
2577 rv = se_compare(lhs[i], n_lhs[i], lhs[j], n_lhs[j], &diff_sign_lhs);
2578 sign_cmp_flag = false;
2579 if (!rv)
2580 return false;
2581 if (n_rhs[i] == 0 && n_rhs[j] == 0) {
2582 /* two expressions, not equations */
2583 if (diff_sign_lhs)
2584 return -1;
2585 else
2586 return 1;
2587 }
2588 /* They are equations, so compare the two right hand sides: */
2589 sign_cmp_flag = true;
2590 rv = se_compare(rhs[i], n_rhs[i], rhs[j], n_rhs[j], &diff_sign_rhs);
2591 sign_cmp_flag = false;
2592 if (!rv)
2593 return false;
2594 return(diff_sign_lhs == diff_sign_rhs);
2595 }
2596
2597 /*
2598 * The compare command.
2599 */
2600 int
compare_cmd(cp)2601 compare_cmd(cp)
2602 char *cp;
2603 {
2604 int i, j;
2605 int diff_sign;
2606 int symb = false, approx = false;
2607
2608 for (;; cp = skip_param(cp)) {
2609 if (strncasecmp(cp, "symbolic", 4) == 0) {
2610 symb = true;
2611 continue;
2612 }
2613 if (strncasecmp(cp, "approximate", 4) == 0) {
2614 approx = true;
2615 continue;
2616 }
2617 break;
2618 }
2619 if (strcmp_tospace(cp, "with") == 0) {
2620 cp = skip_param(cp);
2621 }
2622 i = decstrtol(cp, &cp) - 1;
2623 if (not_defined(i)) {
2624 return false;
2625 }
2626 if (strcmp_tospace(cp, "with") == 0) {
2627 cp = skip_param(cp);
2628 }
2629 if ((j = get_default_en(cp)) < 0) {
2630 return false;
2631 }
2632 if (i == j) {
2633 error(_("Cannot compare an expression with itself."));
2634 return false;
2635 }
2636 show_usage = false;
2637 fprintf(gfp, _("Comparing #%d with #%d...\n"), i + 1, j + 1);
2638 simp_equation(i);
2639 simp_equation(j);
2640 if (n_rhs[i] == 0 || n_rhs[j] == 0) {
2641 if (n_rhs[i] == 0 && n_rhs[j] == 0) {
2642 switch (compare_es(i, j)) {
2643 case 1:
2644 fprintf(gfp, _("Expressions are identical.\n"));
2645 return true;
2646 case -1:
2647 error(_("Expressions differ only in sign (times -1)."));
2648 return false;
2649 }
2650 if (approx) {
2651 debug_string(0, _("Approximating both expressions..."));
2652 approximate(lhs[i], &n_lhs[i]);
2653 approximate(lhs[j], &n_lhs[j]);
2654 switch (compare_es(i, j)) {
2655 case 1:
2656 fprintf(gfp, _("Expressions are identical.\n"));
2657 return true;
2658 case -1:
2659 error(_("Expressions differ only in sign (times -1)."));
2660 return false;
2661 }
2662 }
2663 debug_string(0, _("Simplifying both expressions..."));
2664 symb_flag = symb;
2665 simpa_repeat_side(lhs[i], &n_lhs[i], false, true);
2666 simpa_repeat_side(lhs[j], &n_lhs[j], false, true);
2667 symb_flag = false;
2668 if (approx) {
2669 approximate(lhs[i], &n_lhs[i]);
2670 approximate(lhs[j], &n_lhs[j]);
2671 }
2672 switch (compare_es(i, j)) {
2673 case 1:
2674 fprintf(gfp, _("Expressions are identical.\n"));
2675 return true;
2676 case -1:
2677 error(_("Expressions differ only in sign (times -1)."));
2678 return false;
2679 }
2680 #if !SILENT
2681 if (debug_level >= 0) {
2682 list_sub(i);
2683 list_sub(j);
2684 }
2685 #endif
2686 uf_simp(lhs[i], &n_lhs[i]);
2687 uf_simp(lhs[j], &n_lhs[j]);
2688 if (approx) {
2689 approximate(lhs[i], &n_lhs[i]);
2690 approximate(lhs[j], &n_lhs[j]);
2691 }
2692 switch (compare_es(i, j)) {
2693 case 1:
2694 fprintf(gfp, _("Expressions are identical.\n"));
2695 return true;
2696 case -1:
2697 error(_("Expressions differ only in sign (times -1)."));
2698 return false;
2699 }
2700 fprintf(gfp, _("Expressions differ.\n"));
2701 return false;
2702 }
2703 error(_("Cannot compare an equation with a non-equation."));
2704 return false;
2705 }
2706 if (compare_es(i, j) > 0) {
2707 fprintf(gfp, _("Equations are identical.\n"));
2708 return true;
2709 }
2710 if (solved_equation(i) && solved_equation(j)) {
2711 if (compare_rhs(i, j, &diff_sign)) {
2712 goto times_neg1;
2713 }
2714 if (approx) {
2715 debug_string(0, _("Approximating both equations..."));
2716 approximate(rhs[i], &n_rhs[i]);
2717 approximate(rhs[j], &n_rhs[j]);
2718 if (compare_rhs(i, j, &diff_sign)) {
2719 goto times_neg1;
2720 }
2721 }
2722 debug_string(0, _("Simplifying both equations..."));
2723 symb_flag = symb;
2724 simpa_repeat_side(rhs[i], &n_rhs[i], false, true);
2725 simpa_repeat_side(rhs[j], &n_rhs[j], false, true);
2726 symb_flag = false;
2727 if (approx) {
2728 approximate(rhs[i], &n_rhs[i]);
2729 approximate(rhs[j], &n_rhs[j]);
2730 }
2731 if (compare_rhs(i, j, &diff_sign)) {
2732 goto times_neg1;
2733 }
2734 #if !SILENT
2735 if (debug_level >= 0) {
2736 list_sub(i);
2737 list_sub(j);
2738 }
2739 #endif
2740 uf_simp(rhs[i], &n_rhs[i]);
2741 uf_simp(rhs[j], &n_rhs[j]);
2742 if (approx) {
2743 approximate(rhs[i], &n_rhs[i]);
2744 approximate(rhs[j], &n_rhs[j]);
2745 }
2746 if (compare_rhs(i, j, &diff_sign)) {
2747 goto times_neg1;
2748 }
2749 }
2750 debug_string(0, _("Solving both equations for zero and expanding..."));
2751 if (solve_sub(&zero_token, 1, lhs[i], &n_lhs[i], rhs[i], &n_rhs[i]) <= 0)
2752 return false;
2753 if (solve_sub(&zero_token, 1, lhs[j], &n_lhs[j], rhs[j], &n_rhs[j]) <= 0)
2754 return false;
2755 if (compare_rhs(i, j, &diff_sign)) {
2756 fprintf(gfp, _("Equations are identical.\n"));
2757 return true;
2758 }
2759 uf_simp(rhs[i], &n_rhs[i]);
2760 uf_simp(rhs[j], &n_rhs[j]);
2761 if (compare_rhs(i, j, &diff_sign)) {
2762 fprintf(gfp, _("Equations are identical.\n"));
2763 return true;
2764 }
2765 if (approx) {
2766 debug_string(0, _("Approximating both equations..."));
2767 approximate(rhs[i], &n_rhs[i]);
2768 approximate(rhs[j], &n_rhs[j]);
2769 if (compare_rhs(i, j, &diff_sign)) {
2770 fprintf(gfp, _("Equations are identical.\n"));
2771 return true;
2772 }
2773 }
2774 debug_string(0, _("Simplifying both equations..."));
2775 symb_flag = symb;
2776 simpa_repeat_side(rhs[i], &n_rhs[i], false, false);
2777 simpa_repeat_side(rhs[j], &n_rhs[j], false, false);
2778 symb_flag = false;
2779 if (approx) {
2780 approximate(rhs[i], &n_rhs[i]);
2781 approximate(rhs[j], &n_rhs[j]);
2782 }
2783 if (compare_rhs(i, j, &diff_sign)) {
2784 fprintf(gfp, _("Equations are identical.\n"));
2785 return true;
2786 }
2787 if (solve_sub(&zero_token, 1, lhs[i], &n_lhs[i], rhs[i], &n_rhs[i]) <= 0)
2788 return false;
2789 if (solve_sub(&zero_token, 1, lhs[j], &n_lhs[j], rhs[j], &n_rhs[j]) <= 0)
2790 return false;
2791 uf_simp(rhs[i], &n_rhs[i]);
2792 uf_simp(rhs[j], &n_rhs[j]);
2793 if (approx) {
2794 approximate(rhs[i], &n_rhs[i]);
2795 approximate(rhs[j], &n_rhs[j]);
2796 }
2797 if (compare_rhs(i, j, &diff_sign)) {
2798 fprintf(gfp, _("Equations are identical.\n"));
2799 return true;
2800 }
2801 fprintf(gfp, _("Equations differ.\n"));
2802 return false;
2803 times_neg1:
2804 if (!diff_sign && lhs[i][0].token.variable == lhs[j][0].token.variable) {
2805 fprintf(gfp, _("Equations are identical.\n"));
2806 return true;
2807 }
2808 fprintf(gfp, _("Variable "));
2809 list_proc(lhs[i], n_lhs[i], false);
2810 fprintf(gfp, _(" in the first equation\nis equal to "));
2811 if (diff_sign) {
2812 fprintf(gfp, "-");
2813 }
2814 list_proc(lhs[j], n_lhs[j], false);
2815 fprintf(gfp, _(" in the second equation.\n"));
2816 #if LIBRARY
2817 if (diff_sign)
2818 error(_("RHS appears negated."));
2819 else
2820 error(_("Different LHS variable name, otherwise the same."));
2821 return false;
2822 #else
2823 return 2;
2824 #endif
2825 }
2826
2827 /*
2828 * Display the specified floating point value.
2829 * If it is equal to a simple fraction, display that too.
2830 *
2831 * Return true if a fraction was displayed.
2832 */
2833 int
display_fraction(value)2834 display_fraction(value)
2835 double value;
2836 {
2837 double d4, d5;
2838 int rv = false;
2839
2840 f_to_fraction(value, &d4, &d5);
2841 fprintf(gfp, "%.*g", precision, value);
2842 if (d5 != 1.0) {
2843 fprintf(gfp, " = %.*g/%.*g", precision, d4, precision, d5);
2844 rv = true;
2845 }
2846 fprintf(gfp, "\n");
2847 return rv;
2848 }
2849
2850 /*
2851 * The divide command.
2852 */
2853 int
divide_cmd(cp)2854 divide_cmd(cp)
2855 char *cp;
2856 {
2857 long v = 0, v_tmp; /* Mathomatic variables */
2858 int i, j;
2859 int nleft = 0, nright = 0;
2860 double lcm, d1, d2, d3, d4, d5;
2861 complexs c1, c2, c3;
2862 char *cp_start;
2863
2864 cp_start = cp;
2865 pull_number = -1; /* Operands are last two entered expressions when using library. */
2866 if (*cp && isvarchar(*cp)) {
2867 cp = parse_var(&v, cp);
2868 if (cp == NULL || (*cp && !isspace(*cp) && *cp != ',')) {
2869 if (cp == NULL) {
2870 reset_error();
2871 }
2872 cp = cp_start;
2873 v = 0;
2874 } else {
2875 cp = skip_comma_space(cp);
2876 SP(_("You have entered a base variable."));
2877 EP(_("Polynomial division will be based on this variable."));
2878 point_flag = false;
2879 }
2880 }
2881 i = next_espace();
2882 if (*cp) {
2883 input_column += (cp - cp_start);
2884 cp = parse_expr(rhs[i], &nright, cp, false);
2885 if (cp == NULL || nright <= 0) {
2886 return false;
2887 }
2888 }
2889 if (*cp) {
2890 cp_start = cp;
2891 cp = skip_comma_space(cp);
2892 input_column += (cp - cp_start);
2893 cp = parse_expr(lhs[i], &nleft, cp, false);
2894 if (cp == NULL || extra_characters(cp) || nleft <= 0) {
2895 return false;
2896 }
2897 }
2898 do_repeat_prompt:
2899 /* prompt for the two operands */
2900 my_strlcpy(prompt_str, _("Enter dividend: "), sizeof(prompt_str));
2901 if (nright == 0 && !get_expr(rhs[i], &nright)) {
2902 return repeat_flag;
2903 }
2904 my_strlcpy(prompt_str, _("Enter divisor: "), sizeof(prompt_str));
2905 if (nleft == 0 && !get_expr(lhs[i], &nleft)) {
2906 return repeat_flag;
2907 }
2908 fprintf(gfp, "\n");
2909 /* simplify and expand the operand expressions */
2910 #if 1
2911 simp_loop(rhs[i], &nright);
2912 uf_simp(rhs[i], &nright);
2913 simp_loop(lhs[i], &nleft);
2914 uf_simp(lhs[i], &nleft);
2915 #else
2916 /* approximates, too */
2917 calc_simp(rhs[i], &nright);
2918 calc_simp(lhs[i], &nleft);
2919 #endif
2920 /* if division by zero, display a warning */
2921 if (get_constant(lhs[i], nleft, &d2)) {
2922 check_divide_by_zero(d2);
2923 }
2924 /* Do constant division if 2 normal numbers were entered */
2925 if (get_constant(rhs[i], nright, &d1) && get_constant(lhs[i], nleft, &d2)) {
2926 fprintf(gfp, _("Result of numerical division:\n"));
2927 d3 = gcd_verified(d1, d2);
2928 d4 = modf(d1 / d2, &d5);
2929 fprintf(gfp, "%.*g/%.*g = %.*g", precision, d1, precision, d2, precision, d1 / d2);
2930 if (d3 != 0.0 && d3 != 1.0 && (d2 / d3) != 1.0) {
2931 if ((d1 / d2) < 0) {
2932 fprintf(gfp, " = -%.*g/%.*g", precision, fabs(d1 / d3), precision, fabs(d2 / d3));
2933 } else {
2934 fprintf(gfp, " = %.*g/%.*g", precision, fabs(d1 / d3), precision, fabs(d2 / d3));
2935 }
2936 }
2937 if (d3 != 0 && d4 != 0 && d5 != 0) {
2938 if ((d1 / d2) < 0) {
2939 fprintf(gfp, " = -(%.*g + (%.*g/%.*g))", precision, fabs(d5), precision, fabs(d4 * (d2 / d3)), precision, fabs(d2 / d3));
2940 } else {
2941 fprintf(gfp, " = %.*g + (%.*g/%.*g)", precision, fabs(d5), precision, fabs(d4 * (d2 / d3)), precision, fabs(d2 / d3));
2942 }
2943 }
2944 fprintf(gfp, _("\nQuotient: %.*g, Remainder: %.*g\n"), precision, d5, precision, d4 * d2);
2945 d1 = fabs(d1);
2946 d2 = fabs(d2);
2947 if (d3 == 0.0) {
2948 fprintf(gfp, _("No GCD found.\n"));
2949 if (repeat_flag)
2950 goto do_repeat;
2951 return true;
2952 }
2953 fprintf(gfp, "GCD = ");
2954 if (d3 >= 4.0 && factor_one(d3) && !is_prime()) {
2955 display_unique();
2956 } else {
2957 display_fraction(d3);
2958 }
2959 lcm = (d1 * d2) / d3;
2960 fprintf(gfp, "LCM = ");
2961 if (lcm >= 4.0 && factor_one(lcm) && !is_prime()) {
2962 display_unique();
2963 } else {
2964 display_fraction(lcm);
2965 }
2966 if (repeat_flag)
2967 goto do_repeat;
2968 return true;
2969 }
2970 /* else do complex number division if 2 complex numbers were entered */
2971 if (parse_complex(rhs[i], nright, &c1) && parse_complex(lhs[i], nleft, &c2)) {
2972 fprintf(gfp, _("Result of complex number division:\n"));
2973 c3 = complex_div(c1, c2);
2974 fprintf(gfp, "%.*g %+.*g*i\n\n", precision, c3.re, precision, c3.im);
2975 if (repeat_flag)
2976 goto do_repeat;
2977 return true;
2978 }
2979 /* else do polynomial division and univariate GCD display */
2980 v_tmp = v;
2981 if (poly_div(rhs[i], nright, lhs[i], nleft, &v_tmp)) {
2982 simp_divide(tlhs, &n_tlhs);
2983 simp_divide(trhs, &n_trhs);
2984 list_var(v_tmp, 0);
2985 fprintf(gfp, _("Polynomial division successful using base variable %s.\n"), var_str);
2986 fprintf(gfp, _("The quotient is:\n"));
2987 fractions_and_group(tlhs, &n_tlhs);
2988 list_factor(tlhs, &n_tlhs, false);
2989 fprintf(gfp, _("\n\nThe remainder is:\n"));
2990 fractions_and_group(trhs, &n_trhs);
2991 list_factor(trhs, &n_trhs, false);
2992 fprintf(gfp, "\n");
2993 } else {
2994 SP(_("Polynomial division failed,"));
2995 SP(_("because the given polynomials cannot be divided in the given order,"));
2996 EP(_("according to the rules of polynomial division."));
2997 }
2998 fprintf(gfp, "\n");
2999 j = poly_gcd(rhs[i], nright, lhs[i], nleft, v);
3000 if (j == 0) {
3001 j = poly_gcd(lhs[i], nleft, rhs[i], nright, v);
3002 }
3003 if (j > 0) {
3004 simp_divide(trhs, &n_trhs);
3005 fprintf(gfp, _("Polynomial GCD (after %d Euclidean algorithm iterations):\n"), j);
3006 fractions_and_group(trhs, &n_trhs);
3007 list_factor(trhs, &n_trhs, false);
3008 fprintf(gfp, "\n");
3009 blt(tes, trhs, n_trhs * sizeof(token_type));
3010 n_tes = n_trhs;
3011 if (poly_factor(tes, &n_tes, true)) {
3012 simp_loop(tes, &n_tes);
3013 fprintf(gfp, _("Polynomial GCD (after quick polynomial factoring):\n"));
3014 fractions_and_group(tes, &n_tes);
3015 list_factor(tes, &n_tes, false);
3016 fprintf(gfp, "\n");
3017 }
3018 } else {
3019 SP(_("No additive univariate polynomial GCD found."));
3020 SP(_("This does not mean there is no GCD; it could be multivariate,"));
3021 EP(_("or contain too much floating point round-off error."));
3022 }
3023 if (repeat_flag)
3024 goto do_repeat;
3025 return true;
3026
3027 do_repeat:
3028 nright = 0;
3029 nleft = 0;
3030 goto do_repeat_prompt;
3031 }
3032
3033 /*
3034 * The eliminate command.
3035 */
3036 int
eliminate_cmd(cp)3037 eliminate_cmd(cp)
3038 char *cp;
3039 {
3040 long v, last_v, v1, va[MAX_VARS]; /* Mathomatic variables */
3041 int vc = 0; /* variable count */
3042 int i = 0, n;
3043 int success_flag = false, did_something = false, using_flag;
3044 char used[N_EQUATIONS];
3045 char *cp_start;
3046 char buf[MAX_CMD_LEN];
3047
3048 CLEAR_ARRAY(used);
3049 if (current_not_defined()) {
3050 return false;
3051 }
3052 if (*cp == '\0') {
3053 my_strlcpy(prompt_str, _("Enter variables to eliminate: "), sizeof(prompt_str));
3054 cp = get_string(buf, sizeof(buf));
3055 if (cp == NULL || *cp == '\0') {
3056 return false;
3057 }
3058 }
3059 cp_start = cp;
3060 next_var:
3061 if (vc) {
3062 v = va[--vc];
3063 } else if (*cp) {
3064 if (is_all(cp)) {
3065 cp = skip_param(cp);
3066 vc = 0;
3067 last_v = 0;
3068 for (;;) {
3069 v1 = -1;
3070 for (i = 0; i < n_lhs[cur_equation]; i += 2) {
3071 if (lhs[cur_equation][i].kind == VARIABLE
3072 && lhs[cur_equation][i].token.variable > last_v) {
3073 if (v1 == -1 || lhs[cur_equation][i].token.variable < v1) {
3074 v1 = lhs[cur_equation][i].token.variable;
3075 }
3076 }
3077 }
3078 for (i = 0; i < n_rhs[cur_equation]; i += 2) {
3079 if (rhs[cur_equation][i].kind == VARIABLE
3080 && rhs[cur_equation][i].token.variable > last_v) {
3081 if (v1 == -1 || rhs[cur_equation][i].token.variable < v1) {
3082 v1 = rhs[cur_equation][i].token.variable;
3083 }
3084 }
3085 }
3086 if (v1 == -1)
3087 break;
3088 last_v = v1;
3089 if ((v1 & VAR_MASK) > SIGN) {
3090 if (vc >= ARR_CNT(va)) {
3091 break;
3092 }
3093 va[vc++] = v1;
3094 }
3095 }
3096 goto next_var;
3097 }
3098 cp = parse_var2(&v, cp);
3099 if (cp == NULL) {
3100 return false;
3101 }
3102 } else {
3103 if (repeat_flag) {
3104 if (success_flag) {
3105 success_flag = false;
3106 cp = cp_start;
3107 goto next_var; /* repeat until failure to substitute anything */
3108 }
3109 }
3110 if (did_something) {
3111 did_something = return_result(cur_equation);
3112 } else {
3113 error(_("No substitutions made."));
3114 }
3115 return did_something;
3116 }
3117 using_flag = (strcmp_tospace(cp, "using") == 0);
3118 if (using_flag) {
3119 cp = skip_param(cp);
3120 if (*cp == '#')
3121 cp++;
3122 i = decstrtol(cp, &cp) - 1;
3123 if (not_defined(i)) {
3124 return false;
3125 }
3126 }
3127 if (!var_in_equation(cur_equation, v)) {
3128 #if !SILENT
3129 if (!repeat_flag) {
3130 list_var(v, 0);
3131 printf(_("Variable %s not found in current equation.\n"), var_str);
3132 }
3133 #endif
3134 goto next_var;
3135 }
3136 if (using_flag) {
3137 if (!elim_sub(i, v))
3138 goto next_var;
3139 } else {
3140 n = 1;
3141 i = cur_equation;
3142 for (;; n++) {
3143 if (n >= n_equations) {
3144 goto next_var;
3145 }
3146 if (i <= 0)
3147 i = n_equations - 1;
3148 else
3149 i--;
3150 if (used[i])
3151 continue;
3152 if (n_lhs[i] && n_rhs[i] && var_in_equation(i, v)) {
3153 if (elim_sub(i, v))
3154 break;
3155 }
3156 }
3157 }
3158 success_flag = true;
3159 did_something = true;
3160 used[i] = true;
3161 goto next_var;
3162 }
3163
3164 /*
3165 * Solve equation number i for v and substitute the RHS
3166 * into all occurrences of v in the current equation, then simplify.
3167 */
3168 static int
elim_sub(i,v)3169 elim_sub(i, v)
3170 int i; /* equation number */
3171 long v; /* Mathomatic variable */
3172 {
3173 token_type want;
3174 int solved;
3175
3176 if (i == cur_equation) {
3177 error(_("Error: source and destination are the same."));
3178 return false;
3179 }
3180 solved = (solved_equation(i) && lhs[i][0].token.variable == v);
3181 #if !SILENT
3182 list_var(v, 0);
3183 if (solved) {
3184 fprintf(gfp, _("Eliminating variable %s using solved equation #%d...\n"), var_str, i + 1);
3185 } else {
3186 fprintf(gfp, _("Solving equation #%d for %s and substituting into the current equation...\n"), i + 1, var_str);
3187 }
3188 #endif
3189 if (!solved) {
3190 want.level = 1;
3191 want.kind = VARIABLE;
3192 want.token.variable = v;
3193 if (solve_sub(&want, 1, lhs[i], &n_lhs[i], rhs[i], &n_rhs[i]) <= 0) {
3194 error(_("Solve failed."));
3195 return false;
3196 }
3197 }
3198 subst_var_with_exp(rhs[cur_equation], &n_rhs[cur_equation], rhs[i], n_rhs[i], v);
3199 subst_var_with_exp(lhs[cur_equation], &n_lhs[cur_equation], rhs[i], n_rhs[i], v);
3200
3201 simp_equation(cur_equation);
3202 return true;
3203 }
3204
3205 /*
3206 * The display command.
3207 *
3208 * Displays equations in multi-line fraction format.
3209 *
3210 * Return number of expressions displayed.
3211 */
3212 int
display_cmd(cp)3213 display_cmd(cp)
3214 char *cp;
3215 {
3216 int i, j;
3217 char *cp1;
3218 jmp_buf save_save;
3219 int factor_flag = false, displayed = 0;
3220 int orig_fractions_display_mode, new_fractions_display_mode;
3221
3222 new_fractions_display_mode = orig_fractions_display_mode = fractions_display;
3223 for (;; cp = skip_param(cp)) {
3224 if (strncasecmp(cp, "factor", 4) == 0) {
3225 factor_flag = true;
3226 continue;
3227 }
3228 if (strncasecmp(cp, "simple", 4) == 0) {
3229 new_fractions_display_mode = 1;
3230 continue;
3231 }
3232 if (strncasecmp(cp, "mixed", 3) == 0) {
3233 new_fractions_display_mode = 2;
3234 continue;
3235 }
3236 break;
3237 }
3238 do {
3239 cp1 = cp;
3240 if (!get_range(&cp, &i, &j)) {
3241 return false;
3242 }
3243 if (*cp && cp == cp1) {
3244 error(_("Invalid argument. Expecting equation number or range."));
3245 return false;
3246 }
3247 for (; i <= j; i++) {
3248 if (n_lhs[i] > 0) {
3249 blt(save_save, jmp_save, sizeof(jmp_save));
3250 if (setjmp(jmp_save) != 0) { /* trap errors */
3251 fractions_display = orig_fractions_display_mode;
3252 blt(jmp_save, save_save, sizeof(jmp_save));
3253 printf("Skipping equation number %d.\n", i + 1);
3254 continue;
3255 }
3256 fractions_display = new_fractions_display_mode;
3257 make_fractions_and_group(i);
3258 fractions_display = orig_fractions_display_mode;
3259 if (factor_flag || factor_int_flag) {
3260 factor_int_equation(i);
3261 }
3262 blt(jmp_save, save_save, sizeof(jmp_save));
3263 #if LIBRARY
3264 free_result_str();
3265 result_str = flist_equation_string(i);
3266 if (result_str == NULL)
3267 result_str = list_equation(i, false);
3268 if (result_str)
3269 result_en = i;
3270 if (gfp != stdout) {
3271 if (flist_equation(i) > 0) {
3272 displayed++;
3273 }
3274 }
3275 #else
3276 if (flist_equation(i) > 0) {
3277 displayed++;
3278 }
3279 #endif
3280 }
3281 }
3282 } while (*cp);
3283 #if LIBRARY
3284 return(result_str != NULL);
3285 #else
3286 return(displayed);
3287 #endif
3288 }
3289
3290 /*
3291 * The list command.
3292 */
3293 int
list_cmd(cp)3294 list_cmd(cp)
3295 char *cp;
3296 {
3297 int k;
3298 int first, last;
3299 char *cp1;
3300 int export_flag = 0;
3301 #if SHELL_OUT
3302 char cl[MAX_CMD_LEN];
3303 int primes_flag = false;
3304 int ev; /* exit value */
3305 #endif
3306
3307 if (strncasecmp(cp, "gnuplot", 3) == 0) {
3308 export_flag = 3;
3309 cp = skip_param(cp);
3310 } else if (strncasecmp(cp, "export", 3) == 0) {
3311 export_flag = 2;
3312 cp = skip_param(cp);
3313 } else if (strncasecmp(cp, "maxima", 3) == 0) {
3314 export_flag = 1;
3315 cp = skip_param(cp);
3316 } else if (strncasecmp(cp, "hexadecimal", 3) == 0) {
3317 export_flag = 4;
3318 cp = skip_param(cp);
3319 #if SHELL_OUT
3320 } else if (strncasecmp(cp, "primes", 5) == 0) {
3321 primes_flag = true;
3322 cp = skip_param(cp);
3323 #endif
3324 }
3325 #if SHELL_OUT
3326 if (primes_flag) {
3327 if (gfp && gfp_filename && gfp_filename[0]) {
3328 if (snprintf(cl, sizeof(cl), "matho-primes -u %s >%s%s", cp, gfp_append_flag ? ">" : "", gfp_filename) >= sizeof(cl)) {
3329 error(_("Command-line too long."));
3330 return false;
3331 }
3332 clean_up(); /* end any redirection */
3333 } else {
3334 if (snprintf(cl, sizeof(cl), "matho-primes -u %s", cp) >= sizeof(cl)) {
3335 error(_("Command-line too long."));
3336 return false;
3337 }
3338 }
3339 if ((ev = shell_out(cl))) {
3340 error(_("Abnormal termination of matho-primes."));
3341 printf(_("Decimal exit value = %d, shell command-line = %s\n"), ev, cl);
3342 return false;
3343 }
3344 return true;
3345 }
3346 #endif
3347 do {
3348 cp1 = cp;
3349 if (!get_range(&cp, &first, &last)) {
3350 return false;
3351 }
3352 if (*cp && cp == cp1) {
3353 error(_("Invalid argument. Expecting equation number or range."));
3354 return false;
3355 }
3356 for (k = first; k <= last; k++) {
3357 if (n_lhs[k] <= 0)
3358 continue;
3359 #if LIBRARY
3360 free_result_str();
3361 result_str = list_equation(k, export_flag);
3362 if (result_str)
3363 result_en = k;
3364 else
3365 return false;
3366 if (gfp == stdout) {
3367 continue;
3368 }
3369 #endif
3370 list1_sub(k, export_flag);
3371 }
3372 } while (*cp);
3373 return true;
3374 }
3375
3376 /*
3377 * The code command.
3378 */
3379 int
code_cmd(cp)3380 code_cmd(cp)
3381 char *cp;
3382 {
3383 int i, j, k;
3384 int li, ri;
3385 enum language_list language = C;
3386 int int_flag = false, displayed = false;
3387 char *cp1;
3388
3389 for (;; cp = skip_param(cp)) {
3390 if (strcmp_tospace(cp, "c") == 0 || strcmp_tospace(cp, "c++") == 0) {
3391 language = C;
3392 continue;
3393 }
3394 if (strcmp_tospace(cp, "java") == 0) {
3395 language = JAVA;
3396 continue;
3397 }
3398 if (strcmp_tospace(cp, "python") == 0) {
3399 language = PYTHON;
3400 continue;
3401 }
3402 if (strncasecmp(cp, "integer", 3) == 0) {
3403 int_flag = true;
3404 continue;
3405 }
3406 break;
3407 }
3408 do {
3409 cp1 = cp;
3410 if (!get_range(&cp, &i, &j)) {
3411 return false;
3412 }
3413 if (*cp && cp == cp1) {
3414 error(_("Invalid argument. Expecting equation number or range."));
3415 return false;
3416 }
3417 for (k = i; k <= j; k++) {
3418 if (n_lhs[k] <= 0)
3419 continue;
3420 if (n_rhs[k] == 0 || n_lhs[k] != 1 || lhs[k][0].kind != VARIABLE) {
3421 warning(_("Can't make assignment statement because this is not an equation."));
3422 } else if (!solved_equation(k)) {
3423 warning(_("Equation is not solved for a normal variable."));
3424 }
3425 simp_i(lhs[k], &n_lhs[k]);
3426 if (int_flag) {
3427 /* factor_constants() for more accurate integer results. */
3428 do {
3429 simp_loop(lhs[k], &n_lhs[k]);
3430 } while (factor_constants(lhs[k], &n_lhs[k], 6));
3431 /* Turn the power operator into the multiply operator, if raised to the power of a constant. */
3432 uf_repeat_always(lhs[k], &n_lhs[k]);
3433 }
3434 if (n_rhs[k] > 0) {
3435 simp_i(rhs[k], &n_rhs[k]);
3436 if (int_flag) {
3437 /* factor_constants() for more accurate integer results. */
3438 do {
3439 simp_loop(rhs[k], &n_rhs[k]);
3440 } while (factor_constants(rhs[k], &n_rhs[k], 6));
3441 /* Turn the power operator into the multiply operator, if raised to the power of a constant. */
3442 uf_repeat_always(rhs[k], &n_rhs[k]);
3443 }
3444 }
3445 make_fractions_and_group(k);
3446 if (int_flag) {
3447 if ((!(li = int_expr(lhs[k], n_lhs[k])) || !(ri = int_expr(rhs[k], n_rhs[k])))) {
3448 warning(_("Not an integer expression, but this rounded code may possibly work:"));
3449 } else if (li < 0 || ri < 0) {
3450 warning(_("This integer expression contains non-integer divides:"));
3451 }
3452 }
3453 #if LIBRARY
3454 free_result_str();
3455 result_str = string_code_equation(k, language, int_flag);
3456 if (result_str)
3457 result_en = k;
3458 else
3459 return false;
3460 if (gfp == stdout) {
3461 displayed = true;
3462 continue;
3463 }
3464 #endif
3465 if (list_code_equation(k, language, int_flag) > 0) {
3466 displayed = true;
3467 }
3468 }
3469 } while (*cp);
3470 return displayed;
3471 }
3472
3473 /*
3474 * Compare function for qsort(3).
3475 */
3476 static int
vcmp(p1,p2)3477 vcmp(p1, p2)
3478 sort_type *p1, *p2;
3479 {
3480 if (p2->count == p1->count) {
3481 if (p1->v < p2->v)
3482 return -1;
3483 if (p1->v == p2->v)
3484 return 0;
3485 return 1;
3486 }
3487 return(p2->count - p1->count);
3488 }
3489
3490 /*
3491 * The variables command.
3492 */
3493 int
variables_cmd(cp)3494 variables_cmd(cp)
3495 char *cp;
3496 {
3497 int start, stop;
3498 int k;
3499 int i1;
3500 long v1, last_v; /* Mathomatic variables */
3501 int vc, cnt; /* variable counts */
3502 sort_type va[MAX_VARS]; /* variable array */
3503 token_type *p1;
3504 int n1;
3505 enum language_list lang_code = 0; /* default to no programming language */
3506 int int_flag = false, imag_flag = false, count_flag = false, not_complex = false;
3507 char imag_array[N_EQUATIONS];
3508 char *range_start, *cp1;
3509 int array_element_flag = false;
3510 int rv = false;
3511 int n_tabs = 0;
3512
3513 CLEAR_ARRAY(imag_array);
3514 if (strncasecmp(cp, "counts", 5) == 0) {
3515 cp = skip_param(cp);
3516 count_flag = true;
3517 }
3518 if (strcmp_tospace(cp, "c") == 0 || strcmp_tospace(cp, "c++") == 0) {
3519 cp = skip_param(cp);
3520 lang_code = C;
3521 } else if (strcmp_tospace(cp, "java") == 0) {
3522 cp = skip_param(cp);
3523 lang_code = JAVA;
3524 } else if (strncasecmp(cp, "integer", 3) == 0) {
3525 cp = skip_param(cp);
3526 lang_code = C;
3527 int_flag = true;
3528 }
3529 if (strncasecmp(cp, "counts", 5) == 0) {
3530 cp = skip_param(cp);
3531 count_flag = true;
3532 }
3533 range_start = cp;
3534 do {
3535 cp1 = cp;
3536 if (!get_range(&cp, &start, &stop)) {
3537 return false;
3538 }
3539 if (*cp && cp == cp1) {
3540 error(_("Invalid argument. Expecting equation number or range."));
3541 return false;
3542 }
3543 for (k = start; k <= stop; k++) {
3544 if (n_lhs[k] <= 0)
3545 continue;
3546 if (n_rhs[k] > 0) {
3547 p1 = rhs[k];
3548 n1 = n_rhs[k];
3549 } else {
3550 p1 = lhs[k];
3551 n1 = n_lhs[k];
3552 }
3553 for (i1 = 0; i1 < n1; i1 += 2) {
3554 if (p1[i1].kind == VARIABLE && p1[i1].token.variable == IMAGINARY) {
3555 imag_flag = true;
3556 imag_array[k] = true;
3557 break;
3558 }
3559 }
3560 }
3561 } while (*cp);
3562 show_usage = false;
3563 last_v = 0;
3564 for (vc = 0;;) {
3565 if (vc >= ARR_CNT(va)) {
3566 error(_("Too many variables to list."));
3567 return false;
3568 }
3569 cnt = 0;
3570 v1 = -1;
3571 cp = range_start;
3572 do {
3573 cp1 = cp;
3574 if (!get_range(&cp, &start, &stop)) {
3575 return false;
3576 }
3577 #if DEBUG
3578 if (*cp && cp == cp1) {
3579 error_bug("Bug in variables command.");
3580 }
3581 #endif
3582 for (k = start; k <= stop; k++) {
3583 if (n_lhs[k] <= 0)
3584 continue;
3585 p1 = lhs[k];
3586 n1 = n_lhs[k];
3587 for (i1 = 0; i1 < n1; i1 += 2) {
3588 if (p1[i1].kind == VARIABLE && p1[i1].token.variable > last_v) {
3589 if (v1 == -1 || p1[i1].token.variable < v1) {
3590 v1 = p1[i1].token.variable;
3591 cnt = 1;
3592 } else if (p1[i1].token.variable == v1) {
3593 cnt++;
3594 }
3595 }
3596 }
3597 p1 = rhs[k];
3598 n1 = n_rhs[k];
3599 for (i1 = 0; i1 < n1; i1 += 2) {
3600 if (p1[i1].kind == VARIABLE && p1[i1].token.variable > last_v) {
3601 if (v1 == -1 || p1[i1].token.variable < v1) {
3602 v1 = p1[i1].token.variable;
3603 cnt = 1;
3604 } else if (p1[i1].token.variable == v1) {
3605 cnt++;
3606 }
3607 }
3608 }
3609 }
3610 } while (*cp);
3611 if (v1 == -1)
3612 break;
3613 last_v = v1;
3614 va[vc].v = v1;
3615 va[vc].count = cnt;
3616 vc++;
3617 }
3618 if (vc <= 0) {
3619 if (lang_code == 0) {
3620 error(_("Expression is numeric. No normal variables found."));
3621 return false;
3622 } else {
3623 return true;
3624 }
3625 }
3626 qsort((char *) va, vc, sizeof(*va), vcmp);
3627 for (i1 = 0; i1 < vc; i1++) {
3628 if (lang_code && va[i1].v < SIGN) {
3629 continue;
3630 }
3631 if ((va[i1].v & VAR_MASK) >= SIGN) {
3632 rv = true;
3633 }
3634 n_tabs = list_var(va[i1].v, lang_code ? lang_code : -5);
3635 if (lang_code) {
3636 if (strpbrk(var_str, "[]()"))
3637 array_element_flag = true;
3638 if (imag_flag) {
3639 for (k = 0;; k++) {
3640 if (k >= n_equations) {
3641 not_complex = true;
3642 break;
3643 }
3644 if (imag_array[k] && n_lhs[k] == 1
3645 && lhs[k][0].kind == VARIABLE && lhs[k][0].token.variable == va[i1].v) {
3646 fprintf(gfp, "_Complex ");
3647 n_tabs += 8;
3648 break;
3649 }
3650 }
3651 }
3652 if (int_flag || is_integer_var(va[i1].v) || (va[i1].v & VAR_MASK) == SIGN) {
3653 fprintf(gfp, "int%s%s;", (n_tabs + 1)/8 ? "\t" : "\t\t", var_str);
3654 } else {
3655 fprintf(gfp, "double%s%s;", (n_tabs + 1)/8 ? "\t" : "\t\t", var_str);
3656 }
3657 if (n_tabs >= 7)
3658 n_tabs -= 7;
3659 } else {
3660 fprintf(gfp, "%s", var_str);
3661 }
3662 if (count_flag) {
3663 if ((n_tabs / 8) == 0) {
3664 fprintf(gfp, "\t");
3665 }
3666 fprintf(gfp, _("\t/* count = %d */\n"), va[i1].count);
3667 } else {
3668 fprintf(gfp, "\n");
3669 }
3670 }
3671 if (lang_code && imag_flag && not_complex && rv) {
3672 printf("\n");
3673 warning(_("Some variables might need to be of the complex number type."));
3674 printf(_("Manual adjustments may be necessary\n"));
3675 printf(_("because of the appearance of the imaginary unit (i).\n"));
3676 }
3677 if (!rv) {
3678 error(_("Expressions are all numeric. No variables found."));
3679 }
3680 if (array_element_flag) {
3681 warning(_("Some defined variables were array elements or functions, requiring manual definition."));
3682 rv = false;
3683 }
3684 return rv;
3685 }
3686
3687 /*
3688 * The approximate command.
3689 */
3690 int
approximate_cmd(cp)3691 approximate_cmd(cp)
3692 char *cp;
3693 {
3694 int start, stop;
3695 int k;
3696 char *cp1;
3697
3698 do {
3699 cp1 = cp;
3700 if (!get_range(&cp, &start, &stop)) {
3701 return false;
3702 }
3703 if (*cp && cp == cp1) {
3704 error(_("Invalid argument. Expecting equation number or range."));
3705 return false;
3706 }
3707 for (k = start; k <= stop; k++) {
3708 if (n_lhs[k]) {
3709 approximate(lhs[k], &n_lhs[k]);
3710 if (n_rhs[k]) {
3711 approximate(rhs[k], &n_rhs[k]);
3712 }
3713 if (!return_result(k)) {
3714 return false;
3715 }
3716 }
3717 }
3718 } while (*cp);
3719 return true;
3720 }
3721
3722 /*
3723 * The replace command.
3724 */
3725 int
replace_cmd(cp)3726 replace_cmd(cp)
3727 char *cp;
3728 {
3729 int i, j;
3730 long last_v, v, va[MAX_VARS]; /* Mathomatic variables */
3731 int vc; /* variable count */
3732 char *cp_start, *cp1;
3733 int found, value_entered;
3734 int diff_sign;
3735
3736 cp_start = cp;
3737 if (current_not_defined()) {
3738 return false;
3739 }
3740 i = cur_equation;
3741 for (vc = 0; *cp; vc++) {
3742 if (strcmp_tospace(cp, "with") == 0) {
3743 if (vc) {
3744 repeat_flag = false;
3745 break;
3746 }
3747 }
3748 if (vc >= ARR_CNT(va)) {
3749 error(_("Too many variables specified."));
3750 return false;
3751 }
3752 cp = parse_var2(&va[vc], cp);
3753 if (cp == NULL) {
3754 return false;
3755 }
3756 if (!var_in_equation(i, va[vc])) {
3757 error(_("Variable not found."));
3758 return false;
3759 }
3760 }
3761 replace_again:
3762 n_tlhs = n_lhs[i];
3763 blt(tlhs, lhs[i], n_tlhs * sizeof(token_type));
3764 n_trhs = n_rhs[i];
3765 blt(trhs, rhs[i], n_trhs * sizeof(token_type));
3766 value_entered = false;
3767 last_v = 0;
3768 for (;;) {
3769 v = -1;
3770 for (j = 0; j < n_lhs[i]; j += 2) {
3771 if (lhs[i][j].kind == VARIABLE) {
3772 if (lhs[i][j].token.variable > last_v
3773 && (v == -1 || lhs[i][j].token.variable < v))
3774 v = lhs[i][j].token.variable;
3775 }
3776 }
3777 for (j = 0; j < n_rhs[i]; j += 2) {
3778 if (rhs[i][j].kind == VARIABLE) {
3779 if (rhs[i][j].token.variable > last_v
3780 && (v == -1 || rhs[i][j].token.variable < v))
3781 v = rhs[i][j].token.variable;
3782 }
3783 }
3784 if (v == -1) {
3785 break;
3786 }
3787 last_v = v;
3788 if (vc) {
3789 found = false;
3790 for (j = 0; j < vc; j++) {
3791 if (v == va[j])
3792 found = true;
3793 }
3794 if (!found)
3795 continue;
3796 if (*cp) {
3797 if (strcmp_tospace(cp, "with") != 0) {
3798 return false;
3799 }
3800 cp1 = skip_param(cp);
3801 input_column += (cp1 - cp_start);
3802 if ((cp1 = parse_expr(tes, &n_tes, cp1, true)) == NULL || n_tes <= 0) {
3803 return false;
3804 }
3805 goto do_this;
3806 }
3807 }
3808 list_var(v, 0);
3809 snprintf(prompt_str, sizeof(prompt_str), _("Enter %s: "), var_str);
3810 if (!get_expr(tes, &n_tes)) {
3811 continue;
3812 }
3813 value_entered = true;
3814 do_this:
3815 /* Disguise all variables in the entered expression by making them negative; */
3816 /* That way they won't be improperly substituted later, allowing variable interchange. */
3817 for (j = 0; j < n_tes; j += 2) {
3818 if (tes[j].kind == VARIABLE) {
3819 tes[j].token.variable = -tes[j].token.variable;
3820 }
3821 }
3822 subst_var_with_exp(tlhs, &n_tlhs, tes, n_tes, v);
3823 subst_var_with_exp(trhs, &n_trhs, tes, n_tes, v);
3824 }
3825 /* Restore disguised variables: */
3826 for (j = 0; j < n_tlhs; j += 2)
3827 if (tlhs[j].kind == VARIABLE && tlhs[j].token.variable < 0)
3828 tlhs[j].token.variable = -tlhs[j].token.variable;
3829 for (j = 0; j < n_trhs; j += 2)
3830 if (trhs[j].kind == VARIABLE && trhs[j].token.variable < 0)
3831 trhs[j].token.variable = -trhs[j].token.variable;
3832 if (repeat_flag) {
3833 calc_simp(tlhs, &n_tlhs);
3834 if (n_trhs) {
3835 calc_simp(trhs, &n_trhs);
3836 if (se_compare(tlhs, n_tlhs, trhs, n_trhs, &diff_sign) && !diff_sign) {
3837 fprintf(gfp, _("The result is an identity:\n"));
3838 }
3839 }
3840 list_tdebug(-10);
3841 if (value_entered) {
3842 fprintf(gfp, "Repeating:\n");
3843 goto replace_again;
3844 } else {
3845 return true;
3846 }
3847 }
3848 n_lhs[i] = n_tlhs;
3849 blt(lhs[i], tlhs, n_tlhs * sizeof(token_type));
3850 n_rhs[i] = n_trhs;
3851 blt(rhs[i], trhs, n_trhs * sizeof(token_type));
3852 simp_equation(i);
3853 return return_result(i);
3854 }
3855
3856 /*
3857 * The simplify command.
3858 *
3859 * Returns number of expressions simplified.
3860 */
3861 int
simplify_cmd(cp)3862 simplify_cmd(cp)
3863 char *cp;
3864 {
3865 int i, i1;
3866 int first, last;
3867 int k, k1, total_number_of_solutions, number_simplified = 0;
3868 long counter, counter_max, previous_solution_number[N_EQUATIONS];
3869 sign_array_type sa_mark, sa_value;
3870 int sign_flag = false, quick_flag = false, quickest_flag = false, symb = false, frac_flag = false;
3871 char *cp1;
3872
3873 for (;; cp = skip_param(cp)) {
3874 if (strncasecmp(cp, "sign", 4) == 0) {
3875 sign_flag = true;
3876 continue;
3877 }
3878 if (strncasecmp(cp, "symbolic", 4) == 0) {
3879 symb = true;
3880 continue;
3881 }
3882 if (strcmp_tospace(cp, "quickest") == 0) {
3883 quickest_flag = true;
3884 continue;
3885 }
3886 if (strcmp_tospace(cp, "quick") == 0) {
3887 quick_flag = true;
3888 continue;
3889 }
3890 if (strncasecmp(cp, "fraction", 4) == 0) {
3891 frac_flag = true;
3892 continue;
3893 }
3894 break;
3895 }
3896 do {
3897 cp1 = cp;
3898 if (!get_range(&cp, &first, &last)) {
3899 return false;
3900 }
3901 if (*cp && cp == cp1) {
3902 error(_("Invalid argument. Expecting equation number or range."));
3903 return false;
3904 }
3905 for (i = first; i <= last; i++) {
3906 if (n_lhs[i] <= 0)
3907 continue;
3908 number_simplified++;
3909 symb_flag = symb;
3910 if (quickest_flag) {
3911 simp_equation(i);
3912 } else {
3913 simpa_repeat(i, quick_flag, frac_flag);
3914 }
3915 symb_flag = false;
3916 if (!return_result(i)) {
3917 return false;
3918 }
3919 if (!sign_flag)
3920 continue;
3921 /* Now substitute all sign variables with +1 and -1. */
3922 CLEAR_ARRAY(previous_solution_number);
3923 CLEAR_ARRAY(sa_mark);
3924 for (k1 = 0; k1 < n_lhs[i]; k1 += 2) {
3925 if (lhs[i][k1].kind == VARIABLE && (lhs[i][k1].token.variable & VAR_MASK) == SIGN) {
3926 sa_mark[(lhs[i][k1].token.variable >> VAR_SHIFT) & SUBSCRIPT_MASK] = true;
3927 }
3928 }
3929 for (k1 = 0; k1 < n_rhs[i]; k1 += 2) {
3930 if (rhs[i][k1].kind == VARIABLE && (rhs[i][k1].token.variable & VAR_MASK) == SIGN) {
3931 sa_mark[(rhs[i][k1].token.variable >> VAR_SHIFT) & SUBSCRIPT_MASK] = true;
3932 }
3933 }
3934 for (k1 = 0, k = 0; k1 < ARR_CNT(sa_mark); k1++) {
3935 if (sa_mark[k1]) {
3936 k++;
3937 }
3938 }
3939 if (k == 0)
3940 continue;
3941 counter_max = (1L << k) - 1L;
3942 if (counter_max) {
3943 fprintf(gfp, _("There are %ld possible solutions.\n"), counter_max + 1);
3944 }
3945 for (counter = 0; counter <= counter_max; counter++) {
3946 i1 = next_espace();
3947 copy_espace(i, i1);
3948 for (k1 = 0, k = 0; k1 < ARR_CNT(sa_mark); k1++) {
3949 if (sa_mark[k1]) {
3950 sa_value[k1] = (((1L << k) & counter) != 0);
3951 k++;
3952 }
3953 }
3954 for (k1 = 0; k1 < n_lhs[i1]; k1 += 2) {
3955 if (lhs[i1][k1].kind == VARIABLE && (lhs[i1][k1].token.variable & VAR_MASK) == SIGN) {
3956 if (sa_value[(lhs[i1][k1].token.variable >> VAR_SHIFT) & SUBSCRIPT_MASK]) {
3957 lhs[i1][k1].kind = CONSTANT;
3958 lhs[i1][k1].token.constant = -1.0;
3959 } else {
3960 lhs[i1][k1].kind = CONSTANT;
3961 lhs[i1][k1].token.constant = 1.0;
3962 }
3963 }
3964 }
3965 for (k1 = 0; k1 < n_rhs[i1]; k1 += 2) {
3966 if (rhs[i1][k1].kind == VARIABLE && (rhs[i1][k1].token.variable & VAR_MASK) == SIGN) {
3967 if (sa_value[(rhs[i1][k1].token.variable >> VAR_SHIFT) & SUBSCRIPT_MASK]) {
3968 rhs[i1][k1].kind = CONSTANT;
3969 rhs[i1][k1].token.constant = -1.0;
3970 } else {
3971 rhs[i1][k1].kind = CONSTANT;
3972 rhs[i1][k1].token.constant = 1.0;
3973 }
3974 }
3975 }
3976 for (k1 = 0, k = false; k1 < ARR_CNT(sa_mark); k1++) {
3977 if (sa_mark[k1]) {
3978 if (k) {
3979 fprintf(gfp, ", ");
3980 } else {
3981 fprintf(gfp, _("Solution number %ld with "), counter + 1);
3982 }
3983 list_var((long) SIGN + (((long) k1) << VAR_SHIFT), 0);
3984 fprintf(gfp, "%s = ", var_str);
3985 if (sa_value[k1]) {
3986 fprintf(gfp, "-1");
3987 } else {
3988 fprintf(gfp, "1");
3989 }
3990 k = true;
3991 }
3992 }
3993 if (k)
3994 fprintf(gfp, ":\n");
3995 symb_flag = symb;
3996 if (quickest_flag) {
3997 simp_equation(i1);
3998 } else {
3999 simpa_repeat(i1, quick_flag, frac_flag);
4000 }
4001 symb_flag = false;
4002 for (k1 = 0; k1 < ARR_CNT(previous_solution_number); k1++) {
4003 if (previous_solution_number[k1]) {
4004 if (compare_es(k1, i1) > 0) {
4005 n_lhs[i1] = 0;
4006 n_rhs[i1] = 0;
4007 fprintf(gfp, _("is identical to solution number %ld.\n"), previous_solution_number[k1]);
4008 break;
4009 }
4010 }
4011 }
4012 if (n_lhs[i1]) {
4013 list_sub(i1);
4014 previous_solution_number[i1] = counter + 1;
4015 }
4016 }
4017 total_number_of_solutions = 0;
4018 for (k1 = 0; k1 < ARR_CNT(previous_solution_number); k1++) {
4019 if (previous_solution_number[k1]) {
4020 total_number_of_solutions++;
4021 }
4022 }
4023 if (total_number_of_solutions > 0) {
4024 number_simplified += total_number_of_solutions;
4025 fprintf(gfp, _("%d unique solutions stored in equation spaces for this expression (#%d).\n"), total_number_of_solutions, i + 1);
4026 }
4027 }
4028 } while (*cp);
4029 return number_simplified;
4030 }
4031
4032 /*
4033 * The factor command.
4034 */
4035 int
factor_cmd(cp)4036 factor_cmd(cp)
4037 char *cp;
4038 {
4039 int first, last;
4040 int i1;
4041 int found, rv = true;
4042 long v; /* Mathomatic variable */
4043 int valid_range = false, power_flag = false;
4044 char *cp_start;
4045 int count_down;
4046 char *cp1, *cp2;
4047 double d, ed;
4048 #if !LIBRARY
4049 char buf[MAX_CMD_LEN];
4050 #endif
4051
4052 cp_start = cp;
4053 if (strcmp_tospace(cp, "number") == 0) {
4054 cp = skip_param(cp);
4055 } else if (strcmp_tospace(cp, "numbers") == 0) {
4056 repeat_flag = true;
4057 cp = skip_param(cp);
4058 } else {
4059 if (strcmp_tospace(cp, "power") == 0) {
4060 power_flag = true;
4061 cp = skip_param(cp);
4062 }
4063 valid_range = get_range(&cp, &first, &last);
4064 if (!valid_range) {
4065 #if LIBRARY /* be consistent */
4066 return false;
4067 #else /* be helpful */
4068 if (*cp == '-' || isdigit(*cp)) {
4069 reset_error();
4070 printf(_("Factoring integers on command-line instead:\n"));
4071 point_flag = false;
4072 } else {
4073 extra_characters(cp);
4074 return false;
4075 }
4076 #endif
4077 }
4078 }
4079 if (!valid_range) {
4080 #if LIBRARY
4081 repeat_flag = false;
4082 #endif
4083 do {
4084 if (*cp == '\0') {
4085 retry:
4086 #if LIBRARY
4087 return false;
4088 #else
4089 my_strlcpy(prompt_str, _("Enter integers to factor: "), sizeof(prompt_str));
4090 cp = get_string(buf, sizeof(buf));
4091 if (cp == NULL)
4092 return false;
4093 cp_start = cp;
4094 #endif
4095 }
4096 if (*cp == '\0')
4097 return true;
4098 rv = true;
4099 for (; *cp; ) {
4100 cp1 = cp = skip_space(cp);
4101 errno = 0;
4102 ed = d = strtod(cp, &cp);
4103 if (cp == cp1 || errno || !isfinite(d)) {
4104 goto try_parsing;
4105 }
4106 cp = skip_space(cp);
4107 if (*cp && !isdigit(*cp)) {
4108 if (*cp == '-') {
4109 cp2 = cp = skip_space(++cp);
4110 errno = 0;
4111 ed = strtod(cp, &cp);
4112 if (cp == cp2 || errno || !isfinite(ed) || (*cp && *cp != ',' && !isspace(*cp))) {
4113 goto try_parsing;
4114 }
4115 } else {
4116 try_parsing:
4117 input_column += (cp1 - cp_start);
4118 cp = parse_expr(tes, &n_tes, cp1, false);
4119 if (cp == NULL)
4120 goto retry;
4121 cp_start = cp;
4122 if (n_tes <= 0)
4123 return rv;
4124 calc_simp(tes, &n_tes);
4125 if (n_tes != 1 || tes[0].kind != CONSTANT || !isfinite(tes[0].token.constant)) {
4126 error(_("Integer expected."));
4127 goto retry;
4128 }
4129 ed = d = tes[0].token.constant;
4130 }
4131 }
4132 cp = skip_comma_space(cp);
4133 count_down = (ed < d);
4134 for (; count_down ? (d >= ed) : (d <= ed); count_down ? (d -= 1.0) : (d += 1.0)) {
4135 if (d == 0) {
4136 fprintf(gfp, _("0 can be evenly divided by any number.\n"));
4137 continue;
4138 }
4139 if (!factor_one(d)) {
4140 error(_("Number too large to factor or not an integer."));
4141 rv = false;
4142 break;
4143 }
4144 #if !SILENT
4145 if (is_prime() && debug_level >= 0) {
4146 fprintf(gfp, _("Prime number: "));
4147 }
4148 #endif
4149 if (!display_unique())
4150 rv = false;
4151 }
4152 }
4153 } while (repeat_flag);
4154 return rv;
4155 }
4156 if (power_flag) {
4157 if (extra_characters(cp))
4158 return false;
4159 for (i1 = first; i1 <= last; i1++) {
4160 if (n_lhs[i1]) {
4161 /* factor_power(lhs[i1], &n_lhs[i1]); */
4162 do {
4163 simp_loop(lhs[i1], &n_lhs[i1]);
4164 } while (factor_power(lhs[i1], &n_lhs[i1]));
4165 if (n_rhs[i1]) {
4166 /* factor_power(rhs[i1], &n_rhs[i1]); */
4167 do {
4168 simp_loop(rhs[i1], &n_rhs[i1]);
4169 } while (factor_power(rhs[i1], &n_rhs[i1]));
4170 }
4171 if (!return_result(i1))
4172 return false;
4173 }
4174 }
4175 } else {
4176 do {
4177 v = 0;
4178 if (*cp) {
4179 if ((cp = parse_var2(&v, cp)) == NULL) {
4180 return false;
4181 }
4182 }
4183 if (v) {
4184 found = false;
4185 for (i1 = first; i1 <= last; i1++) {
4186 if (var_in_equation(i1, v)) {
4187 found = true;
4188 break;
4189 }
4190 }
4191 if (!found) {
4192 warning(_("Specified variable not found."));
4193 }
4194 }
4195 for (i1 = first; i1 <= last; i1++) {
4196 #if 0
4197 if (v == 0) {
4198 if (n_lhs[i1]) {
4199 simp_loop(lhs[i1], &n_lhs[i1]);
4200 poly_factor(lhs[i1], &n_lhs[i1], true);
4201 if (n_rhs[i1]) {
4202 simp_loop(rhs[i1], &n_rhs[i1]);
4203 poly_factor(rhs[i1], &n_rhs[i1], true);
4204 }
4205 }
4206 }
4207 #endif
4208 simpv_equation(i1, v);
4209 }
4210 } while (*cp);
4211 for (i1 = first; i1 <= last; i1++) {
4212 if (n_lhs[i1]) {
4213 if (!return_result(i1))
4214 return false;
4215 }
4216 }
4217 }
4218 return true;
4219 }
4220
4221 /*
4222 * Display the number of additive terms in the specified equation space.
4223 *
4224 * Return the total number of terms.
4225 */
4226 int
display_term_count(en)4227 display_term_count(en)
4228 int en; /* equation space number */
4229 {
4230 int left_count = 0, right_count = 0;
4231
4232 if (empty_equation_space(en))
4233 return 0;
4234 left_count = level1_plus_count(lhs[en], n_lhs[en]) + 1;
4235 if (n_rhs[en]) {
4236 right_count = level1_plus_count(rhs[en], n_rhs[en]) + 1;
4237 fprintf(gfp, "#%d: LHS consists of %d term%s; ", en + 1, left_count, (left_count == 1) ? "" : "s");
4238 fprintf(gfp, "RHS consists of %d term%s.\n", right_count, (right_count == 1) ? "" : "s");
4239 } else {
4240 fprintf(gfp, "#%d: ", en + 1);
4241 }
4242 fprintf(gfp, "Expression consists of a total of %d term%s.\n", left_count + right_count, ((left_count + right_count) == 1) ? "" : "s");
4243 return(left_count + right_count);
4244 }
4245
4246 /*
4247 * The unfactor command.
4248 */
4249 int
unfactor_cmd(cp)4250 unfactor_cmd(cp)
4251 char *cp;
4252 {
4253 int first, last;
4254 int k;
4255 int quick_flag = false, fraction_flag = false, power_flag = false, count_flag = false;
4256
4257 for (;; cp = skip_param(cp)) {
4258 if (strncasecmp(cp, "quick", 4) == 0) {
4259 quick_flag = true;
4260 continue;
4261 }
4262 if (strncasecmp(cp, "fraction", 4) == 0 || strncasecmp(cp, "fully", 4) == 0) {
4263 fraction_flag = true;
4264 continue;
4265 }
4266 if (strncasecmp(cp, "power", 4) == 0) {
4267 power_flag = true;
4268 continue;
4269 }
4270 if (strncasecmp(cp, "count", 4) == 0) {
4271 count_flag = true;
4272 continue;
4273 }
4274 break;
4275 }
4276 if (!get_range_eol(&cp, &first, &last)) {
4277 return false;
4278 }
4279 partial_flag = !fraction_flag;
4280 if (power_flag) {
4281 for (k = first; k <= last; k++) {
4282 if (n_lhs[k] <= 0)
4283 continue;
4284 if (quick_flag) {
4285 uf_power(lhs[k], &n_lhs[k]);
4286 } else {
4287 uf_allpower(lhs[k], &n_lhs[k]);
4288 }
4289 elim_loop(lhs[k], &n_lhs[k]);
4290 if (n_rhs[k]) {
4291 if (quick_flag) {
4292 uf_power(rhs[k], &n_rhs[k]);
4293 } else {
4294 uf_allpower(rhs[k], &n_rhs[k]);
4295 }
4296 elim_loop(rhs[k], &n_rhs[k]);
4297 }
4298 if (!return_result(k)) {
4299 partial_flag = true;
4300 return false;
4301 }
4302 if (count_flag) {
4303 display_term_count(k);
4304 }
4305 }
4306 } else {
4307 for (k = first; k <= last; k++) {
4308 if (n_lhs[k] <= 0)
4309 continue;
4310 if (quick_flag) {
4311 uf_tsimp(lhs[k], &n_lhs[k]);
4312 if (n_rhs[k]) {
4313 uf_tsimp(rhs[k], &n_rhs[k]);
4314 }
4315 } else {
4316 uf_simp(lhs[k], &n_lhs[k]);
4317 if (n_rhs[k]) {
4318 uf_simp(rhs[k], &n_rhs[k]);
4319 }
4320 }
4321 if (!return_result(k)) {
4322 partial_flag = true;
4323 return false;
4324 }
4325 if (count_flag) {
4326 display_term_count(k);
4327 }
4328 }
4329 }
4330 partial_flag = true;
4331 return true;
4332 }
4333
4334 int
div_loc_find(expression,n)4335 div_loc_find(expression, n)
4336 token_type *expression;
4337 int n;
4338 {
4339 int k, div_loc;
4340 int level;
4341
4342 level = min_level(expression, n);
4343 for (k = 1, div_loc = -1; k < n; k += 2) {
4344 if (expression[k].level == level && expression[k].token.operatr == DIVIDE) {
4345 if (div_loc >= 0) {
4346 error_bug("Expression not grouped.");
4347 }
4348 div_loc = k;
4349 }
4350 }
4351 return div_loc;
4352 }
4353
4354 /*
4355 * The fraction command.
4356 */
4357 int
fraction_cmd(cp)4358 fraction_cmd(cp)
4359 char *cp;
4360 {
4361 int i, div_loc;
4362 int first, last;
4363 int num_flag = false, den_flag = false, was_fraction;
4364
4365 for (;; cp = skip_param(cp)) {
4366 if (strncasecmp(cp, "numerator", 3) == 0) {
4367 num_flag = true;
4368 continue;
4369 }
4370 if (strncasecmp(cp, "denominator", 3) == 0) {
4371 den_flag = true;
4372 continue;
4373 }
4374 break;
4375 }
4376 if (!get_range_eol(&cp, &first, &last)) {
4377 return false;
4378 }
4379 show_usage = false;
4380 for (i = first; i <= last; i++) {
4381 if (n_lhs[i]) {
4382 was_fraction = false;
4383 simple_frac_repeat_side(lhs[i], &n_lhs[i]);
4384 div_loc = div_loc_find(lhs[i], n_lhs[i]);
4385 if (div_loc > 0) {
4386 was_fraction = true;
4387 if (num_flag && !den_flag) {
4388 n_lhs[i] = div_loc;
4389 } else if (den_flag && !num_flag) {
4390 blt(&lhs[i][0], &lhs[i][div_loc+1], (n_lhs[i] - (div_loc + 1)) * sizeof(token_type));
4391 n_lhs[i] -= (div_loc + 1);
4392 }
4393 }
4394 if (n_rhs[i]) {
4395 simple_frac_repeat_side(rhs[i], &n_rhs[i]);
4396 div_loc = div_loc_find(rhs[i], n_rhs[i]);
4397 if (div_loc > 0) {
4398 was_fraction = true;
4399 if (num_flag && !den_flag) {
4400 n_rhs[i] = div_loc;
4401 } else if (den_flag && !num_flag) {
4402 blt(&rhs[i][0], &rhs[i][div_loc+1], (n_rhs[i] - (div_loc + 1)) * sizeof(token_type));
4403 n_rhs[i] -= (div_loc + 1);
4404 }
4405 }
4406 }
4407 if ((num_flag || den_flag) && !was_fraction) {
4408 warning(_("Expression is not an algebraic fraction."));
4409 if (den_flag) {
4410 error(_("Could not extract denominator."));
4411 return false;
4412 }
4413 }
4414 if (!return_result(i))
4415 return false;
4416 }
4417 }
4418 return true;
4419 }
4420
4421 #if !LIBRARY
4422 /*
4423 * The quit command.
4424 */
4425 int
quit_cmd(cp)4426 quit_cmd(cp)
4427 char *cp;
4428 {
4429 int ev = 0;
4430
4431 if (*cp) {
4432 ev = decstrtol(cp, &cp);
4433 if (extra_characters(cp))
4434 return false;
4435 }
4436 exit_program(ev);
4437 return false; /* to placate the C compiler */
4438 }
4439 #endif
4440
4441 #if !SECURE
4442 /*
4443 * The read command.
4444 */
4445 int
read_cmd(cp)4446 read_cmd(cp)
4447 char *cp;
4448 {
4449 int rv;
4450
4451 if (security_level >= 3) {
4452 show_usage = false;
4453 error(_("Command disabled by security level."));
4454 return false;
4455 }
4456 if (!repeat_flag || *cp == '\0') {
4457 return read_file(cp);
4458 }
4459 do {
4460 rv = read_file(cp);
4461 } while (rv);
4462 return rv;
4463 }
4464
4465 /*
4466 * Main read command code.
4467 *
4468 * Return true if successful.
4469 */
4470 int
read_file(cp)4471 read_file(cp)
4472 char *cp;
4473 {
4474 int rv;
4475 FILE *fp;
4476 char buf[MAX_CMD_LEN];
4477 #if SHELL_OUT
4478 char cl[MAX_CMD_LEN];
4479 int ev; /* exit value */
4480 char *lister;
4481 #endif
4482
4483 if (*cp == '\0') {
4484 #if SHELL_OUT
4485 #if MINGW
4486 lister = "dir /W/P";
4487 #else
4488 lister = "ls -C";
4489 #endif
4490 if (gfp && gfp_filename && gfp_filename[0]) {
4491 if (snprintf(cl, sizeof(cl), "%s >%s%s", lister, gfp_append_flag ? ">" : "", gfp_filename) >= sizeof(cl)) {
4492 error(_("Command-line too long."));
4493 return false;
4494 }
4495 clean_up(); /* end any redirection */
4496 } else {
4497 if (snprintf(cl, sizeof(cl), "%s", lister) >= sizeof(cl)) {
4498 error(_("Command-line too long."));
4499 return false;
4500 }
4501 }
4502 #if !MINGW
4503 printf(_("Listing contents of "));
4504 output_current_directory(stdout);
4505 printf("\n");
4506 #endif
4507 if ((ev = shell_out(cl))) {
4508 error(_("Error executing directory lister."));
4509 printf(_("Decimal exit value = %d, shell command-line = %s\n"), ev, cl);
4510 return false;
4511 }
4512 return true;
4513 #else
4514 error(_("No file name specified."));
4515 return false;
4516 #endif
4517 }
4518 if (snprintf(buf, sizeof(buf), "%s.in", cp) >= sizeof(buf)) {
4519 error(_("File name too long."));
4520 return false;
4521 }
4522 fp = fopen(buf, "r");
4523 if (fp == NULL) {
4524 buf[strlen(cp)] = '\0';
4525 fp = fopen(buf, "r");
4526 if (fp == NULL) {
4527 if (chdir(buf)) {
4528 error(_("Can't open requested file to read or change directory to."));
4529 return false;
4530 } else {
4531 printf(_("Current working directory changed to "));
4532 return output_current_directory(stdout);
4533 }
4534 }
4535 }
4536 rv = read_sub(fp, buf);
4537 show_usage = false;
4538 if (fclose(fp)) {
4539 perror(buf);
4540 rv = 1;
4541 }
4542 if (rv == 100)
4543 return(true);
4544 #if !SILENT
4545 if (!quiet_mode) {
4546 if (rv) {
4547 if (!demo_mode) {
4548 printf(_("Reading of script file \"%s\" aborted due to failure return status\n"), buf);
4549 printf(_("of a command or expression parsing, or some other error listed above.\n"));
4550 }
4551 } else if (debug_level >= 0) {
4552 printf(_("Successfully finished reading script file \"%s\".\n"), buf);
4553 }
4554 }
4555 #endif
4556 return(!rv);
4557 }
4558
4559 /*
4560 * Read and process Mathomatic input from a file pointer.
4561 *
4562 * Return zero if no error, non-zero if read aborted.
4563 */
4564 int
read_sub(fp,filename)4565 read_sub(fp, filename)
4566 FILE *fp; /* open Mathomatic input file */
4567 char *filename; /* filename of fp */
4568 {
4569 int rv;
4570 jmp_buf save_save;
4571 char *cp;
4572 int something_there = false;
4573
4574 if (fp == NULL) {
4575 return -1;
4576 }
4577 blt(save_save, jmp_save, sizeof(jmp_save));
4578 if ((rv = setjmp(jmp_save)) != 0) { /* trap errors */
4579 clean_up();
4580 if (rv == 14) {
4581 error(_("Expression too large."));
4582 }
4583 previous_return_value = 0;
4584 } else {
4585 while ((cp = fgets((char *) tlhs, n_tokens * sizeof(token_type), fp)) != NULL) {
4586 if (*cp) {
4587 something_there = true;
4588 }
4589 if (!display_process(cp)) {
4590 longjmp(jmp_save, 3); /* jump to the above error trap */
4591 }
4592 }
4593 if (!something_there) {
4594 if (chdir(filename)) {
4595 error(_("Empty file (no script to read)."));
4596 rv = 1;
4597 } else {
4598 printf(_("Current directory changed to "));
4599 output_current_directory(stdout);
4600 rv = 100;
4601 }
4602 }
4603 }
4604 blt(jmp_save, save_save, sizeof(jmp_save));
4605 return rv;
4606 }
4607 #endif
4608
4609 #if SHELL_OUT
4610 static int
edit_sub(cp)4611 edit_sub(cp)
4612 char *cp;
4613 {
4614 char cl[MAX_CMD_LEN]; /* command-line */
4615 char *cp1;
4616 int ev; /* exit value */
4617
4618 edit_again:
4619 cp1 = getenv("EDITOR");
4620 if (cp1 == NULL) {
4621 #if CYGWIN || MINGW
4622 cp1 = "notepad";
4623 #else
4624 cp1 = "nano";
4625 #endif
4626 warning("EDITOR environment variable not set; using default text editor.");
4627 }
4628 if (snprintf(cl, sizeof(cl), "%s %s", cp1, cp) >= sizeof(cl)) {
4629 error(_("Editor command-line too long."));
4630 return false;
4631 }
4632 if ((ev = shell_out(cl))) {
4633 error("Error executing editor, check EDITOR environment variable.");
4634 printf(_("Decimal exit value = %d, shell command-line = %s\n"), ev, cl);
4635 return false;
4636 }
4637 clear_all();
4638 if (!read_cmd(cp)) {
4639 if (pause_cmd(_("Prepare to rerun the editor, or type \"quit\""))) {
4640 goto edit_again;
4641 }
4642 }
4643 return true;
4644 }
4645
4646 /*
4647 * The edit command.
4648 */
4649 int
edit_cmd(cp)4650 edit_cmd(cp)
4651 char *cp;
4652 {
4653 FILE *fp;
4654 #if !MINGW
4655 int fd;
4656 #endif
4657 int rv;
4658 char tmp_file[MAX_CMD_LEN];
4659
4660 show_usage = false;
4661 if (security_level) {
4662 if (security_level < 0) {
4663 error(_("Running the editor is not possible with m4."));
4664 } else {
4665 error(_("Command disabled by security level."));
4666 }
4667 return false;
4668 }
4669 clean_up(); /* end any redirection */
4670 if (*cp == '\0') {
4671 #if MINGW
4672 my_strlcpy(tmp_file, "mathomatic.tmp", sizeof(tmp_file));
4673 fp = fopen(tmp_file, "w+");
4674 if (fp == NULL) {
4675 perror(tmp_file);
4676 error(_("Can't create temporary file."));
4677 return false;
4678 }
4679 #else
4680 my_strlcpy(tmp_file, TMP_FILE, sizeof(tmp_file));
4681 fd = mkstemp(tmp_file);
4682 if (fd < 0 || (fp = fdopen(fd, "w+")) == NULL) {
4683 perror(tmp_file);
4684 error(_("Can't create temporary file."));
4685 return false;
4686 }
4687 #endif
4688 gfp = fp;
4689 high_prec = true;
4690 list_cmd("all");
4691 high_prec = false;
4692 gfp = default_out;
4693 rv = !ferror(fp);
4694 if (fclose(fp) || !rv) {
4695 rv = false;
4696 perror(tmp_file);
4697 error(_("Writing temporary file failed."));
4698 } else {
4699 rv = edit_sub(tmp_file);
4700 }
4701 if (unlink(tmp_file)) {
4702 perror(tmp_file);
4703 }
4704 return rv;
4705 } else {
4706 show_usage = true;
4707 if (access(cp, R_OK | W_OK)) {
4708 perror(cp);
4709 error(_("You can only edit existing/writable files or all equation spaces."));
4710 return false;
4711 }
4712 return edit_sub(cp);
4713 }
4714 }
4715 #endif
4716
4717 #if !SECURE
4718 /*
4719 * The save command.
4720 */
4721 int
save_cmd(cp)4722 save_cmd(cp)
4723 char *cp;
4724 {
4725 FILE *fp;
4726 int rv, space_flag = false, error_flag;
4727 char *cp1;
4728
4729 if (security_level >= 2) {
4730 show_usage = false;
4731 error(_("Command disabled by security level."));
4732 return false;
4733 }
4734 clean_up(); /* end any redirection */
4735 if (*cp == '\0') {
4736 error(_("No file name specified; nothing was saved."));
4737 return false;
4738 }
4739 for (cp1 = cp; *cp1; cp1++) {
4740 if (isspace(*cp1)) {
4741 space_flag = true;
4742 }
4743 }
4744 #if !SILENT
4745 if (access(cp, F_OK) == 0) {
4746 if (access(cp, W_OK)) {
4747 perror(cp);
4748 error(_("Specified save file is not writable; choose a different file name."));
4749 return false;
4750 }
4751 snprintf(prompt_str, sizeof(prompt_str), _("File \"%s\" exists, overwrite (y/n)? "), cp);
4752 if (!get_yes_no()) {
4753 error(_("File not overwritten; nothing was saved."));
4754 return false;
4755 }
4756 } else if (space_flag) {
4757 snprintf(prompt_str, sizeof(prompt_str), _("File name \"%s\" contains space characters, create anyways (y/n)? "), cp);
4758 if (!get_yes_no()) {
4759 error(_("Save command aborted; nothing was saved."));
4760 return false;
4761 }
4762 }
4763 #endif
4764 fp = fopen(cp, "w");
4765 if (fp == NULL) {
4766 perror(cp);
4767 error(_("Cannot create specified save file; nothing was saved."));
4768 return false;
4769 }
4770 gfp = fp;
4771 high_prec = true;
4772 rv = list_cmd("all");
4773 high_prec = false;
4774 gfp = default_out;
4775 error_flag = ferror(fp);
4776 if (fclose(fp) || error_flag) {
4777 rv = false;
4778 perror(cp);
4779 }
4780 if (rv) {
4781 #if !SILENT
4782 printf(_("All expressions saved in file \"%s\".\n"), cp);
4783 #endif
4784 } else {
4785 error(_("Error encountered while saving expressions."));
4786 }
4787 return rv;
4788 }
4789 #endif
4790