Annotation of loncom/homework/math_parser/ENode.pm, revision 1.5
1.1 damieng 1: # The LearningOnline Network with CAPA - LON-CAPA
2: # Parsed tree node
3: #
4: # Copyright (C) 2014 Michigan State University Board of Trustees
5: #
6: # This program is free software: you can redistribute it and/or modify
7: # it under the terms of the GNU General Public License as published by
8: # the Free Software Foundation, either version 3 of the License, or
9: # (at your option) any later version.
10: #
11: # This program 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
14: # GNU General Public License for more details.
15: #
16: # You should have received a copy of the GNU General Public License
17: # along with this program. If not, see <http://www.gnu.org/licenses/>.
18: #
19:
20: ##
21: # Parsed tree node. ENode.toMathML(hcolors) contains the code for the transformation into MathML.
22: ##
23: package Apache::math_parser::ENode;
24:
25: use strict;
26: use warnings;
27: use utf8;
28:
1.2 damieng 29: use Switch 'Perl6';
1.1 damieng 30:
31: use aliased 'Apache::math_parser::CalcException';
32: use aliased 'Apache::math_parser::Operator';
33: use aliased 'Apache::math_parser::ParseException';
34: use aliased 'Apache::math_parser::QMatrix';
35: use aliased 'Apache::math_parser::Quantity';
36: use aliased 'Apache::math_parser::QVector';
37: use aliased 'Apache::math_parser::QInterval';
38: use aliased 'Apache::math_parser::QIntervalUnion';
39: use aliased 'Apache::math_parser::QSet';
40: use aliased 'Apache::math_parser::Units';
41:
42: use enum qw(UNKNOWN NAME NUMBER OPERATOR FUNCTION VECTOR INTERVAL SET SUBSCRIPT);
43: use enum qw(NOT_AN_INTERVAL OPEN_OPEN OPEN_CLOSED CLOSED_OPEN CLOSED_CLOSED);
44:
45: ##
46: # @param {integer} type - UNKNOWN | NAME | NUMBER | OPERATOR | FUNCTION | VECTOR | INTERVAL | SET | SUBSCRIPT
47: # @param {Operator} op - The operator
48: # @param {string} value - Node value as a string, undef for type VECTOR
49: # @param {ENode[]} children - The children nodes, only for types OPERATOR, FUNCTION, VECTOR, INTERVAL, SET, SUBSCRIPT
50: # @param {interval_type} - The interval type, NOT_AN_INTERVAL | OPEN_OPEN | OPEN_CLOSED | CLOSED_OPEN | CLOSED_CLOSED
51: ##
52: sub new {
1.3 damieng 53: my ($class, $type, $op, $value, $children, $interval_type) = @_;
54: if (!defined $interval_type) {
55: $interval_type = NOT_AN_INTERVAL;
56: }
1.1 damieng 57: my $self = {
1.3 damieng 58: _type => $type,
59: _op => $op,
60: _value => $value,
61: _children => $children,
62: _interval_type => $interval_type,
1.1 damieng 63: };
64: bless $self, $class;
65: return $self;
66: }
67:
68: # Attribute helpers
69:
70: ##
71: # Node type
72: # @returns {int} UNKNOWN | NAME | NUMBER | OPERATOR | FUNCTION | VECTOR | INTERVAL | SET | SUBSCRIPT
73: ##
74: sub type {
75: my $self = shift;
76: return $self->{_type};
77: }
78:
79: ##
80: # Operator
81: # @returns {Operator}
82: ##
83: sub op {
84: my $self = shift;
85: return $self->{_op};
86: }
87:
88: ##
89: # Node value as a string, undef for type VECTOR.
90: # @returns {string}
91: ##
92: sub value {
93: my $self = shift;
94: return $self->{_value};
95: }
96:
97: ##
98: # The children nodes, only for types OPERATOR, FUNCTION, VECTOR, INTERVAL, SET, SUBSCRIPT
99: # @returns {ENode[]}
100: ##
101: sub children {
102: my $self = shift;
103: return $self->{_children};
104: }
105:
106: ##
107: # The interval type, NOT_AN_INTERVAL | OPEN_OPEN | OPEN_CLOSED | CLOSED_OPEN | CLOSED_CLOSED
108: # @returns {int}
109: ##
110: sub interval_type {
111: my $self = shift;
112: return $self->{_interval_type};
113: }
114:
115:
116: ##
117: # Returns the node as a string, for debug
118: # @returns {string}
119: ##
120: sub toString {
121: my ( $self ) = @_;
122: my $s = '(';
123: given ($self->type) {
124: when (UNKNOWN) { $s .= "UNKNOWN"; }
125: when (NAME) { $s .= "NAME"; }
126: when (NUMBER) { $s .= "NUMBER"; }
127: when (OPERATOR) { $s .= "OPERATOR"; }
128: when (FUNCTION) { $s .= "FUNCTION"; }
129: when (VECTOR) { $s .= "VECTOR"; }
130: when (INTERVAL) { $s .= "INTERVAL"; }
131: when (SET) { $s .= "SET"; }
132: when (SUBSCRIPT) { $s .= "SUBSCRIPT"; }
133: }
134: if (defined $self->op) {
135: $s .= " '" . $self->op->id . "'";
136: }
137: if (defined $self->value) {
138: $s .= " '" . $self->value . "'";
139: }
140: if (defined $self->{_children}) {
141: $s .= ' [';
142: for (my $i = 0; $i < scalar(@{$self->children}); $i++) {
143: $s .= $self->children->[$i]->toString();
144: if ($i != scalar(@{$self->children}) - 1) {
145: $s .= ',';
146: }
147: }
148: $s .= ']';
149: }
150: if (defined $self->interval_type) {
151: $s .= " " . $self->interval_type;
152: }
153: $s.= ')';
154: return $s;
155: }
156:
157: ##
158: # Evaluates the node, returning a quantity or an object from a more complex class using quantities as base components.
159: # Can throw a CalcException if a result cannot be calculated.
160: # @param {CalcEnv} env - Calculation environment.
161: # @returns {Quantity|QVector|QMatrix|QSet|QInterval|QIntervalUnion}
162: ##
163: sub calc {
164: my ( $self, $env ) = @_;
165:
166: given ($self->type) {
167: when (UNKNOWN) {
168: die CalcException->new("Unknown node type: [_1].", $self->value);
169: }
170: when (NAME) {
171: my $name = $self->value;
172: if ($name =~ /^inf$/i) {
173: return Quantity->new(9**9**9);
174: } elsif ($name =~ /^nan$/i) {
175: return Quantity->new(-sin(9**9**9));
176: }
177: if ($env->unit_mode) {
178: my $cst = $env->getConstant($name);
179: if (defined $cst) {
180: return $cst;
181: }
182: return $env->convertToSI($name);
183: } else {
184: my $q = $env->getVariable($name);
185: if (!defined $q) {
186: my $cst = $env->getConstant($name);
187: if (defined $cst) {
188: return $cst;
189: }
190: die CalcException->new("Variable has undefined value: [_1].", $name);
191: }
192: return $q;
193: }
194: }
195: when (NUMBER) {
196: return Quantity->new($self->value);
197: }
198: when (OPERATOR) {
199: my @children = @{$self->children};
200: my ($q1, $q2);
201: if (defined $children[0]) {
202: $q1 = $children[0]->calc($env);
203: }
204: if (defined $children[1]) {
205: $q2 = $children[1]->calc($env);
206: }
207: given ($self->value) {
208: when ("+") {
209: if (!overload::Method($q1, '+')) {
210: die CalcException->new("The [_1] operator is not implemented for this type.", $self->value);
211: }
212: return($q1 + $q2);
213: }
214: when ("-") {
215: if (!defined $q2) {
216: if (!$q1->can('qneg')) {
217: die CalcException->new("Negation is not implemented for this type.");
218: }
219: return($q1->qneg());
220: } else {
221: if (!overload::Method($q1, '-')) {
222: die CalcException->new("The [_1] operator is not implemented for this type.", $self->value);
223: }
224: return($q1 - $q2);
225: }
226: }
227: when ("*") {
228: if (!overload::Method($q1, '*')) {
229: die CalcException->new("The [_1] operator is not implemented for this type.", $self->value);
230: }
231: return($q1 * $q2);
232: }
233: when ("/") {
234: if (!overload::Method($q1, '/')) {
235: die CalcException->new("The [_1] operator is not implemented for this type.", $self->value);
236: }
237: return($q1 / $q2);
238: }
239: when ("^") {
240: if (!overload::Method($q1, '^')) {
241: die CalcException->new("The [_1] operator is not implemented for this type.", $self->value);
242: }
243: return($q1 ^ $q2);
244: }
245: when ("!") {
246: if (!$q1->can('qfact')) {
247: die CalcException->new("The [_1] operator is not implemented for this type.", $self->value);
248: }
249: return $q1->qfact();
250: }
251: when ("%") {
252: if (!$q1->isa(Quantity) || !$q2->isa(Quantity)) {
253: die CalcException->new("The [_1] operator is not implemented for this type.", $self->value);
254: }
255: return(($q1 / Quantity->new(100)) * $q2);
256: }
257: when (".") {
258: # scalar product for vectors, multiplication for matrices
259: if (!$q1->can('qdot')) {
260: die CalcException->new("The [_1] operator is not implemented for this type.", $self->value);
261: }
262: return($q1->qdot($children[1]->calc($env)));
263: }
264: when ("`") {
265: if (!overload::Method($q1, '*')) {
266: die CalcException->new("The [_1] operator is not implemented for this type.", $self->value);
267: }
268: return($q1 * $q2);
269: }
270: when ("=") {
271: if (!$q1->can('qeq')) {
272: die CalcException->new("The [_1] operator is not implemented for this type.", $self->value);
273: }
274: return($q1->qeq($q2, $env->tolerance));
275: }
276: when ("<") {
277: if (!overload::Method($q1, '<')) {
278: die CalcException->new("The [_1] operator is not implemented for this type.", $self->value);
279: }
280: return($q1 < $q2);
281: }
282: when ("<=") {
283: if (!overload::Method($q1, '<=')) {
284: die CalcException->new("The [_1] operator is not implemented for this type.", $self->value);
285: }
286: return($q1 <= $q2);
287: }
288: when (">") {
289: if (!overload::Method($q1, '>')) {
290: die CalcException->new("The [_1] operator is not implemented for this type.", $self->value);
291: }
292: return($q1 > $q2);
293: }
294: when (">=") {
295: if (!overload::Method($q1, '>=')) {
296: die CalcException->new("The [_1] operator is not implemented for this type.", $self->value);
297: }
298: return($q1 >= $q2);
299: }
300: default {
301: die CalcException->new("Unknown operator: [_1].", $self->value);
302: }
303: }
304: }
305: when (FUNCTION) {
306: my @children = @{$self->children};
307: my $fname = $children[0]->value;
308:
309: if (!defined $children[1]) {
310: die CalcException->new("Missing parameter for function [_1].", $fname);
311: }
312: my ($q1, $q2);
1.3 damieng 313: if (string_in_array(['pow', 'sqrt', 'abs', 'exp', 'ln', 'log', 'log10', 'factorial',
1.1 damieng 314: 'mod', 'sgn', 'ceil', 'floor', 'sin', 'cos', 'tan', 'asin', 'acos', 'atan',
1.3 damieng 315: 'atan2', 'sinh', 'cosh', 'tanh', 'asinh', 'acosh', 'atanh'], $fname)) {
1.1 damieng 316: $q1 = $children[1]->calc($env);
317: if (!$q1->isa(Quantity)) {
318: die CalcException->new("The [_1] function is not implemented for this type.", $fname);
319: }
320: }
1.3 damieng 321: if (string_in_array(['pow', 'mod', 'atan2'], $fname)) {
1.1 damieng 322: if (!defined $children[2]) {
323: die CalcException->new("Missing parameter for function [_1].", $fname);
324: }
325: $q2 = $children[2]->calc($env);
326: if (!$q2->isa(Quantity)) {
327: die CalcException->new("The [_1] function is not implemented for this type.", $fname);
328: }
329: }
330: given ($fname) {
331: when ("matrix") { return $self->createVectorOrMatrix($env); }
332: when ("pow") { return $q1->qpow($q2); }
333: when ("sqrt") { return $q1->qsqrt(); }
334: when ("abs") { return $q1->qabs(); }
335: when ("exp") { return $q1->qexp(); }
336: when ("ln") { return $q1->qln(); }
337: when ("log") { return $q1->qln(); }
338: when ("log10") { return $q1->qlog10(); }
339: when ("factorial") { return $q1->qfact(); }
340: when ("mod") { return $q1->qmod($q2); }
341: when ("sgn") { return $q1->qsgn(); }
342: when ("ceil") { return $q1->qceil(); }
343: when ("floor") { return $q1->qfloor(); }
344: when ("sin") { return $q1->qsin(); }
345: when ("cos") { return $q1->qcos(); }
346: when ("tan") { return $q1->qtan(); }
347: when ("asin") { return $q1->qasin(); }
348: when ("acos") { return $q1->qacos(); }
349: when ("atan") { return $q1->qatan(); }
350: when ("atan2") { return $q1->qatan2($q2); }
351: when ("sinh") { return $q1->qsinh(); }
352: when ("cosh") { return $q1->qcosh(); }
353: when ("tanh") { return $q1->qtanh(); }
354: when ("asinh") { return $q1->qasinh(); }
355: when ("acosh") { return $q1->qacosh(); }
356: when ("atanh") { return $q1->qatanh(); }
357: when (["sum","product"]) {
358: if ($env->unit_mode) {
359: die CalcException->new("[_1] cannot work in unit mode.", $fname);
360: }
361: if (scalar(@children) != 5) {
362: die CalcException->new("[_1] should have four parameters.", $fname);
363: }
364: my $var = "".$children[2]->value;
365: if ($var !~ /^[a-zA-Z_][a-zA-Z_0-9]*$/) {
366: die CalcException->new("[_1]: wrong variable name", $fname);
367: }
368: if ($var eq "i") {
369: die CalcException->new("[_1]: please use another variable name, i is the imaginary number.", $fname);
370: }
371: my $initial = $env->getVariable($var);
372: my $var_value_1 = $children[3]->value;
373: my $var_value_2 = $children[4]->value;
374: if ($var_value_1 !~ /^[0-9]+$/) {
375: die CalcException->new("[_1]: the third parameter should be an integer", $fname);
376: }
377: if ($var_value_2 !~ /^[0-9]+$/) {
378: die CalcException->new("[_1]: the fourth parameter should be an integer", $fname);
379: }
380: if ($var_value_1 > $var_value_2) {
381: die CalcException->new("[_1]: are you trying to make me loop forever?", $fname);
382: }
383: my $result;
384: for (my $var_value=$var_value_1; $var_value <= $var_value_2; $var_value++) {
385: $env->setVariable($var, $var_value);
386: my $nq = $children[1]->calc($env);
387: if (!$nq->isa(Quantity) && !$nq->isa(QVector) && !$nq->isa(QMatrix)) {
388: die CalcException->new("[_1]: wrong type for a calculated value", $fname);
389: }
390: if (!defined $result) {
391: $result = $nq;
392: } elsif ($fname eq "sum") {
393: $result += $nq;
394: } else {
395: $result *= $nq;
396: }
397: }
398: $env->setVariable($var, $initial);
399: return $result;
400: }
401: when ("binomial") {
402: if (scalar(@children) != 3) {
403: die CalcException->new("[_1] should have two parameters.", $fname);
404: }
405: my $n = $children[1]->calc($env);
406: my $p = $children[2]->calc($env);
407: if (!$n->isa(Quantity) || !$p->isa(Quantity)) {
408: die CalcException->new("Wrong parameter type for function [_1]", $fname);
409: }
410: return $n->qfact() / ($p->qfact() * ($n - $p)->qfact());
411: }
412: when (["union","intersection"]) {
413: if (!defined $children[2]) {
414: die CalcException->new("Missing parameter for function [_1].", $fname);
415: }
416: my $p1 = $children[1]->calc($env);
417: my $p2 = $children[2]->calc($env);
418: if (!$p1->isa(QSet) && !$p1->isa(QInterval) && !$p1->isa(QIntervalUnion)) {
419: die CalcException->new("Wrong type for function [_1] (should be a set or interval).", $fname);
420: }
421: if ($fname eq "union") {
422: return $p1->union($p2);
423: } else {
424: return $p1->intersection($p2);
425: }
426: }
427: default { die CalcException->new("Unknown function: [_1].",$fname); }
428: }
429: }
430: when (VECTOR) {
431: return $self->createVectorOrMatrix($env);
432: }
433: when (INTERVAL) {
434: my @children = @{$self->children};
435: if (scalar(@children) != 2) {
436: die CalcException->new("Interval should have two parameters.");
437: }
438: my $qmin = $children[0]->calc($env);
439: my $qmax = $children[1]->calc($env);
440: my ($qminopen, $qmaxopen);
441: given ($self->interval_type) {
442: when (OPEN_OPEN) { $qminopen = 1; $qmaxopen = 1; }
443: when (OPEN_CLOSED) { $qminopen = 1; $qmaxopen = 0; }
444: when (CLOSED_OPEN) { $qminopen = 0; $qmaxopen = 1; }
445: when (CLOSED_CLOSED) { $qminopen = 0; $qmaxopen = 0; }
446: }
447: return QInterval->new($qmin, $qmax, $qminopen, $qmaxopen);
448: }
449: when (SET) {
450: my @t = ();
451: foreach my $child (@{$self->children}) {
452: push(@t, $child->calc($env));
453: }
454: return QSet->new(\@t);
455: }
456: when (SUBSCRIPT) {
457: die CalcException->new("Subscript cannot be evaluated: [_1].", $self->value);
458: }
459: }
460: }
461:
462: ##
463: # Returns the equation as a string with the Maxima syntax.
464: # @returns {string}
465: ##
466: sub toMaxima {
467: my ( $self, $env ) = @_;
468:
469: given ($self->type) {
470: when (UNKNOWN) {
471: die CalcException->new("Unknown node type: [_1].", $self->value);
472: }
473: when (NAME) {
474: my $name = $self->value;
475: my $cst = $env->getConstant($name);
476: if (defined $cst) {
477: return $cst;
478: }
479: return($name);
480: }
481: when (NUMBER) {
482: if ($self->value eq "i") {
483: return "%i";
484: } else {
485: return $self->value;
486: }
487: }
488: when (OPERATOR) {
489: my @children = @{$self->children};
490: given ($self->value) {
491: when ("+") {
492: if ($children[0]->type == SET && $children[1]->type == SET) {
493: return("union(".$children[0]->toMaxima().", ".$children[1]->toMaxima().")");
494: } else {
495: return("(".$children[0]->toMaxima()."+".$children[1]->toMaxima().")");
496: }
497: }
498: when ("-") {
499: if (!defined $children[1]) {
500: return("(-".$children[0]->toMaxima().")");
501: } else {
502: return("(".$children[0]->toMaxima()."-".$children[1]->toMaxima().")");
503: }
504: }
505: when ("*") {
506: return("(".$children[0]->toMaxima()."*".$children[1]->toMaxima().")");
507: }
508: when ("/") {
509: return("(".$children[0]->toMaxima()."/".$children[1]->toMaxima().")");
510: }
511: when ("^") {
512: return("(".$children[0]->toMaxima()."^".$children[1]->toMaxima().")");
513: }
514: when ("!") {
515: return("factorial(".$children[0]->toMaxima().")");
516: }
517: when ("%") {
518: return("((".$children[0]->toMaxima()."/100)*".$children[1]->toMaxima().")");
519: }
520: when (".") {
521: # scalar product for vectors, multiplication for matrices
522: return("(".$children[0]->toMaxima().".".$children[1]->toMaxima().")");
523: }
524: when ("`") {
525: return("(".$children[0]->toMaxima()."`".$children[1]->toMaxima().")");
526: }
527: when ("=") {
528: # NOTE: should we use is(...) to evaluate the expression ?
529: return("(".$children[0]->toMaxima()."=".$children[1]->toMaxima().")");
530: }
531: when ("<") {
532: return("(".$children[0]->toMaxima()."<".$children[1]->toMaxima().")");
533: }
534: when (">") {
535: return("(".$children[0]->toMaxima().">".$children[1]->toMaxima().")");
536: }
537: when ("<=") {
538: return("(".$children[0]->toMaxima()."<=".$children[1]->toMaxima().")");
539: }
540: when (">=") {
541: return("(".$children[0]->toMaxima().">=".$children[1]->toMaxima().")");
542: }
543: default {
544: die CalcException->new("Unknown operator: [_1].", $self->value);
545: }
546: }
547: }
548: when (FUNCTION) {
549: my @children = @{$self->children};
550: my $fname = $children[0]->value;
551:
552: given ($fname) {
553: when ("log10") { return "log(".$children[1]->toMaxima().")/log(10)"; }
554: when ("sgn") { return "signum(".$children[1]->toMaxima().")"; }
555: when ("ceil") { return "ceiling(".$children[1]->toMaxima().")"; }
556: default {
557: my $s = $fname."(";
558: for (my $i=1; $i<scalar(@children); $i++) {
559: if ($i != 1) {
560: $s .= ", ";
561: }
562: $s .= $children[$i]->toMaxima();
563: }
564: $s .= ")";
565: return($s);
566: }
567: }
568: }
569: when (VECTOR) {
570: my @children = @{$self->children};
571: my $s;
572: if ($children[0]->type == VECTOR) {
573: $s = "matrix(";
574: } else {
575: $s = "[";
576: }
577: for (my $i=0; $i<scalar(@children); $i++) {
578: if ($i != 0) {
579: $s .= ", ";
580: }
581: $s .= $children[$i]->toMaxima();
582: }
583: if ($children[0]->type == VECTOR) {
584: $s .= ")";
585: } else {
586: $s .= "]";
587: }
588: return($s);
589: }
590: when (INTERVAL) {
591: die CalcException->new("Maxima syntax: intervals are not implemented.");
592: # see http://ieeexplore.ieee.org/xpls/icp.jsp?arnumber=5959544
593: # "New Package in Maxima for Single-Valued Interval Computation on Real Numbers"
594: }
595: when (SET) {
596: my @children = @{$self->children};
597: my $s = "{";
598: for (my $i=0; $i<scalar(@children); $i++) {
599: if ($i != 0) {
600: $s .= ", ";
601: }
602: $s .= $children[$i]->toMaxima();
603: }
604: $s .= "}";
605: return($s);
606: }
607: when (SUBSCRIPT) {
608: my @children = @{$self->children};
609: return("(".$children[0]->toMaxima()."_".$children[1]->toMaxima().")");
610: }
611: }
612: }
613:
614: ##
615: # Returns the equation as a string with the TeX syntax.
616: # @returns {string}
617: ##
618: sub toTeX {
619: my ( $self ) = @_;
620:
621: given ($self->type) {
622: when (UNKNOWN) {
623: die CalcException->new("Unknown node type: [_1].", $self->value);
624: }
625: when (NAME) {
626: my $name = $self->value;
627: if ($name =~ /^([a-zA-Z]+)([0-9]+)$/) {
628: return($1."_{".$2."}");
629: }
630: my @greek = (
631: "alpha", "beta", "gamma", "delta", "epsilon", "zeta",
632: "eta", "theta", "iota", "kappa", "lambda", "mu",
633: "nu", "xi", "omicron", "pi", "rho", "sigma",
634: "tau", "upsilon", "phi", "chi", "psi", "omega",
635: "Alpha", "Beta", "Gamma", "Delta", "Epsilon", "Zeta",
636: "Eta", "Theta", "Iota", "Kappa", "Lambda", "Mu",
637: "Nu", "Xi", "Omicron", "Pi", "Rho", "Sigma",
638: "Tau", "Upsilon", "Phi", "Chi", "Psi", "Omega",
639: );
1.3 damieng 640: if (string_in_array(\@greek, $name)) {
1.1 damieng 641: return('\\'.$name);
642: } elsif ($name eq "hbar") {
643: return("\\hbar");
644: } elsif ($name eq "inf") {
645: return("\\infty");
646: } elsif ($name eq "minf") {
647: return("-\\infty");
648: } else {
649: return($name);
650: }
651: }
652: when (NUMBER) {
653: return $self->value;
654: }
655: when (OPERATOR) {
656: my @children = @{$self->children};
657: my $c0 = $children[0];
658: my $c1 = $children[1];
659: given ($self->value) {
660: when ("+") {
661: # should we add parenthesis ? We need to check if there is a '-' to the left of c1
662: my $par = 0;
663: my $first = $c1;
664: while ($first->type == OPERATOR) {
665: if ($first->value eq "-" && scalar(@{$first->children}) == 1) {
666: $par = 1;
667: last;
668: } elsif ($first->value eq "+" || $first->value eq "-" || $first->value eq "*") {
669: $first = $first->children->[0];
670: } else {
671: last;
672: }
673: }
674: my $s = $c0->toTeX()." + ".$c1->toTeX();
675: if ($par) {
676: $s = "(".$s.")";
677: }
678: return $s;
679: }
680: when ("-") {
681: if (!defined $c1) {
682: return("-".$c0->toTeX());
683: } else {
684: my $s = $c0->toTeX()." - ";
685: my $par = ($c1->type == OPERATOR &&
686: ($c1->value eq "+" || $c1->value eq "-"));
687: if ($par) {
688: $s .= "(".$c1->toTeX().")";
689: } else {
690: $s .= $c1->toTeX();
691: }
692: return $s;
693: }
694: }
695: when ("*") {
696: my $par = ($c0->type == OPERATOR && ($c0->value eq "+" || $c0->value eq "-"));
697: my $s = $c0->toTeX();
698: if ($par) {
699: $s = "(".$s.")";
700: }
701: # should the x operator be visible ? We need to check if there is a number to the left of c1
702: my $firstinc1 = $c1;
703: while ($firstinc1->type == OPERATOR) {
704: $firstinc1 = $firstinc1->children->[0];
705: }
706: # ... and if it's an operation between vectors/matrices, the * operator should be displayed
707: # (it is ambiguous otherwise)
708: # note: this will not work if the matrix is calculated, for instance with 2[1;2]*[3;4]
709: if ($c0->type == VECTOR && $c1->type == VECTOR) {
710: $s .= " * ";
711: } elsif ($firstinc1->type == NUMBER) {
712: $s .= " \\times ";
713: } else {
714: $s .= " ";
715: }
716: $par = ($c1->type == OPERATOR && ($c1->value eq "+" || $c1->value eq "-"));
717: if ($par) {
718: $s .= "(".$c1->toTeX().")";
719: } else {
720: $s .= $c1->toTeX();
721: }
722: return $s;
723: }
724: when ("/") {
725: # NOTE: cfrac would be better but tth does not handle it
726: return("\\frac{".$c0->toTeX()."}{".$c1->toTeX()."}");
727: }
728: when ("^") {
729: my $par;
730: if ($c0->type == FUNCTION) {
731: if ($c0->value eq "sqrt" || $c0->value eq "abs" || $c0->value eq "matrix" ||
732: $c0->value eq "diff") {
733: $par = 0;
734: } else {
735: $par = 1;
736: }
737: } elsif ($c0->type == OPERATOR) {
738: $par = 1;
739: } else {
740: $par = 0;
741: }
742: if ($par) {
743: return("(".$c0->toTeX().")^{".$c1->toTeX()."}");
744: } else {
745: return($c0->toTeX()."^{".$c1->toTeX()."}");
746: }
747: }
748: when ("!") {
1.5 ! damieng 749: my $s = $c0->toTeX();
! 750: if ($c0->type == OPERATOR) {
! 751: $s = "(".$s.")";
! 752: }
! 753: $s .= " !";
! 754: return $s;
1.1 damieng 755: }
756: when ("%") {
757: return($c0->toTeX()." \\% ".$c1->toTeX());
758: }
759: when (".") {
760: # scalar product for vectors, multiplication for matrices
761: my $par = ($c0->type == OPERATOR && ($c0->value eq "+" || $c0->value eq "-"));
762: my $s = $c0->toTeX();
763: if ($par) {
764: $s = "(".$s.")";
765: }
766: $s .= " \\cdot ";
767: $par = ($c1->type == OPERATOR && ($c1->value eq "+" || $c1->value eq "-"));
768: if ($par) {
769: $s .= "(".$c1->toTeX().")";
770: } else {
771: $s .= $c1->toTeX();
772: }
773: return $s;
774: }
775: when ("`") {
776: return($c0->toTeX()." \\mathrm{".$c1->toTeX()."}");
777: }
778: when ("=") {
779: return($c0->toTeX()." = ".$c1->toTeX());
780: }
781: when ("#") {
782: return($c0->toTeX()." \\not ".$c1->toTeX());
783: }
784: when ("<") {
785: return($c0->toTeX()." < ".$c1->toTeX());
786: }
787: when (">") {
788: return($c0->toTeX()." > ".$c1->toTeX());
789: }
790: when ("<=") {
791: return($c0->toTeX()." \\leq ".$c1->toTeX());
792: }
793: when (">=") {
794: return($c0->toTeX()." \\geq ".$c1->toTeX());
795: }
796: default {
797: die CalcException->new("Unknown operator: [_1].", $self->value);
798: }
799: }
800: }
801: when (FUNCTION) {
802: my @children = @{$self->children};
803: my $fname = $children[0]->value;
804: my $c1 = $children[1];
805: my $c2 = $children[2];
806: my $c3 = $children[3];
807: my $c4 = $children[4];
808:
809: given ($fname) {
810: when ("sqrt") { return "\\sqrt{".$c1->toTeX()."}"; }
811: when ("abs") { return "|".$c1->toTeX()."|"; }
812: when ("exp") { return "\\mathrm{e}^{".$c1->toTeX()."}"; }
1.5 ! damieng 813: when ("factorial") {
! 814: my $s = $c1->toTeX();
! 815: if ($c1->type == OPERATOR) {
! 816: $s = "(".$s.")";
! 817: }
! 818: $s .= " !";
! 819: return $s;
! 820: }
1.1 damieng 821: when ("diff") {
822: if (scalar(@children) == 3) {
823: return "\\frac{d}{d".$c2->toTeX()."} ".$c1->toTeX();
824: } else {
825: return "\\frac{d^{".$c3->toTeX()."}}{d ".$c2->toTeX().
826: "^{".$c3->toTeX()."}} ".$c1->toTeX();
827: }
828: }
829: when ("integrate") {
830: if (scalar(@children) == 3) {
831: return "\\int ".$c1->toTeX()." \\ d ".$c2->toTeX();
832: } else {
833: return "\\int_{".$c3->toTeX()."}^{".$c4->toTeX()."} ".
834: $c1->toTeX()." \\ d ".$c2->toTeX();
835: }
836: }
837: when ("sum") {
838: return "\\sum_{".$c2->toTeX()."=".$c3->toTeX().
839: "}^{".$c4->toTeX()."} ".$c1->toTeX();
840: }
841: when ("product") {
842: return "\\prod_{".$c2->toTeX()."=".$c3->toTeX().
843: "}^{".$c4->toTeX()."} ".$c1->toTeX();
844: }
845: when ("limit") {
846: if (scalar(@children) < 4) {
847: return "\\lim ".$c1->toTeX();
848: } elsif (scalar(@children) == 4) {
849: return "\\lim_{".$c2->toTeX()." \\to ".$c3->toTeX().
850: "}".$c1->toTeX();
851: } else {
1.4 damieng 852: my $s = "\\lim_{".$c2->toTeX()." \\to ".$c3->toTeX();
853: if ($c4->value eq "plus") {
854: $s .= "+";
855: } elsif ($c4->value eq "minus") {
856: $s .= "-";
857: }
858: $s .= "}".$c1->toTeX();
859: return $s;
1.1 damieng 860: }
861: }
862: when ("binomial") {
863: return "\\binom{".$c1->toTeX()."}{".$c2->toTeX()."}";
864: }
865: when (["union","intersection"]) {
866: if (!defined $children[2]) {
867: die CalcException->new("Missing parameter for function [_1].", $fname);
868: }
869: if ($c1->type != SET && $c1->type != INTERVAL && $c1->type != FUNCTION) {
870: die CalcException->new("Wrong type for function [_1] (should be a set or interval).", $fname);
871: }
872: if ($fname eq "union") {
873: return $c1->toTeX().' \cup '.$c2->toTeX();
874: } else {
875: return $c1->toTeX().' \cap '.$c2->toTeX();
876: }
877: }
878: when ("sin") { return "\\sin ".$c1->toTeX(); }
879: when ("cos") { return "\\cos ".$c1->toTeX(); }
880: when ("tan") { return "\\tan ".$c1->toTeX(); }
881: when ("asin") { return "\\arcsin ".$c1->toTeX(); }
882: when ("acos") { return "\\arccos ".$c1->toTeX(); }
883: when ("atan") { return "\\arctan ".$c1->toTeX(); }
884: when ("sinh") { return "\\sinh ".$c1->toTeX(); }
885: when ("cosh") { return "\\cosh ".$c1->toTeX(); }
886: when ("tanh") { return "\\tanh ".$c1->toTeX(); }
887: default {
888: my $s = $fname."(";
889: for (my $i=1; $i<scalar(@children); $i++) {
890: if ($i != 1) {
891: $s .= ", ";
892: }
893: $s .= $children[$i]->toTeX();
894: }
895: $s .= ")";
896: return($s);
897: }
898: }
899: }
900: when (VECTOR) {
901: my @children = @{$self->children};
902: # my $s = "\\begin{pmatrix}";
903: # NOTE: pmatrix would be easier, but tth does not recognize it
904: my $col;
905: if (scalar(@children) == 0) {
906: $col = 0;
907: } elsif ($children[0]->type == VECTOR) {
908: $col = scalar(@{$children[0]->children});
909: } else {
910: $col = 1;
911: }
912: my $s = "\\left( \\begin{array}{".('c' x $col)."}";
913: for (my $i=0; $i<scalar(@children); $i++) {
914: if ($i != 0) {
915: $s .= " \\\\ ";
916: }
917: if ($children[0]->type == VECTOR) {
918: # matrix
919: for (my $j=0; $j<scalar(@{$children[$i]->children}); $j++) {
920: if ($j != 0) {
921: $s .= " & ";
922: }
923: $s .= $children[$i]->children->[$j]->toTeX();
924: }
925: } else {
926: # vector
927: $s .= $children[$i]->toTeX();
928: }
929: }
930: # $s .= "\\end{pmatrix}";
931: $s .= "\\end{array} \\right)";
932: return($s);
933: }
934: when (INTERVAL) {
935: my @children = @{$self->children};
936: if (scalar(@children) != 2) {
937: die CalcException->new("Interval should have two parameters.");
938: }
939: my ($qminopen, $qmaxopen);
940: given ($self->interval_type) {
941: when (OPEN_OPEN) { $qminopen = 1; $qmaxopen = 1; }
942: when (OPEN_CLOSED) { $qminopen = 1; $qmaxopen = 0; }
943: when (CLOSED_OPEN) { $qminopen = 0; $qmaxopen = 1; }
944: when (CLOSED_CLOSED) { $qminopen = 0; $qmaxopen = 0; }
945: }
946: my $s = "\\left";
947: if ($qminopen) {
948: $s .= "(";
949: } else {
950: $s .= "[";
951: }
952: $s .= $children[0]->toTeX();
953: $s .= ", ";
954: $s .= $children[1]->toTeX();
955: $s .= "\\right";
956: if ($qmaxopen) {
957: $s .= ")";
958: } else {
959: $s .= "]";
960: }
961: return($s);
962: }
963: when (SET) {
964: my @children = @{$self->children};
965: my $s = "\\left\\{ {";
966: for (my $i=0; $i<scalar(@children); $i++) {
967: if ($i != 0) {
968: $s .= ", ";
969: }
970: $s .= $children[$i]->toTeX();
971: }
972: $s .= "}\\right\\}";
973: return($s);
974: }
975: when (SUBSCRIPT) {
976: my @children = @{$self->children};
977: return($children[0]->toTeX()."_{".$children[1]->toTeX()."}");
978: }
979: }
980: }
981: ##
982: # Creates a vector or a matrix with this node
983: # @param {CalcEnv} env - Calculation environment.
984: # @returns {QVector|QMatrix}
985: ##
986: sub createVectorOrMatrix {
987: my ( $self, $env ) = @_;
988: my @children = @{$self->children};
989: my @t = (); # 1d or 2d array of Quantity
990: my $start;
991: if ($self->type == FUNCTION) {
992: $start = 1;
993: } else {
994: $start = 0;
995: }
996: my $nb1;
997: for (my $i=0; $i < scalar(@children) - $start; $i++) {
998: my $qv = $children[$i+$start]->calc($env);
999: my $nb2;
1000: if ($qv->isa(Quantity)) {
1001: $nb2 = 1;
1002: } else {
1003: $nb2 = scalar(@{$qv->quantities});
1004: }
1005: if (!defined $nb1) {
1006: $nb1 = $nb2;
1007: } elsif ($nb2 != $nb1) {
1008: die CalcException->new("Inconsistent number of elements in a matrix.");
1009: }
1010: if ($qv->isa(Quantity)) {
1011: $t[$i] = $qv;
1012: } else {
1013: $t[$i] = [];
1014: for (my $j=0; $j < scalar(@{$qv->quantities}); $j++) {
1015: $t[$i][$j] = $qv->quantities->[$j];
1016: }
1017: }
1018: }
1019: if (ref($t[0]) eq 'ARRAY') {
1020: return QMatrix->new(\@t);
1021: } else {
1022: return QVector->new(\@t);
1023: }
1024: }
1025:
1.3 damieng 1026: ##
1027: # Tests if a string is in an array (using eq) (to avoid using $value ~~ @array)
1028: # @param {Array<string>} array - reference to the array of strings
1029: # @param {string} value - the string to look for
1030: # @returns 1 if found, 0 otherwise
1031: ##
1032: sub string_in_array {
1033: my ($array, $value) = @_;
1034: foreach my $v (@{$array}) {
1035: if ($v eq $value) {
1036: return 1;
1037: }
1038: }
1039: return 0;
1040: }
1041:
1.1 damieng 1042: 1;
1043: __END__
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>