public function testSemiFactorial() { $this->assertEquals(1, Math::SemiFactorial(0)); $this->assertEquals(1, Math::SemiFactorial(1)); $this->assertEquals(2, Math::SemiFactorial(2)); $this->assertEquals(3, Math::SemiFactorial(3)); $this->assertEquals(8, Math::SemiFactorial(4)); $this->assertEquals(15, Math::SemiFactorial(5)); $this->assertEquals(48, Math::SemiFactorial(6)); $this->assertEquals(105, Math::SemiFactorial(7)); }
/** * Normalize, i.e. make sure the denominator is positive and that * the numerator and denominator have no common factors */ private function normalize() { $gcd = Math::gcd($this->p, $this->q); if ($gcd == 0) { throw new DivisionByZeroException(); } $this->p = $this->p / $gcd; $this->q = $this->q / $gcd; if ($this->q < 0) { $this->p = -$this->p; $this->q = -$this->q; } }
/** * Evaluate a FunctionNode * * Computes the value of a FunctionNode `f(x)`, where f is * an elementary function recognized by StdMathLexer and StdMathParser. * * @see \MathParser\Lexer\StdMathLexer StdMathLexer * @see \MathParser\StdMathParser StdMathParser * @throws UnknownFunctionException if the function respresented by the * FunctionNode is *not* recognized. * * @param FunctionNode $node AST to be evaluated * @retval float */ public function visitFunctionNode(FunctionNode $node) { $inner = $node->getOperand()->accept($this); switch ($node->getName()) { // Trigonometric functions case 'sin': case 'cos': case 'tan': case 'cot': case 'arcsin': case 'arccos': case 'arctan': case 'arccot': case 'exp': case 'log': case 'lg': case 'sinh': case 'cosh': case 'tanh': case 'coth': case 'arsinh': case 'arcosh': case 'artanh': case 'arcoth': throw new \UnexpectedValueException("Expecting rational expression"); case 'abs': return new RationalNode(abs($inner->getNumerator()), $inner->getDenominator()); case 'sgn': if ($inner->getNumerator() >= 0) { return new RationalNode(1, 0); } else { return new RationalNode(-1, 0); } // Powers // Powers case 'sqrt': return $this->rpow($inner, new RationalNode(1, 2)); case '!': if ($inner->getDenominator() == 1 && $inner->getNumerator() >= 0) { return new RationalNode(Math::Factorial($inner->getNumerator()), 1); } throw new \UnexpectedValueException("Expecting positive integer (factorial)"); case '!!': if ($inner->getDenominator() == 1 && $inner->getNumerator() >= 0) { return new RationalNode(Math::SemiFactorial($inner->getNumerator()), 1); } throw new \UnexpectedValueException("Expecting positive integer (factorial)"); default: throw new UnknownFunctionException($node->getName()); } }
/** * Evaluate a FunctionNode * * Computes the value of a FunctionNode `f(x)`, where f is * an elementary function recognized by StdMathLexer and StdMathParser. * * @see \MathParser\Lexer\StdMathLexer StdMathLexer * @see \MathParser\StdMathParser StdMathParser * @throws UnknownFunctionException if the function respresented by the * FunctionNode is *not* recognized. * * @param FunctionNode $node AST to be evaluated * @retval float */ public function visitFunctionNode(FunctionNode $node) { $inner = $node->getOperand()->accept($this); switch ($node->getName()) { // Trigonometric functions case 'sin': return sin($inner); case 'cos': return cos($inner); case 'tan': return tan($inner); case 'cot': return 1 / tan($inner); // Trigonometric functions, argument in degrees // Trigonometric functions, argument in degrees case 'sind': return sin(deg2rad($inner)); case 'cosd': return cos(deg2rad($inner)); case 'tand': return tan(deg2rad($inner)); case 'cotd': return 1 / tan(deg2rad($inner)); // Inverse trigonometric functions // Inverse trigonometric functions case 'arcsin': return asin($inner); case 'arccos': return acos($inner); case 'arctan': return atan($inner); case 'arccot': return pi() / 2 - atan($inner); // Exponentials and logarithms // Exponentials and logarithms case 'exp': return exp($inner); case 'log': return log($inner); case 'lg': return log10($inner); // Powers // Powers case 'sqrt': return sqrt($inner); // Hyperbolic functions // Hyperbolic functions case 'sinh': return sinh($inner); case 'cosh': return cosh($inner); case 'tanh': return tanh($inner); case 'coth': return 1 / tanh($inner); // Inverse hyperbolic functions // Inverse hyperbolic functions case 'arsinh': return asinh($inner); case 'arcosh': return acosh($inner); case 'artanh': return atanh($inner); case 'arcoth': return atanh(1 / $inner); case 'abs': return abs($inner); case 'sgn': return $inner >= 0 ? 1 : 0; case '!': $logGamma = Math::logGamma(1 + $inner); return exp($logGamma); case '!!': if (round($inner) != $inner) { throw new \UnexpectedValueException("Expecting positive integer (semifactorial)"); } return Math::SemiFactorial($inner); default: throw new UnknownFunctionException($node->getName()); } }