Example #1
0
 /**
  * Generate LaTeX code for an ExpressionNode
  *
  * Create a string giving LaTeX code for an ExpressionNode `(x op y)`
  * where `op` is one of `+`, `-`, `*`, `/` or `^`
  *
  * ### Typesetting rules:
  *
  * - Adds parantheses around each operand, if needed. (I.e. if their precedence
  *   lower than that of the current Node.) For example, the AST `(^ (+ 1 2) 3)`
  *   generates `(1+2)^3` but `(+ (^ 1 2) 3)` generates `1^2+3` as expected.
  * - Multiplications are typeset implicitly `(* x y)` returns `xy` or using
  *   `\cdot` if the first factor is a FunctionNode or the (left operand) in the
  *   second factor is a NumberNode, so `(* x 2)` return `x \cdot 2` and `(* (sin x) x)`
  *   return `\sin x \cdot x` (but `(* x (sin x))` returns `x\sin x`)
  * - Divisions are typeset using `\frac`
  * - Exponentiation adds braces around the power when needed.
  *
  * @param ExpressionNode $node AST to be typeset
  * @retval string
  */
 public function visitExpressionNode(ExpressionNode $node)
 {
     $left = $node->getLeft();
     $leftValue = $this->parenthesize($left, $node);
     $operator = $node->getOperator();
     $right = $node->getRight();
     if ($operator == '*') {
         $operator = '';
         if ($left instanceof FunctionNode || $right instanceof NumberNode || $right instanceof IntegerNode || $right instanceof RationalNode || $right instanceof ExpressionNode && $right->getLeft() instanceof NumberNode) {
             $operator = '\\cdot ';
         }
     }
     if ($right) {
         $rightValue = $this->parenthesize($right, $node);
         switch ($operator) {
             case '/':
                 // No parantheses needed
                 return '\\frac{' . $left->accept($this) . '}{' . $right->accept($this) . '}';
             case '^':
                 return $leftValue . '^' . $this->bracesNeeded($right);
             default:
                 return "{$leftValue}{$operator}{$rightValue}";
         }
     }
     return "{$operator}{$leftValue}";
 }
Example #2
0
 /**
  * Print an ExpressionNode.
  *
  * @param ExpressionNode $node
  */
 public function visitExpressionNode(ExpressionNode $node)
 {
     $leftValue = $node->getLeft()->accept($this);
     $operator = $node->getOperator();
     // The operator and the right side are optional, remember?
     if (!$operator) {
         return "{$leftValue}";
     }
     $right = $node->getRight();
     if ($right) {
         $rightValue = $node->getRight()->accept($this);
         return "({$operator}, {$leftValue}, {$rightValue})";
     } else {
         return "({$operator}, {$leftValue})";
     }
 }
Example #3
0
 /**
  * Evaluate an ExpressionNode
  *
  * Computes the value of an ExpressionNode `x op y`
  * where `op` is one of `+`, `-`, `*`, `/` or `^`
  *
  * @throws UnknownOperatorException if the operator is something other than
  *      `+`, `-`, `*`, `/` or `^`
  *
  * @param ExpressionNode $node AST to be evaluated
  * @retval float
  */
 public function visitExpressionNode(ExpressionNode $node)
 {
     $operator = $node->getOperator();
     $leftValue = $node->getLeft()->accept($this);
     if ($node->getRight()) {
         $rightValue = $node->getRight()->accept($this);
     } else {
         $rightValue = null;
     }
     // Perform the right operation based on the operator
     switch ($operator) {
         case '+':
             return $leftValue + $rightValue;
         case '-':
             return $rightValue === null ? -$leftValue : $leftValue - $rightValue;
         case '*':
             return $rightValue * $leftValue;
         case '/':
             if ($rightValue == 0) {
                 throw new DivisionByZeroException();
             }
             return $leftValue / $rightValue;
         case '^':
             return pow($leftValue, $rightValue);
         default:
             throw new UnknownOperatorException($operator);
     }
 }
Example #4
0
 /**
  * Generate ASCII output code for an ExpressionNode
  *
  * Create a string giving ASCII output representing an ExpressionNode `(x op y)`
  * where `op` is one of `+`, `-`, `*`, `/` or `^`
  *
  *
  * @param ExpressionNode $node AST to be typeset
  * @retval string
  */
 public function visitExpressionNode(ExpressionNode $node)
 {
     $left = $node->getLeft();
     $leftValue = $this->parenthesize($left, $node);
     $operator = $node->getOperator();
     $right = $node->getRight();
     if ($right) {
         $rightValue = $this->parenthesize($right, $node);
         switch ($operator) {
             case '^':
                 return $leftValue . '^' . $this->bracesNeeded($right);
             default:
                 return "{$leftValue}{$operator}{$rightValue}";
         }
     }
     return "{$operator}{$leftValue}";
 }
 public function testCannotEvaluateUnknownOperator()
 {
     $node = new ExpressionNode(new RationalNode(1, 1), '+', new VariableNode('x'));
     // We need to cheat here, since the ExpressionNode contructor already
     // throws an UnknownOperatorException when called with, say '%'
     $node->setOperator('%');
     $this->setExpectedException(UnknownOperatorException::class);
     $this->evaluate($node);
 }
 /**
  * Evaluate an ExpressionNode
  *
  * Computes the value of an ExpressionNode `x op y`
  * where `op` is one of `+`, `-`, `*`, `/` or `^`
  *
  * @throws UnknownOperatorException if the operator is something other than
  *      `+`, `-`, `*`, `/` or `^`
  *
  * @param ExpressionNode $node AST to be evaluated
  * @retval float
  */
 public function visitExpressionNode(ExpressionNode $node)
 {
     $operator = $node->getOperator();
     $a = $node->getLeft()->accept($this);
     if ($node->getRight()) {
         $b = $node->getRight()->accept($this);
     } else {
         $b = null;
     }
     // Perform the right operation based on the operator
     switch ($operator) {
         case '+':
             return Complex::add($a, $b);
         case '-':
             if ($b === null) {
                 return Complex::mul($a, -1);
             }
             return Complex::sub($a, $b);
         case '*':
             return Complex::mul($a, $b);
         case '/':
             return Complex::div($a, $b);
         case '^':
             // This needs to be improved.
             return Complex::pow($a, $b);
         default:
             throw new UnknownOperatorException($operator);
     }
 }
Example #7
0
 /**
  * Differentiate an ExpressionNode
  *
  * Using the usual rules for differentiating, create an ExpressionNode
  * giving an AST correctly representing the derivative `(x op y)'`
  * where `op` is one of `+`, `-`, `*`, `/` or `^`
  *
  * ### Differentiation rules:
  *
  * - \\( (f+g)' = f' + g' \\)
  * - \\( (f-g) ' = f' - g' \\)
  * - \\( (-f)' = -f' \\)
  * - \\( (f*g)' = f'g + f g' \\)
  * - \\( (f/g)' = (f' g - f g')/g^2 \\)
  * - \\( (f^g)' = f^g  (g' \\log(f) + g/f) \\) with a simpler expression when g is a NumberNode
  *
  * @throws UnknownOperatorException if the operator is something other than
  *      `+`, `-`, `*`, `/` or `^`
  *
  * @param ExpressionNode $node AST to be differentiated
  * @retval Node
  */
 public function visitExpressionNode(ExpressionNode $node)
 {
     $operator = $node->getOperator();
     $leftValue = $node->getLeft()->accept($this);
     if ($node->getRight()) {
         $rightValue = $node->getRight()->accept($this);
     } else {
         $rightValue = null;
     }
     // Perform the right operation based on the operator
     switch ($operator) {
         case '+':
             return $this->nodeFactory->addition($leftValue, $rightValue);
         case '-':
             return $this->nodeFactory->subtraction($leftValue, $rightValue);
             // Product rule (fg)' = fg' + f'g
         // Product rule (fg)' = fg' + f'g
         case '*':
             return $this->nodeFactory->addition($this->nodeFactory->multiplication($node->getLeft(), $rightValue), $this->nodeFactory->multiplication($leftValue, $node->getRight()));
             // Quotient rule (f/g)' = (f'g - fg')/g^2
         // Quotient rule (f/g)' = (f'g - fg')/g^2
         case '/':
             $term1 = $this->nodeFactory->multiplication($leftValue, $node->getRight());
             $term2 = $this->nodeFactory->multiplication($node->getLeft(), $rightValue);
             $numerator = $this->nodeFactory->subtraction($term1, $term2);
             $denominator = $this->nodeFactory->exponentiation($node->getRight(), new IntegerNode(2));
             return $this->nodeFactory->division($numerator, $denominator);
             // f^g = exp(g log(f)), so (f^g)' = f^g (g'log(f) + g/f)
         // f^g = exp(g log(f)), so (f^g)' = f^g (g'log(f) + g/f)
         case '^':
             $base = $node->getLeft();
             $exponent = $node->getRight();
             if ($exponent instanceof IntegerNode || $exponent instanceof NumberNode) {
                 $power = $exponent->getValue();
                 $fpow = $this->nodeFactory->exponentiation($base, $power - 1);
                 return $this->nodeFactory->multiplication($power, $this->nodeFactory->multiplication($fpow, $leftValue));
             } elseif ($exponent instanceof RationalNode) {
                 $newPower = new RationalNode($exponent->getNumerator() - $exponent->getDenominator(), $exponent->getDenominator());
                 $fpow = $this->nodeFactory->exponentiation($base, $newPower);
                 return $this->nodeFactory->multiplication($exponent, $this->nodeFactory->multiplication($fpow, $leftValue));
             } else {
                 $term1 = $this->nodeFactory->multiplication($rightValue, new FunctionNode('log', $node->getLeft()));
                 $term2 = $this->nodeFactory->division($node->getRight(), $node->getLeft());
                 $factor2 = $this->nodeFactory->addition($term1, $term2);
                 return $this->nodeFactory->multiplication($node, $factor2);
             }
         default:
             throw new UnknownOperatorException($operator);
     }
 }
Example #8
0
 public function parenthesize(Node $node, ExpressionNode $cutoff, $prepend = '', $conservative = false)
 {
     $text = $node->accept($this);
     if ($node instanceof ExpressionNode) {
         // Second term is a unary minus
         if ($node->getOperator() == '-' && $node->getRight() == null) {
             return "({$text})";
         }
         if ($cutoff->getOperator() == '-' && $node->lowerPrecedenceThan($cutoff)) {
             return "({$text})";
         }
         if ($conservative) {
             // Add parentheses more liberally for / and ^ operators,
             // so that e.g. x/(y*z) is printed correctly
             if ($cutoff->getOperator() == '/' && $node->lowerPrecedenceThan($cutoff)) {
                 return "({$text})";
             }
             if ($cutoff->getOperator() == '^' && $node->getOperator() == '^') {
                 return "({$text})";
             }
         }
         if ($node->strictlyLowerPrecedenceThan($cutoff)) {
             return "({$text})";
         }
     }
     if (($node instanceof NumberNode || $node instanceof IntegerNode || $node instanceof RationalNode) && $node->getValue() < 0) {
         return "({$text})";
     }
     // Treat rational numbers as divisions on printing
     if ($node instanceof RationalNode && $node->getDenominator() != 1) {
         $fakeNode = new ExpressionNode($node->getNumerator(), '/', $node->getDenominator());
         if ($fakeNode->lowerPrecedenceThan($cutoff)) {
             return "({$text})";
         }
     }
     return "{$prepend}{$text}";
 }
 /**
  * Evaluate an ExpressionNode
  *
  * Computes the value of an ExpressionNode `x op y`
  * where `op` is one of `+`, `-`, `*`, `/` or `^`
  *
  * @throws UnknownOperatorException if the operator is something other than
  *      `+`, `-`, `*`, `/` or `^`
  *
  * @param ExpressionNode $node AST to be evaluated
  * @retval float
  */
 public function visitExpressionNode(ExpressionNode $node)
 {
     $operator = $node->getOperator();
     $a = $node->getLeft()->accept($this);
     if ($node->getRight()) {
         $b = $node->getRight()->accept($this);
     } else {
         $b = null;
     }
     // Perform the right operation based on the operator
     switch ($operator) {
         case '+':
             $p = $a->getNumerator() * $b->getDenominator() + $a->getDenominator() * $b->getNumerator();
             $q = $a->getDenominator() * $b->getDenominator();
             return new RationalNode($p, $q);
         case '-':
             if ($b === null) {
                 return new RationalNode(-$a->getNumerator(), $a->getDenominator());
             }
             $p = $a->getNumerator() * $b->getDenominator() - $a->getDenominator() * $b->getNumerator();
             $q = $a->getDenominator() * $b->getDenominator();
             return new RationalNode($p, $q);
         case '*':
             $p = $a->getNumerator() * $b->getNumerator();
             $q = $a->getDenominator() * $b->getDenominator();
             return new RationalNode($p, $q);
         case '/':
             if ($b->getNumerator() == 0) {
                 throw new DivisionByZeroException();
             }
             $p = $a->getNumerator() * $b->getDenominator();
             $q = $a->getDenominator() * $b->getNumerator();
             return new RationalNode($p, $q);
         case '^':
             return $this->rpow($a, $b);
         default:
             throw new UnknownOperatorException($operator);
     }
 }
Example #10
0
 public function testCanCompareExpressionNodes()
 {
     $node = new ExpressionNode(new VariableNode('x'), '+', new VariableNode('y'));
     $node2 = new ExpressionNode(new VariableNode('x'), '-', new VariableNode('y'));
     $node3 = new ExpressionNode(new VariableNode('x'), '-', null);
     $node4 = new ExpressionNode(null, '-', new VariableNode('y'));
     $other = new VariableNode('x');
     $this->assertFalse($node->compareTo(null));
     $this->assertFalse($node->compareTo($other));
     $this->assertTrue($node->compareTo($node));
     $this->assertTrue($node2->compareTo($node2));
     $this->assertTrue($node3->compareTo($node3));
     $this->assertTrue($node4->compareTo($node4));
     $this->assertFalse($node->compareTo($node2));
     $this->assertFalse($node->compareTo($node3));
     $this->assertFalse($node->compareTo($node4));
     $this->assertFalse($node2->compareTo($node3));
     $this->assertFalse($node2->compareTo($node4));
     $this->assertFalse($node3->compareTo($node4));
     $this->assertFalse($node2->compareTo($node4));
 }
Example #11
0
 /**
  * Simplify the given ExpressionNode, using the appropriate factory.
  *
  * @param ExpressionNode $node
  * @retval Node Simplified version of the input
  */
 public function simplify(ExpressionNode $node)
 {
     switch ($node->getOperator()) {
         case '+':
             return $this->addition($node->getLeft(), $node->getRight());
         case '-':
             return $this->subtraction($node->getLeft(), $node->getRight());
         case '*':
             return $this->multiplication($node->getLeft(), $node->getRight());
         case '/':
             return $this->division($node->getLeft(), $node->getRight());
         case '^':
             return $this->exponentiation($node->getLeft(), $node->getRight());
     }
 }
 public function testCanCreateTemporaryUnaryMinusNode()
 {
     $node = new ExpressionNode(null, '~', null);
     $this->assertEquals($node->getOperator(), '~');
     $this->assertNull($node->getRight());
     $this->assertNull($node->getLeft());
     $this->assertEquals($node->getPrecedence(), 25);
 }