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