diff --git a/poincare/Makefile b/poincare/Makefile index 895346848..47280a776 100644 --- a/poincare/Makefile +++ b/poincare/Makefile @@ -40,6 +40,7 @@ objs += $(addprefix poincare/src/,\ arc_sine.o\ arc_tangent.o\ arithmetic.o\ + binomial_coefficient.o\ complex.o\ cosine.o\ decimal.o\ @@ -214,6 +215,7 @@ tests += $(addprefix poincare/test/,\ tree/tree_by_reference.cpp\ expression.cpp\ addition.cpp\ + function.cpp\ helper.cpp\ integer.cpp\ logarithm.cpp\ diff --git a/poincare/include/poincare.h b/poincare/include/poincare.h index dc3452420..5972abf6e 100644 --- a/poincare/include/poincare.h +++ b/poincare/include/poincare.h @@ -110,6 +110,7 @@ #include #include #include +#include #include #include #include diff --git a/poincare/include/poincare/binomial_coefficient.h b/poincare/include/poincare/binomial_coefficient.h index 6bd974456..83244688b 100644 --- a/poincare/include/poincare/binomial_coefficient.h +++ b/poincare/include/poincare/binomial_coefficient.h @@ -1,30 +1,56 @@ #ifndef POINCARE_BINOMIAL_COEFFICIENT_H #define POINCARE_BINOMIAL_COEFFICIENT_H -#include +#include +#include #include -#include namespace Poincare { -class BinomialCoefficient : public StaticHierarchy<2> { - using StaticHierarchy<2>::StaticHierarchy; +class BinomialCoefficientNode : public ExpressionNode { public: - Type type() const override; + static BinomialCoefficientNode * FailedAllocationStaticNode(); + BinomialCoefficientNode * failedAllocationStaticNode() override { return FailedAllocationStaticNode(); } + + // TreeNode + size_t size() const override { return sizeof(BinomialCoefficientNode); } + int numberOfChildren() const override { return 2; } +#if POINCARE_TREE_LOG + virtual void logNodeName(std::ostream & stream) const override { + stream << "BinomialCoefficient"; + } +#endif + + // ExpressionNode + + // Properties + Type type() const override{ return Type::BinomialCoefficient; } template static T compute(T k, T n); private: - constexpr static int k_maxNValue = 300; - /* Layout */ - LayoutRef createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; - int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override { - return SerializationHelper::Prefix(this, buffer, bufferSize, floatDisplayMode, numberOfSignificantDigits, "binomial"); - } - /* Simplification */ + // Layout + LayoutReference createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; + int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; + // Simplification Expression shallowReduce(Context& context, Preferences::AngleUnit angleUnit) const override; - /* Evaluation */ + // Evaluation Evaluation approximate(SinglePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } Evaluation approximate(DoublePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } - template Evaluation templatedApproximate(Context& context, Preferences::AngleUnit angleUnit) const; + template Complex templatedApproximate(Context& context, Preferences::AngleUnit angleUnit) const; +}; + +class BinomialCoefficient : public Expression { +public: + BinomialCoefficient() : Expression(TreePool::sharedPool()->createTreeNode()) {} + BinomialCoefficient(const BinomialCoefficientNode * n) : Expression(n) {} + BinomialCoefficient(Expression child1, Expression child2) : BinomialCoefficient() { + replaceChildAtIndexInPlace(0, child1); + replaceChildAtIndexInPlace(1, child2); + } + + // Expression + Expression shallowReduce(Context& context, Preferences::AngleUnit angleUnit) const; +private: + constexpr static int k_maxNValue = 300; }; } diff --git a/poincare/src/binomial_coefficient.cpp b/poincare/src/binomial_coefficient.cpp index 084fe5d10..4b0f0d496 100644 --- a/poincare/src/binomial_coefficient.cpp +++ b/poincare/src/binomial_coefficient.cpp @@ -1,96 +1,45 @@ #include -#include -#include #include - -extern "C" { +#include +#include +#include #include #include -} #include namespace Poincare { -ExpressionNode::Type BinomialCoefficient::type() const { - return Type::BinomialCoefficient; +BinomialCoefficientNode * BinomialCoefficientNode::FailedAllocationStaticNode() { + static AllocationFailureExpressionNode failure; + TreePool::sharedPool()->registerStaticNodeIfRequired(&failure); + return &failure; } -Expression * BinomialCoefficient::clone() const { - BinomialCoefficient * b = new BinomialCoefficient(m_operands, true); - return b; +Expression BinomialCoefficientNode::shallowReduce(Context& context, Preferences::AngleUnit angleUnit) const { + return BinomialCoefficient(this).shallowReduce(context, angleUnit); } -Expression BinomialCoefficient::shallowReduce(Context& context, Preferences::AngleUnit angleUnit) const { - Expression e = Expression::defaultShallowReduce(context, angleUnit); - if (e.isUndefinedOrAllocationFailure()) { - return e; - } - Expression * op0 = childAtIndex(0); - Expression * op1 = childAtIndex(1); -#if MATRIX_EXACT_REDUCING - if (op0->type() == Type::Matrix || op1->type() == Type::Matrix) { - return replaceWith(new Undefined(), true); - } -#endif - if (op0->type() == Type::Rational) { - Rational * r0 = static_cast(op0); - if (!r0->denominator().isOne() || r0->numerator().isNegative()) { - return replaceWith(new Undefined(), true); - } - } - if (op1->type() == Type::Rational) { - Rational * r1 = static_cast(op1); - if (!r1->denominator().isOne() || r1->numerator().isNegative()) { - return replaceWith(new Undefined(), true); - } - } - if (op0->type() != Type::Rational || op1->type() != Type::Rational) { - return this; - } - Rational * r0 = static_cast(op0); - Rational * r1 = static_cast(op1); - - Integer n = r0->numerator(); - Integer k = r1->numerator(); - if (n.isLowerThan(k)) { - return replaceWith(new Undefined(), true); - } - /* if n is too big, we do not reduce to avoid too long computation. - * The binomial coefficient will be evaluate approximatively later */ - if (Integer(k_maxNValue).isLowerThan(n)) { - return this; - } - Rational result(1); - Integer kBis = Integer::Subtraction(n, k); - k = kBis.isLowerThan(k) ? kBis : k; - int clippedK = k.extractedInt(); // Authorized because k < n < k_maxNValue - for (int i = 0; i < clippedK; i++) { - Rational factor = Rational(Integer::Subtraction(n, Integer(i)), Integer::Subtraction(k, Integer(i))); - result = Rational::Multiplication(result, factor); - } - return replaceWith(new Rational(result), true); -} - -LayoutRef BinomialCoefficient::createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const { +LayoutReference BinomialCoefficientNode::createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const { return BinomialCoefficientLayoutRef( childAtIndex(0)->createLayout(floatDisplayMode, numberOfSignificantDigits), childAtIndex(1)->createLayout(floatDisplayMode, numberOfSignificantDigits)); } -template -Complex * BinomialCoefficient::templatedApproximate(Context& context, Preferences::AngleUnit angleUnit) const { - Evaluation * nInput = childAtIndex(0)->privateApproximate(T(), context, angleUnit); - Evaluation * kInput = childAtIndex(1)->privateApproximate(T(), context, angleUnit); - T n = nInput->toScalar(); - T k = kInput->toScalar(); - delete nInput; - delete kInput; - return new Complex(compute(k, n)); +int BinomialCoefficientNode::serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const { + return SerializationHelper::Prefix(this, buffer, bufferSize, floatDisplayMode, numberOfSignificantDigits, "binomial"); } +template +Complex BinomialCoefficientNode::templatedApproximate(Context& context, Preferences::AngleUnit angleUnit) const { + Evaluation nInput = childAtIndex(0)->approximate(T(), context, angleUnit); + Evaluation kInput = childAtIndex(1)->approximate(T(), context, angleUnit); + T n = nInput.toScalar(); + T k = kInput.toScalar(); + return Complex(compute(k, n)); +} template -T BinomialCoefficient::compute(T k, T n) { +T BinomialCoefficientNode::compute(T k, T n) { k = k > (n-k) ? n-k : k; if (std::isnan(n) || std::isnan(k) || n != std::round(n) || k != std::round(k) || k > n || k < 0 || n < 0) { return NAN; @@ -105,7 +54,58 @@ T BinomialCoefficient::compute(T k, T n) { return std::round(result); } -template double BinomialCoefficient::compute(double k, double n); -template float BinomialCoefficient::compute(float k, float n); +Expression BinomialCoefficient::shallowReduce(Context& context, Preferences::AngleUnit angleUnit) const { + Expression e = Expression::defaultShallowReduce(context, angleUnit); + if (e.isUndefinedOrAllocationFailure()) { + return e; + } + Expression op0 = childAtIndex(0); + Expression op1 = childAtIndex(1); +#if MATRIX_EXACT_REDUCING + if (op0.type() == ExpressionNode::Type::Matrix || op1.type() == ExpressionNode::Type::Matrix) { + return Undefined(); + } +#endif + if (op0.type() == ExpressionNode::Type::Rational) { + Rational r0 = static_cast(op0); + if (!r0.integerDenominator().isOne() || r0.integerDenominator().isNegative()) { + return Undefined(); + } + } + if (op1.type() == ExpressionNode::Type::Rational) { + Rational r1 = static_cast(op1); + if (!r1.integerDenominator().isOne() || r1.integerDenominator().isNegative()) { + return Undefined(); + } + } + if (op0.type() != ExpressionNode::Type::Rational || op1.type() != ExpressionNode::Type::Rational) { + return *this; + } + Rational r0 = static_cast(op0); + Rational r1 = static_cast(op1); + + Integer n = r0.signedIntegerNumerator(); + Integer k = r1.signedIntegerNumerator(); + if (n.isLowerThan(k)) { + return Undefined(); + } + /* If n is too big, we do not reduce in order to avoid too long computation. + * The binomial coefficient will be approximatively evaluated later. */ + if (Integer(k_maxNValue).isLowerThan(n)) { + return *this; + } + Rational result(1); + Integer kBis = Integer::Subtraction(n, k); + k = kBis.isLowerThan(k) ? kBis : k; + int clippedK = k.extractedInt(); // Authorized because k < n < k_maxNValue + for (int i = 0; i < clippedK; i++) { + Rational factor = Rational(Integer::Subtraction(n, Integer(i)), Integer::Subtraction(k, Integer(i))); + result = Rational::Multiplication(result, factor); + } + return Rational(result); +} + +template double BinomialCoefficientNode::compute(double k, double n); +template float BinomialCoefficientNode::compute(float k, float n); } diff --git a/poincare/src/expression_lexer.l b/poincare/src/expression_lexer.l index 257825f98..3ef88cf4e 100644 --- a/poincare/src/expression_lexer.l +++ b/poincare/src/expression_lexer.l @@ -123,7 +123,8 @@ asin { poincare_expression_yylval.expression = ArcSine(); return FUNCTION; } asinh { poincare_expression_yylval.expression = HyperbolicArcSine(); return FUNCTION; } atan { poincare_expression_yylval.expression = ArcTangent(); return FUNCTION; } atanh { poincare_expression_yylval.expression = HyperbolicArcTangent(); return FUNCTION; } - /*binomial { poincare_expression_yylval.expression = new BinomialCoefficient(); return FUNCTION; } +binomial { poincare_expression_yylval.expression = BinomialCoefficient(); return FUNCTION; } + /* ceil { poincare_expression_yylval.expression = new Ceiling(); return FUNCTION; } confidence { poincare_expression_yylval.expression = new ConfidenceInterval(); return FUNCTION; } diff { poincare_expression_yylval.expression = new Derivative(); return FUNCTION; } diff --git a/poincare/test/function.cpp b/poincare/test/function.cpp index e417dd19a..93824f3ac 100644 --- a/poincare/test/function.cpp +++ b/poincare/test/function.cpp @@ -8,17 +8,20 @@ using namespace Poincare; template -void assert_exp_is_bounded(Expression * exp, T lowBound, T upBound, bool upBoundIncluded = false) { +void assert_exp_is_bounded(Expression exp, T lowBound, T upBound, bool upBoundIncluded = false) { GlobalContext globalContext; - T result = exp->approximateToScalar(globalContext, Radian); + T result = exp.approximateToScalar(globalContext, Radian); assert(result >= lowBound); assert(result < upBound || (result == upBound && upBoundIncluded)); } QUIZ_CASE(poincare_parse_function) { +#if 0 assert_parsed_expression_type("abs(-1)", ExpressionNode::Type::AbsoluteValue); assert_parsed_expression_type("arg(2+I)", ExpressionNode::Type::ComplexArgument); +#endif assert_parsed_expression_type("binomial(10, 4)", ExpressionNode::Type::BinomialCoefficient); +#if 0 assert_parsed_expression_type("ceil(0.2)", ExpressionNode::Type::Ceiling); assert_parsed_expression_type("diff(2*x, 2)", ExpressionNode::Type::Derivative); #if MATRICES_ARE_DEFINED @@ -57,10 +60,12 @@ QUIZ_CASE(poincare_parse_function) { assert_parsed_expression_type("transpose([[1,2,3][4,5,6][7,8,9]])", ExpressionNode::Type::MatrixTranspose); #endif assert_parsed_expression_type("6!", ExpressionNode::Type::Factorial); +#endif } QUIZ_CASE(poincare_function_evaluate) { +#if 0 assert_parsed_expression_evaluates_to("abs(-1)", "1"); assert_parsed_expression_evaluates_to("abs(-1)", "1"); @@ -73,8 +78,10 @@ QUIZ_CASE(poincare_function_evaluate) { assert_parsed_expression_evaluates_to("abs([[3+2I,3+4I][5+2I,3+2I]])", "[[3.605551,5][5.385165,3.605551]]"); assert_parsed_expression_evaluates_to("abs([[3+2I,3+4I][5+2I,3+2I]])", "[[3.605551275464,5][5.3851648071345,3.605551275464]]"); +#endif assert_parsed_expression_evaluates_to("binomial(10, 4)", "210"); assert_parsed_expression_evaluates_to("binomial(10, 4)", "210"); +#if 0 assert_parsed_expression_evaluates_to("ceil(0.2)", "1"); assert_parsed_expression_evaluates_to("ceil(0.2)", "1"); @@ -223,13 +230,17 @@ QUIZ_CASE(poincare_function_evaluate) { assert_exp_is_bounded(exp, 4.0f, 45.0f, true); assert_exp_is_bounded(exp, 4.0, 45.0, true); delete exp; +#endif } QUIZ_CASE(poincare_function_simplify) { +#if 0 assert_parsed_expression_simplify_to("abs(P)", "P"); assert_parsed_expression_simplify_to("abs(-P)", "P"); +#endif assert_parsed_expression_simplify_to("binomial(20,3)", "1140"); assert_parsed_expression_simplify_to("binomial(20,10)", "184756"); +#if 0 assert_parsed_expression_simplify_to("ceil(-1.3)", "-1"); assert_parsed_expression_simplify_to("conj(1/2)", "1/2"); assert_parsed_expression_simplify_to("quo(19,3)", "6"); @@ -261,4 +272,5 @@ QUIZ_CASE(poincare_function_simplify) { assert_parsed_expression_simplify_to("permute(99,4)", "90345024"); assert_parsed_expression_simplify_to("permute(20,-10)", "undef"); assert_parsed_expression_simplify_to("re(1/2)", "1/2"); +#endif }