diff --git a/.gitignore b/.gitignore index c4a677a32..6ee6fa339 100644 --- a/.gitignore +++ b/.gitignore @@ -12,11 +12,11 @@ poincare/src/expression_parser.cpp poincare/src/expression_parser.hpp # No rulegen generated files -poincare/src/simplify/rules_generation/rules_tokens.h -poincare/src/simplify/rules_generation/rules_lexer.cpp -poincare/src/simplify/rules_generation/rules_parser.cpp -poincare/src/simplify/rules_generation/rulegen -poincare/src/simplify/rules.cpp +poincare/src/simplification/rulegen/rules_tokens.h +poincare/src/simplification/rulegen/rules_lexer.cpp +poincare/src/simplification/rulegen/rules_parser.cpp +poincare/src/simplification/rulegen/rulegen +poincare/src/simplification/demo_ruleset.h # Font related generated files. kandinsky/fonts/rasterizer diff --git a/apps/apps_container.cpp b/apps/apps_container.cpp index 6a1beb87f..fb5a54edd 100644 --- a/apps/apps_container.cpp +++ b/apps/apps_container.cpp @@ -29,7 +29,7 @@ AppsContainer::AppsContainer() : Poincare::Expression::setCircuitBreaker(AppsContainer::poincareCircuitBreaker); } -bool AppsContainer::poincareCircuitBreaker(const Poincare::Expression * e) { +bool AppsContainer::poincareCircuitBreaker() { Ion::Keyboard::State state = Ion::Keyboard::scan(); return state.keyDown(Ion::Keyboard::Key::A6); } diff --git a/apps/apps_container.h b/apps/apps_container.h index 5eca2eef7..c7ed5b272 100644 --- a/apps/apps_container.h +++ b/apps/apps_container.h @@ -26,7 +26,7 @@ class AppsContainer : public Container { public: AppsContainer(); - static bool poincareCircuitBreaker(const Poincare::Expression * e); + static bool poincareCircuitBreaker(); virtual int numberOfApps() = 0; virtual App::Snapshot * appSnapshotAtIndex(int index) = 0; App::Snapshot * hardwareTestAppSnapshot(); diff --git a/apps/calculation/Makefile b/apps/calculation/Makefile index f7d65e7c7..c25380236 100644 --- a/apps/calculation/Makefile +++ b/apps/calculation/Makefile @@ -9,7 +9,9 @@ app_objs += $(addprefix apps/calculation/,\ history_view_cell.o\ history_controller.o\ local_context.o\ + output_expressions_view.o\ scrollable_expression_view.o\ + scrollable_output_expressions_view.o\ selectable_table_view.o\ text_field.o\ ) diff --git a/apps/calculation/calculation.cpp b/apps/calculation/calculation.cpp index 941f23113..72a9ba3cf 100644 --- a/apps/calculation/calculation.cpp +++ b/apps/calculation/calculation.cpp @@ -7,11 +7,14 @@ namespace Calculation { Calculation::Calculation() : m_inputText(), - m_outputText(), + m_exactOutputText(), + m_approximateOutputText(), m_input(nullptr), m_inputLayout(nullptr), - m_output(nullptr), - m_outputLayout(nullptr) + m_exactOutput(nullptr), + m_exactOutputLayout(nullptr), + m_approximateOutput(nullptr), + m_approximateOutputLayout(nullptr) { } @@ -24,37 +27,51 @@ Calculation::~Calculation() { delete m_input; m_input = nullptr; } - if (m_output != nullptr) { - delete m_output; - m_output = nullptr; + if (m_exactOutput != nullptr) { + delete m_exactOutput; + m_exactOutput = nullptr; } - if (m_outputLayout != nullptr) { - delete m_outputLayout; - m_outputLayout = nullptr; + if (m_exactOutputLayout != nullptr) { + delete m_exactOutputLayout; + m_exactOutputLayout = nullptr; + } + if (m_approximateOutput != nullptr) { + delete m_approximateOutput; + m_approximateOutput = nullptr; + } + if (m_approximateOutputLayout != nullptr) { + delete m_approximateOutputLayout; + m_approximateOutputLayout = nullptr; } } Calculation& Calculation::operator=(const Calculation& other) { const char * otherInputText = other.m_inputText; - const char * otherOutputText = other.m_outputText; + const char * otherExactOutputText = other.m_exactOutputText; + const char * otherApproximateOutputText = other.m_approximateOutputText; reset(); strlcpy(m_inputText, otherInputText, sizeof(m_inputText)); - strlcpy(m_outputText, otherOutputText, sizeof(m_outputText)); + strlcpy(m_exactOutputText, otherExactOutputText, sizeof(m_exactOutputText)); + strlcpy(m_approximateOutputText, otherApproximateOutputText, sizeof(m_approximateOutputText)); return *this; } void Calculation::reset() { m_inputText[0] = 0; - m_outputText[0] = 0; + m_exactOutputText[0] = 0; + m_approximateOutputText[0] = 0; tidy(); } void Calculation::setContent(const char * c, Context * context) { reset(); - strlcpy(m_inputText, c, sizeof(m_inputText)); - Evaluation * evaluation = input()->evaluate(*context); - evaluation->writeTextInBuffer(m_outputText, sizeof(m_outputText)); - delete evaluation; + m_input = Expression::parse(c); + m_input->writeTextInBuffer(m_inputText, sizeof(m_inputText)); + m_exactOutput = input()->clone(); + Expression::Simplify(&m_exactOutput, *context); + m_exactOutput->writeTextInBuffer(m_exactOutputText, sizeof(m_exactOutputText)); + m_approximateOutput = m_exactOutput->evaluate(*context); + m_approximateOutput->writeTextInBuffer(m_approximateOutputText, sizeof(m_approximateOutputText)); } const char * Calculation::inputText() { @@ -62,7 +79,18 @@ const char * Calculation::inputText() { } const char * Calculation::outputText() { - return m_outputText; + if (shouldApproximateOutput()) { + return m_approximateOutputText; + } + return m_exactOutputText; +} + +const char * Calculation::exactOutputText() { + return m_exactOutputText; +} + +const char * Calculation::approximateOutputText() { + return m_approximateOutputText; } Expression * Calculation::input() { @@ -79,36 +107,29 @@ ExpressionLayout * Calculation::inputLayout() { return m_inputLayout; } -Evaluation * Calculation::output(Context * context) { - if (m_output == nullptr) { - /* To ensure that the expression 'm_output' is a matrix or a complex, we - * call 'evaluate'. */ - Expression * exp = Expression::parse(m_outputText); - if (exp != nullptr) { - m_output = exp->evaluate(*context); - delete exp; - } else { - m_output = new Complex(Complex::Float(NAN)); - } +Expression * Calculation::output(Context * context) { + if (shouldApproximateOutput()) { + return approximateOutput(context); } - return m_output; + return exactOutput(context); } ExpressionLayout * Calculation::outputLayout(Context * context) { - if (m_outputLayout == nullptr && output(context) != nullptr) { - m_outputLayout = output(context)->createLayout(); + if (shouldApproximateOutput()) { + return approximateOutputLayout(context); } - return m_outputLayout; + return exactOutputLayout(context); } bool Calculation::isEmpty() { /* To test if a calculation is empty, we need to test either m_inputText or - * m_outputText, the only two fields that are not lazy-loaded. We choose - * m_outputText to consider that a calculation being added is still empty - * until the end of the method 'setContent'. Indeed, during 'setContent' - * method, 'ans' evaluation calls the evaluation of the last calculation - * only if the calculation being filled is not taken into account.*/ - if (strlen(m_outputText) == 0) { + * m_exactOutputText or m_approximateOutputText, the only three fields that + * are not lazy-loaded. We choose m_exactOutputText to consider that a + * calculation being added is still empty until the end of the method + * 'setContent'. Indeed, during 'setContent' method, 'ans' evaluation calls + * the evaluation of the last calculation only if the calculation being + * filled is not taken into account.*/ + if (strlen(m_approximateOutputText) == 0) { return true; } return false; @@ -123,14 +144,77 @@ void Calculation::tidy() { delete m_inputLayout; } m_inputLayout = nullptr; - if (m_output != nullptr) { - delete m_output; + if (m_exactOutput != nullptr) { + delete m_exactOutput; } - m_output = nullptr; - if (m_outputLayout != nullptr) { - delete m_outputLayout; + m_exactOutput = nullptr; + if (m_exactOutputLayout != nullptr) { + delete m_exactOutputLayout; } - m_outputLayout = nullptr; + m_exactOutputLayout = nullptr; + if (m_approximateOutput != nullptr) { + delete m_approximateOutput; + } + m_approximateOutput = nullptr; + if (m_approximateOutputLayout != nullptr) { + delete m_approximateOutputLayout; + } + m_approximateOutputLayout = nullptr; +} + +Expression * Calculation::exactOutput(Context * context) { + if (m_exactOutput == nullptr) { + /* To ensure that the expression 'm_exactOutput' is a simplified, we + * call 'simplifyAndBeautify'. */ + m_exactOutput = Expression::parse(m_exactOutputText); + if (m_exactOutput != nullptr) { + Expression::Simplify(&m_exactOutput, *context); + } else { + m_exactOutput = new Undefined(); + } + } + return m_exactOutput; +} + +ExpressionLayout * Calculation::exactOutputLayout(Context * context) { + if (m_exactOutputLayout == nullptr && exactOutput(context) != nullptr) { + m_exactOutputLayout = exactOutput(context)->createLayout(); + } + return m_exactOutputLayout; +} + +Expression * Calculation::approximateOutput(Context * context) { + if (m_approximateOutput == nullptr) { + /* To ensure that the expression 'm_output' is a matrix or a complex, we + * call 'evaluate'. */ + Expression * exp = Expression::parse(m_approximateOutputText); + if (exp != nullptr) { + m_approximateOutput = exp->evaluate(*context); + delete exp; + } else { + m_approximateOutput = new Complex(Complex::Float(NAN)); + } + } + return m_approximateOutput; +} + +ExpressionLayout * Calculation::approximateOutputLayout(Context * context) { + if (m_approximateOutputLayout == nullptr && approximateOutput(context) != nullptr) { + m_approximateOutputLayout = approximateOutput(context)->createLayout(); + } + return m_approximateOutputLayout; +} + +bool Calculation::shouldApproximateOutput() { + if (strcmp(m_exactOutputText, m_approximateOutputText) == 0) { + return true; + } + if (strcmp(m_exactOutputText, m_inputText) == 0) { + return true; + } + return input()->recursivelyMatches([](const Expression * e) { + return e->type() == Expression::Type::Decimal || Expression::IsMatrix(e); + }); } } diff --git a/apps/calculation/calculation.h b/apps/calculation/calculation.h index ffda27608..e85e4dc2b 100644 --- a/apps/calculation/calculation.h +++ b/apps/calculation/calculation.h @@ -16,22 +16,32 @@ public: Calculation& operator=(Calculation&& other) = delete; /* c.reset() is the equivalent of c = Calculation() without copy assingment. */ void reset(); + void setContent(const char * c, Poincare::Context * context); const char * inputText(); const char * outputText(); + const char * exactOutputText(); + const char * approximateOutputText(); Poincare::Expression * input(); Poincare::ExpressionLayout * inputLayout(); - Poincare::Evaluation * output(Poincare::Context * context); + Poincare::Expression * output(Poincare::Context * context); + Poincare::Expression * approximateOutput(Poincare::Context * context); Poincare::ExpressionLayout * outputLayout(Poincare::Context * context); - void setContent(const char * c, Poincare::Context * context); + Poincare::ExpressionLayout * exactOutputLayout(Poincare::Context * context); + Poincare::ExpressionLayout * approximateOutputLayout(Poincare::Context * context); bool isEmpty(); void tidy(); + bool shouldApproximateOutput(); private: + Poincare::Expression * exactOutput(Poincare::Context * context); char m_inputText[::TextField::maxBufferSize()]; - char m_outputText[2*::TextField::maxBufferSize()]; + char m_exactOutputText[2*::TextField::maxBufferSize()]; + char m_approximateOutputText[2*::TextField::maxBufferSize()]; Poincare::Expression * m_input; Poincare::ExpressionLayout * m_inputLayout; - Poincare::Evaluation * m_output; - Poincare::ExpressionLayout * m_outputLayout; + Poincare::Expression * m_exactOutput; + Poincare::ExpressionLayout * m_exactOutputLayout; + Poincare::Expression * m_approximateOutput; + Poincare::ExpressionLayout * m_approximateOutputLayout; }; } diff --git a/apps/calculation/history_controller.cpp b/apps/calculation/history_controller.cpp index 5bf9a5a95..ca2b1e699 100644 --- a/apps/calculation/history_controller.cpp +++ b/apps/calculation/history_controller.cpp @@ -47,7 +47,12 @@ bool HistoryController::handleEvent(Ion::Events::Event event) { if (subviewType == HistoryViewCell::SubviewType::Input) { editController->insertTextBody(calculation->inputText()); } else { - editController->insertTextBody(calculation->outputText()); + OutputExpressionsView::SubviewType outputSubviewType = selectedCell->outputView()->selectedSubviewType(); + if (outputSubviewType == OutputExpressionsView::SubviewType::ExactOutput) { + editController->insertTextBody(calculation->exactOutputText()); + } else { + editController->insertTextBody(calculation->approximateOutputText()); + } } return true; } @@ -97,7 +102,12 @@ bool HistoryController::handleEvent(Ion::Events::Event event) { if (subviewType == HistoryViewCell::SubviewType::Input) { Clipboard::sharedClipboard()->store(calculation->inputText()); } else { - Clipboard::sharedClipboard()->store(calculation->outputText()); + OutputExpressionsView::SubviewType outputSubviewType = selectedCell->outputView()->selectedSubviewType(); + if (outputSubviewType == OutputExpressionsView::SubviewType::ExactOutput) { + Clipboard::sharedClipboard()->store(calculation->exactOutputText()); + } else { + Clipboard::sharedClipboard()->store(calculation->approximateOutputText()); + } } return true; } @@ -109,14 +119,12 @@ void HistoryController::tableViewDidChangeSelection(SelectableTableView * t, int if (selectedCell == nullptr) { return; } - if (selectedRow() < previousSelectedCellY) { - selectedCell->setSelectedSubviewType(HistoryViewCell::SubviewType::Output); - } - if (selectedRow() >= previousSelectedCellY) { - selectedCell->setSelectedSubviewType(HistoryViewCell::SubviewType::Input); - } if (previousSelectedCellY == -1) { selectedCell->setSelectedSubviewType(HistoryViewCell::SubviewType::Output); + } else if (selectedRow() < previousSelectedCellY) { + selectedCell->setSelectedSubviewType(HistoryViewCell::SubviewType::Output); + } else if (selectedRow() > previousSelectedCellY) { + selectedCell->setSelectedSubviewType(HistoryViewCell::SubviewType::Input); } app()->setFirstResponder(selectedCell); selectedCell->reloadCell(); @@ -142,6 +150,7 @@ void HistoryController::willDisplayCellForIndex(HighlightCell * cell, int index) HistoryViewCell * myCell = (HistoryViewCell *)cell; myCell->setCalculation(m_calculationStore->calculationAtIndex(index)); myCell->setEven(index%2 == 0); + myCell->reloadCell(); } KDCoordinate HistoryController::rowHeight(int j) { @@ -151,7 +160,9 @@ KDCoordinate HistoryController::rowHeight(int j) { Calculation * calculation = m_calculationStore->calculationAtIndex(j); KDCoordinate inputHeight = calculation->inputLayout()->size().height(); App * calculationApp = (App *)app(); - KDCoordinate outputHeight = calculation->outputLayout(calculationApp->localContext())->size().height(); + KDCoordinate exactOutputHeight = calculation->exactOutputLayout(calculationApp->localContext())->size().height(); + KDCoordinate approximateOutputHeight = calculation->approximateOutputLayout(calculationApp->localContext())->size().height(); + KDCoordinate outputHeight = calculation->shouldApproximateOutput() || approximateOutputHeight > exactOutputHeight ? approximateOutputHeight : exactOutputHeight; return inputHeight + outputHeight + 3*HistoryViewCell::k_digitVerticalMargin; } diff --git a/apps/calculation/history_view_cell.cpp b/apps/calculation/history_view_cell.cpp index d99b90b13..02c1ec198 100644 --- a/apps/calculation/history_view_cell.cpp +++ b/apps/calculation/history_view_cell.cpp @@ -10,11 +10,46 @@ namespace Calculation { HistoryViewCell::HistoryViewCell(Responder * parentResponder) : Responder(parentResponder), m_inputView(this), - m_outputView(this), + m_scrollableOutputView(this), m_selectedSubviewType(HistoryViewCell::SubviewType::Output) { } +OutputExpressionsView * HistoryViewCell::outputView() { + return m_scrollableOutputView.outputView(); + +} +void HistoryViewCell::setEven(bool even) { + EvenOddCell::setEven(even); + m_inputView.setBackgroundColor(backgroundColor()); + m_scrollableOutputView.outputView()->setEven(even); +} + +void HistoryViewCell::setHighlighted(bool highlight) { + m_highlighted = highlight; + m_inputView.setBackgroundColor(backgroundColor()); + m_scrollableOutputView.outputView()->setHighlighted(false); + if (isHighlighted()) { + if (m_selectedSubviewType == SubviewType::Input) { + m_inputView.setBackgroundColor(Palette::Select); + } else { + m_scrollableOutputView.outputView()->setHighlighted(true); + } + } + reloadScroll(); +} + +void HistoryViewCell::reloadCell() { + m_scrollableOutputView.outputView()->reloadCell(); + layoutSubviews(); + reloadScroll(); +} + +void HistoryViewCell::reloadScroll() { + m_inputView.reloadScroll(); + m_scrollableOutputView.reloadScroll(); +} + KDColor HistoryViewCell::backgroundColor() const { KDColor background = m_even ? Palette::WallScreen : KDColorWhite; return background; @@ -26,7 +61,7 @@ int HistoryViewCell::numberOfSubviews() const { } View * HistoryViewCell::subviewAtIndex(int index) { - View * views[2] = {&m_inputView, &m_outputView}; + View * views[2] = {&m_inputView, &m_scrollableOutputView}; return views[index]; } @@ -39,41 +74,29 @@ void HistoryViewCell::layoutSubviews() { } else { m_inputView.setFrame(KDRect(Metric::HistoryHorizontalMargin, k_digitVerticalMargin, inputSize.width(), inputSize.height())); } - KDSize outputSize = m_outputView.minimalSizeForOptimalDisplay(); + KDSize outputSize = m_scrollableOutputView.minimalSizeForOptimalDisplay(); if (outputSize.width() + Metric::HistoryHorizontalMargin > width) { - m_outputView.setFrame(KDRect(Metric::HistoryHorizontalMargin, inputSize.height() + 2*k_digitVerticalMargin, width - Metric::HistoryHorizontalMargin, height - inputSize.height() - 3*k_digitVerticalMargin)); + m_scrollableOutputView.setFrame(KDRect(Metric::HistoryHorizontalMargin, inputSize.height() + 2*k_digitVerticalMargin, width - Metric::HistoryHorizontalMargin, height - inputSize.height() - 3*k_digitVerticalMargin)); } else { - m_outputView.setFrame(KDRect(width - outputSize.width() - Metric::HistoryHorizontalMargin, inputSize.height() + 2*k_digitVerticalMargin, outputSize.width(), height - inputSize.height() - 3*k_digitVerticalMargin)); + m_scrollableOutputView.setFrame(KDRect(width - outputSize.width() - Metric::HistoryHorizontalMargin, inputSize.height() + 2*k_digitVerticalMargin, outputSize.width(), height - inputSize.height() - 3*k_digitVerticalMargin)); } } void HistoryViewCell::setCalculation(Calculation * calculation) { m_inputView.setExpression(calculation->inputLayout()); App * calculationApp = (App *)app(); - m_outputView.setExpression(calculation->outputLayout(calculationApp->localContext())); -} - -void HistoryViewCell::reloadCell() { - m_outputView.setBackgroundColor(backgroundColor()); - m_inputView.setBackgroundColor(backgroundColor()); - if (isHighlighted()) { - if (m_selectedSubviewType == SubviewType::Output) { - m_outputView.setBackgroundColor(Palette::Select); - } else { - m_inputView.setBackgroundColor(Palette::Select); - } - } - layoutSubviews(); - EvenOddCell::reloadCell(); - m_inputView.reloadScroll(); - m_outputView.reloadScroll(); + /* Both output expressions have to be updated at the same time. The + * outputView points to deleted layouts and a call to + * outputView()->layoutSubviews() is going to fail. */ + Poincare::ExpressionLayout * outputExpressions[2] = {calculation->approximateOutputLayout(calculationApp->localContext()), calculation->shouldApproximateOutput() ? nullptr : calculation->exactOutputLayout(calculationApp->localContext())}; + m_scrollableOutputView.outputView()->setExpressions(outputExpressions); } void HistoryViewCell::didBecomeFirstResponder() { if (m_selectedSubviewType == SubviewType::Input) { app()->setFirstResponder(&m_inputView); } else { - app()->setFirstResponder(&m_outputView); + app()->setFirstResponder(&m_scrollableOutputView); } } @@ -83,6 +106,7 @@ HistoryViewCell::SubviewType HistoryViewCell::selectedSubviewType() { void HistoryViewCell::setSelectedSubviewType(HistoryViewCell::SubviewType subviewType) { m_selectedSubviewType = subviewType; + setHighlighted(isHighlighted()); } bool HistoryViewCell::handleEvent(Ion::Events::Event event) { @@ -94,7 +118,6 @@ bool HistoryViewCell::handleEvent(Ion::Events::Event event) { HistoryViewCell * selectedCell = (HistoryViewCell *)(tableView->selectedCell()); selectedCell->setSelectedSubviewType(otherSubviewType); app()->setFirstResponder(selectedCell); - selectedCell->reloadCell(); return true; } return false; diff --git a/apps/calculation/history_view_cell.h b/apps/calculation/history_view_cell.h index 1d876c937..1519bc9ee 100644 --- a/apps/calculation/history_view_cell.h +++ b/apps/calculation/history_view_cell.h @@ -4,6 +4,7 @@ #include #include "calculation.h" #include "scrollable_expression_view.h" +#include "scrollable_output_expressions_view.h" namespace Calculation { @@ -15,6 +16,9 @@ public: }; HistoryViewCell(Responder * parentResponder); void reloadCell() override; + void reloadScroll(); + void setEven(bool even) override; + void setHighlighted(bool highlight) override; KDColor backgroundColor() const override; void setCalculation(Calculation * calculation); int numberOfSubviews() const override; @@ -25,10 +29,11 @@ public: constexpr static KDCoordinate k_digitVerticalMargin = 5; SubviewType selectedSubviewType(); void setSelectedSubviewType(HistoryViewCell::SubviewType subviewType); + OutputExpressionsView * outputView(); private: constexpr static KDCoordinate k_resultWidth = 80; ScrollableExpressionView m_inputView; - ScrollableExpressionView m_outputView; + ScrollableOutputExpressionsView m_scrollableOutputView; SubviewType m_selectedSubviewType; }; diff --git a/apps/calculation/local_context.cpp b/apps/calculation/local_context.cpp index 1f9983d1e..acc491953 100644 --- a/apps/calculation/local_context.cpp +++ b/apps/calculation/local_context.cpp @@ -10,17 +10,17 @@ LocalContext::LocalContext(GlobalContext * parentContext, CalculationStore * cal { } -Evaluation * LocalContext::ansValue() { +Expression * LocalContext::ansValue() { if (m_calculationStore->numberOfCalculations() == 0) { return m_parentContext->defaultExpression(); } Calculation * lastCalculation = m_calculationStore->calculationAtIndex(m_calculationStore->numberOfCalculations()-1); - return lastCalculation->output(m_parentContext); + return lastCalculation->approximateOutput(m_parentContext); } -void LocalContext::setExpressionForSymbolName(Expression * expression, const Symbol * symbol) { +void LocalContext::setExpressionForSymbolName(const Expression * expression, const Symbol * symbol, Context & context) { if (symbol->name() != Symbol::SpecialSymbols::Ans) { - m_parentContext->setExpressionForSymbolName(expression, symbol); + m_parentContext->setExpressionForSymbolName(expression, symbol, context); } } diff --git a/apps/calculation/local_context.h b/apps/calculation/local_context.h index d8791577a..400dd85da 100644 --- a/apps/calculation/local_context.h +++ b/apps/calculation/local_context.h @@ -9,10 +9,10 @@ namespace Calculation { class LocalContext : public Poincare::Context { public: LocalContext(Poincare::GlobalContext * parentContext, CalculationStore * calculationStore); - void setExpressionForSymbolName(Poincare::Expression * expression, const Poincare::Symbol * symbol) override; + void setExpressionForSymbolName(const Poincare::Expression * expression, const Poincare::Symbol * symbol, Context & context) override; const Poincare::Expression * expressionForSymbol(const Poincare::Symbol * symbol) override; private: - Poincare::Evaluation * ansValue(); + Poincare::Expression * ansValue(); CalculationStore * m_calculationStore; Poincare::GlobalContext * m_parentContext; }; diff --git a/apps/calculation/output_expressions_view.cpp b/apps/calculation/output_expressions_view.cpp new file mode 100644 index 000000000..e24bc7bf3 --- /dev/null +++ b/apps/calculation/output_expressions_view.cpp @@ -0,0 +1,125 @@ +#include "output_expressions_view.h" +#include "scrollable_output_expressions_view.h" +#include "../i18n.h" +#include +using namespace Poincare; + +namespace Calculation { + +OutputExpressionsView::OutputExpressionsView(Responder * parentResponder) : + Responder(parentResponder), + m_approximateExpressionView(), + m_approximateSign(KDText::FontSize::Large, I18n::Message::AlmostEqual, 0.5f, 0.5f, Palette::GreyDark), + m_exactExpressionView(), + m_selectedSubviewType(OutputExpressionsView::SubviewType::ExactOutput) +{ +} + +void OutputExpressionsView::setExpressions(ExpressionLayout ** expressionsLayout) { + m_approximateExpressionView.setExpression(expressionsLayout[0]); + m_exactExpressionView.setExpression(expressionsLayout[1]); + layoutSubviews(); +} + +void OutputExpressionsView::setHighlighted(bool highlight) { + // Do not call HighlightCell::setHighlighted to avoid marking all cell as dirty + m_highlighted = highlight; + m_exactExpressionView.setBackgroundColor(backgroundColor()); + m_approximateExpressionView.setBackgroundColor(backgroundColor()); + if (highlight) { + if (m_selectedSubviewType == SubviewType::ExactOutput) { + m_exactExpressionView.setBackgroundColor(Palette::Select); + } else { + m_approximateExpressionView.setBackgroundColor(Palette::Select); + } + } +} + +KDColor OutputExpressionsView::backgroundColor() const { + KDColor background = m_even ? Palette::WallScreen : KDColorWhite; + return background; +} + +void OutputExpressionsView::reloadCell() { + setHighlighted(isHighlighted()); + m_approximateSign.setBackgroundColor(backgroundColor()); + if (numberOfSubviews() == 1) { + m_approximateExpressionView.setTextColor(KDColorBlack); + } else { + m_approximateExpressionView.setTextColor(Palette::GreyDark); + } + layoutSubviews(); +} + +KDSize OutputExpressionsView::minimalSizeForOptimalDisplay() const { + KDSize approximateExpressionSize = m_approximateExpressionView.minimalSizeForOptimalDisplay(); + if (numberOfSubviews() == 1) { + return approximateExpressionSize; + } + KDSize exactExpressionSize = m_exactExpressionView.minimalSizeForOptimalDisplay(); + KDSize approximateSignSize = m_approximateSign.minimalSizeForOptimalDisplay(); + return KDSize(exactExpressionSize.width()+approximateSignSize.width()+approximateExpressionSize.width()+2*k_digitHorizontalMargin, exactExpressionSize.height() > approximateExpressionSize.height() ? exactExpressionSize.height() : approximateExpressionSize.height()); +} + +void OutputExpressionsView::didBecomeFirstResponder() { + if (numberOfSubviews() == 1) { + setSelectedSubviewType(SubviewType::ApproximativeOutput); + } else { + setSelectedSubviewType(SubviewType::ExactOutput); + } +} + +bool OutputExpressionsView::handleEvent(Ion::Events::Event event) { + if (numberOfSubviews() == 1) { + return false; + } + ScrollableOutputExpressionsView * scrollResponder = static_cast(parentResponder()); + KDCoordinate offset = scrollResponder->manualScrollingOffset().x(); + bool rightExpressionIsVisible = minimalSizeForOptimalDisplay().width() - m_approximateExpressionView.minimalSizeForOptimalDisplay().width() - offset < scrollResponder->bounds().width() +; + bool leftExpressionIsVisible = m_exactExpressionView.minimalSizeForOptimalDisplay().width() - offset < scrollResponder->bounds().width(); + if ((event == Ion::Events::Right && m_selectedSubviewType == SubviewType::ExactOutput && rightExpressionIsVisible) || + (event == Ion::Events::Left && m_selectedSubviewType == SubviewType::ApproximativeOutput && leftExpressionIsVisible)) { + SubviewType otherSubviewType = m_selectedSubviewType == SubviewType::ExactOutput ? SubviewType::ApproximativeOutput : SubviewType::ExactOutput; + setSelectedSubviewType(otherSubviewType); + return true; + } + return false; +} + +OutputExpressionsView::SubviewType OutputExpressionsView::selectedSubviewType() { + return m_selectedSubviewType; +} + +void OutputExpressionsView::setSelectedSubviewType(OutputExpressionsView::SubviewType subviewType) { + m_selectedSubviewType = subviewType; + setHighlighted(isHighlighted()); +} + +int OutputExpressionsView::numberOfSubviews() const { + if (m_exactExpressionView.expressionLayout() != nullptr) { + return 3; + } + return 1; +} + +View * OutputExpressionsView::subviewAtIndex(int index) { + View * views[3] = {&m_approximateExpressionView, &m_approximateSign, &m_exactExpressionView}; + return views[index]; +} + +void OutputExpressionsView::layoutSubviews() { + KDCoordinate height = bounds().height(); + KDSize approximateExpressionSize = m_approximateExpressionView.minimalSizeForOptimalDisplay(); + if (numberOfSubviews() == 1) { + m_approximateExpressionView.setFrame(KDRect(0, 0, approximateExpressionSize.width(), height)); + return; + } + KDSize exactExpressionSize = m_exactExpressionView.minimalSizeForOptimalDisplay(); + m_exactExpressionView.setFrame(KDRect(0, 0, exactExpressionSize.width(), height)); + KDSize approximateSignSize = m_approximateSign.minimalSizeForOptimalDisplay(); + m_approximateSign.setFrame(KDRect(k_digitHorizontalMargin+exactExpressionSize.width(), 0, approximateSignSize.width(), height)); + m_approximateExpressionView.setFrame(KDRect(2*k_digitHorizontalMargin+exactExpressionSize.width()+approximateSignSize.width(), 0, approximateExpressionSize.width(), height)); +} + +} diff --git a/apps/calculation/output_expressions_view.h b/apps/calculation/output_expressions_view.h new file mode 100644 index 000000000..c49ccb882 --- /dev/null +++ b/apps/calculation/output_expressions_view.h @@ -0,0 +1,37 @@ +#ifndef CALCULATION_OUTPUT_EXPRESSIONS_VIEW_H +#define CALCULATION_OUTPUT_EXPRESSIONS_VIEW_H + +#include + +namespace Calculation { + +class OutputExpressionsView : public EvenOddCell, public Responder { +public: + enum class SubviewType { + ExactOutput, + ApproximativeOutput + }; + OutputExpressionsView(Responder * parentResponder); + void setExpressions(Poincare::ExpressionLayout ** expressionsLayout); + KDColor backgroundColor() const override; + void setHighlighted(bool highlight) override; + void reloadCell() override; + KDSize minimalSizeForOptimalDisplay() const override; + void didBecomeFirstResponder() override; + bool handleEvent(Ion::Events::Event event) override; + SubviewType selectedSubviewType(); + void setSelectedSubviewType(SubviewType subviewType); +private: + int numberOfSubviews() const override; + View * subviewAtIndex(int index) override; + void layoutSubviews() override; + constexpr static KDCoordinate k_digitHorizontalMargin = 10; + ExpressionView m_approximateExpressionView; + MessageTextView m_approximateSign; + ExpressionView m_exactExpressionView; + SubviewType m_selectedSubviewType; +}; + +} + +#endif diff --git a/apps/calculation/scrollable_output_expressions_view.cpp b/apps/calculation/scrollable_output_expressions_view.cpp new file mode 100644 index 000000000..e6b30dc04 --- /dev/null +++ b/apps/calculation/scrollable_output_expressions_view.cpp @@ -0,0 +1,29 @@ +#include "scrollable_output_expressions_view.h" +#include +using namespace Poincare; + +namespace Calculation { + +ScrollableOutputExpressionsView::ScrollableOutputExpressionsView(Responder * parentResponder) : + ScrollableView(parentResponder, &m_outputView, this), + m_outputView(this) +{ +} + +OutputExpressionsView * ScrollableOutputExpressionsView::outputView() { + return &m_outputView; +} + +void ScrollableOutputExpressionsView::didBecomeFirstResponder() { + app()->setFirstResponder(&m_outputView); +} + +KDSize ScrollableOutputExpressionsView::minimalSizeForOptimalDisplay() const { + return m_outputView.minimalSizeForOptimalDisplay(); +} + +KDPoint ScrollableOutputExpressionsView::manualScrollingOffset() const { + return m_manualScrollingOffset; +} + +} diff --git a/apps/calculation/scrollable_output_expressions_view.h b/apps/calculation/scrollable_output_expressions_view.h new file mode 100644 index 000000000..dc01cad51 --- /dev/null +++ b/apps/calculation/scrollable_output_expressions_view.h @@ -0,0 +1,22 @@ +#ifndef CALCULATION_SCROLLABLE_OUTPUT_EXPRESSIONS_VIEW_H +#define CALCULATION_SCROLLABLE_OUTPUT_EXPRESSIONS_VIEW_H + +#include +#include "output_expressions_view.h" + +namespace Calculation { + +class ScrollableOutputExpressionsView : public ScrollableView, public ScrollViewDataSource { +public: + ScrollableOutputExpressionsView(Responder * parentResponder); + OutputExpressionsView * outputView(); + void didBecomeFirstResponder() override; + KDSize minimalSizeForOptimalDisplay() const override; + KDPoint manualScrollingOffset() const; +private: + OutputExpressionsView m_outputView; +}; + +} + +#endif diff --git a/apps/graph/cartesian_function.cpp b/apps/graph/cartesian_function.cpp index a9bb025b8..1f7077865 100644 --- a/apps/graph/cartesian_function.cpp +++ b/apps/graph/cartesian_function.cpp @@ -19,8 +19,7 @@ void CartesianFunction::setDisplayDerivative(bool display) { double CartesianFunction::approximateDerivative(double x, Poincare::Context * context) const { Poincare::Complex abscissa = Poincare::Complex::Float(x); Poincare::Expression * args[2] = {expression(), &abscissa}; - Poincare::Derivative derivative; - derivative.setArgument(args, 2, true); + Poincare::Derivative derivative(args, true); return derivative.approximate(*context); } diff --git a/apps/i18n.py b/apps/i18n.py index 57d8d1f81..8d316c859 100644 --- a/apps/i18n.py +++ b/apps/i18n.py @@ -12,7 +12,8 @@ ion_special_characters = { u'λ': "Ion::Charset::SmallLambda", u'μ': "Ion::Charset::SmallMu", u'σ': "Ion::Charset::SmallSigma", - u'≤': "Ion::Charset::LessEqual" + u'≤': "Ion::Charset::LessEqual", + u'≈': "Ion::Charset::AlmostEqual" } def ion_char(i18n_letter): diff --git a/apps/sequence/sequence.cpp b/apps/sequence/sequence.cpp index ac04bfa1a..2e8b1aca0 100644 --- a/apps/sequence/sequence.cpp +++ b/apps/sequence/sequence.cpp @@ -286,13 +286,13 @@ T Sequence::templatedEvaluateAtAbscissa(T x, Poincare::Context * context) const return bufferValue(0); } LocalContext subContext = LocalContext(context); - Poincare::Symbol nSymbol = Poincare::Symbol(symbol()); + Poincare::Symbol nSymbol(symbol()); int start = indexBuffer(0) < 0 || indexBuffer(0) > n ? 0 : indexBuffer(0); T un = indexBuffer(0) < 0 || indexBuffer(0) > n ? firstInitialConditionExpression()->approximate(*context) : bufferValue(0); for (int i = start; i < n; i++) { subContext.setValueForSequenceRank(un, name(), 0); Poincare::Complex e = Poincare::Complex::Float(i); - subContext.setExpressionForSymbolName(&e, &nSymbol); + subContext.setExpressionForSymbolName(&e, &nSymbol, subContext); un = expression()->approximate(subContext); } setBufferValue(un, 0); @@ -315,7 +315,7 @@ T Sequence::templatedEvaluateAtAbscissa(T x, Poincare::Context * context) const return bufferValue(1); } LocalContext subContext = LocalContext(context); - Poincare::Symbol nSymbol = Poincare::Symbol(symbol()); + Poincare::Symbol nSymbol(symbol()); int start = indexBuffer(0) >= 0 && indexBuffer(0) < n && indexBuffer(1) > 0 && indexBuffer(1) <= n && indexBuffer(0) + 1 == indexBuffer(1) ? indexBuffer(0) : 0; T un = indexBuffer(0) >= 0 && indexBuffer(0) < n && indexBuffer(1) > 0 && indexBuffer(1) <= n && indexBuffer(0) + 1 == indexBuffer(1) ? bufferValue(0) : firstInitialConditionExpression()->approximate(*context); T un1 = indexBuffer(0) >= 0 && indexBuffer(0) < n && indexBuffer(1) > 0 && indexBuffer(1) <= n && indexBuffer(0) + 1 == indexBuffer(1) ? bufferValue(1) : secondInitialConditionExpression()->approximate(*context); @@ -323,7 +323,7 @@ T Sequence::templatedEvaluateAtAbscissa(T x, Poincare::Context * context) const subContext.setValueForSequenceRank(un, name(), 0); subContext.setValueForSequenceRank(un1, name(), 1); Poincare::Complex e = Poincare::Complex::Float(i); - subContext.setExpressionForSymbolName(&e, &nSymbol); + subContext.setExpressionForSymbolName(&e, &nSymbol, subContext); un = un1; un1 = expression()->approximate(subContext); } diff --git a/apps/shared.universal.i18n b/apps/shared.universal.i18n index f456c8aac..455305adb 100644 --- a/apps/shared.universal.i18n +++ b/apps/shared.universal.i18n @@ -65,3 +65,4 @@ AtanhCommandWithArg = "atanh(x)" Prediction95CommandWithArg = "prediction95(p,n)" PredictionCommandWithArg = "prediction(p,n)" ConfidenceCommandWithArg = "confidence(f,n)" +AlmostEqual = "≈" diff --git a/apps/shared/editable_cell_table_view_controller.cpp b/apps/shared/editable_cell_table_view_controller.cpp index 43206e6e6..18337bcbe 100644 --- a/apps/shared/editable_cell_table_view_controller.cpp +++ b/apps/shared/editable_cell_table_view_controller.cpp @@ -3,6 +3,7 @@ #include "../constant.h" #include "text_field_delegate_app.h" #include +#include using namespace Poincare; diff --git a/apps/shared/float_parameter_controller.cpp b/apps/shared/float_parameter_controller.cpp index 2b15f4c16..04f925f6c 100644 --- a/apps/shared/float_parameter_controller.cpp +++ b/apps/shared/float_parameter_controller.cpp @@ -3,6 +3,7 @@ #include "../apps_container.h" #include "text_field_delegate_app.h" #include +#include using namespace Poincare; diff --git a/apps/shared/function.cpp b/apps/shared/function.cpp index d2abd8eb6..36bca4846 100644 --- a/apps/shared/function.cpp +++ b/apps/shared/function.cpp @@ -102,9 +102,9 @@ bool Function::isEmpty() { template T Function::templatedEvaluateAtAbscissa(T x, Poincare::Context * context) const { Poincare::VariableContext variableContext = Poincare::VariableContext(symbol(), context); - Poincare::Symbol xSymbol = Poincare::Symbol(symbol()); + Poincare::Symbol xSymbol(symbol()); Poincare::Complex e = Poincare::Complex::Float(x); - variableContext.setExpressionForSymbolName(&e, &xSymbol); + variableContext.setExpressionForSymbolName(&e, &xSymbol, variableContext); return expression()->approximate(variableContext); } diff --git a/apps/shared/list_controller.cpp b/apps/shared/list_controller.cpp index d4f8129f3..90dcee1d4 100644 --- a/apps/shared/list_controller.cpp +++ b/apps/shared/list_controller.cpp @@ -126,6 +126,7 @@ void ListController::willDisplayCellAtLocation(HighlightCell * cell, int i, int EvenOddCell * myCell = (EvenOddCell *)cell; myCell->setEven(j%2 == 0); myCell->setHighlighted(i == selectedColumn() && j == selectedRow()); + myCell->reloadCell(); } int ListController::numberOfButtons(ButtonRowController::Position position) const { diff --git a/apps/shared/new_function_cell.cpp b/apps/shared/new_function_cell.cpp index fe2bdac10..18387df61 100644 --- a/apps/shared/new_function_cell.cpp +++ b/apps/shared/new_function_cell.cpp @@ -9,8 +9,13 @@ NewFunctionCell::NewFunctionCell(I18n::Message text) : { } -void NewFunctionCell::reloadCell() { - EvenOddCell::reloadCell(); +void NewFunctionCell::setEven(bool even) { + EvenOddCell::setEven(even); + m_messageTextView.setBackgroundColor(backgroundColor()); +} + +void NewFunctionCell::setHighlighted(bool highlight) { + EvenOddCell::setHighlighted(highlight); m_messageTextView.setBackgroundColor(backgroundColor()); } diff --git a/apps/shared/new_function_cell.h b/apps/shared/new_function_cell.h index 06dbd6a46..7a8418b6c 100644 --- a/apps/shared/new_function_cell.h +++ b/apps/shared/new_function_cell.h @@ -8,7 +8,8 @@ namespace Shared { class NewFunctionCell : public EvenOddCell { public: NewFunctionCell(I18n::Message text); - void reloadCell() override; + void setEven(bool even) override; + void setHighlighted(bool highlight) override; int numberOfSubviews() const override; View * subviewAtIndex(int index) override; void layoutSubviews() override; diff --git a/apps/shared/text_field_delegate_app.cpp b/apps/shared/text_field_delegate_app.cpp index d07e1f7d7..0bc1595ca 100644 --- a/apps/shared/text_field_delegate_app.cpp +++ b/apps/shared/text_field_delegate_app.cpp @@ -79,11 +79,10 @@ bool TextFieldDelegateApp::textFieldShouldFinishEditing(TextField * textField, I bool TextFieldDelegateApp::textFieldDidReceiveEvent(TextField * textField, Ion::Events::Event event) { if (textField->textFieldShouldFinishEditing(event) && textField->isEditing()) { Expression * exp = Expression::parse(textField->text()); - bool invalidText = (exp == nullptr || !exp->hasValidNumberOfArguments()); if (exp != nullptr) { delete exp; } - if (invalidText) { + if (exp == nullptr) { textField->app()->displayWarning(I18n::Message::SyntaxError); return true; } diff --git a/apps/variable_box_controller.cpp b/apps/variable_box_controller.cpp index 6c04a04a4..caed30184 100644 --- a/apps/variable_box_controller.cpp +++ b/apps/variable_box_controller.cpp @@ -77,12 +77,12 @@ bool VariableBoxController::ContentViewController::handleEvent(Ion::Events::Even } if (event == Ion::Events::Backspace && m_currentPage != Page::RootMenu) { if (m_currentPage == Page::Scalar) { - const Symbol symbol = Symbol('A'+selectedRow()); - m_context->setExpressionForSymbolName(nullptr, &symbol); + const Symbol symbol('A'+selectedRow()); + m_context->setExpressionForSymbolName(nullptr, &symbol, *m_context); } if (m_currentPage == Page::Matrix) { const Symbol symbol = Symbol::matrixSymbol('0'+(char)selectedRow()); - m_context->setExpressionForSymbolName(nullptr, &symbol); + m_context->setExpressionForSymbolName(nullptr, &symbol, *m_context); } m_selectableTableView.reloadData(); return true; @@ -137,7 +137,7 @@ void VariableBoxController::ContentViewController::willDisplayCellForIndex(Highl char label[3]; putLabelAtIndexInBuffer(index, label); myCell->setLabel(label); - const Evaluation * evaluation = expressionForIndex(index); + const Expression * evaluation = expressionForIndex(index); if (m_currentPage == Page::Scalar) { myCell->displayExpression(false); char buffer[PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits)]; @@ -148,11 +148,13 @@ void VariableBoxController::ContentViewController::willDisplayCellForIndex(Highl myCell->displayExpression(true); if (evaluation) { /* TODO: implement list contexts */ - myCell->setExpression(evaluation); + // TODO: handle matrix and scalar! + const Matrix * matrixEvaluation = static_cast(evaluation); + myCell->setExpression(matrixEvaluation); char buffer[2*PrintFloat::bufferSizeForFloatsWithPrecision(2)+1]; - int numberOfChars = Complex::convertFloatToText(evaluation->numberOfRows(), buffer, PrintFloat::bufferSizeForFloatsWithPrecision(2), 2, Expression::FloatDisplayMode::Decimal); + int numberOfChars = Complex::convertFloatToText(matrixEvaluation->numberOfRows(), buffer, PrintFloat::bufferSizeForFloatsWithPrecision(2), 2, Expression::FloatDisplayMode::Decimal); buffer[numberOfChars++] = 'x'; - Complex::convertFloatToText(evaluation->numberOfColumns(), buffer+numberOfChars, PrintFloat::bufferSizeForFloatsWithPrecision(2), 2, Expression::FloatDisplayMode::Decimal); + Complex::convertFloatToText(matrixEvaluation->numberOfColumns(), buffer+numberOfChars, PrintFloat::bufferSizeForFloatsWithPrecision(2), 2, Expression::FloatDisplayMode::Decimal); myCell->setSubtitle(buffer); } else { myCell->setExpression(nullptr); @@ -167,7 +169,7 @@ KDCoordinate VariableBoxController::ContentViewController::rowHeight(int index) if (m_currentPage == Page::Scalar) { return k_leafRowHeight; } - const Evaluation * expression = expressionForIndex(index); + const Expression * expression = expressionForIndex(index); if (expression) { ExpressionLayout * layout = expression->createLayout(); KDCoordinate expressionHeight = layout->size().height(); @@ -201,14 +203,14 @@ int VariableBoxController::ContentViewController::typeAtLocation(int i, int j) { return 0; } -const Evaluation * VariableBoxController::ContentViewController::expressionForIndex(int index) { +const Expression * VariableBoxController::ContentViewController::expressionForIndex(int index) { if (m_currentPage == Page::Scalar) { const Symbol symbol = Symbol('A'+index); - return m_context->evaluationForSymbol(&symbol); + return m_context->expressionForSymbol(&symbol); } if (m_currentPage == Page::Matrix) { const Symbol symbol = Symbol::matrixSymbol('0'+(char)index); - return m_context->evaluationForSymbol(&symbol); + return m_context->expressionForSymbol(&symbol); } #if LIST_VARIABLES if (m_currentPage == Page::List) { diff --git a/apps/variable_box_controller.h b/apps/variable_box_controller.h index 51aa009bf..802d2f30d 100644 --- a/apps/variable_box_controller.h +++ b/apps/variable_box_controller.h @@ -56,7 +56,7 @@ private: Page pageAtIndex(int index); void putLabelAtIndexInBuffer(int index, char * buffer); I18n::Message nodeLabelAtIndex(int index); - const Poincare::Evaluation * expressionForIndex(int index); + const Poincare::Expression * expressionForIndex(int index); Poincare::GlobalContext * m_context; TextField * m_textFieldCaller; diff --git a/escher/include/escher/expression_view.h b/escher/include/escher/expression_view.h index 660b4a441..f5632a453 100644 --- a/escher/include/escher/expression_view.h +++ b/escher/include/escher/expression_view.h @@ -15,6 +15,7 @@ class ExpressionView : public View { public: ExpressionView(float horizontalAlignment = 0.0f, float verticalAlignment = 0.5f, KDColor textColor = KDColorBlack, KDColor backgroundColor = KDColorWhite); + Poincare::ExpressionLayout * expressionLayout() const; void setExpression(Poincare::ExpressionLayout * expressionLayout); void drawRect(KDContext * ctx, KDRect rect) const override; void setBackgroundColor(KDColor backgroundColor); diff --git a/escher/include/escher/text_field.h b/escher/include/escher/text_field.h index 79b84d1a0..424458f9a 100644 --- a/escher/include/escher/text_field.h +++ b/escher/include/escher/text_field.h @@ -71,7 +71,7 @@ protected: * [[1.234567e-123*e^(1.234567e-123*i), 1.234567e-123*e^(1.234567e-123*i)]]). * In order to be able to record those output text, k_maxBufferSize must be * over 70. */ - constexpr static int k_maxBufferSize = 100; + constexpr static int k_maxBufferSize = 152; private: int numberOfSubviews() const override { return 1; } void layoutSubviews() override; diff --git a/escher/src/even_odd_cell.cpp b/escher/src/even_odd_cell.cpp index c8d90e582..3b6671f8a 100644 --- a/escher/src/even_odd_cell.cpp +++ b/escher/src/even_odd_cell.cpp @@ -8,8 +8,10 @@ EvenOddCell::EvenOddCell() : } void EvenOddCell::setEven(bool even) { + if (even != m_even) { m_even = even; reloadCell(); + } } KDColor EvenOddCell::backgroundColor() const { diff --git a/escher/src/expression_view.cpp b/escher/src/expression_view.cpp index 26e282e5e..cc5c27a3a 100644 --- a/escher/src/expression_view.cpp +++ b/escher/src/expression_view.cpp @@ -11,19 +11,27 @@ ExpressionView::ExpressionView(float horizontalAlignment, float verticalAlignmen { } +ExpressionLayout * ExpressionView::expressionLayout() const { + return m_expressionLayout; +} + void ExpressionView::setExpression(ExpressionLayout * expressionLayout) { m_expressionLayout = expressionLayout; markRectAsDirty(bounds()); } void ExpressionView::setBackgroundColor(KDColor backgroundColor) { - m_backgroundColor = backgroundColor; - markRectAsDirty(bounds()); + if (m_backgroundColor != backgroundColor) { + m_backgroundColor = backgroundColor; + markRectAsDirty(bounds()); + } } void ExpressionView::setTextColor(KDColor textColor) { - m_textColor = textColor; - markRectAsDirty(bounds()); + if (textColor != m_textColor) { + m_textColor = textColor; + markRectAsDirty(bounds()); + } } void ExpressionView::setAlignment(float horizontalAlignment, float verticalAlignment) { diff --git a/ion/include/ion/charset.h b/ion/include/ion/charset.h index 2bbb41d0c..fb463525f 100644 --- a/ion/include/ion/charset.h +++ b/ion/include/ion/charset.h @@ -22,6 +22,9 @@ enum Charset : char { Root = (char)144, LessEqual = (char)145, GreaterEqual = (char)146, + MultiplicationSign = (char)147, + MiddleDot = (char)148, + AlmostEqual = (char)149 }; } diff --git a/ion/src/shared/events.cpp b/ion/src/shared/events.cpp index 02d62cd14..2692a7748 100644 --- a/ion/src/shared/events.cpp +++ b/ion/src/shared/events.cpp @@ -31,6 +31,7 @@ static constexpr const char k_complexI[2] = {Ion::Charset::IComplex, 0}; static constexpr const char k_exponential[5] = {Ion::Charset::Exponential, '^', '(', ')', 0}; static constexpr const char k_sto[2] = {Ion::Charset::Sto, 0}; static constexpr const char k_exponent[2] = {Ion::Charset::Exponent, 0}; +static constexpr const char k_multiplicationSign[2] = {Ion::Charset::MultiplicationSign, 0}; static constexpr EventData s_dataForEvent[4*Event::PageSize] = { // Plain @@ -40,7 +41,7 @@ static constexpr EventData s_dataForEvent[4*Event::PageSize] = { T(k_exponential), T("ln()"), T("log()"), T(k_complexI), T(","), T("^"), T("sin()"), T("cos()"), T("tan()"), T(k_pi), T(k_root), T("^2"), T("7"), T("8"), T("9"), T("("), T(")"), U(), - T("4"), T("5"), T("6"), T("*"), T("/"), U(), + T("4"), T("5"), T("6"), T(k_multiplicationSign), T("/"), U(), T("1"), T("2"), T("3"), T("+"), T("-"), U(), T("0"), T("."), T(k_exponent), TL(), TL(), U(), // Shift diff --git a/kandinsky/fonts/unicode_for_symbol.c b/kandinsky/fonts/unicode_for_symbol.c index 949cf53c8..150ba9f04 100644 --- a/kandinsky/fonts/unicode_for_symbol.c +++ b/kandinsky/fonts/unicode_for_symbol.c @@ -2,4 +2,4 @@ wchar_t codePointForSymbol[NUMBER_OF_SYMBOLS] = {0x222b, 0x0078, 0x0305, 0x0079, 0x0305, 0x0393, 0x03a3, 0x03b8, 0x03bb, 0x03bc, 0x03c0, 0x03c3, 0x0456, - 0x1D07, 0x2032, 0x212e, 0x2192, 0x221A, 0x2264, 0x2265}; + 0x1D07, 0x2032, 0x212e, 0x2192, 0x221A, 0x2264, 0x2265, 0x00D7, 0x00B7, 0x2248}; diff --git a/kandinsky/fonts/unicode_for_symbol.h b/kandinsky/fonts/unicode_for_symbol.h index d7fcaf323..4a76c5da2 100644 --- a/kandinsky/fonts/unicode_for_symbol.h +++ b/kandinsky/fonts/unicode_for_symbol.h @@ -3,7 +3,7 @@ #include -#define NUMBER_OF_SYMBOLS 20 +#define NUMBER_OF_SYMBOLS 23 extern wchar_t codePointForSymbol[NUMBER_OF_SYMBOLS]; diff --git a/libaxx/include/utility b/libaxx/include/utility index 883f176fb..8885d6088 100644 --- a/libaxx/include/utility +++ b/libaxx/include/utility @@ -7,8 +7,8 @@ template struct remove_reference {typedef T type;}; template struct remove_reference {typedef T type;}; template struct remove_reference {typedef T type;}; -template typename remove_reference::type&& move(T&&) { - return a; +template typename remove_reference::type&& move(T&& a) { + return (typename remove_reference::type&&)a; } } diff --git a/poincare/Makefile b/poincare/Makefile index 41bfc408c..c62d58696 100644 --- a/poincare/Makefile +++ b/poincare/Makefile @@ -1,6 +1,7 @@ SFLAGS += -Ipoincare/include #include poincare/src/simplify/Makefile +#include poincare/src/simplification/Makefile objs += $(addprefix poincare/src/,\ absolute_value.o\ @@ -8,31 +9,32 @@ objs += $(addprefix poincare/src/,\ arc_cosine.o\ arc_sine.o\ arc_tangent.o\ - binary_operation.o\ + arithmetic.o\ binomial_coefficient.o\ + bounded_static_hierarchy.o\ ceiling.o\ complex.o\ complex_argument.o\ - complex_matrix.o\ confidence_interval.o\ conjugate.o\ cosine.o\ + decimal.o\ derivative.o\ determinant.o\ + division.o\ division_quotient.o\ division_remainder.o\ - evaluation.o\ + dynamic_hierarchy.o\ + evaluation_engine.o\ expression.o\ expression_lexer.o\ expression_parser.o\ factorial.o\ floor.o\ frac_part.o\ - fraction.o\ - function.o\ - expression_matrix.o\ global_context.o\ great_common_divisor.o\ + hierarchy.o\ hyperbolic_arc_cosine.o\ hyperbolic_arc_sine.o\ hyperbolic_arc_tangent.o\ @@ -42,8 +44,8 @@ objs += $(addprefix poincare/src/,\ imaginary_part.o\ integer.o\ integral.o\ + layout_engine.o\ list_data.o\ - leaf_expression.o\ least_common_multiple.o\ logarithm.o\ matrix.o\ @@ -62,18 +64,24 @@ objs += $(addprefix poincare/src/,\ prediction_interval.o\ preferences.o\ product.o\ + rational.o\ real_part.o\ round.o\ sequence.o\ + simplification_engine.o\ sine.o\ square_root.o\ + static_hierarchy.o\ store.o\ subtraction.o\ sum.o\ symbol.o\ tangent.o\ + trigonometry.o\ + undefined.o\ variable_context.o\ ) + objs += $(addprefix poincare/src/layout/,\ baseline_relative_layout.o\ bracket_layout.o\ @@ -93,9 +101,18 @@ objs += $(addprefix poincare/src/layout/,\ ) tests += $(addprefix poincare/test/,\ + arithmetic.cpp\ + convert_expression_to_text.cpp\ + helper.cpp\ + integer.cpp\ + simplify_easy.cpp\ +) + +testsi += $(addprefix poincare/test/,\ addition.cpp\ + arithmetic.cpp\ complex.cpp\ - fraction.cpp\ + division.cpp\ function.cpp\ helper.cpp\ integer.cpp\ @@ -104,23 +121,8 @@ tests += $(addprefix poincare/test/,\ product.cpp\ power.cpp\ subtraction.cpp\ - symbol.cpp\ - trigo.cpp\ -) - -# tests += $(addprefix poincare/test/,\ - addition.cpp\ - float.cpp\ - fraction.cpp\ - identity.cpp\ - integer.cpp\ - matrix.cpp\ - product.cpp\ - power.cpp\ simplify_utils.cpp\ - simplify_addition.cpp\ - simplify_product.cpp\ - subtraction.cpp\ + symbol.cpp\ trigo.cpp\ ) diff --git a/poincare/README.txt b/poincare/README.txt index 5aa15f062..bb9e908a3 100644 --- a/poincare/README.txt +++ b/poincare/README.txt @@ -11,7 +11,7 @@ Simplify fractions (x^2+5.x+6)/(x+2) -> x+3 Polynomials (x+1)^2-x^2 -> 2*x+1 -Fractions +Divisions (2*x)/(x^2-1) - 1/(x-1) -> 1/(x+1) Functional identities ln(2x) - ln(x) -> ln(2) diff --git a/poincare/include/poincare.h b/poincare/include/poincare.h index 8ee81fb55..2a0a4a801 100644 --- a/poincare/include/poincare.h +++ b/poincare/include/poincare.h @@ -12,24 +12,21 @@ #include #include #include -#include #include #include #include #include +#include #include #include +#include #include #include -#include #include #include #include #include #include -#include -#include -#include #include #include #include @@ -38,7 +35,6 @@ #include #include #include -#include #include #include #include @@ -58,6 +54,7 @@ #include #include #include +#include #include #include #include @@ -67,6 +64,7 @@ #include #include #include +#include #include #endif diff --git a/poincare/include/poincare/absolute_value.h b/poincare/include/poincare/absolute_value.h index 17fd3d8e0..e4a6030d5 100644 --- a/poincare/include/poincare/absolute_value.h +++ b/poincare/include/poincare/absolute_value.h @@ -1,25 +1,35 @@ #ifndef POINCARE_ABSOLUTE_VALUE_H #define POINCARE_ABSOLUTE_VALUE_H -#include +#include +#include +#include namespace Poincare { -class AbsoluteValue : public Function { +class AbsoluteValue : public StaticHierarchy<1> { + using StaticHierarchy<1>::StaticHierarchy; public: - AbsoluteValue(); Type type() const override; - Expression * cloneWithDifferentOperands(Expression ** newOperands, - int numberOfOperands, bool cloneOperands = true) const override; + Expression * clone() const override; + Sign sign() const override { return Sign::Positive; } private: - Complex computeComplex(const Complex c, AngleUnit angleUnit) const override { - return templatedComputeComplex(c); - } - Complex computeComplex(const Complex c, AngleUnit angleUnit) const override { - return templatedComputeComplex(c); - } - template Complex templatedComputeComplex(const Complex c) const; + Expression * setSign(Sign s, Context & context, AngleUnit angleUnit) override; + /* Layout */ ExpressionLayout * privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const override; + int writeTextInBuffer(char * buffer, int bufferSize) const override { + return LayoutEngine::writePrefixExpressionTextInBuffer(this, buffer, bufferSize, "abs"); + } + /* Simplification */ + Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; + /* Evaluation */ + template static Complex computeOnComplex(const Complex c, AngleUnit angleUnit); + Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { + return EvaluationEngine::map(this, context, angleUnit, computeOnComplex); + } + Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { + return EvaluationEngine::map(this, context, angleUnit, computeOnComplex); + } }; } diff --git a/poincare/include/poincare/addition.h b/poincare/include/poincare/addition.h index cd702bf9f..9f2a6a9d5 100644 --- a/poincare/include/poincare/addition.h +++ b/poincare/include/poincare/addition.h @@ -1,28 +1,56 @@ #ifndef POINCARE_ADDITION_H #define POINCARE_ADDITION_H -#include +#include +#include +#include +#include namespace Poincare { -class Addition : public BinaryOperation { - using BinaryOperation::BinaryOperation; +class Addition : public DynamicHierarchy { + using DynamicHierarchy::DynamicHierarchy; + friend class Logarithm; + friend class Multiplication; + friend class Subtraction; public: Type type() const override; - Expression * cloneWithDifferentOperands(Expression** newOperands, - int numnerOfOperands, bool cloneOperands = true) const override; - bool isCommutative() const override; + Expression * clone() const override; + /* Evaluation */ template static Complex compute(const Complex c, const Complex d); - template static Evaluation * computeOnMatrices(Evaluation * m, Evaluation * n); - template static Evaluation * computeOnComplexAndMatrix(const Complex * c, Evaluation * m); + template static Matrix * computeOnMatrices(const Matrix * m, const Matrix * n) { + return EvaluationEngine::elementWiseOnComplexMatrices(m, n, compute); + } + template static Matrix * computeOnComplexAndMatrix(const Complex * c, const Matrix * m) { + return EvaluationEngine::elementWiseOnComplexAndComplexMatrix(c, m, compute); + } private: - Complex privateCompute(const Complex c, const Complex d) const override { - return compute(c, d); + /* Layout */ + ExpressionLayout * privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const override { + return LayoutEngine::createInfixLayout(this, floatDisplayMode, complexFormat, name()); } - Complex privateCompute(const Complex c, const Complex d) const override { - return compute(c, d); + int writeTextInBuffer(char * buffer, int bufferSize) const override { + return LayoutEngine::writeInfixExpressionTextInBuffer(this, buffer, bufferSize, name()); } - ExpressionLayout * privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const override; + static const char * name() { return "+"; } + + /* Simplification */ + Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; + Expression * shallowBeautify(Context & context, AngleUnit angleUnit) override; + Expression * factorizeOnCommonDenominator(Context & context, AngleUnit angleUnit); + void factorizeOperands(Expression * e1, Expression * e2, Context & context, AngleUnit angleUnit); + static const Rational RationalFactor(Expression * e); + static bool TermsHaveIdenticalNonRationalFactors(const Expression * e1, const Expression * e2); + /* Evaluation */ + template static Matrix * computeOnMatrixAndComplex(const Matrix * m, const Complex * c) { + return EvaluationEngine::elementWiseOnComplexAndComplexMatrix(c, m, compute); + } + Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { + return EvaluationEngine::mapReduce(this, context, angleUnit, compute, computeOnComplexAndMatrix, computeOnMatrixAndComplex, computeOnMatrices); + } + Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { + return EvaluationEngine::mapReduce(this, context, angleUnit, compute, computeOnComplexAndMatrix, computeOnMatrixAndComplex, computeOnMatrices); + } }; } diff --git a/poincare/include/poincare/arc_cosine.h b/poincare/include/poincare/arc_cosine.h index 66135bc63..81e29eb21 100644 --- a/poincare/include/poincare/arc_cosine.h +++ b/poincare/include/poincare/arc_cosine.h @@ -1,24 +1,38 @@ #ifndef POINCARE_ARC_COSINE_H #define POINCARE_ARC_COSINE_H -#include +#include +#include +#include namespace Poincare { -class ArcCosine : public Function { +class ArcCosine : public StaticHierarchy<1> { + using StaticHierarchy<1>::StaticHierarchy; public: - ArcCosine(); Type type() const override; - Expression * cloneWithDifferentOperands(Expression ** newOperands, - int numberOfOperands, bool cloneOperands = true) const override; + Expression * clone() const override; private: - Complex computeComplex(const Complex c, AngleUnit angleUnit) const override { - return templatedComputeComplex(c, angleUnit); + /* Layout */ + ExpressionLayout * privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const override { + return LayoutEngine::createPrefixLayout(this, floatDisplayMode, complexFormat, name()); } - Complex computeComplex(const Complex c, AngleUnit angleUnit) const override { - return templatedComputeComplex(c, angleUnit); + int writeTextInBuffer(char * buffer, int bufferSize) const override { + return LayoutEngine::writePrefixExpressionTextInBuffer(this, buffer, bufferSize, name()); + } + const char * name() const { + return "acos"; + } + /* Simplification */ + Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; + /* Evaluation */ + template static Complex computeOnComplex(const Complex c, AngleUnit angleUnit); + Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { + return EvaluationEngine::map(this, context, angleUnit,computeOnComplex); + } + Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { + return EvaluationEngine::map(this, context, angleUnit, computeOnComplex); } - template Complex templatedComputeComplex(const Complex c, AngleUnit angleUnit) const; }; } diff --git a/poincare/include/poincare/arc_sine.h b/poincare/include/poincare/arc_sine.h index 5c137c325..dcceec881 100644 --- a/poincare/include/poincare/arc_sine.h +++ b/poincare/include/poincare/arc_sine.h @@ -1,24 +1,36 @@ #ifndef POINCARE_ARC_SINE_H #define POINCARE_ARC_SINE_H -#include +#include +#include +#include namespace Poincare { -class ArcSine : public Function { +class ArcSine : public StaticHierarchy<1> { + using StaticHierarchy<1>::StaticHierarchy; public: - ArcSine(); Type type() const override; - Expression * cloneWithDifferentOperands(Expression ** newOperands, - int numberOfOperands, bool cloneOperands = true) const override; + Expression * clone() const override; private: - Complex computeComplex(const Complex c, AngleUnit angleUnit) const override { - return templatedComputeComplex(c, angleUnit); + /* Layout */ + ExpressionLayout * privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const override { + return LayoutEngine::createPrefixLayout(this, floatDisplayMode, complexFormat, name()); } - Complex computeComplex(const Complex c, AngleUnit angleUnit) const override { - return templatedComputeComplex(c, angleUnit); + int writeTextInBuffer(char * buffer, int bufferSize) const override { + return LayoutEngine::writePrefixExpressionTextInBuffer(this, buffer, bufferSize, name()); + } + const char * name() const { return "asin"; } + /* Simplification */ + Expression * shallowReduce(Context & context, AngleUnit angleUnit) override; + /* Evaluation */ + template static Complex computeOnComplex(const Complex c, AngleUnit angleUnit); + Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { + return EvaluationEngine::map(this, context, angleUnit,computeOnComplex); + } + Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { + return EvaluationEngine::map(this, context, angleUnit, computeOnComplex); } - template Complex templatedComputeComplex(const Complex c, AngleUnit angleUnit) const; }; } diff --git a/poincare/include/poincare/arc_tangent.h b/poincare/include/poincare/arc_tangent.h index 83f58324e..b9a34a536 100644 --- a/poincare/include/poincare/arc_tangent.h +++ b/poincare/include/poincare/arc_tangent.h @@ -1,24 +1,36 @@ #ifndef POINCARE_ARC_TANGENT_H #define POINCARE_ARC_TANGENT_H -#include +#include +#include +#include namespace Poincare { -class ArcTangent: public Function { +class ArcTangent : public StaticHierarchy<1> { + using StaticHierarchy<1>::StaticHierarchy; public: - ArcTangent(); Type type() const override; - Expression * cloneWithDifferentOperands(Expression ** newOperands, - int numberOfOperands, bool cloneOperands = true) const override; + Expression * clone() const override; private: - Complex computeComplex(const Complex c, AngleUnit angleUnit) const override { - return templatedComputeComplex(c, angleUnit); + /* Layout */ + ExpressionLayout * privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const override { + return LayoutEngine::createPrefixLayout(this, floatDisplayMode, complexFormat, name()); } - Complex computeComplex(const Complex c, AngleUnit angleUnit) const override { - return templatedComputeComplex(c, angleUnit); + int writeTextInBuffer(char * buffer, int bufferSize) const override { + return LayoutEngine::writePrefixExpressionTextInBuffer(this, buffer, bufferSize, name()); + } + const char * name() const { return "atan"; } + /* Simplification */ + Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; + /* Evaluation */ + template static Complex computeOnComplex(const Complex c, AngleUnit angleUnit); + Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { + return EvaluationEngine::map(this, context, angleUnit,computeOnComplex); + } + Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { + return EvaluationEngine::map(this, context, angleUnit, computeOnComplex); } - template Complex templatedComputeComplex(const Complex c, AngleUnit angleUnit) const; }; } diff --git a/poincare/include/poincare/arithmetic.h b/poincare/include/poincare/arithmetic.h new file mode 100644 index 000000000..0d0d14d3b --- /dev/null +++ b/poincare/include/poincare/arithmetic.h @@ -0,0 +1,19 @@ +#ifndef POINCARE_ARITHMETIC_H +#define POINCARE_ARITHMETIC_H + +#include +namespace Poincare { + +class Arithmetic { +public: + static Integer LCM(const Integer * i, const Integer * j); + static Integer GCD(const Integer * i, const Integer * j); + static void PrimeFactorization(const Integer * i, Integer * outputFactors, Integer * outputCoefficients, int outputLength); + constexpr static int k_numberOfPrimeFactors = 1000; + constexpr static int k_maxNumberOfPrimeFactors = 32; + static const Integer k_primorial32; +}; + +} + +#endif diff --git a/poincare/include/poincare/binary_operation.h b/poincare/include/poincare/binary_operation.h deleted file mode 100644 index 49eb30538..000000000 --- a/poincare/include/poincare/binary_operation.h +++ /dev/null @@ -1,60 +0,0 @@ -#ifndef POINCARE_BINARY_OPERATION_H -#define POINCARE_BINARY_OPERATION_H - -#include -#include -#include -#include - -namespace Poincare { - -class BinaryOperation : public Expression { -public: - BinaryOperation(); - BinaryOperation(Expression ** operands, bool cloneOperands = true); - ~BinaryOperation(); - BinaryOperation(const BinaryOperation& other) = delete; - BinaryOperation(BinaryOperation&& other) = delete; - BinaryOperation& operator=(const BinaryOperation& other) = delete; - BinaryOperation& operator=(BinaryOperation&& other) = delete; - bool hasValidNumberOfArguments() const override; - const Expression * operand(int i) const override; - int numberOfOperands() const override; - Expression * clone() const override; -protected: - Expression * m_operands[2]; - virtual Evaluation * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - virtual Evaluation * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - template Evaluation * templatedEvaluate(Context& context, AngleUnit angleUnit) const; - - virtual Evaluation * computeOnComplexAndComplexMatrix(const Complex * c, Evaluation * n) const { - return templatedComputeOnComplexAndComplexMatrix(c, n); - } - virtual Evaluation * computeOnComplexAndComplexMatrix(const Complex * c, Evaluation * n) const { - return templatedComputeOnComplexAndComplexMatrix(c, n); - } - template Evaluation * templatedComputeOnComplexAndComplexMatrix(const Complex * c, Evaluation * n) const; - - virtual Evaluation * computeOnComplexMatrixAndComplex(Evaluation * m, const Complex * d) const { - return templatedComputeOnComplexMatrixAndComplex(m, d); - } - virtual Evaluation * computeOnComplexMatrixAndComplex(Evaluation * m, const Complex * d) const { - return templatedComputeOnComplexMatrixAndComplex(m, d); - } - template Evaluation * templatedComputeOnComplexMatrixAndComplex(Evaluation * m, const Complex * d) const; - - virtual Evaluation * computeOnComplexMatrices(Evaluation * m, Evaluation * n) const { - return templatedComputeOnComplexMatrices(m, n); - } - virtual Evaluation * computeOnComplexMatrices(Evaluation * m, Evaluation * n) const { - return templatedComputeOnComplexMatrices(m, n); - } - template Evaluation * templatedComputeOnComplexMatrices(Evaluation * m, Evaluation * n) const; - - virtual Complex privateCompute(const Complex c, const Complex d) const = 0; - virtual Complex privateCompute(const Complex c, const Complex d) const = 0; -}; - -} - -#endif diff --git a/poincare/include/poincare/binomial_coefficient.h b/poincare/include/poincare/binomial_coefficient.h index e37f1aede..f0215e7c8 100644 --- a/poincare/include/poincare/binomial_coefficient.h +++ b/poincare/include/poincare/binomial_coefficient.h @@ -1,21 +1,29 @@ #ifndef POINCARE_BINOMIAL_COEFFICIENT_H #define POINCARE_BINOMIAL_COEFFICIENT_H -#include +#include +#include namespace Poincare { -class BinomialCoefficient : public Function { +class BinomialCoefficient : public StaticHierarchy<2> { + using StaticHierarchy<2>::StaticHierarchy; public: - BinomialCoefficient(); Type type() const override; - Expression * cloneWithDifferentOperands(Expression ** newOperands, - int numberOfOperands, bool cloneOperands = true) const override; + Expression * clone() const override; private: - Evaluation * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - Evaluation * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - template Evaluation * templatedEvaluate(Context& context, AngleUnit angleUnit) const; + constexpr static int k_maxNValue = 300; + /* Layout */ ExpressionLayout * privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const override; + int writeTextInBuffer(char * buffer, int bufferSize) const override { + return LayoutEngine::writePrefixExpressionTextInBuffer(this, buffer, bufferSize, "binomial"); + } + /* Simplification */ + Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; + /* Evaluation */ + Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } + Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } + template Expression * templatedEvaluate(Context& context, AngleUnit angleUnit) const; }; } diff --git a/poincare/include/poincare/bounded_static_hierarchy.h b/poincare/include/poincare/bounded_static_hierarchy.h new file mode 100644 index 000000000..52665f0e2 --- /dev/null +++ b/poincare/include/poincare/bounded_static_hierarchy.h @@ -0,0 +1,24 @@ +#ifndef POINCARE_BOUNDED_STATIC_HIERARCHY_H +#define POINCARE_BOUNDED_STATIC_HIERARCHY_H + +#include + +namespace Poincare { + +template +class BoundedStaticHierarchy : public StaticHierarchy { +public: + BoundedStaticHierarchy(); + BoundedStaticHierarchy(const Expression * expression, bool cloneOperands = true); // Specialized constructor for StaticHierarchy<2> + BoundedStaticHierarchy(const Expression * expression1, const Expression * expression2, bool cloneOperands = true); // Specialized constructor for StaticHierarchy<2> + BoundedStaticHierarchy(const Expression * const * operands, int numberOfOperands, bool cloneOperands = true); + void setArgument(ListData * listData, int numberOfEntries, bool clone) override; + int numberOfOperands() const override { return m_numberOfOperands; } + bool hasValidNumberOfOperands(int numberOfOperands) const override; +private: + int m_numberOfOperands; +}; + +} + +#endif diff --git a/poincare/include/poincare/ceiling.h b/poincare/include/poincare/ceiling.h index a5f865abc..3bd704e49 100644 --- a/poincare/include/poincare/ceiling.h +++ b/poincare/include/poincare/ceiling.h @@ -1,25 +1,34 @@ #ifndef POINCARE_CEILING_H #define POINCARE_CEILING_H -#include +#include +#include +#include namespace Poincare { -class Ceiling : public Function { +class Ceiling : public StaticHierarchy<1> { + using StaticHierarchy<1>::StaticHierarchy; public: - Ceiling(); Type type() const override; - Expression * cloneWithDifferentOperands(Expression ** newOperands, - int numberOfOperands, bool cloneOperands = true) const override; + Expression * clone() const override; private: - Complex computeComplex(const Complex c, AngleUnit angleUnit) const override { - return templatedComputeComplex(c); - } - Complex computeComplex(const Complex c, AngleUnit angleUnit) const override { - return templatedComputeComplex(c); - } - template Complex templatedComputeComplex(const Complex c) const; + /* Layout */ ExpressionLayout * privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const override; + int writeTextInBuffer(char * buffer, int bufferSize) const override { + return LayoutEngine::writePrefixExpressionTextInBuffer(this, buffer, bufferSize, name()); + } + const char * name() const { return "ceil"; } + /* Simplification */ + Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; + /* Evaluation */ + template static Complex computeOnComplex(const Complex c, AngleUnit angleUnit); + Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { + return EvaluationEngine::map(this, context, angleUnit, computeOnComplex); + } + Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { + return EvaluationEngine::map(this, context, angleUnit, computeOnComplex); + } }; } diff --git a/poincare/include/poincare/complex.h b/poincare/include/poincare/complex.h index 82b2a8c24..a61500a03 100644 --- a/poincare/include/poincare/complex.h +++ b/poincare/include/poincare/complex.h @@ -1,8 +1,9 @@ #ifndef POINCARE_COMPLEX_H #define POINCARE_COMPLEX_H -#include #include +#include +#include namespace Poincare { @@ -22,7 +23,7 @@ namespace PrintFloat { } template -class Complex : public Evaluation { +class Complex : public StaticHierarchy<0> { public: Complex() : m_a(0), m_b(0) {} static Complex Float(T x); @@ -31,29 +32,21 @@ public: Complex(const char * integralPart, int integralPartLength, bool integralNegative, const char * fractionalPart, int fractionalPartLength, const char * exponent, int exponentLength, bool exponentNegative); - T toScalar() const override; - const Complex * operand(int i) const override { - return complexOperand(i); - } - int numberOfRows() const override; - int numberOfColumns() const override; - Expression::Type type() const override; - Complex * clone() const override; - Evaluation * cloneWithDifferentOperands(Expression** newOperands, - int numberOfOperands, bool cloneOperands = true) const override; - int writeTextInBuffer(char * buffer, int bufferSize) const override; - Evaluation * createDeterminant() const override { - return clone(); - } - Evaluation * createInverse() const override; - Evaluation * createTrace() const override { - return clone(); - } + Complex(const Complex & other); + Complex& operator=(const Complex& other); + T a() const; T b() const; T r() const; T th() const; Complex conjugate() const; + T toScalar() const; + + /* Expression */ + Expression::Type type() const override; + Complex * clone() const override; + int writeTextInBuffer(char * buffer, int bufferSize) const override; + /* The parameter 'DisplayMode' refers to the way to display float 'scientific' * or 'auto'. The scientific mode returns float with style -1.2E2 whereas * the auto mode tries to return 'natural' float like (0.021) and switches @@ -68,12 +61,11 @@ public: static int convertFloatToText(T d, char * buffer, int bufferSize, int numberOfSignificantDigits, Expression::FloatDisplayMode mode = Expression::FloatDisplayMode::Default); private: Complex(T a, T b); - const Complex * complexOperand(int i) const override; constexpr static int k_numberOfSignificantDigits = 7; ExpressionLayout * privateCreateLayout(Expression::FloatDisplayMode floatDisplayMode, Expression::ComplexFormat complexFormat) const override; - Evaluation * privateEvaluate(Expression::SinglePrecision p, Context& context, Expression::AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - Evaluation * privateEvaluate(Expression::DoublePrecision p, Context& context, Expression::AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - template Evaluation * templatedEvaluate(Context& context, Expression::AngleUnit angleUnit) const; + Expression * privateEvaluate(Expression::SinglePrecision p, Context& context, Expression::AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } + Expression * privateEvaluate(Expression::DoublePrecision p, Context& context, Expression::AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } + template Complex * templatedEvaluate(Context& context, Expression::AngleUnit angleUnit) const; /* We here define the buffer size to write the lengthest float possible. * At maximum, the number has 7 significant digits so, in the worst case it * has the form -1.999999e-308 (7+7+1 char) (the auto mode is always diff --git a/poincare/include/poincare/complex_argument.h b/poincare/include/poincare/complex_argument.h index 64d66366e..cee734982 100644 --- a/poincare/include/poincare/complex_argument.h +++ b/poincare/include/poincare/complex_argument.h @@ -1,24 +1,36 @@ #ifndef POINCARE_COMPLEX_ARGUMENT_H #define POINCARE_COMPLEX_ARGUMENT_H -#include +#include +#include +#include namespace Poincare { -class ComplexArgument : public Function { +class ComplexArgument : public StaticHierarchy<1> { + using StaticHierarchy<1>::StaticHierarchy; public: - ComplexArgument(); Type type() const override; - Expression * cloneWithDifferentOperands(Expression ** newOperands, - int numberOfOperands, bool cloneOperands = true) const override; + Expression * clone() const override; private: - Complex computeComplex(const Complex c, AngleUnit angleUnit) const override { - return templatedComputeComplex(c); + /* Layout */ + ExpressionLayout * privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const override { + return LayoutEngine::createPrefixLayout(this, floatDisplayMode, complexFormat, name()); } - Complex computeComplex(const Complex c, AngleUnit angleUnit) const override { - return templatedComputeComplex(c); + int writeTextInBuffer(char * buffer, int bufferSize) const override { + return LayoutEngine::writePrefixExpressionTextInBuffer(this, buffer, bufferSize, name()); + } + const char * name() const { return "arg"; } + /* Simplification */ + Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; + /* Evaluation */ + template static Complex computeOnComplex(const Complex c, AngleUnit angleUnit); + Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { + return EvaluationEngine::map(this, context, angleUnit,computeOnComplex); + } + Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { + return EvaluationEngine::map(this, context, angleUnit, computeOnComplex); } - template Complex templatedComputeComplex(const Complex c) const; }; } diff --git a/poincare/include/poincare/complex_matrix.h b/poincare/include/poincare/complex_matrix.h deleted file mode 100644 index aed8cb155..000000000 --- a/poincare/include/poincare/complex_matrix.h +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef POINCARE_COMPLEX_MATRIX_H -#define POINCARE_COMPLEX_MATRIX_H - -#include - -namespace Poincare { - -template -class ComplexMatrix : public Evaluation { -public: - ComplexMatrix(const Complex * complexes, int numberOfRows, int numberOfColumns); - ~ComplexMatrix(); - ComplexMatrix(const ComplexMatrix& other) = delete; - ComplexMatrix(ComplexMatrix&& other) = delete; - ComplexMatrix& operator=(const ComplexMatrix& other) = delete; - ComplexMatrix& operator=(ComplexMatrix&& other) = delete; - T toScalar() const override; - const Complex * complexOperand(int i) const override; - int numberOfRows() const override; - int numberOfColumns() const override; - ComplexMatrix * clone() const override; - ComplexMatrix * cloneWithDifferentOperands(Expression** newOperands, - int numberOfOperands, bool cloneOperands = true) const override; - static Evaluation * createIdentity(int dim); -private: - Evaluation * privateEvaluate(Expression::SinglePrecision p, Context& context, Expression::AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - Evaluation * privateEvaluate(Expression::DoublePrecision p, Context& context, Expression::AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - template Evaluation * templatedEvaluate(Context& context, Expression::AngleUnit angleUnit) const; - Complex * m_values; - int m_numberOfRows; - int m_numberOfColumns; -}; - -} - -#endif - diff --git a/poincare/include/poincare/confidence_interval.h b/poincare/include/poincare/confidence_interval.h index 40ad792c3..9f953e0af 100644 --- a/poincare/include/poincare/confidence_interval.h +++ b/poincare/include/poincare/confidence_interval.h @@ -1,20 +1,31 @@ #ifndef POINCARE_CONFIDENCE_INTERVAL_H #define POINCARE_CONFIDENCE_INTERVAL_H -#include +#include +#include namespace Poincare { -class ConfidenceInterval : public Function { +class ConfidenceInterval : public StaticHierarchy<2> { + using StaticHierarchy<2>::StaticHierarchy; public: - ConfidenceInterval(); Type type() const override; - Expression * cloneWithDifferentOperands(Expression ** newOperands, - int numberOfOperands, bool cloneOperands = true) const override; + Expression * clone() const override; private: - Evaluation * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - Evaluation * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - template Evaluation * templatedEvaluate(Context& context, AngleUnit angleUnit) const; + /* Layout */ + ExpressionLayout * privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const override { + return LayoutEngine::createPrefixLayout(this, floatDisplayMode, complexFormat, name()); + } + int writeTextInBuffer(char * buffer, int bufferSize) const override { + return LayoutEngine::writePrefixExpressionTextInBuffer(this, buffer, bufferSize, name()); + } + const char * name() const { return "confidence"; } + /* Simplification */ + Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; + /* Evaluation */ + Expression * privateEvaluate(Expression::SinglePrecision p, Context& context, Expression::AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } + Expression * privateEvaluate(Expression::DoublePrecision p, Context& context, Expression::AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } + template Expression * templatedEvaluate(Context& context, AngleUnit angleUnit) const; }; } diff --git a/poincare/include/poincare/conjugate.h b/poincare/include/poincare/conjugate.h index 3e97c3eda..9eb33eaa6 100644 --- a/poincare/include/poincare/conjugate.h +++ b/poincare/include/poincare/conjugate.h @@ -1,25 +1,33 @@ #ifndef POINCARE_CONJUGATE_H #define POINCARE_CONJUGATE_H -#include +#include +#include +#include namespace Poincare { -class Conjugate : public Function { +class Conjugate : public StaticHierarchy<1> { + using StaticHierarchy<1>::StaticHierarchy; public: - Conjugate(); Type type() const override; - Expression * cloneWithDifferentOperands(Expression ** newOperands, - int numberOfOperands, bool cloneOperands = true) const override; + Expression * clone() const override; private: - Complex computeComplex(const Complex c, AngleUnit angleUnit) const override { - return templatedComputeComplex(c); - } - Complex computeComplex(const Complex c, AngleUnit angleUnit) const override { - return templatedComputeComplex(c); - } - template Complex templatedComputeComplex(const Complex c) const; + /* Layout */ ExpressionLayout * privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const override; + int writeTextInBuffer(char * buffer, int bufferSize) const override { + return LayoutEngine::writePrefixExpressionTextInBuffer(this, buffer, bufferSize, "conjugate"); + } + /* Simplification */ + Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; + /* Evaluation */ + template static Complex computeOnComplex(const Complex c, AngleUnit angleUnit); + Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { + return EvaluationEngine::map(this, context, angleUnit,computeOnComplex); + } + Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { + return EvaluationEngine::map(this, context, angleUnit, computeOnComplex); + } }; } diff --git a/poincare/include/poincare/context.h b/poincare/include/poincare/context.h index c098d64a6..a414dede7 100644 --- a/poincare/include/poincare/context.h +++ b/poincare/include/poincare/context.h @@ -9,7 +9,7 @@ namespace Poincare { class Context { public: virtual const Expression * expressionForSymbol(const Symbol * symbol) = 0; - virtual void setExpressionForSymbolName(Expression * expression, const Symbol * symbol) = 0; + virtual void setExpressionForSymbolName(const Expression * expression, const Symbol * symbol, Context & context) = 0; }; } diff --git a/poincare/include/poincare/cosine.h b/poincare/include/poincare/cosine.h index 53f1b1212..2c23dc3a9 100644 --- a/poincare/include/poincare/cosine.h +++ b/poincare/include/poincare/cosine.h @@ -1,23 +1,37 @@ #ifndef POINCARE_COSINE_H #define POINCARE_COSINE_H -#include +#include +#include +#include +#include namespace Poincare { -class Cosine : public Function { +class Cosine : public StaticHierarchy<1>::StaticHierarchy { + using StaticHierarchy<1>::StaticHierarchy; + friend class Tangent; public: - Cosine(); Type type() const override; - Expression * cloneWithDifferentOperands(Expression ** newOperands, - int numberOfOperands, bool cloneOperands = true) const override; - template static Complex compute(const Complex c, AngleUnit angleUnit = AngleUnit::Radian); + Expression * clone() const override; + template static Complex computeOnComplex(const Complex c, AngleUnit angleUnit = AngleUnit::Radian); private: - Complex computeComplex(const Complex c, AngleUnit angleUnit) const override { - return compute(c, angleUnit); + /* Layout */ + ExpressionLayout * privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const override { + return LayoutEngine::createPrefixLayout(this, floatDisplayMode, complexFormat, name()); } - Complex computeComplex(const Complex c, AngleUnit angleUnit) const override { - return compute(c, angleUnit); + int writeTextInBuffer(char * buffer, int bufferSize) const override { + return LayoutEngine::writePrefixExpressionTextInBuffer(this, buffer, bufferSize, name()); + } + const char * name() const { return "cos"; } + /* Simplication */ + Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; + /* Evaluation */ + Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { + return EvaluationEngine::map(this, context, angleUnit,computeOnComplex); + } + Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { + return EvaluationEngine::map(this, context, angleUnit, computeOnComplex); } }; diff --git a/poincare/include/poincare/decimal.h b/poincare/include/poincare/decimal.h new file mode 100644 index 000000000..72242b183 --- /dev/null +++ b/poincare/include/poincare/decimal.h @@ -0,0 +1,51 @@ +#ifndef POINCARE_DECIMAL_H +#define POINCARE_DECIMAL_H + +#include +#include + +namespace Poincare { + +/* A decimal as 0.01234 is stored that way: + * - m_mantissa = 1234 + * - m_exponent = -2 + */ + +class Decimal : public StaticHierarchy<0> { +public: + static int exponent(const char * integralPart, int integralPartLength, const char * fractionalPart, int fractionalPartLength, const char * exponent, int exponentLength, bool exponentNegative); + static Integer mantissa(const char * integralPart, int integralPartLength, const char * fractionalPart, int fractionalPartLength, bool negative); + Decimal(Integer mantissa, int exponent); + Decimal(double f); + int exponent() const { return m_exponent; } + Integer mantissa() const { return m_mantissa; } + // Expression subclassing + Type type() const override; + Expression * clone() const override; + int writeTextInBuffer(char * buffer, int bufferSize) const override; + Sign sign() const override { return m_mantissa.isNegative() ? Sign::Negative : Sign::Positive; } +private: + constexpr static int k_doublePrecision = 15; + constexpr static double k_biggestMantissaFromDouble = 999999999999999; + constexpr static int k_maxDoubleExponent = 308; + int numberOfDigitsInMantissaWithoutSign() const; + /* Comparison */ + int simplificationOrderSameType(const Expression * e) const override; + /* Layout */ + ExpressionLayout * privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const override; + /* Simplification */ + Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; + Expression * shallowBeautify(Context& context, AngleUnit angleUnit) override; + /* Evaluation */ + Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } + Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } + template Expression * templatedEvaluate(Context& context, Expression::AngleUnit angleUnit) const; + + constexpr static int k_maxLength = 10; + Integer m_mantissa; + int m_exponent; +}; + +} + +#endif diff --git a/poincare/include/poincare/derivative.h b/poincare/include/poincare/derivative.h index 95263859a..351fed8a0 100644 --- a/poincare/include/poincare/derivative.h +++ b/poincare/include/poincare/derivative.h @@ -1,21 +1,32 @@ #ifndef POINCARE_DERIVATIVE_H #define POINCARE_DERIVATIVE_H -#include +#include +#include #include namespace Poincare { -class Derivative : public Function { +class Derivative : public StaticHierarchy<2> { + using StaticHierarchy<2>::StaticHierarchy; public: - Derivative(); Type type() const override; - Expression * cloneWithDifferentOperands(Expression ** newOperands, - int numberOfOperands, bool cloneOperands = true) const override; + Expression * clone() const override; private: - Evaluation * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - Evaluation * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - template Evaluation * templatedEvaluate(Context& context, AngleUnit angleUnit) const; + /* Layout */ + ExpressionLayout * privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const override { + return LayoutEngine::createPrefixLayout(this, floatDisplayMode, complexFormat, name()); + } + int writeTextInBuffer(char * buffer, int bufferSize) const override { + return LayoutEngine::writePrefixExpressionTextInBuffer(this, buffer, bufferSize, name()); + } + const char * name() const { return "diff"; } + /* Simplification */ + Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; + /* Evaluation */ + Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } + Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } + template Expression * templatedEvaluate(Context& context, AngleUnit angleUnit) const; template T growthRateAroundAbscissa(T x, T h, VariableContext variableContext, AngleUnit angleUnit) const; template T approximateDerivate2(T x, T h, VariableContext xContext, AngleUnit angleUnit) const; // TODO: Change coefficients? diff --git a/poincare/include/poincare/determinant.h b/poincare/include/poincare/determinant.h index 0a5a52311..6bb607695 100644 --- a/poincare/include/poincare/determinant.h +++ b/poincare/include/poincare/determinant.h @@ -1,20 +1,32 @@ #ifndef POINCARE_DETERMINANT_H #define POINCARE_DETERMINANT_H -#include +#include +#include +#include namespace Poincare { -class Determinant : public Function { +class Determinant : public StaticHierarchy<1> { + using StaticHierarchy<1>::StaticHierarchy; public: - Determinant(); Type type() const override; - Expression * cloneWithDifferentOperands(Expression ** newOperands, - int numberOfOperands, bool cloneOperands = true) const override; + Expression * clone() const override; private: - Evaluation * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - Evaluation * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - template Evaluation * templatedEvaluate(Context& context, AngleUnit angleUnit) const; + /* Layout */ + ExpressionLayout * privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const override { + return LayoutEngine::createPrefixLayout(this, floatDisplayMode, complexFormat, name()); + } + int writeTextInBuffer(char * buffer, int bufferSize) const override { + return LayoutEngine::writePrefixExpressionTextInBuffer(this, buffer, bufferSize, name()); + } + const char * name() const { return "det"; } + /* Simplification */ + Expression * shallowReduce(Context & context, AngleUnit angleUnit) override; + /* Evaluation */ + Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } + Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } + template Expression * templatedEvaluate(Context& context, AngleUnit angleUnit) const; }; } diff --git a/poincare/include/poincare/division.h b/poincare/include/poincare/division.h new file mode 100644 index 000000000..d30fe3bd3 --- /dev/null +++ b/poincare/include/poincare/division.h @@ -0,0 +1,45 @@ +#ifndef POINCARE_DIVISION_H +#define POINCARE_DIVISION_H + +#include +#include +#include + +namespace Poincare { + +class Division : public StaticHierarchy<2> { + using StaticHierarchy<2>::StaticHierarchy; + friend class Multiplication; + friend class Power; +public: + Type type() const override; + Expression * clone() const override; + template static Complex compute(const Complex c, const Complex d); +private: + /* Layout */ + ExpressionLayout * privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const override; + int writeTextInBuffer(char * buffer, int bufferSize) const override { + return LayoutEngine::writeInfixExpressionTextInBuffer(this, buffer, bufferSize, "/", [](const Expression * e) { + return e->type() == Type::Multiplication || e->type() == Type::Addition || e->type() == Type::Subtraction || e->type() == Type::Opposite; + }); + } + /* Simplification */ + Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; + /* Evaluation */ + template static Matrix * computeOnMatrixAndComplex(const Matrix * m, const Complex * c) { + return EvaluationEngine::elementWiseOnComplexAndComplexMatrix(c, m, compute); + } + template static Matrix * computeOnComplexAndMatrix(const Complex * c, const Matrix * n); + template static Matrix * computeOnMatrices(const Matrix * m, const Matrix * n); + + virtual Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { + return EvaluationEngine::mapReduce(this, context, angleUnit, compute, computeOnComplexAndMatrix, computeOnMatrixAndComplex, computeOnMatrices); + } + virtual Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { + return EvaluationEngine::mapReduce(this, context, angleUnit, compute, computeOnComplexAndMatrix, computeOnMatrixAndComplex, computeOnMatrices); + } +}; + +} + +#endif diff --git a/poincare/include/poincare/division_quotient.h b/poincare/include/poincare/division_quotient.h index 0e0a779aa..4c2171187 100644 --- a/poincare/include/poincare/division_quotient.h +++ b/poincare/include/poincare/division_quotient.h @@ -1,20 +1,32 @@ #ifndef POINCARE_DIVISION_QUOTIENT_H #define POINCARE_DIVISION_QUOTIENT_H -#include +#include +#include +#include namespace Poincare { -class DivisionQuotient : public Function { +class DivisionQuotient : public StaticHierarchy<2> { + using StaticHierarchy<2>::StaticHierarchy; public: - DivisionQuotient(); Type type() const override; - Expression * cloneWithDifferentOperands(Expression ** newOperands, - int numberOfOperands, bool cloneOperands = true) const override; + Expression * clone() const override; private: - Evaluation * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - Evaluation * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - template Evaluation * templatedEvaluate(Context& context, AngleUnit angleUnit) const; + /* Layout */ + ExpressionLayout * privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const override { + return LayoutEngine::createPrefixLayout(this, floatDisplayMode, complexFormat, name()); + } + int writeTextInBuffer(char * buffer, int bufferSize) const override { + return LayoutEngine::writePrefixExpressionTextInBuffer(this, buffer, bufferSize, name()); + } + const char * name() const { return "quo"; } + /* Simplification */ + Expression * shallowReduce(Context & context, AngleUnit angleUnit) override; + /* Evaluation */ + Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } + Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } + template Complex * templatedEvaluate(Context& context, AngleUnit angleUnit) const; }; } diff --git a/poincare/include/poincare/division_remainder.h b/poincare/include/poincare/division_remainder.h index cee47de98..3c4b1dc87 100644 --- a/poincare/include/poincare/division_remainder.h +++ b/poincare/include/poincare/division_remainder.h @@ -1,20 +1,33 @@ #ifndef POINCARE_DIVISION_REMAINDER_H #define POINCARE_DIVISION_REMAINDER_H -#include +#include +#include +#include namespace Poincare { -class DivisionRemainder : public Function { +class DivisionRemainder : public StaticHierarchy<2> { + using StaticHierarchy<2>::StaticHierarchy; public: - DivisionRemainder(); Type type() const override; - Expression * cloneWithDifferentOperands(Expression ** newOperands, - int numberOfOperands, bool cloneOperands = true) const override; + Expression * clone() const override; private: - Evaluation * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - Evaluation * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - template Evaluation * templatedEvaluate(Context& context, AngleUnit angleUnit) const; + /* Layout */ + ExpressionLayout * privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const override { + return LayoutEngine::createPrefixLayout(this, floatDisplayMode, complexFormat, name()); + } + int writeTextInBuffer(char * buffer, int bufferSize) const override { + return LayoutEngine::writePrefixExpressionTextInBuffer(this, buffer, bufferSize, name()); + } + const char * name() const { return "rem"; } + /* Simplification */ + Expression * shallowReduce(Context & context, AngleUnit angleUnit) override; + /* Evaluation */ + Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } + Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } + template Complex * templatedEvaluate(Context& context, AngleUnit angleUnit) const; + }; } diff --git a/poincare/include/poincare/dynamic_hierarchy.h b/poincare/include/poincare/dynamic_hierarchy.h new file mode 100644 index 000000000..2c94e079c --- /dev/null +++ b/poincare/include/poincare/dynamic_hierarchy.h @@ -0,0 +1,43 @@ +#ifndef POINCARE_DYNAMIC_HIERARCHY_H +#define POINCARE_DYNAMIC_HIERARCHY_H + +#include +#include + +namespace Poincare { + +class DynamicHierarchy : public Hierarchy { +public: + DynamicHierarchy(); + DynamicHierarchy(const Expression * const * operands, int numberOfOperands, bool cloneOperands = true); + DynamicHierarchy(const Expression * operand1, const Expression * operand2, bool cloneOperands = true) : + DynamicHierarchy(ExpressionArray(operand1, operand2), 2, cloneOperands) {} + ~DynamicHierarchy(); + DynamicHierarchy(const DynamicHierarchy & other) = delete; + DynamicHierarchy(DynamicHierarchy && other) = delete; + DynamicHierarchy& operator=(const DynamicHierarchy & other) = delete; + DynamicHierarchy& operator=(DynamicHierarchy && other) = delete; + + int numberOfOperands() const override { return m_numberOfOperands; } + const Expression * const * operands() const override { return m_operands; }; + + void removeOperand(const Expression * e, bool deleteAfterRemoval = true); + void addOperands(const Expression * const * operands, int numberOfOperands); + void addOperand(Expression * operand); + void addOperandAtIndex(Expression * operand, int index); + void mergeOperands(DynamicHierarchy * d); + typedef int (*ExpressionOrder)(const Expression * e1, const Expression * e2); + void sortOperands(ExpressionOrder order); + Expression * squashUnaryHierarchy(); +protected: + const Expression ** m_operands; + int m_numberOfOperands; +private: + void removeOperandAtIndex(int i, bool deleteAfterRemoval); + int simplificationOrderSameType(const Expression * e) const override; + int simplificationOrderGreaterType(const Expression * e) const override; +}; + +} + +#endif diff --git a/poincare/include/poincare/evaluation.h b/poincare/include/poincare/evaluation.h deleted file mode 100644 index eff614094..000000000 --- a/poincare/include/poincare/evaluation.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef POINCARE_EVALUATION_H -#define POINCARE_EVALUATION_H - -#include - -namespace Poincare { - -template -class Complex; - -template -class Evaluation : public Matrix { -public: - virtual T toScalar() const = 0; - Type type() const override; - bool hasValidNumberOfArguments() const override; - virtual const Expression * operand(int i) const override; - virtual const Complex * complexOperand(int i) const = 0; - virtual Evaluation * clone() const override = 0; - virtual Evaluation * createTrace() const; - virtual Evaluation * createDeterminant() const; - virtual Evaluation * createInverse() const; - Evaluation * createTranspose() const; -}; - -} - -#endif diff --git a/poincare/include/poincare/evaluation_engine.h b/poincare/include/poincare/evaluation_engine.h new file mode 100644 index 000000000..9d256c101 --- /dev/null +++ b/poincare/include/poincare/evaluation_engine.h @@ -0,0 +1,28 @@ +#ifndef POINCARE_EVALUATION_ENGINE_H +#define POINCARE_EVALUATION_ENGINE_H + +#include +#include +#include + +namespace Poincare { + +class EvaluationEngine { +public: + template using ComplexCompute = Complex(*)(const Complex, Expression::AngleUnit angleUnit); + template static Expression * map(const Expression * expression, Context& context, Expression::AngleUnit angleUnit, ComplexCompute compute); + + template using ComplexAndComplexReduction = Complex(*)(const Complex, const Complex); + template using ComplexAndMatrixReduction = Matrix * (*)(const Complex * c, const Matrix * m); + template using MatrixAndComplexReduction = Matrix * (*)(const Matrix * m, const Complex * c); + template using MatrixAndMatrixReduction = Matrix * (*)(const Matrix * m, const Matrix * n); + template static Expression * mapReduce(const Expression * expression, Context& context, Expression::AngleUnit angleUnit, ComplexAndComplexReduction computeOnComplexes, ComplexAndMatrixReduction computeOnComplexAndMatrix, MatrixAndComplexReduction computeOnMatrixAndComplex, MatrixAndMatrixReduction computeOnMatrices); + + template static Matrix * elementWiseOnComplexAndComplexMatrix(const Complex * c, const Matrix * n, ComplexAndComplexReduction computeOnComplexes); + template static Matrix * elementWiseOnComplexMatrices(const Matrix * m, const Matrix * n, ComplexAndComplexReduction computeOnComplexes); + +}; + +} + +#endif diff --git a/poincare/include/poincare/expression.h b/poincare/include/poincare/expression.h index 730d93ba2..df9a1f6a2 100644 --- a/poincare/include/poincare/expression.h +++ b/poincare/include/poincare/expression.h @@ -2,38 +2,107 @@ #define POINCARE_EXPRESSION_H #include -#include +extern "C" { +#include +} namespace Poincare { class Context; -template -class Evaluation; +template class Complex; +class Rational; class Expression { + friend class Undefined; + friend class Rational; + friend class Decimal; + friend class Multiplication; + friend class Power; + friend class Addition; + friend class Factorial; + friend class Division; + friend class Store; + friend class Sine; + friend class Cosine; + friend class Tangent; + friend class AbsoluteValue; + friend class ArcCosine; + friend class ArcSine; + friend class ArcTangent; + friend class BinomialCoefficient; + friend class Ceiling; + friend class ComplexArgument; + friend class ConfidenceInterval; + friend class Conjugate; + friend class Derivative; + friend class Determinant; + friend class DivisionQuotient; + friend class DivisionRemainder; + friend class Floor; + friend class FracPart; + friend class GreatCommonDivisor; + friend class HyperbolicArcCosine; + friend class HyperbolicArcSine; + friend class HyperbolicArcTangent; + friend class HyperbolicCosine; + friend class HyperbolicSine; + friend class HyperbolicTangent; + friend class ImaginaryPart; + friend class Integral; + friend class LeastCommonMultiple; + friend class Logarithm; + friend class MatrixDimension; + friend class MatrixInverse; + friend class MatrixTrace; + friend class MatrixTranspose; + friend class NaperianLogarithm; + friend class NthRoot; + friend class Opposite; + friend class Parenthesis; + friend class PermuteCoefficient; + friend class PredictionInterval; + friend class Product; + friend class RealPart; + friend class Round; + friend class SquareRoot; + friend class Subtraction; + friend class Sum; + friend class Symbol; + friend class Matrix; + friend class SimplificationRoot; + friend class Sequence; + friend class Trigonometry; + friend class EvaluationEngine; + friend class SimplificationEngine; + public: enum class Type : uint8_t { - AbsoluteValue, + Undefined = 0, + Rational = 1, + Decimal, + Multiplication, + Power, Addition, + Factorial, + Division, + Store, + Sine, + Cosine, + Tangent, + AbsoluteValue, ArcCosine, ArcSine, ArcTangent, BinomialCoefficient, Ceiling, - Complex, ComplexArgument, - ConfidenceInterval, Conjugate, - Cosine, Derivative, Determinant, DivisionQuotient, DivisionRemainder, - Factorial, - Float, Floor, FracPart, - ExpressionMatrix, GreatCommonDivisor, HyperbolicArcCosine, HyperbolicArcSine, @@ -42,39 +111,31 @@ public: HyperbolicSine, HyperbolicTangent, ImaginaryPart, - Integer, Integral, - Logarithm, LeastCommonMultiple, - MatrixDimension, - MatrixInverse, + Logarithm, MatrixTrace, - MatrixTranspose, - Multiplication, NaperianLogarithm, NthRoot, - Evaluation, Opposite, - PredictionInterval, - Fraction, Parenthesis, PermuteCoefficient, - Power, Product, RealPart, Round, - Sine, SquareRoot, - Store, - Sum, Subtraction, + Sum, Symbol, - Tangent, - }; - enum class AngleUnit { - Degree = 0, - Radian = 1, - Default = 2 + + Complex, + Matrix, + ConfidenceInterval, + MatrixDimension, + MatrixInverse, + MatrixTranspose, + PredictionInterval, + SimplificationRoot, }; enum class FloatDisplayMode { Decimal = 0, @@ -86,70 +147,122 @@ public: Polar = 1, Default = 2 }; + enum class AngleUnit { + Degree = 0, + Radian = 1, + Default = 2 + }; + + /* Constructor & Destructor */ static Expression * parse(char const * string); virtual ~Expression() = default; - virtual bool hasValidNumberOfArguments() const = 0; - ExpressionLayout * createLayout(FloatDisplayMode floatDisplayMode = FloatDisplayMode::Default, ComplexFormat complexFormat = ComplexFormat::Default) const; // Returned object must be deleted - virtual const Expression * operand(int i) const = 0; - virtual int numberOfOperands() const = 0; virtual Expression * clone() const = 0; - virtual Expression * cloneWithDifferentOperands(Expression** newOperands, - int numberOfOperands, bool cloneOperands = true) const = 0; - - // TODO: Consider std::unique_ptr - see https://google-styleguide.googlecode.com/svn/trunk/cppguide.html#Ownership_and_Smart_Pointers - - /* This tests whether two expressions are the same, this takes into account - * commutativity of operators. - * - * For example 3+5 is identical to 5+3 but is not identical to 8. - */ - bool isIdenticalTo(const Expression * e) const; - - /* This tests whether two expressions are equivalent. - * This is done by testing wheter they simplify to the same expression. - * - * For example: - * - 3+5 and 4+4 are equivalent. - * - (x+y)*z and x*z+y*z are equivalent. - * - * Here we assume that two equivalent expressions have the same - * simplification, we don't really know whether that's the case, - * nevertheless we are sure that if two expressions simplify to the same - * expression they are indeed equivalent. - */ - bool isEquivalentTo(Expression * e) const; - - /* Compare the value of two expressions. - * This only make sense if the two values are of the same type - */ - virtual bool valueEquals(const Expression * e) const; - Expression * simplify() const; + /* Poor man's RTTI */ virtual Type type() const = 0; - virtual bool isCommutative() const; - typedef bool (*CircuitBreaker)(const Expression * e); - static void setCircuitBreaker(CircuitBreaker cb); - bool shouldStopProcessing() const; + /* Circuit breaker */ + typedef bool (*CircuitBreaker)(); + static void setCircuitBreaker(CircuitBreaker cb); + static bool shouldStopProcessing(); - /* The function evaluate creates a new expression and thus mallocs memory. + /* Hierarchy */ + virtual const Expression * operand(int i) const = 0; + Expression * editableOperand(int i) { return const_cast(operand(i)); } + virtual int numberOfOperands() const = 0; + Expression * parent() const { return m_parent; } + void setParent(Expression * parent) { m_parent = parent; } + bool hasAncestor(const Expression * e) const; + virtual void replaceOperand(const Expression * oldOperand, Expression * newOperand, bool deleteOldOperand = true) = 0; + Expression * replaceWith(Expression * newOperand, bool deleteAfterReplace = true); + virtual void swapOperands(int i, int j) = 0; + + /* Properties */ + enum class Sign { + Negative = -1, + Unknown = 0, + Positive = 1 + }; + virtual Sign sign() const { return Sign::Unknown; } + typedef bool (*ExpressionTest)(const Expression * e); + bool recursivelyMatches(ExpressionTest test) const; + static bool IsMatrix(const Expression * e); + + /* Comparison */ + /* isIdenticalTo is the "easy" equality, it returns true if both trees have + * same structures and all their nodes have same types and values (ie, + * sqrt(pi^2) is NOT identical to pi). */ + bool isIdenticalTo(const Expression * e) const { + /* We use the simplification order only because it is a already-coded total + * order on expresssions. */ + return SimplificationOrder(this, e) == 0; + } + + /* Layout Engine */ + ExpressionLayout * createLayout(FloatDisplayMode floatDisplayMode = FloatDisplayMode::Default, ComplexFormat complexFormat = ComplexFormat::Default) const; // Returned object must be deleted + virtual int writeTextInBuffer(char * buffer, int bufferSize) const = 0; + + /* Simplification */ + static void Simplify(Expression ** expressionAddress, Context & context, AngleUnit angleUnit = AngleUnit::Default); + + /* Evaluation Engine + * The function evaluate creates a new expression and thus mallocs memory. * Do not forget to delete the new expression to avoid leaking. */ - template Evaluation * evaluate(Context& context, AngleUnit angleUnit = AngleUnit::Default) const; + template Expression * evaluate(Context& context, AngleUnit angleUnit = AngleUnit::Default) const; template T approximate(Context& context, AngleUnit angleUnit = AngleUnit::Default) const; template static T approximate(const char * text, Context& context, AngleUnit angleUnit = AngleUnit::Default); - virtual int writeTextInBuffer(char * buffer, int bufferSize) const; protected: + /* Constructor */ + Expression() : m_parent(nullptr) {} + /* Evaluation Engine */ typedef float SinglePrecision; typedef double DoublePrecision; template static T epsilon(); + constexpr static int k_maxNumberOfSteps = 10000; + + /* Simplification */ + /* SimplificationOrder returns: + * 1 if e1 > e2 + * -1 if e1 < e2 + * 0 if e1 == e + * Following the order described in Computer Algebra and Symbolic Computation, + * Joel S. Cohen (section 3.1). The order groups like terms together to avoid + * quadratic complexity when factorizing addition or multiplication. For + * example, it groups terms with same bases together (ie Pi, Pi^3) and with + * same non-rational factors together (ie Pi, 2*Pi). */ + static int SimplificationOrder(const Expression * e1, const Expression * e2); private: + /* Properties */ + virtual Expression * setSign(Sign s, Context & context, AngleUnit angleUnit) { assert(false); return nullptr; } + /* Comparison */ + /* In the simplification order, most expressions are compared by only + * comparing their types. However hierarchical expressions of same type would + * compare their operands and thus need to reimplement + * simplificationOrderSameType. Besides, operations that can be simplified + * (ie +, *, ^, !) have specific rules to group like terms together and thus + * reimplement simplificationOrderGreaterType. */ + virtual int simplificationOrderGreaterType(const Expression * e) const { return -1; } + //TODO: What should be the implementation for complex? + virtual int simplificationOrderSameType(const Expression * e) const { return 0; } + /* Layout Engine */ virtual ExpressionLayout * privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const = 0; - virtual Evaluation * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const = 0; - virtual Evaluation * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const = 0; - bool sequentialOperandsIdentity(const Expression * e) const; - bool commutativeOperandsIdentity(const Expression * e) const; - bool combinatoryCommutativeOperandsIdentity(const Expression * e, - bool * operandMatched, int leftToMatch) const; + /* Simplification */ + static void Reduce(Expression ** expressionAddress, Context & context, AngleUnit angleUnit, bool recursively = true); + Expression * deepBeautify(Context & context, AngleUnit angleUnit); + Expression * deepReduce(Context & context, AngleUnit angleUnit); + // TODO: should be virtual pure + virtual Expression * shallowReduce(Context & context, AngleUnit angleUnit); + virtual Expression * shallowBeautify(Context & context, AngleUnit angleUnit) { return this; }; + + // Private methods used in simplification process + virtual Expression * cloneDenominator(Context & context, AngleUnit angleUnit) const { + return nullptr; + } + /* Evaluation Engine */ + virtual Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const = 0; + virtual Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const = 0; + + Expression * m_parent; }; } diff --git a/poincare/include/poincare/expression_matrix.h b/poincare/include/poincare/expression_matrix.h deleted file mode 100644 index d2b66a0ce..000000000 --- a/poincare/include/poincare/expression_matrix.h +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef POINCARE_EXPRESSION_MATRIX_H -#define POINCARE_EXPRESSION_MATRIX_H - -#include - -namespace Poincare { - -class ExpressionMatrix : public Matrix { -public: - ExpressionMatrix(MatrixData * matrixData); - ExpressionMatrix(Expression ** newOperands, int numberOfOperands, int m_numberOfRows, int m_numberOfColumns, bool cloneOperands); - ~ExpressionMatrix(); - ExpressionMatrix(const Matrix& other) = delete; - ExpressionMatrix(Matrix&& other) = delete; - ExpressionMatrix& operator=(const ExpressionMatrix& other) = delete; - ExpressionMatrix& operator=(ExpressionMatrix&& other) = delete; - bool hasValidNumberOfArguments() const override; - int numberOfRows() const override; - int numberOfColumns() const override; - const Expression * operand(int i) const override; - Expression * clone() const override; - Type type() const override; - Expression * cloneWithDifferentOperands(Expression** newOperands, - int numberOfOperands, bool cloneOperands = true) const override; -private: - Evaluation * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - Evaluation * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - template Evaluation * templatedEvaluate(Context& context, AngleUnit angleUnit) const; - MatrixData * m_matrixData; -}; - -} - -#endif diff --git a/poincare/include/poincare/factorial.h b/poincare/include/poincare/factorial.h index 6cda8fc2d..e0c04785a 100644 --- a/poincare/include/poincare/factorial.h +++ b/poincare/include/poincare/factorial.h @@ -1,25 +1,30 @@ #ifndef POINCARE_FACTORIAL_H #define POINCARE_FACTORIAL_H -#include +#include +#include namespace Poincare { -class Factorial : public Function { +class Factorial : public StaticHierarchy<1> { public: - Factorial(Expression * argument, bool clone = true); + Factorial(const Expression * argument, bool clone = true); Type type() const override; - Expression * cloneWithDifferentOperands(Expression ** newOperands, - int numberOfOperands, bool cloneOperands = true) const override; + Expression * clone() const override; private: - Complex computeComplex(const Complex c, AngleUnit angleUnit) const override { - return templatedComputeComplex(c); + constexpr static int k_maxOperandValue = 100; + template static Complex computeOnComplex(const Complex c, AngleUnit angleUnit); + Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { + return EvaluationEngine::map(this, context, angleUnit,computeOnComplex); } - Complex computeComplex(const Complex c, AngleUnit angleUnit) const override { - return templatedComputeComplex(c); + Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { + return EvaluationEngine::map(this, context, angleUnit, computeOnComplex); } - template Complex templatedComputeComplex(const Complex c) const; + Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; ExpressionLayout * privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const override; + int writeTextInBuffer(char * buffer, int bufferSize) const override; + int simplificationOrderGreaterType(const Expression * e) const override; + int simplificationOrderSameType(const Expression * e) const override; }; } diff --git a/poincare/include/poincare/floor.h b/poincare/include/poincare/floor.h index 0ecbf9f6d..8ad78242a 100644 --- a/poincare/include/poincare/floor.h +++ b/poincare/include/poincare/floor.h @@ -1,25 +1,34 @@ #ifndef POINCARE_FLOOR_H #define POINCARE_FLOOR_H -#include +#include +#include +#include namespace Poincare { -class Floor : public Function { +class Floor : public StaticHierarchy<1> { + using StaticHierarchy<1>::StaticHierarchy; public: - Floor(); Type type() const override; - Expression * cloneWithDifferentOperands(Expression ** newOperands, - int numberOfOperands, bool cloneOperands = true) const override; + Expression * clone() const override; private: - Complex computeComplex(const Complex c, AngleUnit angleUnit) const override { - return templatedComputeComplex(c); - } - Complex computeComplex(const Complex c, AngleUnit angleUnit) const override { - return templatedComputeComplex(c); - } - template Complex templatedComputeComplex(const Complex c) const; + /* Layout */ ExpressionLayout * privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const override; + int writeTextInBuffer(char * buffer, int bufferSize) const override { + return LayoutEngine::writePrefixExpressionTextInBuffer(this, buffer, bufferSize, name()); + } + const char * name() const { return "floor"; } + /* Simplification */ + Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; + /* Evaluation */ + template static Complex computeOnComplex(const Complex c, AngleUnit angleUnit); + Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { + return EvaluationEngine::map(this, context, angleUnit, computeOnComplex); + } + Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { + return EvaluationEngine::map(this, context, angleUnit, computeOnComplex); + } }; } diff --git a/poincare/include/poincare/frac_part.h b/poincare/include/poincare/frac_part.h index d948bf88d..745034d24 100644 --- a/poincare/include/poincare/frac_part.h +++ b/poincare/include/poincare/frac_part.h @@ -1,24 +1,36 @@ #ifndef POINCARE_FRAC_PART_H #define POINCARE_FRAC_PART_H -#include +#include +#include +#include namespace Poincare { -class FracPart : public Function { +class FracPart : public StaticHierarchy<1> { + using StaticHierarchy<1>::StaticHierarchy; public: - FracPart(); Type type() const override; - Expression * cloneWithDifferentOperands(Expression ** newOperands, - int numberOfOperands, bool cloneOperands = true) const override; + Expression * clone() const override; private: - Complex computeComplex(const Complex c, AngleUnit angleUnit) const override { - return templatedComputeComplex(c); + /* Layout */ + ExpressionLayout * privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const override { + return LayoutEngine::createPrefixLayout(this, floatDisplayMode, complexFormat, name()); } - Complex computeComplex(const Complex c, AngleUnit angleUnit) const override { - return templatedComputeComplex(c); + int writeTextInBuffer(char * buffer, int bufferSize) const override { + return LayoutEngine::writePrefixExpressionTextInBuffer(this, buffer, bufferSize, name()); + } + const char * name() const { return "frac"; } + /* Simplification */ + Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; + /* Evaluation */ + template static Complex computeOnComplex(const Complex c, AngleUnit angleUnit); + Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { + return EvaluationEngine::map(this, context, angleUnit, computeOnComplex); + } + Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { + return EvaluationEngine::map(this, context, angleUnit, computeOnComplex); } - template Complex templatedComputeComplex(const Complex c) const; }; } diff --git a/poincare/include/poincare/fraction.h b/poincare/include/poincare/fraction.h deleted file mode 100644 index ac5dd7018..000000000 --- a/poincare/include/poincare/fraction.h +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef POINCARE_FRACTION_H -#define POINCARE_FRACTION_H - -#include - -namespace Poincare { - -class Fraction : public BinaryOperation { - using BinaryOperation::BinaryOperation; -public: - Type type() const override; - Expression * cloneWithDifferentOperands(Expression** newOperands, - int numnerOfOperands, bool cloneOperands = true) const override; - template static Complex compute(const Complex c, const Complex d); -private: - ExpressionLayout * privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const override; - - Evaluation * computeOnComplexAndComplexMatrix(const Complex * c, Evaluation * n) const override { - return templatedComputeOnComplexAndComplexMatrix(c, n); - } - Evaluation * computeOnComplexAndComplexMatrix(const Complex * c, Evaluation * n) const override { - return templatedComputeOnComplexAndComplexMatrix(c, n); - } - template Evaluation * templatedComputeOnComplexAndComplexMatrix(const Complex * c, Evaluation * n) const; - - Evaluation * computeOnComplexMatrices(Evaluation * m, Evaluation * n) const override { - return templatedComputeOnComplexMatrices(m, n); - } - Evaluation * computeOnComplexMatrices(Evaluation * m, Evaluation * n) const override { - return templatedComputeOnComplexMatrices(m, n); - } - template Evaluation * templatedComputeOnComplexMatrices(Evaluation * m, Evaluation * n) const; - - Complex privateCompute(const Complex c, const Complex d) const override { - return compute(c, d); - } - Complex privateCompute(const Complex c, const Complex d) const override { - return compute(c, d); - } -}; - -} - -#endif diff --git a/poincare/include/poincare/function.h b/poincare/include/poincare/function.h deleted file mode 100644 index d9b387c8f..000000000 --- a/poincare/include/poincare/function.h +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef POINCARE_FUNCTION_H -#define POINCARE_FUNCTION_H - -#include -#include -#include -#include -#include - -namespace Poincare { - -/* The Function class represents the built-in math functions such as cos, sin, - * tan, log, etc... */ - -class Function : public Expression { -public: - Function(const char * name, int requiredNumberOfArguments = 1); - ~Function(); - Function(const Function& other) = delete; - Function(Function&& other) = delete; - Function& operator=(const Function& other) = delete; - Function& operator=(Function&& other) = delete; - void setArgument(Expression ** args, int numberOfArguments, bool clone = true); - void setArgument(ListData * listData, bool clone = true); - bool hasValidNumberOfArguments() const override; - const Expression * operand(int i) const override; - int numberOfOperands() const override; - Expression * clone() const override; -protected: - virtual Complex computeComplex(const Complex c, AngleUnit angleUnit) const { - return Complex::Float(NAN); - } - virtual Complex computeComplex(const Complex c, AngleUnit angleUnit) const { - return Complex::Float(NAN); - } - virtual Evaluation * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - virtual Evaluation * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - template Evaluation * templatedEvaluate(Context& context, AngleUnit angleUnit) const; - ExpressionLayout * privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const override; - void build(Expression ** args, int numberOfArguments, bool clone); - void clean(); - Expression ** m_args; - int m_numberOfArguments; - int m_requiredNumberOfArguments; - const char * m_name; -}; - -} - -#endif diff --git a/poincare/include/poincare/global_context.h b/poincare/include/poincare/global_context.h index 3d8108e3f..5a3d58be9 100644 --- a/poincare/include/poincare/global_context.h +++ b/poincare/include/poincare/global_context.h @@ -2,8 +2,8 @@ #define POINCARE_GLOBAL_CONTEXT_H #include +#include #include -#include namespace Poincare { @@ -21,11 +21,8 @@ public: GlobalContext& operator=(GlobalContext&& other) = delete; /* The expression recorded in global context is already a expression. * Otherwise, we would need the context and the angle unit to evaluate it */ - const Expression * expressionForSymbol(const Symbol * symbol) override { - return evaluationForSymbol(symbol); - } - const Evaluation * evaluationForSymbol(const Symbol * symbol); - void setExpressionForSymbolName(Expression * expression, const Symbol * symbol) override; + const Expression * expressionForSymbol(const Symbol * symbol) override; + void setExpressionForSymbolName(const Expression * expression, const Symbol * symbol, Context & context) override; static constexpr uint16_t k_maxNumberOfScalarExpressions = 26; static constexpr uint16_t k_maxNumberOfListExpressions = 10; static constexpr uint16_t k_maxNumberOfMatrixExpressions = 10; @@ -33,9 +30,10 @@ public: private: int symbolIndex(const Symbol * symbol) const; Complex * m_expressions[k_maxNumberOfScalarExpressions]; - ComplexMatrix * m_matrixExpressions[k_maxNumberOfMatrixExpressions]; + Matrix * m_matrixExpressions[k_maxNumberOfMatrixExpressions]; Complex m_pi; Complex m_e; + Complex m_i; }; } diff --git a/poincare/include/poincare/great_common_divisor.h b/poincare/include/poincare/great_common_divisor.h index 959d114fc..4005715f0 100644 --- a/poincare/include/poincare/great_common_divisor.h +++ b/poincare/include/poincare/great_common_divisor.h @@ -1,20 +1,32 @@ #ifndef POINCARE_GREAT_COMMON_DIVISOR_H #define POINCARE_GREAT_COMMON_DIVISOR_H -#include +#include +#include +#include namespace Poincare { -class GreatCommonDivisor : public Function { +class GreatCommonDivisor : public StaticHierarchy<2> { + using StaticHierarchy<2>::StaticHierarchy; public: - GreatCommonDivisor(); Type type() const override; - Expression * cloneWithDifferentOperands(Expression ** newOperands, - int numberOfOperands, bool cloneOperands = true) const override; + Expression * clone() const override; private: - Evaluation * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - Evaluation * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - template Evaluation * templatedEvaluate(Context& context, AngleUnit angleUnit) const; + /* Layout */ + ExpressionLayout * privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const override { + return LayoutEngine::createPrefixLayout(this, floatDisplayMode, complexFormat, name()); + } + int writeTextInBuffer(char * buffer, int bufferSize) const override { + return LayoutEngine::writePrefixExpressionTextInBuffer(this, buffer, bufferSize, name()); + } + const char * name() const { return "gcd"; } + /* Simplification */ + Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; + /* Evaluation */ + Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } + Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } + template Complex * templatedEvaluate(Context& context, AngleUnit angleUnit) const; }; } diff --git a/poincare/include/poincare/hierarchy.h b/poincare/include/poincare/hierarchy.h new file mode 100644 index 000000000..43f6769b0 --- /dev/null +++ b/poincare/include/poincare/hierarchy.h @@ -0,0 +1,25 @@ +#ifndef POINCARE_HIERARCHY_H +#define POINCARE_HIERARCHY_H + +#include + +namespace Poincare { + +class Hierarchy : public Expression { +public: + using Expression::Expression; + const Expression * operand(int i) const override; + void swapOperands(int i, int j) override; + void replaceOperand(const Expression * oldOperand, Expression * newOperand, bool deleteOldOperand = true) override; + void detachOperand(const Expression * e); // Removes an operand WITHOUT deleting it + void detachOperands(); // Removes all operands WITHOUT deleting them + virtual const Expression * const * operands() const = 0; +protected: + static const Expression * const * ExpressionArray(const Expression * e1, const Expression * e2); +private: + void detachOperandAtIndex(int i); +}; + +} + +#endif diff --git a/poincare/include/poincare/hyperbolic_arc_cosine.h b/poincare/include/poincare/hyperbolic_arc_cosine.h index 3829bfe98..4122a0e20 100644 --- a/poincare/include/poincare/hyperbolic_arc_cosine.h +++ b/poincare/include/poincare/hyperbolic_arc_cosine.h @@ -1,24 +1,36 @@ #ifndef POINCARE_HYPERBOLIC_ARC_COSINE_H #define POINCARE_HYPERBOLIC_ARC_COSINE_H -#include +#include +#include +#include namespace Poincare { -class HyperbolicArcCosine : public Function { +class HyperbolicArcCosine : public StaticHierarchy<1> { + using StaticHierarchy<1>::StaticHierarchy; public: - HyperbolicArcCosine(); Type type() const override; - Expression * cloneWithDifferentOperands(Expression ** newOperands, - int numberOfOperands, bool cloneOperands = true) const override; + Expression * clone() const override; private: - Complex computeComplex(const Complex c, AngleUnit angleUnit) const override { - return templatedComputeComplex(c); + /* Layout */ + ExpressionLayout * privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const override { + return LayoutEngine::createPrefixLayout(this, floatDisplayMode, complexFormat, name()); } - Complex computeComplex(const Complex c, AngleUnit angleUnit) const override { - return templatedComputeComplex(c); + int writeTextInBuffer(char * buffer, int bufferSize) const override { + return LayoutEngine::writePrefixExpressionTextInBuffer(this, buffer, bufferSize, name()); + } + const char * name() const { return "acosh"; } + /* Simplification */ + Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; + /* Evaluation */ + template static Complex computeOnComplex(const Complex c, AngleUnit angleUnit); + Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { + return EvaluationEngine::map(this, context, angleUnit,computeOnComplex); + } + Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { + return EvaluationEngine::map(this, context, angleUnit, computeOnComplex); } - template Complex templatedComputeComplex(const Complex c) const; }; } diff --git a/poincare/include/poincare/hyperbolic_arc_sine.h b/poincare/include/poincare/hyperbolic_arc_sine.h index 2ea12039a..e0ddecba9 100644 --- a/poincare/include/poincare/hyperbolic_arc_sine.h +++ b/poincare/include/poincare/hyperbolic_arc_sine.h @@ -1,24 +1,36 @@ #ifndef POINCARE_HYPERBOLIC_ARC_SINE_H #define POINCARE_HYPERBOLIC_ARC_SINE_H -#include +#include +#include +#include namespace Poincare { -class HyperbolicArcSine : public Function { +class HyperbolicArcSine : public StaticHierarchy<1> { + using StaticHierarchy<1>::StaticHierarchy; public: - HyperbolicArcSine(); Type type() const override; - Expression * cloneWithDifferentOperands(Expression ** newOperands, - int numberOfOperands, bool cloneOperands = true) const override; + Expression * clone() const override; private: - Complex computeComplex(const Complex c, AngleUnit angleUnit) const override { - return templatedComputeComplex(c); + /* Layout */ + ExpressionLayout * privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const override { + return LayoutEngine::createPrefixLayout(this, floatDisplayMode, complexFormat, name()); } - Complex computeComplex(const Complex c, AngleUnit angleUnit) const override { - return templatedComputeComplex(c); + int writeTextInBuffer(char * buffer, int bufferSize) const override { + return LayoutEngine::writePrefixExpressionTextInBuffer(this, buffer, bufferSize, name()); + } + const char * name() const { return "asinh"; } + /* Simplification */ + Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; + /* Evaluation */ + template static Complex computeOnComplex(const Complex c, AngleUnit angleUnit); + Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { + return EvaluationEngine::map(this, context, angleUnit,computeOnComplex); + } + Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { + return EvaluationEngine::map(this, context, angleUnit, computeOnComplex); } - template Complex templatedComputeComplex(const Complex c) const; }; } diff --git a/poincare/include/poincare/hyperbolic_arc_tangent.h b/poincare/include/poincare/hyperbolic_arc_tangent.h index 1f7e6d1cc..755394c64 100644 --- a/poincare/include/poincare/hyperbolic_arc_tangent.h +++ b/poincare/include/poincare/hyperbolic_arc_tangent.h @@ -1,24 +1,36 @@ #ifndef POINCARE_HYPERBOLIC_ARC_TANGENT_H #define POINCARE_HYPERBOLIC_ARC_TANGENT_H -#include +#include +#include +#include namespace Poincare { -class HyperbolicArcTangent : public Function { +class HyperbolicArcTangent : public StaticHierarchy<1> { + using StaticHierarchy<1>::StaticHierarchy; public: - HyperbolicArcTangent(); Type type() const override; - Expression * cloneWithDifferentOperands(Expression ** newOperands, - int numberOfOperands, bool cloneOperands = true) const override; + Expression * clone() const override; private: - Complex computeComplex(const Complex c, AngleUnit angleUnit) const override { - return templatedComputeComplex(c); + /* Layout */ + ExpressionLayout * privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const override { + return LayoutEngine::createPrefixLayout(this, floatDisplayMode, complexFormat, name()); } - Complex computeComplex(const Complex c, AngleUnit angleUnit) const override { - return templatedComputeComplex(c); + int writeTextInBuffer(char * buffer, int bufferSize) const override { + return LayoutEngine::writePrefixExpressionTextInBuffer(this, buffer, bufferSize, name()); + } + const char * name() const { return "atanh"; } + /* Simplification */ + Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; + /* Evaluation */ + template static Complex computeOnComplex(const Complex c, AngleUnit angleUnit); + Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { + return EvaluationEngine::map(this, context, angleUnit,computeOnComplex); + } + Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { + return EvaluationEngine::map(this, context, angleUnit, computeOnComplex); } - template Complex templatedComputeComplex(const Complex c) const; }; } diff --git a/poincare/include/poincare/hyperbolic_cosine.h b/poincare/include/poincare/hyperbolic_cosine.h index ef50567af..31dc054fc 100644 --- a/poincare/include/poincare/hyperbolic_cosine.h +++ b/poincare/include/poincare/hyperbolic_cosine.h @@ -1,23 +1,35 @@ #ifndef POINCARE_HYPERBOLIC_COSINE_H #define POINCARE_HYPERBOLIC_COSINE_H -#include +#include +#include +#include namespace Poincare { -class HyperbolicCosine : public Function { +class HyperbolicCosine : public StaticHierarchy<1> { + using StaticHierarchy<1>::StaticHierarchy; public: - HyperbolicCosine(); Type type() const override; - Expression * cloneWithDifferentOperands(Expression ** newOperands, - int numberOfOperands, bool cloneOperands = true) const override; - template static Complex compute(const Complex c); + Expression * clone() const override; + template static Complex computeOnComplex(const Complex c, AngleUnit angleUnit); private: - Complex computeComplex(const Complex c, AngleUnit angleUnit) const override { - return compute(c); + /* Layout */ + ExpressionLayout * privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const override { + return LayoutEngine::createPrefixLayout(this, floatDisplayMode, complexFormat, name()); } - Complex computeComplex(const Complex c, AngleUnit angleUnit) const override { - return compute(c); + int writeTextInBuffer(char * buffer, int bufferSize) const override { + return LayoutEngine::writePrefixExpressionTextInBuffer(this, buffer, bufferSize, name()); + } + const char * name() const { return "cosh"; } + /* Simplification */ + Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; + /* Evaluation */ + Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { + return EvaluationEngine::map(this, context, angleUnit,computeOnComplex); + } + Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { + return EvaluationEngine::map(this, context, angleUnit, computeOnComplex); } }; diff --git a/poincare/include/poincare/hyperbolic_sine.h b/poincare/include/poincare/hyperbolic_sine.h index 873f0afd7..244bb7d5e 100644 --- a/poincare/include/poincare/hyperbolic_sine.h +++ b/poincare/include/poincare/hyperbolic_sine.h @@ -1,23 +1,35 @@ #ifndef POINCARE_HYPERBOLIC_SINE_H #define POINCARE_HYPERBOLIC_SINE_H -#include +#include +#include +#include namespace Poincare { -class HyperbolicSine : public Function { +class HyperbolicSine : public StaticHierarchy<1> { + using StaticHierarchy<1>::StaticHierarchy; public: - HyperbolicSine(); Type type() const override; - Expression * cloneWithDifferentOperands(Expression ** newOperands, - int numberOfOperands, bool cloneOperands = true) const override; - template static Complex compute(const Complex c); + Expression * clone() const override; + template static Complex computeOnComplex(const Complex c, AngleUnit angleUnit); private: - Complex computeComplex(const Complex c, AngleUnit angleUnit) const override { - return compute(c); + /* Layout */ + ExpressionLayout * privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const override { + return LayoutEngine::createPrefixLayout(this, floatDisplayMode, complexFormat, name()); } - Complex computeComplex(const Complex c, AngleUnit angleUnit) const override { - return compute(c); + int writeTextInBuffer(char * buffer, int bufferSize) const override { + return LayoutEngine::writePrefixExpressionTextInBuffer(this, buffer, bufferSize, name()); + } + const char * name() const { return "sinh"; } + /* Simplification */ + Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; + /* Evaluation */ + Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { + return EvaluationEngine::map(this, context, angleUnit,computeOnComplex); + } + Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { + return EvaluationEngine::map(this, context, angleUnit, computeOnComplex); } }; diff --git a/poincare/include/poincare/hyperbolic_tangent.h b/poincare/include/poincare/hyperbolic_tangent.h index b354d818c..e18448eb8 100644 --- a/poincare/include/poincare/hyperbolic_tangent.h +++ b/poincare/include/poincare/hyperbolic_tangent.h @@ -1,23 +1,35 @@ #ifndef POINCARE_HYPERBOLIC_TANGENT_H #define POINCARE_HYPERBOLIC_TANGENT_H -#include +#include +#include +#include namespace Poincare { -class HyperbolicTangent : public Function { +class HyperbolicTangent : public StaticHierarchy<1> { + using StaticHierarchy<1>::StaticHierarchy; public: - HyperbolicTangent(); Type type() const override; - Expression * cloneWithDifferentOperands(Expression ** newOperands, - int numberOfOperands, bool cloneOperands = true) const override; - template static Complex compute(const Complex c); + Expression * clone() const override; + template static Complex computeOnComplex(const Complex c, AngleUnit angleUnit); private: - Complex computeComplex(const Complex c, AngleUnit angleUnit) const override { - return compute(c); + /* Layout */ + ExpressionLayout * privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const override { + return LayoutEngine::createPrefixLayout(this, floatDisplayMode, complexFormat, name()); } - Complex computeComplex(const Complex c, AngleUnit angleUnit) const override { - return compute(c); + int writeTextInBuffer(char * buffer, int bufferSize) const override { + return LayoutEngine::writePrefixExpressionTextInBuffer(this, buffer, bufferSize, name()); + } + const char * name() const { return "tanh"; } + /* Simplification */ + Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; + /* Evaluation */ + Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { + return EvaluationEngine::map(this, context, angleUnit,computeOnComplex); + } + Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { + return EvaluationEngine::map(this, context, angleUnit, computeOnComplex); } }; diff --git a/poincare/include/poincare/imaginary_part.h b/poincare/include/poincare/imaginary_part.h index 77bc3698d..f2f713d74 100644 --- a/poincare/include/poincare/imaginary_part.h +++ b/poincare/include/poincare/imaginary_part.h @@ -1,24 +1,36 @@ #ifndef POINCARE_IMAGINARY_PART_H #define POINCARE_IMAGINARY_PART_H -#include +#include +#include +#include namespace Poincare { -class ImaginaryPart : public Function { +class ImaginaryPart : public StaticHierarchy<1> { + using StaticHierarchy<1>::StaticHierarchy; public: - ImaginaryPart(); Type type() const override; - Expression * cloneWithDifferentOperands(Expression ** newOperands, - int numberOfOperands, bool cloneOperands = true) const override; + Expression * clone() const override; private: - Complex computeComplex(const Complex c, AngleUnit angleUnit) const override { - return templatedComputeComplex(c); + /* Layout */ + ExpressionLayout * privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const override { + return LayoutEngine::createPrefixLayout(this, floatDisplayMode, complexFormat, name()); } - Complex computeComplex(const Complex c, AngleUnit angleUnit) const override { - return templatedComputeComplex(c); + int writeTextInBuffer(char * buffer, int bufferSize) const override { + return LayoutEngine::writePrefixExpressionTextInBuffer(this, buffer, bufferSize, name()); + } + const char * name() const { return "im"; } + /* Simplification */ + Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; + /* Evaluation */ + template static Complex computeOnComplex(const Complex c, AngleUnit angleUnit); + Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { + return EvaluationEngine::map(this, context, angleUnit,computeOnComplex); + } + Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { + return EvaluationEngine::map(this, context, angleUnit, computeOnComplex); } - template Complex templatedComputeComplex(const Complex c) const; }; } diff --git a/poincare/include/poincare/integer.h b/poincare/include/poincare/integer.h index a02423e0c..e4112786f 100644 --- a/poincare/include/poincare/integer.h +++ b/poincare/include/poincare/integer.h @@ -1,66 +1,116 @@ #ifndef POINCARE_INTEGER_H #define POINCARE_INTEGER_H -#include +#include #include - -typedef int32_t native_int_t; -typedef uint32_t native_uint_t; -typedef uint64_t double_native_uint_t; +#include namespace Poincare { -class Integer : public LeafExpression { +/* All algorithm should be improved with: + * Modern Computer Arithmetic, Richard P. Brent and Paul Zimmermann */ + +struct IntegerDivision; + +class Integer { public: - Integer(native_int_t i); + typedef uint16_t half_native_uint_t; + typedef int32_t native_int_t; + typedef int64_t double_native_int_t; + typedef uint32_t native_uint_t; + typedef uint64_t double_native_uint_t; + + // FIXME: This constructor should be constexpr + Integer(native_int_t i = 0) : + m_digit(i>0 ? i : -i), + m_numberOfDigits(1), + m_negative(i<0) + { + } + Integer(double_native_int_t i); Integer(const char * digits, bool negative = false); // Digits are NOT NULL-terminated - Type type() const override; + static Integer exponent(int fractionalPartLength, const char * exponent, int exponentLength, bool exponentNegative); + static Integer numerator(const char * integralPart, int integralPartLength, const char * fractionalPart, int fractionalPartLength, bool negative, Integer * exponent); + static Integer denominator(Integer * exponent); ~Integer(); Integer(Integer&& other); // C++11 move constructor Integer& operator=(Integer&& other); // C++11 move assignment operator - Integer(const Integer& other) = delete; - Integer& operator=(const Integer& other) = delete; + Integer(const Integer& other); // C++11 copy constructor + Integer& operator=(const Integer& other); // C++11 copy assignment operator + + // Getter & Setter + bool isNegative() const { return m_negative; } + void setNegative(bool negative); + int extractedInt() { assert(m_numberOfDigits == 1 && m_digit <= 0x7FFFFFFF); return m_negative ? -m_digit : m_digit; } + + // Comparison + static int NaturalOrder(const Integer & i, const Integer & j); + bool isEqualTo(const Integer & other) const; + bool isLowerThan(const Integer & other) const; + + // Layout + int writeTextInBuffer(char * buffer, int bufferSize) const; + ExpressionLayout * createLayout() const; + + // Approximation + template T approximate() const; // Arithmetic - Integer add(const Integer &other) const; - Integer subtract(const Integer &other) const; - Integer multiply_by(const Integer &other) const; - Integer divide_by(const Integer &other) const; - - bool operator<(const Integer &other) const; - bool operator==(const Integer &other) const; - - bool valueEquals(const Expression * e) const override; - - Expression * clone() const override; + static Integer Addition(const Integer & i, const Integer & j); + static Integer Subtraction(const Integer & i, const Integer & j); + static Integer Multiplication(const Integer & i, const Integer & j); + static Integer Factorial(const Integer & i); + static IntegerDivision Division(const Integer & numerator, const Integer & denominator); + static Integer Power(const Integer & i, const Integer & j); + //static Integer Division(const Integer & i, const Integer & j); + //static IntegerDivision division(const Integer & i, const Integer & j); + bool isOne() const { return (m_numberOfDigits == 1 && digit(0) == 1 && !m_negative); }; + bool isTwo() const { return (m_numberOfDigits == 1 && digit(0) == 2 && !m_negative); }; + bool isTen() const { return (m_numberOfDigits == 1 && digit(0) == 10 && !m_negative); }; + bool isMinusOne() const { return (m_numberOfDigits == 1 && digit(0) == 1 && m_negative); }; + bool isZero() const { return (m_numberOfDigits == 1 && digit(0) == 0); }; private: - ExpressionLayout * privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const override; - Evaluation * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override; - Evaluation * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override; - Integer add(const Integer &other, bool inverse_other_negative) const; - int8_t ucmp(const Integer &other) const; // -1, 0, or 1 - Integer usum(const Integer &other, bool subtract, bool output_negative) const; - /* WARNING: This constructor takes ownership of the bits array and will free it! */ - Integer(native_uint_t * digits, uint16_t numberOfDigits, bool negative); - native_uint_t * m_digits; // LITTLE-ENDIAN - uint16_t m_numberOfDigits; // In base native_uint_max - bool m_negative; + Integer(const native_uint_t * digits, uint16_t numberOfDigits, bool negative); + static Integer IntegerWithHalfDigitAtIndex(half_native_uint_t halfDigit, int index); - /* - // TODO: Small-int optimization - union { - uint32_t m_staticBits; - char * m_dynamicBits; - }; - */ + void releaseDynamicIvars(); + static int8_t ucmp(const Integer & a, const Integer & b); // -1, 0, or 1 + static Integer usum(const Integer & a, const Integer & b, bool subtract, bool outputNegative); + static Integer addition(const Integer & a, const Integer & b, bool inverseBNegative); + static IntegerDivision udiv(const Integer & a, const Integer & b); + bool usesImmediateDigit() const { return m_numberOfDigits == 1; } + native_uint_t digit(int i) const { + assert(i >= 0 && i < m_numberOfDigits); + return (usesImmediateDigit() ? m_digit : m_digits[i]); + } + uint16_t numberOfHalfDigits() const { + native_uint_t d = digit(m_numberOfDigits-1); + native_uint_t halfBase = 1 << (8*sizeof(half_native_uint_t)); + return (d >= halfBase ? 2*m_numberOfDigits : 2*m_numberOfDigits-1); + } + half_native_uint_t halfDigit(int i) const { + assert(i >= 0); + if (i >= numberOfHalfDigits()) { + return 0; + } + return (usesImmediateDigit() ? ((half_native_uint_t *)&m_digit)[i] : ((half_native_uint_t *)m_digits)[i]); + } + // Small integer optimization. Similar to short string optimization. + union { + const native_uint_t * m_digits; // Little-endian + native_uint_t m_digit; + }; + uint16_t m_numberOfDigits; // In base native_uint_max + bool m_negative; // Make sure zero cannot be negative + + static_assert(sizeof(native_int_t) <= sizeof(native_uint_t), "native_uint_t should be able to contain native_int_t data"); + static_assert(sizeof(double_native_uint_t) == 2*sizeof(native_uint_t), "double_native_uint_t should be twice the size of native_uint_t"); }; -class Division { -public: - Division(const Integer &numerator, const Integer &denominator); - Integer m_quotient; - Integer m_remainder; +struct IntegerDivision { + Integer quotient; + Integer remainder; }; } diff --git a/poincare/include/poincare/integral.h b/poincare/include/poincare/integral.h index 267ca0d07..593f1e14a 100644 --- a/poincare/include/poincare/integral.h +++ b/poincare/include/poincare/integral.h @@ -1,22 +1,30 @@ #ifndef POINCARE_INTEGRAL_H #define POINCARE_INTEGRAL_H -#include +#include #include +#include +#include namespace Poincare { -class Integral : public Function { +class Integral : public StaticHierarchy<3> { + using StaticHierarchy<3>::StaticHierarchy; public: - Integral(); Type type() const override; - Expression * cloneWithDifferentOperands(Expression ** newOperands, - int numberOfOperands, bool cloneOperands = true) const override; + Expression * clone() const override; private: - Evaluation * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - Evaluation * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - template Evaluation * templatedEvaluate(Context& context, AngleUnit angleUnit) const; + /* Layout */ ExpressionLayout * privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const override; + int writeTextInBuffer(char * buffer, int bufferSize) const override { + return LayoutEngine::writePrefixExpressionTextInBuffer(this, buffer, bufferSize, "int"); + } + /* Simplification */ + Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; + /* Evaluation */ + Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } + Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } + template Complex * templatedEvaluate(Context& context, AngleUnit angleUnit) const; template struct DetailedResult { diff --git a/poincare/include/poincare/layout_engine.h b/poincare/include/poincare/layout_engine.h new file mode 100644 index 000000000..cf1bc1e00 --- /dev/null +++ b/poincare/include/poincare/layout_engine.h @@ -0,0 +1,20 @@ +#ifndef POINCARE_LAYOUT_ENGINE_H +#define POINCARE_LAYOUT_ENGINE_H + +#include + +namespace Poincare { + +class LayoutEngine { +public: + static ExpressionLayout * createInfixLayout(const Expression * expression, Expression::FloatDisplayMode floatDisplayMode, Expression::ComplexFormat complexFormat, const char * operatorName); + static ExpressionLayout * createPrefixLayout(const Expression * expression, Expression::FloatDisplayMode floatDisplayMode, Expression::ComplexFormat complexFormat, const char * operatorName); + + typedef bool (*OperandNeedParenthesis)(const Expression * e); + static int writeInfixExpressionTextInBuffer(const Expression * expression, char * buffer, int bufferSize, const char * operatorName, OperandNeedParenthesis operandNeedParenthesis = [](const Expression * e) { return e->type() == Expression::Type::Opposite; }); + static int writePrefixExpressionTextInBuffer(const Expression * expression, char * buffer, int bufferSize, const char * operatorName); +}; + +} + +#endif diff --git a/poincare/include/poincare/leaf_expression.h b/poincare/include/poincare/leaf_expression.h deleted file mode 100644 index a078d7832..000000000 --- a/poincare/include/poincare/leaf_expression.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef POINCARE_LEAF_EXPRESSION_H -#define POINCARE_LEAF_EXPRESSION_H - -#include - -namespace Poincare { - -class LeafExpression : public Expression { -public: - bool hasValidNumberOfArguments() const override; - const Expression * operand(int i) const override; - int numberOfOperands() const override; - Expression * cloneWithDifferentOperands(Expression** newOperands, - int numberOfOperands, bool cloneOperands = true) const override; -}; - -} - -#endif diff --git a/poincare/include/poincare/least_common_multiple.h b/poincare/include/poincare/least_common_multiple.h index 6700d5143..d151b9c0d 100644 --- a/poincare/include/poincare/least_common_multiple.h +++ b/poincare/include/poincare/least_common_multiple.h @@ -1,20 +1,32 @@ #ifndef POINCARE_LEAST_COMMON_MULTIPLE_H #define POINCARE_LEAST_COMMON_MULTIPLE_H -#include +#include +#include +#include namespace Poincare { -class LeastCommonMultiple : public Function { +class LeastCommonMultiple : public StaticHierarchy<2> { + using StaticHierarchy<2>::StaticHierarchy; public: - LeastCommonMultiple(); Type type() const override; - Expression * cloneWithDifferentOperands(Expression ** newOperands, - int numberOfOperands, bool cloneOperands = true) const override; + Expression * clone() const override; private: - Evaluation * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - Evaluation * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - template Evaluation * templatedEvaluate(Context& context, AngleUnit angleUnit) const; + /* Layout */ + ExpressionLayout * privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const override { + return LayoutEngine::createPrefixLayout(this, floatDisplayMode, complexFormat, name()); + } + int writeTextInBuffer(char * buffer, int bufferSize) const override { + return LayoutEngine::writePrefixExpressionTextInBuffer(this, buffer, bufferSize, name()); + } + const char * name() const { return "lcm"; } + /* Simplification */ + Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; + /* Evaluation */ + Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } + Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } + template Complex * templatedEvaluate(Context& context, AngleUnit angleUnit) const; }; } diff --git a/poincare/include/poincare/list_data.h b/poincare/include/poincare/list_data.h index 4ca4a05f8..dede2436e 100644 --- a/poincare/include/poincare/list_data.h +++ b/poincare/include/poincare/list_data.h @@ -17,6 +17,7 @@ public: Expression ** operands() const; const Expression * operand(int i) const; void pushExpression(Expression * operand); + void detachOperands(); private: int m_numberOfOperands; Expression ** m_operands; diff --git a/poincare/include/poincare/logarithm.h b/poincare/include/poincare/logarithm.h index 439bb1975..5b3b0dcef 100644 --- a/poincare/include/poincare/logarithm.h +++ b/poincare/include/poincare/logarithm.h @@ -1,29 +1,34 @@ #ifndef POINCARE_LOGARITHM_H #define POINCARE_LOGARITHM_H -#include +#include +#include +#include namespace Poincare { -class Logarithm : public Function { +class Logarithm : public BoundedStaticHierarchy<2> { + using BoundedStaticHierarchy<2>::BoundedStaticHierarchy; + friend class NaperianLogarithm; public: - Logarithm(); - bool hasValidNumberOfArguments() const override; Type type() const override; - Expression * cloneWithDifferentOperands(Expression ** newOperands, - int numberOfOperands, bool cloneOperands = true) const override; + Expression * clone() const override; private: - Evaluation * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - Evaluation * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - template Evaluation * templatedEvaluate(Context& context, AngleUnit angleUnit) const; + /* Layout */ ExpressionLayout * privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const override; - Complex computeComplex(const Complex c, AngleUnit angleUnit) const override { - return templatedComputeComplex(c); + int writeTextInBuffer(char * buffer, int bufferSize) const override { + return LayoutEngine::writePrefixExpressionTextInBuffer(this, buffer, bufferSize, "log"); } - Complex computeComplex(const Complex c, AngleUnit angleUnit) const override { - return templatedComputeComplex(c); - } - template Complex templatedComputeComplex(const Complex c) const; + /* Simplification */ + Expression * shallowReduce(Context & context, AngleUnit angleUnit) override; + Expression * shallowBeautify(Context & context, AngleUnit angleUnit) override; + bool parentIsAPowerOfSameBase() const; + Expression * splitInteger(Integer i, bool isDenominator, Context & context, AngleUnit angleUnit); + /* Evaluation */ + template static Complex computeOnComplex(const Complex c, AngleUnit angleUnit); + Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } + Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } + template Expression * templatedEvaluate(Context& context, AngleUnit angleUnit) const; }; } diff --git a/poincare/include/poincare/matrix.h b/poincare/include/poincare/matrix.h index 425a14139..1ed00cbc6 100644 --- a/poincare/include/poincare/matrix.h +++ b/poincare/include/poincare/matrix.h @@ -1,22 +1,42 @@ #ifndef POINCARE_MATRIX_H #define POINCARE_MATRIX_H -#include +#include #include namespace Poincare { -class Matrix : public Expression { +class Matrix : public DynamicHierarchy { public: - virtual const Expression * operand(int i) const override = 0; - int numberOfOperands() const override; - virtual int numberOfRows() const = 0; - virtual int numberOfColumns() const = 0; - /* If the buffer is too small, the function fills the buffer until reaching - * buffer size */ + Matrix(MatrixData * matrixData); // pilfer the operands of matrixData + Matrix(const Expression * const * operands, int numberOfRows, int numberOfColumns, bool cloneOperands = true); + int numberOfRows() const; + int numberOfColumns() const; + + /* Expression */ + Type type() const override; + Expression * clone() const override; int writeTextInBuffer(char * buffer, int bufferSize) const override; + + /* Operation on matrix */ + /* createDeterminant, createTrace and createInverse can only be called on an + * matrix of complex expressions. createDeterminant and createTrace return + * a complex expression and createInverse returns a matrix of complex + * expressions or nullptr if the inverse could not be computed. */ + template Complex * createTrace() const; + template Complex * createDeterminant() const; + template Matrix * createInverse() const; + Matrix * createTranspose() const; + static Matrix * createIdentity(int dim); + template static Matrix * createApproximateIdentity(int dim); private: + /* Layout */ ExpressionLayout * privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const override; + /* Evaluation */ + Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } + Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } + template Expression * templatedEvaluate(Context& context, AngleUnit angleUnit) const; + int m_numberOfRows; }; } diff --git a/poincare/include/poincare/matrix_data.h b/poincare/include/poincare/matrix_data.h index 5dc3d1eb8..f168aaf1b 100644 --- a/poincare/include/poincare/matrix_data.h +++ b/poincare/include/poincare/matrix_data.h @@ -12,7 +12,6 @@ class Complex; class MatrixData { public: MatrixData(ListData * listData, bool clone); - MatrixData(Expression ** newOperands, int numberOfOperands, int m_numberOfRows, int m_numberOfColumns, bool cloneOperands); ~MatrixData(); MatrixData(const MatrixData& other) = delete; MatrixData(MatrixData&& other) = delete; @@ -21,11 +20,11 @@ public: void pushListData(ListData * listData, bool clone); int numberOfRows(); int numberOfColumns(); - Expression ** operands() const; + void pilferOperands(const Expression *** newStorageAddress); private: int m_numberOfRows; int m_numberOfColumns; - Expression ** m_operands; + const Expression ** m_operands; static Complex * defaultExpression(); }; diff --git a/poincare/include/poincare/matrix_dimension.h b/poincare/include/poincare/matrix_dimension.h index 209378e43..a3922e12b 100644 --- a/poincare/include/poincare/matrix_dimension.h +++ b/poincare/include/poincare/matrix_dimension.h @@ -1,20 +1,32 @@ #ifndef POINCARE_MATRIX_DIMENSION_H #define POINCARE_MATRIX_DIMENSION_H -#include +#include +#include +#include namespace Poincare { -class MatrixDimension : public Function { +class MatrixDimension : public StaticHierarchy<1> { + using StaticHierarchy<1>::StaticHierarchy; public: - MatrixDimension(); Type type() const override; - Expression * cloneWithDifferentOperands(Expression ** newOperands, - int numberOfOperands, bool cloneOperands = true) const override; + Expression * clone() const override; private: - Evaluation * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - Evaluation * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - template Evaluation * templatedEvaluate(Context& context, AngleUnit angleUnit) const; + /* Layout */ + ExpressionLayout * privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const override { + return LayoutEngine::createPrefixLayout(this, floatDisplayMode, complexFormat, name()); + } + int writeTextInBuffer(char * buffer, int bufferSize) const override { + return LayoutEngine::writePrefixExpressionTextInBuffer(this, buffer, bufferSize, name()); + } + const char * name() const { return "dimension"; } + /* Simplification */ + Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; + /* Evaluation */ + Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } + Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } + template Expression * templatedEvaluate(Context& context, AngleUnit angleUnit) const; }; } diff --git a/poincare/include/poincare/matrix_inverse.h b/poincare/include/poincare/matrix_inverse.h index 4cd1d2510..7037ebf54 100644 --- a/poincare/include/poincare/matrix_inverse.h +++ b/poincare/include/poincare/matrix_inverse.h @@ -1,20 +1,32 @@ #ifndef POINCARE_MATRIX_INVERSE_H #define POINCARE_MATRIX_INVERSE_H -#include +#include +#include +#include namespace Poincare { -class MatrixInverse : public Function { +class MatrixInverse : public StaticHierarchy<1> { + using StaticHierarchy<1>::StaticHierarchy; public: - MatrixInverse(); Type type() const override; - Expression * cloneWithDifferentOperands(Expression ** newOperands, - int numberOfOperands, bool cloneOperands = true) const override; + Expression * clone() const override; private: - Evaluation * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - Evaluation * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - template Evaluation * templatedEvaluate(Context& context, AngleUnit angleUnit) const; + /* Evaluation */ + ExpressionLayout * privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const override { + return LayoutEngine::createPrefixLayout(this, floatDisplayMode, complexFormat, name()); + } + int writeTextInBuffer(char * buffer, int bufferSize) const override { + return LayoutEngine::writePrefixExpressionTextInBuffer(this, buffer, bufferSize, name()); + } + const char * name() const { return "inverse"; } + /* Simplification */ + Expression * shallowReduce(Context & context, AngleUnit angleUnit) override; + /* Evaluation */ + Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } + Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } + template Expression * templatedEvaluate(Context& context, AngleUnit angleUnit) const; }; } diff --git a/poincare/include/poincare/matrix_trace.h b/poincare/include/poincare/matrix_trace.h index 487690133..e54ce0df5 100644 --- a/poincare/include/poincare/matrix_trace.h +++ b/poincare/include/poincare/matrix_trace.h @@ -1,20 +1,32 @@ #ifndef POINCARE_MATRIX_TRACE_H #define POINCARE_MATRIX_TRACE_H -#include +#include +#include +#include namespace Poincare { -class MatrixTrace : public Function { +class MatrixTrace : public StaticHierarchy<1> { + using StaticHierarchy<1>::StaticHierarchy; public: - MatrixTrace(); Type type() const override; - Expression * cloneWithDifferentOperands(Expression ** newOperands, - int numberOfOperands, bool cloneOperands = true) const override; + Expression * clone() const override; private: - Evaluation * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - Evaluation * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - template Evaluation * templatedEvaluate(Context& context, AngleUnit angleUnit) const; + /* Layout */ + ExpressionLayout * privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const override { + return LayoutEngine::createPrefixLayout(this, floatDisplayMode, complexFormat, name()); + } + int writeTextInBuffer(char * buffer, int bufferSize) const override { + return LayoutEngine::writePrefixExpressionTextInBuffer(this, buffer, bufferSize, name()); + } + const char * name() const { return "trace"; } + /* Simplification */ + Expression * shallowReduce(Context & context, AngleUnit angleUnit) override; + /* Evaluation */ + Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } + Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } + template Expression * templatedEvaluate(Context& context, AngleUnit angleUnit) const; }; } diff --git a/poincare/include/poincare/matrix_transpose.h b/poincare/include/poincare/matrix_transpose.h index d1842657c..13f27bb00 100644 --- a/poincare/include/poincare/matrix_transpose.h +++ b/poincare/include/poincare/matrix_transpose.h @@ -1,20 +1,32 @@ #ifndef POINCARE_MATRIX_TRANSPOSE_H #define POINCARE_MATRIX_TRANSPOSE_H -#include +#include +#include +#include namespace Poincare { -class MatrixTranspose : public Function { +class MatrixTranspose : public StaticHierarchy<1> { + using StaticHierarchy<1>::StaticHierarchy; public: - MatrixTranspose(); Type type() const override; - Expression * cloneWithDifferentOperands(Expression ** newOperands, - int numberOfOperands, bool cloneOperands = true) const override; + Expression * clone() const override; private: - Evaluation * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - Evaluation * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - template Evaluation * templatedEvaluate(Context& context, AngleUnit angleUnit) const; + /* Layout */ + ExpressionLayout * privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const override { + return LayoutEngine::createPrefixLayout(this, floatDisplayMode, complexFormat, name()); + } + int writeTextInBuffer(char * buffer, int bufferSize) const override { + return LayoutEngine::writePrefixExpressionTextInBuffer(this, buffer, bufferSize, name()); + } + const char * name() const { return "transpose"; } + /* Simplification */ + Expression * shallowReduce(Context & context, AngleUnit angleUnit) override; + /* Evaluation */ + Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } + Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } + template Expression * templatedEvaluate(Context& context, AngleUnit angleUnit) const; }; } diff --git a/poincare/include/poincare/multiplication.h b/poincare/include/poincare/multiplication.h index 221e6b510..a4e6e1358 100644 --- a/poincare/include/poincare/multiplication.h +++ b/poincare/include/poincare/multiplication.h @@ -1,34 +1,65 @@ #ifndef POINCARE_MULTIPLICATION_H #define POINCARE_MULTIPLICATION_H -#include +#include +#include +#include namespace Poincare { -class Multiplication : public BinaryOperation { - using BinaryOperation::BinaryOperation; +class Multiplication : public DynamicHierarchy { + using DynamicHierarchy::DynamicHierarchy; + friend class Addition; + friend class Division; + friend class Logarithm; + friend class Opposite; + friend class Power; + friend class Subtraction; public: Type type() const override; - Expression * cloneWithDifferentOperands(Expression** newOperands, - int numnerOfOperands, bool cloneOperands = true) const override; - template static Evaluation * computeOnMatrices(Evaluation * m, Evaluation * n); - template static Evaluation * computeOnComplexAndMatrix(const Complex * c, Evaluation * m); + Expression * clone() const override; + Sign sign() const override; + /* Evaluation */ template static Complex compute(const Complex c, const Complex d); + template static Matrix * computeOnComplexAndMatrix(const Complex * c, const Matrix * m) { + return EvaluationEngine::elementWiseOnComplexAndComplexMatrix(c, m, compute); + } + template static Matrix * computeOnMatrices(const Matrix * m, const Matrix * n); private: + /* Property */ + Expression * setSign(Sign s, Context & context, AngleUnit angleUnit) override; + /* Layout */ ExpressionLayout * privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const override; + int writeTextInBuffer(char * buffer, int bufferSize) const override; + /* Simplification */ + Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; + Expression * privateShallowReduce(Context& context, AngleUnit angleUnit, bool expand); + void factorizeBase(Expression * e1, Expression * e2, Context & context, AngleUnit angleUnit); + void factorizeExponent(Expression * e1, Expression * e2, Context & context, AngleUnit angleUnit); + Expression * distributeOnOperandAtIndex(int index, Context & context, AngleUnit angleUnit); + Expression * cloneDenominator(Context & context, AngleUnit angleUnit) const override; + void addMissingFactors(Expression * factor, Context & context, AngleUnit angleUnit); + void factorizeSineAndCosine(Expression * o1, Expression * o2, Context & context, AngleUnit angleUnit); + static bool HaveSameNonRationalFactors(const Expression * e1, const Expression * e2); + static bool TermsHaveIdenticalBase(const Expression * e1, const Expression * e2); + static bool TermsHaveIdenticalExponent(const Expression * e1, const Expression * e2); + static bool TermHasRationalBase(const Expression * e); + static bool TermHasIntegerExponent(const Expression * e); + static bool TermHasRationalExponent(const Expression * e); + static const Expression * CreateExponent(Expression * e); + Expression * shallowBeautify(Context & context, AngleUnit angleUnit) override; + // Warning: mergeNegativePower not always returns a multiplication: *(b^-1,c^-1) -> (bc)^-1 + Expression * mergeNegativePower(Context & context, AngleUnit angleUnit); + /* Evaluation */ - Evaluation * computeOnComplexMatrices(Evaluation * m, Evaluation * n) const override { - return computeOnMatrices(m, n); + template static Matrix * computeOnMatrixAndComplex(const Matrix * m, const Complex * c) { + return EvaluationEngine::elementWiseOnComplexAndComplexMatrix(c, m, compute); } - Evaluation * computeOnComplexMatrices(Evaluation * m, Evaluation * n) const override { - return computeOnMatrices(m, n); + Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { + return EvaluationEngine::mapReduce(this, context, angleUnit, compute, computeOnComplexAndMatrix, computeOnMatrixAndComplex, computeOnMatrices); } - - Complex privateCompute(const Complex c, const Complex d) const override { - return compute(c, d); - } - Complex privateCompute(const Complex c, const Complex d) const override { - return compute(c, d); + Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { + return EvaluationEngine::mapReduce(this, context, angleUnit, compute, computeOnComplexAndMatrix, computeOnMatrixAndComplex, computeOnMatrices); } }; diff --git a/poincare/include/poincare/naperian_logarithm.h b/poincare/include/poincare/naperian_logarithm.h index e89680345..5034cd456 100644 --- a/poincare/include/poincare/naperian_logarithm.h +++ b/poincare/include/poincare/naperian_logarithm.h @@ -1,24 +1,36 @@ #ifndef POINCARE_NAPERIAN_LOGARITHM_H #define POINCARE_NAPERIAN_LOGARITHM_H -#include +#include +#include +#include namespace Poincare { -class NaperianLogarithm : public Function { +class NaperianLogarithm : public StaticHierarchy<1> { + using StaticHierarchy<1>::StaticHierarchy; public: - NaperianLogarithm(); Type type() const override; - Expression * cloneWithDifferentOperands(Expression ** newOperands, - int numberOfOperands, bool cloneOperands = true) const override; + Expression * clone() const override; private: - Complex computeComplex(const Complex c, AngleUnit angleUnit) const override { - return templatedComputeComplex(c); + /* Layout */ + ExpressionLayout * privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const override { + return LayoutEngine::createPrefixLayout(this, floatDisplayMode, complexFormat, name()); } - Complex computeComplex(const Complex c, AngleUnit angleUnit) const override { - return templatedComputeComplex(c); + int writeTextInBuffer(char * buffer, int bufferSize) const override { + return LayoutEngine::writePrefixExpressionTextInBuffer(this, buffer, bufferSize, name()); + } + const char * name() const { return "ln"; } + /* Simplification */ + Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; + /* Evaluation */ + template static Complex computeOnComplex(const Complex c, AngleUnit angleUnit); + Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { + return EvaluationEngine::map(this, context, angleUnit,computeOnComplex); + } + Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { + return EvaluationEngine::map(this, context, angleUnit, computeOnComplex); } - template Complex templatedComputeComplex(const Complex c) const; }; } diff --git a/poincare/include/poincare/nth_root.h b/poincare/include/poincare/nth_root.h index f494fe33f..3455772d0 100644 --- a/poincare/include/poincare/nth_root.h +++ b/poincare/include/poincare/nth_root.h @@ -1,22 +1,31 @@ #ifndef POINCARE_NTH_ROOT_H #define POINCARE_NTH_ROOT_H -#include +#include +#include +#include namespace Poincare { -class NthRoot : public Function { +class NthRoot : public StaticHierarchy<2> { + using StaticHierarchy<2>::StaticHierarchy; public: - NthRoot(); Type type() const override; - Expression * cloneWithDifferentOperands(Expression ** newOperands, - int numberOfOperands, bool cloneOperands = true) const override; + Expression * clone() const override; private: - Evaluation * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - Evaluation * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - template Evaluation * templatedEvaluate(Context& context, AngleUnit angleUnit) const; + /* Layout */ ExpressionLayout * privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const override; - template Complex compute(const Complex c, const Complex d) const; + int writeTextInBuffer(char * buffer, int bufferSize) const override { + return LayoutEngine::writePrefixExpressionTextInBuffer(this, buffer, bufferSize, "root"); + } + /* Simplification */ + Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; + /* Evaluation */ + template static Complex compute(const Complex c, const Complex d); + Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } + Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } + template Expression * templatedEvaluate(Context& context, AngleUnit angleUnit) const; + }; } diff --git a/poincare/include/poincare/opposite.h b/poincare/include/poincare/opposite.h index e54093ad6..954409bfc 100644 --- a/poincare/include/poincare/opposite.h +++ b/poincare/include/poincare/opposite.h @@ -1,34 +1,31 @@ #ifndef POINCARE_OPPOSITE_H #define POINCARE_OPPOSITE_H -#include -#include +#include +#include +#include namespace Poincare { -class Opposite : public Expression { +class Opposite : public StaticHierarchy<1> { + using StaticHierarchy<1>::StaticHierarchy; public: - Opposite(Expression * operand, bool cloneOperands = true); - ~Opposite(); - Opposite(const Opposite& other) = delete; - Opposite(Opposite&& other) = delete; - Opposite& operator=(const Opposite& other) = delete; - Opposite& operator=(Opposite&& other) = delete; - bool hasValidNumberOfArguments() const override; - const Expression * operand(int i) const override; - int numberOfOperands() const override; Expression * clone() const override; Type type() const override; - Expression * cloneWithDifferentOperands(Expression** newOperands, - int numnerOfOperands, bool cloneOperands = true) const override; - template static Complex compute(const Complex c); - template static Evaluation * computeOnMatrix(Evaluation * m); + template static Complex compute(const Complex c, AngleUnit angleUnit); private: - Evaluation * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - Evaluation * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - template Evaluation * templatedEvaluate(Context& context, AngleUnit angleUnit) const; + /* Layout */ ExpressionLayout * privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const override; - Expression * m_operand; + int writeTextInBuffer(char * buffer, int bufferSize) const override; + /* Simplification */ + Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; + /* Evaluation */ + Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { + return EvaluationEngine::map(this, context, angleUnit, compute); + } + Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { + return EvaluationEngine::map(this, context, angleUnit, compute); + } }; } diff --git a/poincare/include/poincare/parenthesis.h b/poincare/include/poincare/parenthesis.h index 5b9b8178d..93c61a0be 100644 --- a/poincare/include/poincare/parenthesis.h +++ b/poincare/include/poincare/parenthesis.h @@ -1,31 +1,30 @@ #ifndef POINCARE_PARENTHESIS_H #define POINCARE_PARENTHESIS_H -#include +#include +#include namespace Poincare { -class Parenthesis : public Expression { +class Parenthesis : public StaticHierarchy<1> { +public: + using StaticHierarchy<1>::StaticHierarchy; public: - Parenthesis(Expression * operand, bool cloneOperands = true); - ~Parenthesis(); - Parenthesis(const Parenthesis& other) = delete; - Parenthesis(Parenthesis&& other) = delete; - Parenthesis& operator=(const Parenthesis& other) = delete; - Parenthesis& operator=(Parenthesis&& other) = delete; - bool hasValidNumberOfArguments() const override; - const Expression * operand(int i) const override; - int numberOfOperands() const override; Expression * clone() const override; Type type() const override; - Expression * cloneWithDifferentOperands(Expression** newOperands, - int numnerOfOperands, bool cloneOperands = true) const override; private: + /* Layout */ ExpressionLayout * privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const override; - Evaluation * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - Evaluation * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - template Evaluation * templatedEvaluate(Context& context, AngleUnit angleUnit) const; - Expression * m_operand; + int writeTextInBuffer(char * buffer, int bufferSize) const override { + return LayoutEngine::writePrefixExpressionTextInBuffer(this, buffer, bufferSize, ""); + } + /* Simplification */ + Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; + /* Evaluation */ + Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } + Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } + template Expression * templatedEvaluate(Context& context, AngleUnit angleUnit) const; + }; } diff --git a/poincare/include/poincare/permute_coefficient.h b/poincare/include/poincare/permute_coefficient.h index 82e2f1140..0cf54c594 100644 --- a/poincare/include/poincare/permute_coefficient.h +++ b/poincare/include/poincare/permute_coefficient.h @@ -1,20 +1,33 @@ #ifndef POINCARE_PERMUTE_COEFFICIENT_H #define POINCARE_PERMUTE_COEFFICIENT_H -#include +#include +#include +#include namespace Poincare { -class PermuteCoefficient : public Function { +class PermuteCoefficient : public StaticHierarchy<2> { + using StaticHierarchy<2>::StaticHierarchy; public: - PermuteCoefficient(); Type type() const override; - Expression * cloneWithDifferentOperands(Expression ** newOperands, - int numberOfOperands, bool cloneOperands = true) const override; + Expression * clone() const override; private: - Evaluation * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - Evaluation * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - template Evaluation * templatedEvaluate(Context& context, AngleUnit angleUnit) const; + constexpr static int k_maxNValue = 100; + /* Layout */ + ExpressionLayout * privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const override { + return LayoutEngine::createPrefixLayout(this, floatDisplayMode, complexFormat, name()); + } + int writeTextInBuffer(char * buffer, int bufferSize) const override { + return LayoutEngine::writePrefixExpressionTextInBuffer(this, buffer, bufferSize, name()); + } + const char * name() const { return "permute"; } + /* Simplification */ + Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; + /* Evaluation */ + Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } + Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } + template Complex * templatedEvaluate(Context& context, AngleUnit angleUnit) const; }; } diff --git a/poincare/include/poincare/power.h b/poincare/include/poincare/power.h index b86b7d997..ca2e54e12 100644 --- a/poincare/include/poincare/power.h +++ b/poincare/include/poincare/power.h @@ -1,49 +1,64 @@ #ifndef POINCARE_POWER_H #define POINCARE_POWER_H -#include +#include +#include +#include +#include namespace Poincare { -class Power : public BinaryOperation { - using BinaryOperation::BinaryOperation; +class Power : public StaticHierarchy<2> { + using StaticHierarchy<2>::StaticHierarchy; + friend class Multiplication; + friend class NthRoot; + friend class SquareRoot; + friend class Addition; public: Type type() const override; - Expression * cloneWithDifferentOperands(Expression** newOperands, - int numnerOfOperands, bool cloneOperands = true) const override; + Expression * clone() const override; + Sign sign() const override; template static Complex compute(const Complex c, const Complex d); private: - constexpr static float k_maxNumberOfSteps = 10000.0f; + /* Property */ + Expression * setSign(Sign s, Context & context, AngleUnit angleUnit) override; + /* Layout */ ExpressionLayout * privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const override; - Complex privateCompute(const Complex c, const Complex d) const override { - return compute(c, d); + int writeTextInBuffer(char * buffer, int bufferSize) const override { + return LayoutEngine::writeInfixExpressionTextInBuffer(this, buffer, bufferSize, name(), [](const Expression * e) { + return e->type() == Type::Division || e->type() == Type::Multiplication || e->type() == Type::Addition || e->type() == Type::Subtraction || e->type() == Type::Opposite; + }); } - Complex privateCompute(const Complex c, const Complex d) const override { - return compute(c, d); + static const char * name() { return "^"; } + /* Simplify */ + Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; + Expression * shallowBeautify(Context & context, AngleUnit angleUnit) override; + int simplificationOrderGreaterType(const Expression * e) const override; + int simplificationOrderSameType(const Expression * e) const override; + Expression * simplifyPowerPower(Power * p, Expression * r, Context & context, AngleUnit angleUnit); + Expression * cloneDenominator(Context & context, AngleUnit angleUnit) const override; + Expression * simplifyPowerMultiplication(Multiplication * m, Expression * r, Context & context, AngleUnit angleUnit); + Expression * simplifyRationalRationalPower(Expression * result, Rational * a, Rational * b, Context & context, AngleUnit angleUnit); + Expression * removeSquareRootsFromDenominator(Context & context, AngleUnit angleUnit); + bool parentIsALogarithmOfSameBase() const; + bool isNthRootOfUnity() const; + static Expression * CreateSimplifiedIntegerRationalPower(Integer i, Rational * r, bool isDenominator, Context & context, AngleUnit angleUnit); + static Expression * CreateNthRootOfUnity(const Rational r); + static bool TermIsARationalSquareRootOrRational(const Expression * e); + static const Rational * RadicandInExpression(const Expression * e); + static const Rational * RationalFactorInExpression(const Expression * e); + /* Evaluation */ + constexpr static int k_maxApproximatePowerMatrix = 1000; + constexpr static int k_maxExactPowerMatrix = 100; + template static Matrix * computeOnComplexAndMatrix(const Complex * c, const Matrix * n) { return nullptr; } + template static Matrix * computeOnMatrixAndComplex(const Matrix * m, const Complex * d); + template static Matrix * computeOnMatrices(const Matrix * m, const Matrix * n) { return nullptr; } + Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { + return EvaluationEngine::mapReduce(this, context, angleUnit, compute, computeOnComplexAndMatrix, computeOnMatrixAndComplex, computeOnMatrices); } - Evaluation * computeOnComplexAndComplexMatrix(const Complex * c, Evaluation * n) const override { - return templatedComputeOnComplexAndComplexMatrix(c, n); + Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { + return EvaluationEngine::mapReduce(this, context, angleUnit, compute, computeOnComplexAndMatrix, computeOnMatrixAndComplex, computeOnMatrices); } - Evaluation * computeOnComplexAndComplexMatrix(const Complex * c, Evaluation * n) const override { - return templatedComputeOnComplexAndComplexMatrix(c, n); - } - template Evaluation * templatedComputeOnComplexAndComplexMatrix(const Complex * c, Evaluation * n) const; - - Evaluation * computeOnComplexMatrixAndComplex(Evaluation * m, const Complex * d) const override { - return templatedComputeOnComplexMatrixAndComplex(m, d); - } - Evaluation * computeOnComplexMatrixAndComplex(Evaluation * m, const Complex * d) const override { - return templatedComputeOnComplexMatrixAndComplex(m, d); - } - template Evaluation * templatedComputeOnComplexMatrixAndComplex(Evaluation * m, const Complex * d) const; - - Evaluation * computeOnComplexMatrices(Evaluation * m, Evaluation * n) const override { - return templatedComputeOnComplexMatrices(m, n); - } - Evaluation * computeOnComplexMatrices(Evaluation * m, Evaluation * n) const override { - return templatedComputeOnComplexMatrices(m, n); - } - template Evaluation * templatedComputeOnComplexMatrices(Evaluation * m, Evaluation * n) const; }; } diff --git a/poincare/include/poincare/prediction_interval.h b/poincare/include/poincare/prediction_interval.h index 9d3b4c2d7..bef5d3963 100644 --- a/poincare/include/poincare/prediction_interval.h +++ b/poincare/include/poincare/prediction_interval.h @@ -1,20 +1,31 @@ #ifndef POINCARE_PREDICTION_INTERVAL_H #define POINCARE_PREDICTION_INTERVAL_H -#include +#include +#include namespace Poincare { -class PredictionInterval : public Function { +class PredictionInterval : public StaticHierarchy<2> { + using StaticHierarchy<2>::StaticHierarchy; public: - PredictionInterval(); Type type() const override; - Expression * cloneWithDifferentOperands(Expression ** newOperands, - int numberOfOperands, bool cloneOperands = true) const override; + Expression * clone() const override; private: - Evaluation * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - Evaluation * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - template Evaluation * templatedEvaluate(Context& context, AngleUnit angleUnit) const; + /* Layout */ + ExpressionLayout * privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const override { + return LayoutEngine::createPrefixLayout(this, floatDisplayMode, complexFormat, name()); + } + int writeTextInBuffer(char * buffer, int bufferSize) const override { + return LayoutEngine::writePrefixExpressionTextInBuffer(this, buffer, bufferSize, name()); + } + const char * name() const { return "prediction95"; } + /* Simplification */ + Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; + /* Evaluation */ + Expression * privateEvaluate(Expression::SinglePrecision p, Context& context, Expression::AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } + Expression * privateEvaluate(Expression::DoublePrecision p, Context& context, Expression::AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } + template Expression * templatedEvaluate(Context& context, AngleUnit angleUnit) const; }; } diff --git a/poincare/include/poincare/product.h b/poincare/include/poincare/product.h index 9940d1e3c..031b85bf7 100644 --- a/poincare/include/poincare/product.h +++ b/poincare/include/poincare/product.h @@ -6,21 +6,21 @@ namespace Poincare { class Product : public Sequence { + using Sequence::Sequence; public: - Product(); Type type() const override; - Expression * cloneWithDifferentOperands(Expression ** newOperands, - int numberOfOperands, bool cloneOperands = true) const override; + Expression * clone() const override; private: + const char * name() const override; int emptySequenceValue() const override; ExpressionLayout * createSequenceLayoutWithArgumentLayouts(ExpressionLayout * subscriptLayout, ExpressionLayout * superscriptLayout, ExpressionLayout * argumentLayout) const override; - Evaluation * evaluateWithNextTerm(Evaluation * a, Evaluation * b) const override { - return templatedEvaluateWithNextTerm(a, b); + Expression * evaluateWithNextTerm(DoublePrecision p, Expression * a, Expression * b) const override { + return templatedEvaluateWithNextTerm(a, b); } - Evaluation * evaluateWithNextTerm(Evaluation * a, Evaluation * b) const override { - return templatedEvaluateWithNextTerm(a, b); + Expression * evaluateWithNextTerm(SinglePrecision p, Expression * a, Expression * b) const override { + return templatedEvaluateWithNextTerm(a, b); } - template Evaluation * templatedEvaluateWithNextTerm(Evaluation * a, Evaluation * b) const; + template Expression * templatedEvaluateWithNextTerm(Expression * a, Expression * b) const; }; } diff --git a/poincare/include/poincare/rational.h b/poincare/include/poincare/rational.h new file mode 100644 index 000000000..776298378 --- /dev/null +++ b/poincare/include/poincare/rational.h @@ -0,0 +1,65 @@ +#ifndef POINCARE_RATIONAL_H +#define POINCARE_RATIONAL_H + +#include +#include +#include + +namespace Poincare { + +class Rational : public StaticHierarchy<0> { + friend class Power; + friend class Multiplication; +public: + /* The constructor build a irreductible fraction whose sign is on numerator */ + Rational(const Integer numerator, const Integer denominator); + Rational(const Integer numerator); + Rational(Integer::native_int_t i) : Rational(Integer(i)) {} + Rational(Integer::native_int_t i, Integer::native_int_t j) : Rational(Integer(i), Integer(j)) {} + + Rational(const Rational & other); + Rational & operator=(const Rational & other); + // Getter + const Integer numerator() const; + const Integer denominator() const; + // Expression subclassing + Type type() const override; + Expression * clone() const override; + Sign sign() const override; + Expression * cloneDenominator(Context & context, AngleUnit angleUnit) const override; + + // Basic test + bool isZero() const { return m_numerator.isZero(); } + bool isOne() const { return m_numerator.isOne() && m_denominator.isOne(); } + bool isMinusOne() const { return m_numerator.isMinusOne() && m_denominator.isOne(); } + bool isHalf() const { return m_numerator.isOne() && m_denominator.isTwo(); } + bool isMinusHalf() const { return m_numerator.isMinusOne() && m_denominator.isTwo(); } + bool isTen() const { return m_numerator.isTen() && m_denominator.isOne(); } + + // Arithmetic + static Rational Addition(const Rational & i, const Rational & j); + static Rational Multiplication(const Rational & i, const Rational & j); + static Rational Power(const Rational & i, const Integer & j); + static int NaturalOrder(const Rational & i, const Rational & j); +private: + ExpressionLayout * privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const override; + int writeTextInBuffer(char * buffer, int bufferSize) const override; + Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } + Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } + template Complex * templatedEvaluate(Context& context, Expression::AngleUnit angleUnit) const; + Expression * shallowBeautify(Context & context, AngleUnit angleUnit) override; + Expression * setSign(Sign s); + Expression * setSign(Sign s, Context & context, AngleUnit angleUnit) override { + return setSign(s); + } + + /* Sorting */ + int simplificationOrderSameType(const Expression * e) const override; + + Integer m_numerator; + Integer m_denominator; +}; + +} + +#endif diff --git a/poincare/include/poincare/real_part.h b/poincare/include/poincare/real_part.h index 4634fb655..bcc7b2424 100644 --- a/poincare/include/poincare/real_part.h +++ b/poincare/include/poincare/real_part.h @@ -1,24 +1,36 @@ -#ifndef POINCARE_REAL_PART_H -#define POINCARE_REAL_PART_H +#ifndef POINCARE_REEL_PART_H +#define POINCARE_REEL_PART_H -#include +#include +#include +#include namespace Poincare { -class RealPart : public Function { +class RealPart : public StaticHierarchy<1> { + using StaticHierarchy<1>::StaticHierarchy; public: - RealPart(); Type type() const override; - Expression * cloneWithDifferentOperands(Expression ** newOperands, - int numberOfOperands, bool cloneOperands = true) const override; + Expression * clone() const override; private: - Complex computeComplex(const Complex c, AngleUnit angleUnit) const override { - return templatedComputeComplex(c); + /* Layout */ + ExpressionLayout * privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const override { + return LayoutEngine::createPrefixLayout(this, floatDisplayMode, complexFormat, name()); } - Complex computeComplex(const Complex c, AngleUnit angleUnit) const override { - return templatedComputeComplex(c); + int writeTextInBuffer(char * buffer, int bufferSize) const override { + return LayoutEngine::writePrefixExpressionTextInBuffer(this, buffer, bufferSize, name()); + } + const char * name() const { return "re"; } + /* Simplification */ + Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; + /* Evaluation */ + template static Complex computeOnComplex(const Complex c, AngleUnit angleUnit); + Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { + return EvaluationEngine::map(this, context, angleUnit,computeOnComplex); + } + Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { + return EvaluationEngine::map(this, context, angleUnit, computeOnComplex); } - template Complex templatedComputeComplex(const Complex c) const; }; } diff --git a/poincare/include/poincare/round.h b/poincare/include/poincare/round.h index cddf9d34f..b9353bb7a 100644 --- a/poincare/include/poincare/round.h +++ b/poincare/include/poincare/round.h @@ -1,20 +1,32 @@ #ifndef POINCARE_ROUND_H #define POINCARE_ROUND_H -#include +#include +#include +#include namespace Poincare { -class Round : public Function { +class Round : public StaticHierarchy<2> { + using StaticHierarchy<2>::StaticHierarchy; public: - Round(); Type type() const override; - Expression * cloneWithDifferentOperands(Expression ** newOperands, - int numberOfOperands, bool cloneOperands = true) const override; + Expression * clone() const override; private: - Evaluation * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - Evaluation * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - template Evaluation * templatedEvaluate(Context& context, AngleUnit angleUnit) const; + /* Layout */ + ExpressionLayout * privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const override { + return LayoutEngine::createPrefixLayout(this, floatDisplayMode, complexFormat, name()); + } + int writeTextInBuffer(char * buffer, int bufferSize) const override { + return LayoutEngine::writePrefixExpressionTextInBuffer(this, buffer, bufferSize, name()); + } + const char * name() const { return "round"; } + /* Simplification */ + Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; + /* Complex */ + Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } + Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } + template Complex * templatedEvaluate(Context& context, AngleUnit angleUnit) const; }; } diff --git a/poincare/include/poincare/sequence.h b/poincare/include/poincare/sequence.h index 2ab73080f..503b5529c 100644 --- a/poincare/include/poincare/sequence.h +++ b/poincare/include/poincare/sequence.h @@ -1,23 +1,28 @@ #ifndef POINCARE_SEQUENCE_H #define POINCARE_SEQUENCE_H -#include +#include +#include +#include namespace Poincare { -class Sequence : public Function { -public: - Sequence(const char * name); +class Sequence : public StaticHierarchy<3> { + using StaticHierarchy<3>::StaticHierarchy; private: - constexpr static float k_maxNumberOfSteps = 10000.0f; ExpressionLayout * privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const override; - Evaluation * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - Evaluation * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - template Evaluation * templatedEvaluate(Context& context, AngleUnit angleUnit) const; + int writeTextInBuffer(char * buffer, int bufferSize) const override { + return LayoutEngine::writePrefixExpressionTextInBuffer(this, buffer, bufferSize, name()); + } virtual ExpressionLayout * createSequenceLayoutWithArgumentLayouts(ExpressionLayout * subscriptLayout, ExpressionLayout * superscriptLayout, ExpressionLayout * argumentLayout) const = 0; + virtual const char * name() const = 0; + /* Evaluation */ + Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } + Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } + template Expression * templatedEvaluate(Context& context, AngleUnit angleUnit) const; virtual int emptySequenceValue() const = 0; - virtual Evaluation * evaluateWithNextTerm(Evaluation * a, Evaluation * b) const = 0; - virtual Evaluation * evaluateWithNextTerm(Evaluation * a, Evaluation * b) const = 0; + virtual Expression * evaluateWithNextTerm(SinglePrecision p, Expression * a, Expression * b) const = 0; + virtual Expression * evaluateWithNextTerm(DoublePrecision p, Expression * a, Expression * b) const = 0; }; } diff --git a/poincare/include/poincare/simplification_engine.h b/poincare/include/poincare/simplification_engine.h new file mode 100644 index 000000000..95d22c72d --- /dev/null +++ b/poincare/include/poincare/simplification_engine.h @@ -0,0 +1,20 @@ +#ifndef POINCARE_SIMPLIFICATION_ENGINE_H +#define POINCARE_SIMPLIFICATION_ENGINE_H + +#if MATRIX_EXACT_REDUCING + +#include + +namespace Poincare { + +class SimplificationEngine { +public: + static Expression * map(Expression * e, Context & context, Expression::AngleUnit angleUnit); + +}; + +} + +#endif + +#endif diff --git a/poincare/include/poincare/simplification_root.h b/poincare/include/poincare/simplification_root.h new file mode 100644 index 000000000..033c3e5b9 --- /dev/null +++ b/poincare/include/poincare/simplification_root.h @@ -0,0 +1,40 @@ +#ifndef POINCARE_SIMPLIFICATION_ROOT_H +#define POINCARE_SIMPLIFICATION_ROOT_H + +#include + +namespace Poincare { + +class SimplificationRoot : public StaticHierarchy<1> { +public: + SimplificationRoot(Expression * e) : StaticHierarchy<1>(&e, false) { + e->setParent(this); + } + ~SimplificationRoot() { + if (m_operands[0] != nullptr) { + detachOperand(operand(0)); + } + /* We don't want to clone the expression provided at construction. + * So we don't want it to be deleted when we're destroyed (parent destructor). */ + } + Expression * clone() const override { return nullptr; } + Type type() const override { return Expression::Type::SimplificationRoot; } + ExpressionLayout * privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const override { + return nullptr; + } + int writeTextInBuffer(char * buffer, int bufferSize) const override { return 0; } + Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { + assert(false); + return nullptr; + } + Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { + assert(false); + return nullptr; + } +private: + Expression * shallowReduce(Context & context, AngleUnit angleUnit) override { return this; } +}; + +} + +#endif diff --git a/poincare/include/poincare/sine.h b/poincare/include/poincare/sine.h index be5c5bde0..300b224d6 100644 --- a/poincare/include/poincare/sine.h +++ b/poincare/include/poincare/sine.h @@ -1,23 +1,37 @@ #ifndef POINCARE_SINE_H #define POINCARE_SINE_H -#include +#include +#include +#include +#include namespace Poincare { -class Sine : public Function { +class Sine : public StaticHierarchy<1> { + using StaticHierarchy<1>::StaticHierarchy; + friend class Tangent; public: - Sine(); Type type() const override; - Expression * cloneWithDifferentOperands(Expression ** newOperands, - int numberOfOperands, bool cloneOperands = true) const override; - template static Complex compute(const Complex c, AngleUnit angleUnit = AngleUnit::Radian); + Expression * clone() const override; + template static Complex computeOnComplex(const Complex c, AngleUnit angleUnit = AngleUnit::Radian); private: - Complex computeComplex(const Complex c, AngleUnit angleUnit) const override { - return compute(c, angleUnit); + /* Layout */ + ExpressionLayout * privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const override { + return LayoutEngine::createPrefixLayout(this, floatDisplayMode, complexFormat, name()); } - Complex computeComplex(const Complex c, AngleUnit angleUnit) const override { - return compute(c, angleUnit); + int writeTextInBuffer(char * buffer, int bufferSize) const override { + return LayoutEngine::writePrefixExpressionTextInBuffer(this, buffer, bufferSize, name()); + } + const char * name() const { return "sin"; } + /* Simplication */ + Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; + /* Evaluation */ + Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { + return EvaluationEngine::map(this, context, angleUnit,computeOnComplex); + } + Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { + return EvaluationEngine::map(this, context, angleUnit, computeOnComplex); } }; diff --git a/poincare/include/poincare/square_root.h b/poincare/include/poincare/square_root.h index 798507c80..e567363cb 100644 --- a/poincare/include/poincare/square_root.h +++ b/poincare/include/poincare/square_root.h @@ -1,25 +1,32 @@ #ifndef POINCARE_SQUARE_ROOT_H #define POINCARE_SQUARE_ROOT_H -#include +#include +#include +#include namespace Poincare { -class SquareRoot : public Function { +class SquareRoot : public StaticHierarchy<1> { + using StaticHierarchy<1>::StaticHierarchy; public: - SquareRoot(); Type type() const override; - Expression * cloneWithDifferentOperands(Expression ** newOperands, - int numberOfOperands, bool cloneOperands = true) const override; + Expression * clone() const override; private: + /* Layout */ ExpressionLayout * privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const override; - Complex computeComplex(const Complex c, AngleUnit angleUnit) const override { - return templatedComputeComplex(c); + int writeTextInBuffer(char * buffer, int bufferSize) const override; + /* Simplification */ + Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; + /* Evaluation */ + template static Complex computeOnComplex(const Complex c, AngleUnit angleUnit); + Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { + return EvaluationEngine::map(this, context, angleUnit,computeOnComplex); } - Complex computeComplex(const Complex c, AngleUnit angleUnit) const override { - return templatedComputeComplex(c); + Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { + return EvaluationEngine::map(this, context, angleUnit, computeOnComplex); } - template Complex templatedComputeComplex(const Complex c) const; + }; } diff --git a/poincare/include/poincare/static_hierarchy.h b/poincare/include/poincare/static_hierarchy.h new file mode 100644 index 000000000..26c2e33a7 --- /dev/null +++ b/poincare/include/poincare/static_hierarchy.h @@ -0,0 +1,34 @@ +#ifndef POINCARE_STATIC_HIERARCHY_H +#define POINCARE_STATIC_HIERARCHY_H + +#include +#include + +namespace Poincare { + +template +class StaticHierarchy : public Hierarchy { +public: + StaticHierarchy(); + StaticHierarchy(const Expression * const * operands, bool cloneOperands = true); + StaticHierarchy(const Expression * expression, bool cloneOperands = true); // Specialized constructor for StaticHierarchy<1> + StaticHierarchy(const Expression * expression1, const Expression * expression2, bool cloneOperands = true); // Specialized constructor for StaticHierarchy<2> + ~StaticHierarchy(); + StaticHierarchy(const StaticHierarchy & other) = delete; + StaticHierarchy(StaticHierarchy && other) = delete; + StaticHierarchy& operator=(const StaticHierarchy & other) = delete; + StaticHierarchy& operator=(StaticHierarchy && other) = delete; + + virtual void setArgument(ListData * listData, int numberOfEntries, bool clone); + int numberOfOperands() const override { return T; } + const Expression * const * operands() const override { return m_operands; } + virtual bool hasValidNumberOfOperands(int numberOfOperands) const; +protected: + void build(const Expression * const * operands, int numberOfOperands, bool cloneOperands); + int simplificationOrderSameType(const Expression * e) const override; + const Expression * m_operands[T]; +}; + +} + +#endif diff --git a/poincare/include/poincare/store.h b/poincare/include/poincare/store.h index 60d5456fe..4cbc28795 100644 --- a/poincare/include/poincare/store.h +++ b/poincare/include/poincare/store.h @@ -3,31 +3,27 @@ #include #include +#include +#include namespace Poincare { -class Store : public Expression { +class Store : public StaticHierarchy<2> { public: - Store(Symbol * symbol, Expression * value, bool clone = true); - ~Store(); - Store(const Store& other) = delete; - Store(Store&& other) = delete; - Store& operator=(const Store& other) = delete; - Store& operator=(Store&& other) = delete; - bool hasValidNumberOfArguments() const override; + using StaticHierarchy<2>::StaticHierarchy; Type type() const override; - const Expression * operand(int i) const override; - int numberOfOperands() const override; Expression * clone() const override; - Expression * cloneWithDifferentOperands(Expression ** newOperands, - int numberOfOperands, bool cloneOperands = true) const override; private: + /* Layout */ ExpressionLayout * privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const override; - Evaluation * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - Evaluation * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - template Evaluation * templatedEvaluate(Context& context, AngleUnit angleUnit) const; - Symbol * m_symbol; - Expression * m_value; + int writeTextInBuffer(char * buffer, int bufferSize) const override; + /* Evalutation */ + Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } + Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } + template Expression * templatedEvaluate(Context& context, AngleUnit angleUnit) const; + + const Symbol * symbol() const { return static_cast(operand(1)); } + const Expression * value() const { return operand(0); } }; } diff --git a/poincare/include/poincare/subtraction.h b/poincare/include/poincare/subtraction.h index 15be7a073..2f249b9f1 100644 --- a/poincare/include/poincare/subtraction.h +++ b/poincare/include/poincare/subtraction.h @@ -1,33 +1,43 @@ #ifndef POINCARE_SUBSTRACTION_H #define POINCARE_SUBSTRACTION_H -#include +#include +#include +#include namespace Poincare { -class Subtraction : public BinaryOperation { - using BinaryOperation::BinaryOperation; +class Subtraction : public StaticHierarchy<2> { + using StaticHierarchy<2>::StaticHierarchy; public: Type type() const override; - Expression * cloneWithDifferentOperands(Expression** newOperands, - int numnerOfOperands, bool cloneOperands = true) const override; + Expression * clone() const override; template static Complex compute(const Complex c, const Complex d); private: - ExpressionLayout * privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const override; + /* Layout */ + ExpressionLayout * privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const override { + return LayoutEngine::createInfixLayout(this, floatDisplayMode, complexFormat, name()); + } + int writeTextInBuffer(char * buffer, int bufferSize) const override { + return LayoutEngine::writeInfixExpressionTextInBuffer(this, buffer, bufferSize, name()); + } + static const char * name() { return "-"; } + /* Simplification */ + Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; + /* Evaluation */ + template static Matrix * computeOnMatrixAndComplex(const Matrix * m, const Complex * c) { + return EvaluationEngine::elementWiseOnComplexAndComplexMatrix(c, m, compute); + } + template static Matrix * computeOnComplexAndMatrix(const Complex * c, const Matrix * n); + template static Matrix * computeOnMatrices(const Matrix * m, const Matrix * n) { + return EvaluationEngine::elementWiseOnComplexMatrices(m, n, compute); + } - Evaluation * computeOnComplexAndComplexMatrix(const Complex * c, Evaluation * n) const override { - return templatedComputeOnComplexAndComplexMatrix(c, n); + Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { + return EvaluationEngine::mapReduce(this, context, angleUnit, compute, computeOnComplexAndMatrix, computeOnMatrixAndComplex, computeOnMatrices); } - Evaluation * computeOnComplexAndComplexMatrix(const Complex * c, Evaluation * n) const override { - return templatedComputeOnComplexAndComplexMatrix(c, n); - } - template Evaluation * templatedComputeOnComplexAndComplexMatrix(const Complex * c, Evaluation * n) const; - - Complex privateCompute(const Complex c, const Complex d) const override { - return compute(c, d); - } - Complex privateCompute(const Complex c, const Complex d) const override { - return compute(c, d); + Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { + return EvaluationEngine::mapReduce(this, context, angleUnit, compute, computeOnComplexAndMatrix, computeOnMatrixAndComplex, computeOnMatrices); } }; diff --git a/poincare/include/poincare/sum.h b/poincare/include/poincare/sum.h index 94bf71c6b..b4ceff686 100644 --- a/poincare/include/poincare/sum.h +++ b/poincare/include/poincare/sum.h @@ -6,21 +6,21 @@ namespace Poincare { class Sum : public Sequence { + using Sequence::Sequence; public: - Sum(); Type type() const override; - Expression * cloneWithDifferentOperands(Expression ** newOperands, - int numberOfOperands, bool cloneOperands = true) const override; + Expression * clone() const override; private: + const char * name() const override; int emptySequenceValue() const override; ExpressionLayout * createSequenceLayoutWithArgumentLayouts(ExpressionLayout * subscriptLayout, ExpressionLayout * superscriptLayout, ExpressionLayout * argumentLayout) const override; - Evaluation * evaluateWithNextTerm(Evaluation * a, Evaluation * b) const override { - return templatedEvaluateWithNextTerm(a, b); + Expression * evaluateWithNextTerm(DoublePrecision p, Expression * a, Expression * b) const override { + return templatedEvaluateWithNextTerm(a, b); } - Evaluation * evaluateWithNextTerm(Evaluation * a, Evaluation * b) const override { - return templatedEvaluateWithNextTerm(a, b); + Expression * evaluateWithNextTerm(SinglePrecision p, Expression * a, Expression * b) const override { + return templatedEvaluateWithNextTerm(a, b); } - template Evaluation * templatedEvaluateWithNextTerm(Evaluation * a, Evaluation * b) const; + template Expression * templatedEvaluateWithNextTerm(Expression * a, Expression * b) const; }; } diff --git a/poincare/include/poincare/symbol.h b/poincare/include/poincare/symbol.h index e0fd7d1d4..d071272bb 100644 --- a/poincare/include/poincare/symbol.h +++ b/poincare/include/poincare/symbol.h @@ -1,11 +1,11 @@ #ifndef POINCARE_SYMBOL_H #define POINCARE_SYMBOL_H -#include +#include namespace Poincare { -class Symbol : public LeafExpression { +class Symbol : public StaticHierarchy<0> { public: enum SpecialSymbols : char { /* We can use characters from 1 to 31 as they do not correspond to usual @@ -30,16 +30,23 @@ public: }; static SpecialSymbols matrixSymbol(char index); Symbol(char name); - Type type() const override; + Symbol(Symbol&& other); // C++11 move constructor char name() const; + Type type() const override; Expression * clone() const override; - bool valueEquals(const Expression * e) const override; + Sign sign() const override; bool isMatrixSymbol() const; private: - Evaluation * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - Evaluation * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } - template Evaluation * templatedEvaluate(Context& context, AngleUnit angleUnit) const; + const char * textForSpecialSymbols(char name) const; + /* Comparison */ + int simplificationOrderSameType(const Expression * e) const override; + /* Layout */ ExpressionLayout * privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const override; + int writeTextInBuffer(char * buffer, int bufferSize) const override; + /* Evaluation */ + Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } + Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } + template Expression * templatedEvaluate(Context& context, AngleUnit angleUnit) const; const char m_name; }; diff --git a/poincare/include/poincare/tangent.h b/poincare/include/poincare/tangent.h index 8bc5bd777..04f70029e 100644 --- a/poincare/include/poincare/tangent.h +++ b/poincare/include/poincare/tangent.h @@ -1,24 +1,37 @@ #ifndef POINCARE_TANGENT_H #define POINCARE_TANGENT_H -#include +#include +#include +#include + namespace Poincare { -class Tangent : public Function { +class Tangent : public StaticHierarchy<1> { + using StaticHierarchy<1>::StaticHierarchy; public: - Tangent(); Type type() const override; - Expression * cloneWithDifferentOperands(Expression ** newOperands, - int numnerOfOperands, bool cloneOperands = true) const override; + Expression * clone() const override; private: - Complex computeComplex(const Complex c, AngleUnit angleUnit) const override { - return templatedComputeComplex(c, angleUnit); + /* Layout */ + ExpressionLayout * privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const override { + return LayoutEngine::createPrefixLayout(this, floatDisplayMode, complexFormat, name()); } - Complex computeComplex(const Complex c, AngleUnit angleUnit) const override { - return templatedComputeComplex(c, angleUnit); + int writeTextInBuffer(char * buffer, int bufferSize) const override { + return LayoutEngine::writePrefixExpressionTextInBuffer(this, buffer, bufferSize, name()); + } + const char * name() const { return "tan"; } + /* Simplication */ + Expression * shallowReduce(Context& context, AngleUnit angleUnit) override; + /* Evaluation */ + template static Complex computeOnComplex(const Complex c, AngleUnit angleUnit = AngleUnit::Radian); + Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { + return EvaluationEngine::map(this, context, angleUnit,computeOnComplex); + } + Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { + return EvaluationEngine::map(this, context, angleUnit, computeOnComplex); } - template Complex templatedComputeComplex(const Complex c, AngleUnit angleUnit) const; }; } diff --git a/poincare/include/poincare/trigonometry.h b/poincare/include/poincare/trigonometry.h new file mode 100644 index 000000000..5d7967571 --- /dev/null +++ b/poincare/include/poincare/trigonometry.h @@ -0,0 +1,23 @@ +#ifndef POINCARE_TRIGONOMETRY_H +#define POINCARE_TRIGONOMETRY_H + +#include + +namespace Poincare { + +class Trigonometry { +public: + enum class Function { + Cosine = 0, + Sine = 1, + }; + static Expression * shallowReduceDirectFunction(Expression * e, Context& context, Expression::AngleUnit angleUnit); + static Expression * shallowReduceInverseFunction(Expression * e, Context& context, Expression::AngleUnit angleUnit); + static bool ExpressionIsEquivalentToTangent(const Expression * e); + constexpr static int k_numberOfEntries = 37; + static Expression * table(const Expression * e, Expression::Type type, Context & context, Expression::AngleUnit angleUnit); // , Function f, bool inverse +}; + +} + +#endif diff --git a/poincare/include/poincare/undefined.h b/poincare/include/poincare/undefined.h new file mode 100644 index 000000000..11154047d --- /dev/null +++ b/poincare/include/poincare/undefined.h @@ -0,0 +1,25 @@ +#ifndef POINCARE_UNDEFINED_H +#define POINCARE_UNDEFINED_H + +#include +#include + +namespace Poincare { + +class Undefined : public StaticHierarchy<0> { +public: + Type type() const override; + Expression * clone() const override; + int writeTextInBuffer(char * buffer, int bufferSize) const override; +private: + /* Layout */ + ExpressionLayout * privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const override; + /* Evaluation */ + Expression * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } + Expression * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { return templatedEvaluate(context, angleUnit); } + template Complex * templatedEvaluate(Context& context, AngleUnit angleUnit) const; +}; + +} + +#endif diff --git a/poincare/include/poincare/variable_context.h b/poincare/include/poincare/variable_context.h index 8dfbfeeb9..bbd2d9ed2 100644 --- a/poincare/include/poincare/variable_context.h +++ b/poincare/include/poincare/variable_context.h @@ -10,7 +10,7 @@ template class VariableContext : public Context { public: VariableContext(char name, Context * parentContext = nullptr); - void setExpressionForSymbolName(Expression * expression, const Symbol * symbol) override; + void setExpressionForSymbolName(const Expression * expression, const Symbol * symbol, Context & context) override; const Expression * expressionForSymbol(const Symbol * symbol) override; private: char m_name; diff --git a/poincare/src/absolute_value.cpp b/poincare/src/absolute_value.cpp index 20fb6ac3f..62bbe0000 100644 --- a/poincare/src/absolute_value.cpp +++ b/poincare/src/absolute_value.cpp @@ -1,5 +1,6 @@ #include #include +#include #include "layout/absolute_value_layout.h" extern "C" { @@ -9,32 +10,50 @@ extern "C" { namespace Poincare { -AbsoluteValue::AbsoluteValue() : - Function("abs") -{ -} - Expression::Type AbsoluteValue::type() const { return Type::AbsoluteValue; } -Expression * AbsoluteValue::cloneWithDifferentOperands(Expression** newOperands, - int numberOfOperands, bool cloneOperands) const { - assert(newOperands != nullptr); - AbsoluteValue * a = new AbsoluteValue(); - a->setArgument(newOperands, numberOfOperands, cloneOperands); +Expression * AbsoluteValue::clone() const { + AbsoluteValue * a = new AbsoluteValue(m_operands, true); return a; } -template -Complex AbsoluteValue::templatedComputeComplex(const Complex c) const { - return Complex::Float(c.r()); +Expression * AbsoluteValue::setSign(Sign s, Context & context, AngleUnit angleUnit) { + assert(s == Sign::Positive); + return this; } ExpressionLayout * AbsoluteValue::privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const { assert(floatDisplayMode != FloatDisplayMode::Default); assert(complexFormat != ComplexFormat::Default); - return new AbsoluteValueLayout(m_args[0]->createLayout(floatDisplayMode, complexFormat)); + return new AbsoluteValueLayout(operand(0)->createLayout(floatDisplayMode, complexFormat)); +} + +Expression * AbsoluteValue::shallowReduce(Context& context, AngleUnit angleUnit) { + Expression * e = Expression::shallowReduce(context, angleUnit); + if (e != this) { + return e; + } + Expression * op = editableOperand(0); +#if MATRIX_EXACT_REDUCING + if (op->type() == Type::Matrix) { + return SimplificationEngine::map(this, context, angleUnit); + } +#endif + if (op->sign() == Sign::Positive) { + return replaceWith(op, true); + } + if (op->sign() == Sign::Negative) { + Expression * newOp = op->setSign(Sign::Positive, context, angleUnit); + return replaceWith(newOp, true); + } + return this; +} + +template +Complex AbsoluteValue::computeOnComplex(const Complex c, AngleUnit angleUnit) { + return Complex::Float(c.r()); } } diff --git a/poincare/src/addition.cpp b/poincare/src/addition.cpp index 8bfac4c7d..0580f915a 100644 --- a/poincare/src/addition.cpp +++ b/poincare/src/addition.cpp @@ -1,12 +1,14 @@ #include -#include +#include +#include +#include +#include +#include +#include extern "C" { #include #include } -#include "layout/horizontal_layout.h" -#include "layout/string_layout.h" -#include "layout/parenthesis_layout.h" namespace Poincare { @@ -14,51 +16,308 @@ Expression::Type Addition::type() const { return Type::Addition; } -ExpressionLayout * Addition::privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const { - assert(floatDisplayMode != FloatDisplayMode::Default); - assert(complexFormat != ComplexFormat::Default); - ExpressionLayout** children_layouts = new ExpressionLayout * [3]; - children_layouts[0] = m_operands[0]->createLayout(floatDisplayMode, complexFormat); - children_layouts[1] = new StringLayout("+", 1); - children_layouts[2] = m_operands[1]->type() == Type::Opposite ? new ParenthesisLayout(m_operands[1]->createLayout(floatDisplayMode, complexFormat)) : m_operands[1]->createLayout(floatDisplayMode, complexFormat); - ExpressionLayout * layout = new HorizontalLayout(children_layouts, 3); - delete[] children_layouts; - return layout; +Expression * Addition::clone() const { + if (numberOfOperands() == 0) { + return new Addition(); + } + return new Addition(operands(), numberOfOperands(), true); } +/* Simplication */ + +Expression * Addition::shallowReduce(Context& context, AngleUnit angleUnit) { + Expression * e = Expression::shallowReduce(context, angleUnit); + if (e != this) { + return e; + } + /* Step 1: Addition is associative, so let's start by merging children which + * also are additions themselves. */ + int i = 0; + int initialNumberOfOperands = numberOfOperands(); + while (i < initialNumberOfOperands) { + Expression * o = editableOperand(i); + if (o->type() == Type::Addition) { + mergeOperands(static_cast(o)); + continue; + } + i++; + } + + // Step 2: Sort the operands + sortOperands(Expression::SimplificationOrder); + +#if MATRIX_EXACT_REDUCING + /* Step 2bis: get rid of matrix */ + int n = 1; + int m = 1; + /* All operands have been simplified so if any operand contains a matrix, it + * is at the root node of the operand. Moreover, thanks to the simplification + * order, all matrix operands (if any) are the last operands. */ + Expression * lastOperand = editableOperand(numberOfOperands()-1); + if (lastOperand->type() == Type::Matrix) { + // Create in-place the matrix of addition M (in place of the last operand) + Matrix * resultMatrix = static_cast(lastOperand); + n = resultMatrix->numberOfRows(); + m = resultMatrix->numberOfColumns(); + removeOperand(resultMatrix, false); + /* Scan (starting at the end) accross the addition operands to find any + * other matrix */ + int i = numberOfOperands()-1; + while (i >= 0 && operand(i)->type() == Type::Matrix) { + Matrix * currentMatrix = static_cast(editableOperand(i)); + int on = currentMatrix->numberOfRows(); + int om = currentMatrix->numberOfColumns(); + if (on != n || om != m) { + return replaceWith(new Undefined(), true); + } + // Dispatch the current matrix operands in the created additions matrix + for (int j = 0; j < n*m; j++) { + Addition * a = new Addition(); + Expression * resultMatrixEntryJ = resultMatrix->editableOperand(j); + resultMatrix->replaceOperand(resultMatrixEntryJ, a, false); + a->addOperand(currentMatrix->editableOperand(j)); + a->addOperand(resultMatrixEntryJ); + a->shallowReduce(context, angleUnit); + } + currentMatrix->detachOperands(); + removeOperand(currentMatrix, true); + i--; + } + // Distribute the remaining addition on matrix operands + for (int i = 0; i < n*m; i++) { + Addition * a = static_cast(clone()); + Expression * entryI = resultMatrix->editableOperand(i); + resultMatrix->replaceOperand(entryI, a, false); + a->addOperand(entryI); + a->shallowReduce(context, angleUnit); + } + return replaceWith(resultMatrix, true)->shallowReduce(context, angleUnit); + } +#endif + + /* Step 3: Factorize like terms. Thanks to the simplification order, those are + * next to each other at this point. */ + i = 0; + while (i < numberOfOperands()-1) { + Expression * o1 = editableOperand(i); + Expression * o2 = editableOperand(i+1); + if (o1->type() == Type::Rational && o2->type() == Type::Rational) { + Rational r1 = *static_cast(o1); + Rational r2 = *static_cast(o2); + Rational a = Rational::Addition(r1, r2); + replaceOperand(o1, new Rational(a), true); + removeOperand(o2, true); + continue; + } + if (TermsHaveIdenticalNonRationalFactors(o1, o2)) { + factorizeOperands(o1, o2, context, angleUnit); + continue; + } + i++; + } + + /* Step 4: Let's remove zeroes if there's any. It's important to do this after + * having factorized because factorization can lead to new zeroes. For example + * pi+(-1)*pi. We don't remove the last zero if it's the only operand left + * though. */ + i = 0; + while (i < numberOfOperands()) { + Expression * o = editableOperand(i); + if (o->type() == Type::Rational && static_cast(o)->isZero() && numberOfOperands() > 1) { + removeOperand(o, true); + continue; + } + i++; + } + + // Step 5: Let's remove the addition altogether if it has a single operand + Expression * result = squashUnaryHierarchy(); + + // Step 6: Last but not least, let's put everything under a common denominator + if (result == this && parent()->type() != Type::Addition) { + // squashUnaryHierarchy didn't do anything: we're not an unary hierarchy + result = factorizeOnCommonDenominator(context, angleUnit); + } + + return result; +} + +Expression * Addition::factorizeOnCommonDenominator(Context & context, AngleUnit angleUnit) { + // We want to turn (a/b+c/d+e/b) into (a*d+b*c+e*d)/(b*d) + + // Step 1: We want to compute the common denominator, b*d + Multiplication * commonDenominator = new Multiplication(); + for (int i = 0; i < numberOfOperands(); i++) { + Expression * denominator = operand(i)->cloneDenominator(context, angleUnit); + if (denominator) { + // Make commonDenominator = LeastCommonMultiple(commonDenominator, denominator); + commonDenominator->addMissingFactors(denominator, context, angleUnit); + delete denominator; + } + } + if (commonDenominator->numberOfOperands() == 0) { + delete commonDenominator; + // If commonDenominator is empty this means that no operand was a fraction. + return this; + } + + // Step 2: Create the numerator. We start with this being a/b+c/d+e/b and we + // want to create numerator = a/b*b*d + c/d*b*d + e/b*b*d + Addition * numerator = new Addition(); + for (int i=0; i < numberOfOperands(); i++) { + numerator->addOperand(new Multiplication(operand(i), commonDenominator, true)); + } + // Step 3: Add the denominator + Power * inverseDenominator = new Power(commonDenominator, new Rational(-1), false); + Multiplication * result = new Multiplication(numerator, inverseDenominator, false); + + // Step 4: Simplify the numerator to a*d + c*b + e*d + numerator->deepReduce(context, angleUnit); + + // Step 5: Simplify the denominator (in case it's a rational number) + commonDenominator->deepReduce(context, angleUnit); + inverseDenominator->shallowReduce(context, angleUnit); + + /* Step 6: We simplify the resulting multiplication forbidding any + * distribution of multiplication on additions (to avoid an infinite loop). */ + return static_cast(replaceWith(result, true))->privateShallowReduce(context, angleUnit, false); +} + +void Addition::factorizeOperands(Expression * e1, Expression * e2, Context & context, AngleUnit angleUnit) { + /* This function factorizes two operands which only differ by a rational + * factor. For example, if this is Addition(2*pi, 3*pi), then 2*pi and 3*pi + * could be merged, and this turned into Addition(5*pi). */ + assert(e1->parent() == this && e2->parent() == this); + + // Step 1: Find the new rational factor + Rational * r = new Rational(Rational::Addition(RationalFactor(e1), RationalFactor(e2))); + + // Step 2: Get rid of one of the operands + removeOperand(e2, true); + + // Step 3: Use the new rational factor. Create a multiplication if needed + Multiplication * m = nullptr; + if (e1->type() == Type::Multiplication) { + m = static_cast(e1); + } else { + m = new Multiplication(); + e1->replaceWith(m, false); + m->addOperand(e1); + } + if (m->operand(0)->type() == Type::Rational) { + m->replaceOperand(m->operand(0), r, true); + } else { + m->addOperand(r); + } + + // Step 4: Reduce the multiplication (in case the new rational factor is zero) + m->shallowReduce(context, angleUnit); +} + +const Rational Addition::RationalFactor(Expression * e) { + if (e->type() == Type::Multiplication && e->operand(0)->type() == Type::Rational) { + return *(static_cast(e->operand(0))); + } + return Rational(1); +} + +static inline int NumberOfNonRationalFactors(const Expression * e) { + if (e->type() != Expression::Type::Multiplication) { + return 1; // Or (e->type() != Type::Rational); + } + int result = e->numberOfOperands(); + if (e->operand(0)->type() == Expression::Type::Rational) { + result--; + } + return result; +} + +static inline const Expression * FirstNonRationalFactor(const Expression * e) { + if (e->type() != Expression::Type::Multiplication) { + return e; + } + if (e->operand(0)->type() == Expression::Type::Rational) { + return e->numberOfOperands() > 1 ? e->operand(1) : nullptr; + } + return e->operand(0); +} + +bool Addition::TermsHaveIdenticalNonRationalFactors(const Expression * e1, const Expression * e2) { + /* Given two expressions, say wether they only differ by a rational factor. + * For example, 2*pi and pi do, 2*pi and 2*ln(2) don't. */ + + int numberOfNonRationalFactorsInE1 = NumberOfNonRationalFactors(e1); + int numberOfNonRationalFactorsInE2 = NumberOfNonRationalFactors(e2); + + if (numberOfNonRationalFactorsInE1 != numberOfNonRationalFactorsInE2) { + return false; + } + + int numberOfNonRationalFactors = numberOfNonRationalFactorsInE1; + if (numberOfNonRationalFactors == 1) { + return FirstNonRationalFactor(e1)->isIdenticalTo(FirstNonRationalFactor(e2)); + } else { + assert(numberOfNonRationalFactors > 1); + return Multiplication::HaveSameNonRationalFactors(e1, e2); + } +} + +Expression * Addition::shallowBeautify(Context & context, AngleUnit angleUnit) { + /* Beautifying Addition essentially consists in adding Subtractions if needed. + * In practice, we want to turn "a+(-1)*b" into "a-b". Or, more precisely, any + * "a+(-r)*b" into "a-r*b" where r is a positive Rational. + * Note: the process will slightly differ if the negative product occurs on + * the first term: we want to turn "Addition(Multiplication(-1,b))" into + * "Opposite(b)". + * Last but not least, special care must be taken when iterating over operands + * since we may remove some during the process. */ + + for (int i=0; itype() != Type::Multiplication || operand(i)->operand(0)->type() != Type::Rational || operand(i)->operand(0)->sign() != Sign::Negative) { + // Ignore terms which are not like "(-r)*a" + continue; + } + + Multiplication * m = static_cast(editableOperand(i)); + + if (static_cast(m->operand(0))->isMinusOne()) { + m->removeOperand(m->operand(0), true); + } else { + m->editableOperand(0)->setSign(Sign::Positive, context, angleUnit); + } + Expression * subtractant = m->squashUnaryHierarchy(); + + if (i == 0) { + Opposite * o = new Opposite(subtractant, true); + replaceOperand(subtractant, o, true); + } else { + const Expression * op1 = operand(i-1); + removeOperand(op1, false); + Subtraction * s = new Subtraction(op1, subtractant, false); + replaceOperand(subtractant, s, false); + /* CAUTION: we removed an operand. So we need to decrement i to make sure + * the next iteration is actually on the next operand. */ + i--; + } + } + + return squashUnaryHierarchy(); +} + +/* Evaluation */ + template Complex Addition::compute(const Complex c, const Complex d) { return Complex::Cartesian(c.a()+d.a(), c.b()+d.b()); } -template -Evaluation * Addition::computeOnMatrices(Evaluation * m, Evaluation * n) { - Addition a; - return a.computeOnComplexMatrices(m,n); -} +template Complex Poincare::Addition::compute(Poincare::Complex, Poincare::Complex); +template Complex Poincare::Addition::compute(Poincare::Complex, Poincare::Complex); -template -Evaluation * Addition::computeOnComplexAndMatrix(const Complex * c, Evaluation * m) { - Addition a; - return a.computeOnComplexAndComplexMatrix(c,m); -} +template Matrix* Addition::computeOnMatrices(const Matrix*,const Matrix*); +template Matrix* Addition::computeOnMatrices(const Matrix*,const Matrix*); -Expression * Addition::cloneWithDifferentOperands(Expression** newOperands, - int numberOfOperands, bool cloneOperands) const { - return new Addition(newOperands, cloneOperands); -} - -bool Addition::isCommutative() const { - return true; -} - -template Poincare::Complex Poincare::Addition::compute(Poincare::Complex, Poincare::Complex); -template Poincare::Complex Poincare::Addition::compute(Poincare::Complex, Poincare::Complex); - -template Poincare::Evaluation* Poincare::Addition::computeOnMatrices(Poincare::Evaluation*, Poincare::Evaluation*); -template Poincare::Evaluation* Poincare::Addition::computeOnMatrices(Poincare::Evaluation*, Poincare::Evaluation*); - -template Poincare::Evaluation* Poincare::Addition::computeOnComplexAndMatrix(Poincare::Complex const*, Poincare::Evaluation*); -template Poincare::Evaluation* Poincare::Addition::computeOnComplexAndMatrix(Poincare::Complex const*, Poincare::Evaluation*); +template Matrix* Addition::computeOnComplexAndMatrix(Complex const*, const Matrix*); +template Matrix* Addition::computeOnComplexAndMatrix(Complex const*, const Matrix*); } diff --git a/poincare/src/arc_cosine.cpp b/poincare/src/arc_cosine.cpp index ec22a2505..13fc28d2e 100644 --- a/poincare/src/arc_cosine.cpp +++ b/poincare/src/arc_cosine.cpp @@ -1,4 +1,6 @@ #include +#include +#include extern "C" { #include } @@ -6,25 +8,30 @@ extern "C" { namespace Poincare { -ArcCosine::ArcCosine() : - Function("acos") -{ -} - Expression::Type ArcCosine::type() const { return Type::ArcCosine; } -Expression * ArcCosine::cloneWithDifferentOperands(Expression** newOperands, - int numberOfOperands, bool cloneOperands) const { - assert(newOperands != nullptr); - ArcCosine * c = new ArcCosine(); - c->setArgument(newOperands, numberOfOperands, cloneOperands); - return c; +Expression * ArcCosine::clone() const { + ArcCosine * a = new ArcCosine(m_operands, true); + return a; +} + +Expression * ArcCosine::shallowReduce(Context& context, AngleUnit angleUnit) { + Expression * e = Expression::shallowReduce(context, angleUnit); + if (e != this) { + return e; + } +#if MATRIX_EXACT_REDUCING + if (operand(0)->type() == Type::Matrix) { + return SimplificationEngine::map(this, context, angleUnit); + } +#endif + return Trigonometry::shallowReduceInverseFunction(this, context, angleUnit); } template -Complex ArcCosine::templatedComputeComplex(const Complex c, AngleUnit angleUnit) const { +Complex ArcCosine::computeOnComplex(const Complex c, AngleUnit angleUnit) { assert(angleUnit != AngleUnit::Default); if (c.b() != 0) { return Complex::Float(NAN); diff --git a/poincare/src/arc_sine.cpp b/poincare/src/arc_sine.cpp index 61c774ad7..a593e92f1 100644 --- a/poincare/src/arc_sine.cpp +++ b/poincare/src/arc_sine.cpp @@ -1,4 +1,6 @@ #include +#include +#include extern "C" { #include } @@ -6,25 +8,30 @@ extern "C" { namespace Poincare { -ArcSine::ArcSine() : - Function("asin") -{ -} - Expression::Type ArcSine::type() const { return Type::ArcSine; } -Expression * ArcSine::cloneWithDifferentOperands(Expression** newOperands, - int numberOfOperands, bool cloneOperands) const { - assert(newOperands != nullptr); - ArcSine * s = new ArcSine(); - s->setArgument(newOperands, numberOfOperands, cloneOperands); - return s; +Expression * ArcSine::clone() const { + ArcSine * a = new ArcSine(m_operands, true); + return a; +} + +Expression * ArcSine::shallowReduce(Context& context, AngleUnit angleUnit) { + Expression * e = Expression::shallowReduce(context, angleUnit); + if (e != this) { + return e; + } +#if MATRIX_EXACT_REDUCING + if (operand(0)->type() == Type::Matrix) { + return SimplificationEngine::map(this, context, angleUnit); + } +#endif + return Trigonometry::shallowReduceInverseFunction(this, context, angleUnit); } template -Complex ArcSine::templatedComputeComplex(const Complex c, AngleUnit angleUnit) const { +Complex ArcSine::computeOnComplex(const Complex c, AngleUnit angleUnit) { assert(angleUnit != AngleUnit::Default); if (c.b() != 0) { return Complex::Float(NAN); diff --git a/poincare/src/arc_tangent.cpp b/poincare/src/arc_tangent.cpp index 24b90e046..7d5fd6d96 100644 --- a/poincare/src/arc_tangent.cpp +++ b/poincare/src/arc_tangent.cpp @@ -1,4 +1,6 @@ #include +#include +#include extern "C" { #include } @@ -6,25 +8,30 @@ extern "C" { namespace Poincare { -ArcTangent::ArcTangent() : - Function("atan") -{ -} - Expression::Type ArcTangent::type() const { return Type::ArcTangent; } -Expression * ArcTangent::cloneWithDifferentOperands(Expression** newOperands, - int numberOfOperands, bool cloneOperands) const { - assert(newOperands != nullptr); - ArcTangent * t = new ArcTangent(); - t->setArgument(newOperands, numberOfOperands, cloneOperands); - return t; +Expression * ArcTangent::clone() const { + ArcTangent * a = new ArcTangent(m_operands, true); + return a; +} + +Expression * ArcTangent::shallowReduce(Context& context, AngleUnit angleUnit) { + Expression * e = Expression::shallowReduce(context, angleUnit); + if (e != this) { + return e; + } +#if MATRIX_EXACT_REDUCING + if (operand(0)->type() == Type::Matrix) { + return SimplificationEngine::map(this, context, angleUnit); + } +#endif + return Trigonometry::shallowReduceInverseFunction(this, context, angleUnit); } template -Complex ArcTangent::templatedComputeComplex(const Complex c, AngleUnit angleUnit) const { +Complex ArcTangent::computeOnComplex(const Complex c, AngleUnit angleUnit) { assert(angleUnit != AngleUnit::Default); if (c.b() != 0) { return Complex::Float(NAN); diff --git a/poincare/src/arithmetic.cpp b/poincare/src/arithmetic.cpp new file mode 100644 index 000000000..f5fe302d1 --- /dev/null +++ b/poincare/src/arithmetic.cpp @@ -0,0 +1,82 @@ +#include +#include + +namespace Poincare { + +const Integer Arithmetic::k_primorial32("525896479052627740771371797072411912900610967452630"); + +Integer Arithmetic::LCM(const Integer * a, const Integer * b) { + if (a->isZero() || b->isZero()) { + return Integer(0); + } + Integer signResult = Integer::Division(Integer::Multiplication(*a, *b), GCD(a,b)).quotient; + signResult.setNegative(false); + return signResult; +} + +Integer Arithmetic::GCD(const Integer * a, const Integer * b) { + Integer i = *a; + Integer j = *b; + i.setNegative(false); + j.setNegative(false); + do { + if (i.isZero()) { + return j; + } + if (j.isZero()) { + return i; + } + if (j.isLowerThan(i)) { + i = Integer::Division(i, j).remainder; + } else { + j = Integer::Division(j, i).remainder; + } + } while(true); +} + +int primeFactors[Arithmetic::k_numberOfPrimeFactors] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997, 1009, 1013, 1019, 1021, 1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069, 1087, 1091, 1093, 1097, 1103, 1109, 1117, 1123, 1129, 1151, 1153, 1163, 1171, 1181, 1187, 1193, 1201, 1213, 1217, 1223, 1229, 1231, 1237, 1249, 1259, 1277, 1279, 1283, 1289, 1291, 1297, 1301, 1303, 1307, 1319, 1321, 1327, 1361, 1367, 1373, 1381, 1399, 1409, 1423, 1427, 1429, 1433, 1439, 1447, 1451, 1453, 1459, 1471, 1481, 1483, 1487, 1489, 1493, 1499, 1511, 1523, 1531, 1543, 1549, 1553, 1559, 1567, 1571, 1579, 1583, 1597, 1601, 1607, 1609, 1613, 1619, 1621, 1627, 1637, 1657, 1663, 1667, 1669, 1693, 1697, 1699, 1709, 1721, 1723, 1733, 1741, 1747, 1753, 1759, 1777, 1783, 1787, 1789, 1801, 1811, 1823, 1831, 1847, 1861, 1867, 1871, 1873, 1877, 1879, 1889, 1901, 1907, 1913, 1931, 1933, 1949, 1951, 1973, 1979, 1987, 1993, 1997, 1999, 2003, 2011, 2017, 2027, 2029, 2039, 2053, 2063, 2069, 2081, 2083, 2087, 2089, 2099, 2111, 2113, 2129, 2131, 2137, 2141, 2143, 2153, 2161, 2179, 2203, 2207, 2213, 2221, 2237, 2239, 2243, 2251, 2267, 2269, 2273, 2281, 2287, 2293, 2297, 2309, 2311, 2333, 2339, 2341, 2347, 2351, 2357, 2371, 2377, 2381, 2383, 2389, 2393, 2399, 2411, 2417, 2423, 2437, 2441, 2447, 2459, 2467, 2473, 2477, 2503, 2521, 2531, 2539, 2543, 2549, 2551, 2557, 2579, 2591, 2593, 2609, 2617, 2621, 2633, 2647, 2657, 2659, 2663, 2671, 2677, 2683, 2687, 2689, 2693, 2699, 2707, 2711, 2713, 2719, 2729, 2731, 2741, 2749, 2753, 2767, 2777, 2789, 2791, 2797, 2801, 2803, 2819, 2833, 2837, 2843, 2851, 2857, 2861, 2879, 2887, 2897, 2903, 2909, 2917, 2927, 2939, 2953, 2957, 2963, 2969, 2971, 2999, 3001, 3011, 3019, 3023, 3037, 3041, 3049, 3061, 3067, 3079, 3083, 3089, 3109, 3119, 3121, 3137, 3163, 3167, 3169, 3181, 3187, 3191, 3203, 3209, 3217, 3221, 3229, 3251, 3253, 3257, 3259, 3271, 3299, 3301, 3307, 3313, 3319, 3323, 3329, 3331, 3343, 3347, 3359, 3361, 3371, 3373, 3389, 3391, 3407, 3413, 3433, 3449, 3457, 3461, 3463, 3467, 3469, 3491, 3499, 3511, 3517, 3527, 3529, 3533, 3539, 3541, 3547, 3557, 3559, 3571, 3581, 3583, 3593, 3607, 3613, 3617, 3623, 3631, 3637, 3643, + 3659, 3671, 3673, 3677, 3691, 3697, 3701, 3709, 3719, 3727, 3733, 3739, 3761, 3767, 3769, 3779, 3793, 3797, 3803, 3821, 3823, 3833, 3847, 3851, 3853, 3863, 3877, 3881, 3889, 3907, 3911, 3917, 3919, 3923, 3929, 3931, 3943, 3947, 3967, 3989, 4001, 4003, 4007, 4013, 4019, 4021, 4027, 4049, 4051, 4057, 4073, 4079, 4091, 4093, 4099, 4111, 4127, 4129, 4133, 4139, 4153, 4157, 4159, 4177, 4201, 4211, 4217, 4219, 4229, 4231, 4241, 4243, 4253, 4259, 4261, 4271, 4273, 4283, 4289, 4297, 4327, 4337, 4339, 4349, 4357, 4363, 4373, 4391, 4397, 4409, 4421, 4423, 4441, 4447, 4451, 4457, 4463, 4481, 4483, 4493, 4507, 4513, 4517, 4519, 4523, 4547, 4549, 4561, 4567, 4583, 4591, 4597, 4603, 4621, 4637, 4639, 4643, 4649, 4651, 4657, 4663, 4673, 4679, 4691, 4703, 4721, 4723, 4729, 4733, 4751, 4759, 4783, 4787, 4789, 4793, 4799, 4801, 4813, 4817, 4831, 4861, 4871, 4877, 4889, 4903, 4909, 4919, 4931, 4933, 4937, 4943, 4951, 4957, 4967, 4969, 4973, 4987, 4993, 4999, 5003, 5009, 5011, 5021, 5023, 5039, 5051, 5059, 5077, 5081, 5087, 5099, 5101, 5107, 5113, 5119, 5147, 5153, 5167, 5171, 5179, 5189, 5197, 5209, 5227, 5231, 5233, 5237, 5261, 5273, 5279, 5281, 5297, 5303, 5309, 5323, 5333, 5347, 5351, 5381, 5387, 5393, 5399, 5407, 5413, 5417, 5419, 5431, 5437, 5441, 5443, 5449, 5471, 5477, 5479, 5483, 5501, 5503, 5507, 5519, 5521, 5527, 5531, 5557, 5563, 5569, 5573, 5581, 5591, 5623, 5639, 5641, 5647, 5651, 5653, 5657, 5659, 5669, 5683, 5689, 5693, 5701, 5711, 5717, 5737, 5741, 5743, 5749, 5779, 5783, 5791, 5801, 5807, 5813, 5821, 5827, 5839, 5843, 5849, 5851, 5857, 5861, 5867, 5869, 5879, 5881, 5897, 5903, 5923, 5927, 5939, 5953, 5981, 5987, 6007, 6011, 6029, 6037, 6043, 6047, 6053, 6067, 6073, 6079, 6089, 6091, 6101, 6113, 6121, 6131, 6133, 6143, 6151, 6163, 6173, 6197, 6199, 6203, 6211, 6217, 6221, 6229, 6247, 6257, 6263, 6269, 6271, 6277, 6287, 6299, 6301, 6311, 6317, 6323, 6329, 6337, 6343, 6353, 6359, 6361, 6367, 6373, 6379, 6389, 6397, 6421, 6427, 6449, 6451, 6469, 6473, 6481, 6491, 6521, 6529, 6547, 6551, 6553, 6563, 6569, 6571, 6577, 6581, 6599, 6607, 6619, 6637, 6653, 6659, 6661, 6673, 6679, 6689, 6691, 6701, 6703, 6709, 6719, 6733, 6737, 6761, 6763, 6779, 6781, 6791, 6793, 6803, 6823, 6827, 6829, 6833, 6841, 6857, 6863, 6869, 6871, 6883, 6899, 6907, 6911, 6917, 6947, 6949, 6959, 6961, 6967, 6971, 6977, 6983, 6991, 6997, 7001, 7013, 7019, 7027, 7039, 7043, 7057, 7069, 7079, 7103, 7109, 7121, 7127, 7129, 7151, 7159, 7177, 7187, 7193, 7207, 7211, 7213, 7219, 7229, 7237, 7243, 7247, 7253, 7283, 7297, 7307, 7309, 7321, 7331, 7333, 7349, 7351, 7369, 7393, 7411, 7417, 7433, 7451, 7457, 7459, 7477, 7481, 7487, 7489, 7499, 7507, 7517, 7523, 7529, 7537, 7541, 7547, 7549, 7559, 7561, 7573, 7577, 7583, 7589, 7591, 7603, 7607, 7621, 7639, 7643, 7649, 7669, 7673, 7681, 7687, 7691, 7699, 7703, 7717, 7723, 7727, 7741, 7753, 7757, 7759, 7789, 7793, 7817, 7823, 7829, 7841, 7853, 7867, 7873, 7877, 7879, 7883, 7901, 7907, 7919}; + +// we can go to 7907*7907 = 62 520 649 +void Arithmetic::PrimeFactorization(const Integer * n, Integer * outputFactors, Integer * outputCoefficients, int outputLength) { + /* First we look for prime divisors in the table primeFactors (to speed up + * the prime factorization for low numbers). When k_numberOfPrimeFactors is + * overflow, try every number as divisor. */ + for (int index = 0; index < outputLength; index++) { + outputCoefficients[index] = Integer(0); + } + Integer m = *n; + m.setNegative(false); + if (m.isEqualTo(Integer(1))) { + return; + } + int t = 0; // n prime factor index + int k = 0; // prime factor index + Integer testedPrimeFactor = Integer(primeFactors[k]); // prime factor + outputFactors[t] = testedPrimeFactor; + IntegerDivision d = {.quotient = 0, .remainder = 0}; + bool stopCondition; + do { + d = Integer::Division(m, testedPrimeFactor); + stopCondition = outputFactors[t].isLowerThan(d.quotient); // We evaluate the condition here in case we move d.quotient in n + if (d.remainder.isEqualTo(Integer(0))) { + outputCoefficients[t] = Integer::Addition(outputCoefficients[t], Integer(1)); + m = std::move(d.quotient); + if (m.isEqualTo(Integer(1))) { + return; + } + continue; + } + if (!outputCoefficients[t].isEqualTo(Integer(0))) { + t++; + } + k++; + testedPrimeFactor = k < k_numberOfPrimeFactors ? Integer(primeFactors[k]) : Integer::Addition(testedPrimeFactor, Integer(1)); + outputFactors[t] = testedPrimeFactor; + } while (stopCondition); + outputFactors[t] = std::move(m); + outputCoefficients[t] = Integer::Addition(outputCoefficients[t], Integer(1)); +} +} + + diff --git a/poincare/src/binary_operation.cpp b/poincare/src/binary_operation.cpp deleted file mode 100644 index 4505c0635..000000000 --- a/poincare/src/binary_operation.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include -#include -#include -extern "C" { -#include -#include -} - -namespace Poincare { - -BinaryOperation::BinaryOperation() -{ - m_operands[0] = nullptr; - m_operands[1] = nullptr; -} - -BinaryOperation::BinaryOperation(Expression ** operands, bool cloneOperands) { - assert(operands != nullptr); - assert(operands[0] != nullptr); - assert(operands[1] != nullptr); - if (cloneOperands) { - m_operands[0] = operands[0]->clone(); - m_operands[1] = operands[1]->clone(); - } else { - m_operands[0] = operands[0]; - m_operands[1] = operands[1]; - } -} - -BinaryOperation::~BinaryOperation() { - if (m_operands[1] != nullptr) { - delete m_operands[1]; - } - if (m_operands[0] != nullptr) { - delete m_operands[0]; - } -} - -bool BinaryOperation::hasValidNumberOfArguments() const { - return m_operands[0]->hasValidNumberOfArguments() && m_operands[1]->hasValidNumberOfArguments(); -} - -int BinaryOperation::numberOfOperands() const { - return 2; -} - -const Expression * BinaryOperation::operand(int i) const { - assert(i >= 0); - assert(i < 2); - return m_operands[i]; -} - -Expression * BinaryOperation::clone() const { - return this->cloneWithDifferentOperands((Expression**) m_operands, 2, true); -} - -template Evaluation * BinaryOperation::templatedEvaluate(Context& context, AngleUnit angleUnit) const { - Evaluation * leftOperandEvalutation = m_operands[0]->evaluate(context, angleUnit); - Evaluation * rightOperandEvalutation = m_operands[1]->evaluate(context, angleUnit); - Evaluation * result = nullptr; - if (leftOperandEvalutation->numberOfRows() == 1 && leftOperandEvalutation->numberOfColumns() == 1 && rightOperandEvalutation->numberOfRows() == 1 && rightOperandEvalutation->numberOfColumns() == 1) { - result = new Complex(privateCompute(*(leftOperandEvalutation->complexOperand(0)), *(rightOperandEvalutation->complexOperand(0)))); - } else if (leftOperandEvalutation->numberOfRows() == 1 && leftOperandEvalutation->numberOfColumns() == 1) { - result = computeOnComplexAndComplexMatrix(leftOperandEvalutation->complexOperand(0), rightOperandEvalutation); - } else if (rightOperandEvalutation->numberOfRows() == 1 && rightOperandEvalutation->numberOfColumns() == 1) { - result = computeOnComplexMatrixAndComplex(leftOperandEvalutation, rightOperandEvalutation->complexOperand(0)); - } else { - result = computeOnComplexMatrices(leftOperandEvalutation, rightOperandEvalutation); - } - delete leftOperandEvalutation; - delete rightOperandEvalutation; - if (result == nullptr) { - result = new Complex(Complex::Float(NAN)); - } - return result; -} - -template Evaluation * BinaryOperation::templatedComputeOnComplexAndComplexMatrix(const Complex * c, Evaluation * n) const { - return computeOnComplexMatrixAndComplex(n, c); -} - -template Evaluation * BinaryOperation::templatedComputeOnComplexMatrixAndComplex(Evaluation * m, const Complex * d) const { - Complex * operands = new Complex[m->numberOfRows()*m->numberOfColumns()]; - for (int i = 0; i < m->numberOfOperands(); i++) { - operands[i] = privateCompute(*(m->complexOperand(i)), *d); - } - Evaluation * result = new ComplexMatrix(operands, m->numberOfRows(), m->numberOfColumns()); - delete[] operands; - return result; -} - -template Evaluation * BinaryOperation::templatedComputeOnComplexMatrices(Evaluation * m, Evaluation * n) const { - if (m->numberOfRows() != n->numberOfRows() && m->numberOfColumns() != n->numberOfColumns()) { - return nullptr; - } - Complex * operands = new Complex[m->numberOfRows()*m->numberOfColumns()]; - for (int i = 0; i < m->numberOfOperands(); i++) { - operands[i] = privateCompute(*(m->complexOperand(i)), *(n->complexOperand(i))); - } - Evaluation * result = new ComplexMatrix(operands, m->numberOfRows(), m->numberOfColumns()); - delete[] operands; - return result; -} - -} - -template Poincare::Evaluation* Poincare::BinaryOperation::templatedComputeOnComplexAndComplexMatrix(Poincare::Complex const*, Poincare::Evaluation*) const; -template Poincare::Evaluation* Poincare::BinaryOperation::templatedComputeOnComplexAndComplexMatrix(Poincare::Complex const*, Poincare::Evaluation*) const; diff --git a/poincare/src/binomial_coefficient.cpp b/poincare/src/binomial_coefficient.cpp index 3a18e8ff5..ae2812a92 100644 --- a/poincare/src/binomial_coefficient.cpp +++ b/poincare/src/binomial_coefficient.cpp @@ -1,6 +1,7 @@ #include -#include +#include #include +#include #include "layout/parenthesis_layout.h" #include "layout/grid_layout.h" @@ -12,49 +13,98 @@ extern "C" { namespace Poincare { -BinomialCoefficient::BinomialCoefficient() : - Function("binomial", 2) -{ -} - Expression::Type BinomialCoefficient::type() const { return Type::BinomialCoefficient; } -Expression * BinomialCoefficient::cloneWithDifferentOperands(Expression** newOperands, - int numberOfOperands, bool cloneOperands) const { - assert(newOperands != nullptr); - BinomialCoefficient * bc = new BinomialCoefficient(); - bc->setArgument(newOperands, numberOfOperands, cloneOperands); - return bc; +Expression * BinomialCoefficient::clone() const { + BinomialCoefficient * b = new BinomialCoefficient(m_operands, true); + return b; } -template -Evaluation * BinomialCoefficient::templatedEvaluate(Context& context, AngleUnit angleUnit) const { - Evaluation * nInput = m_args[0]->evaluate(context, angleUnit); - Evaluation * kInput = m_args[1]->evaluate(context, angleUnit); - T n = nInput->toScalar(); - T k = kInput->toScalar(); - delete nInput; - delete kInput; - if (std::isnan(n) || std::isnan(k) || n != (int)n || k != (int)k || k > n || k < 0 || n < 0) { - return new Complex(Complex::Float(NAN)); +Expression * BinomialCoefficient::shallowReduce(Context& context, AngleUnit angleUnit) { + Expression * e = Expression::shallowReduce(context, angleUnit); + if (e != this) { + return e; } - T result = 1; - for (int i = 0; i < (int)k; i++) { - result *= (n-(T)i)/(k-(T)i); + Expression * op0 = editableOperand(0); + Expression * op1 = editableOperand(1); +#if MATRIX_EXACT_REDUCING + if (op0->type() == Type::Matrix || op1->type() == Type::Matrix) { + return replaceWith(new Undefined(), true); } - return new Complex(Complex::Float(std::round(result))); +#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); } ExpressionLayout * BinomialCoefficient::privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const { assert(floatDisplayMode != FloatDisplayMode::Default); assert(complexFormat != ComplexFormat::Default); ExpressionLayout * childrenLayouts[2]; - childrenLayouts[0] = m_args[0]->createLayout(floatDisplayMode, complexFormat); - childrenLayouts[1] = m_args[1]->createLayout(floatDisplayMode, complexFormat); + childrenLayouts[0] = operand(0)->createLayout(floatDisplayMode, complexFormat); + childrenLayouts[1] = operand(1)->createLayout(floatDisplayMode, complexFormat); return new ParenthesisLayout(new GridLayout(childrenLayouts, 2, 1)); } +template +Expression * BinomialCoefficient::templatedEvaluate(Context& context, AngleUnit angleUnit) const { + Expression * nInput = operand(0)->evaluate(context, angleUnit); + Expression * kInput = operand(1)->evaluate(context, angleUnit); + if (nInput->type() != Type::Complex || kInput->type() != Type::Complex) { + return new Complex(Complex::Float(NAN)); + } + T n = static_cast *>(nInput)->toScalar(); + T k = static_cast *>(kInput)->toScalar(); + delete nInput; + delete kInput; + 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 new Complex(Complex::Float(NAN)); + } + T result = 1; + for (int i = 0; i < k; i++) { + result *= (n-(T)i)/(k-(T)i); + if (std::isinf(result)) { + return new Complex(Complex::Float(result)); + } + } + return new Complex(Complex::Float(std::round(result))); } +} diff --git a/poincare/src/bounded_static_hierarchy.cpp b/poincare/src/bounded_static_hierarchy.cpp new file mode 100644 index 000000000..03db1111f --- /dev/null +++ b/poincare/src/bounded_static_hierarchy.cpp @@ -0,0 +1,47 @@ +#include +extern "C" { +#include +} + +namespace Poincare { + +template +BoundedStaticHierarchy::BoundedStaticHierarchy() : + StaticHierarchy(), + m_numberOfOperands(0) +{ +} + +template +BoundedStaticHierarchy::BoundedStaticHierarchy(const Expression * const * operands, int numberOfOperands, bool cloneOperands) : + m_numberOfOperands(numberOfOperands) +{ + StaticHierarchy::build(operands, numberOfOperands, cloneOperands); +} + +template<> +BoundedStaticHierarchy<2>::BoundedStaticHierarchy(const Expression * e1, const Expression * e2, bool cloneOperands) : + BoundedStaticHierarchy(ExpressionArray(e1, e2), 2, cloneOperands) +{ +} + +template<> +BoundedStaticHierarchy<2>::BoundedStaticHierarchy(const Expression * e, bool cloneOperands) : + BoundedStaticHierarchy((Expression **)&e, 1, cloneOperands) +{ +} + +template +void BoundedStaticHierarchy::setArgument(ListData * listData, int numberOfOperands, bool clone) { + StaticHierarchy::setArgument(listData, numberOfOperands, clone); + m_numberOfOperands = listData->numberOfOperands(); +} + +template +bool BoundedStaticHierarchy::hasValidNumberOfOperands(int numberOfOperands) const { + return numberOfOperands >= 1 && numberOfOperands <= T; +} + +template class Poincare::BoundedStaticHierarchy<2>; + +} diff --git a/poincare/src/ceiling.cpp b/poincare/src/ceiling.cpp index b77508094..3fd5205e4 100644 --- a/poincare/src/ceiling.cpp +++ b/poincare/src/ceiling.cpp @@ -1,6 +1,9 @@ #include #include "layout/ceiling_layout.h" - +#include +#include +#include +#include extern "C" { #include } @@ -8,25 +11,48 @@ extern "C" { namespace Poincare { -Ceiling::Ceiling() : - Function("ceil") -{ -} - Expression::Type Ceiling::type() const { return Type::Ceiling; } -Expression * Ceiling::cloneWithDifferentOperands(Expression** newOperands, - int numberOfOperands, bool cloneOperands) const { - assert(newOperands != nullptr); - Ceiling * c = new Ceiling(); - c->setArgument(newOperands, numberOfOperands, cloneOperands); +Expression * Ceiling::clone() const { + Ceiling * c = new Ceiling(m_operands, true); return c; } +Expression * Ceiling::shallowReduce(Context& context, AngleUnit angleUnit) { + Expression * e = Expression::shallowReduce(context, angleUnit); + if (e != this) { + return e; + } + Expression * op = editableOperand(0); +#if MATRIX_EXACT_REDUCING + if (op->type() == Type::Matrix) { + return SimplificationEngine::map(this, context, angleUnit); + } +#endif + if (op->type() == Type::Symbol) { + Symbol * s = static_cast(op); + if (s->name() == Ion::Charset::SmallPi) { + return replaceWith(new Rational(4), true); + } + if (s->name() == Ion::Charset::Exponential) { + return replaceWith(new Rational(3), true); + } + } + if (op->type() != Type::Rational) { + return this; + } + Rational * r = static_cast(op); + IntegerDivision div = Integer::Division(r->numerator(), r->denominator()); + if (div.remainder.isZero()) { + return replaceWith(new Rational(div.quotient), true); + } + return replaceWith(new Rational(Integer::Addition(div.quotient, Integer(1))), true); +} + template -Complex Ceiling::templatedComputeComplex(const Complex c) const { +Complex Ceiling::computeOnComplex(const Complex c, AngleUnit angleUnit) { if (c.b() != 0) { return Complex::Float(NAN); } @@ -36,7 +62,7 @@ Complex Ceiling::templatedComputeComplex(const Complex c) const { ExpressionLayout * Ceiling::privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const { assert(floatDisplayMode != FloatDisplayMode::Default); assert(complexFormat != ComplexFormat::Default); - return new CeilingLayout(m_args[0]->createLayout(floatDisplayMode, complexFormat)); + return new CeilingLayout(m_operands[0]->createLayout(floatDisplayMode, complexFormat)); } } diff --git a/poincare/src/commutative_operation.cpp b/poincare/src/commutative_operation.cpp new file mode 100644 index 000000000..c55173768 --- /dev/null +++ b/poincare/src/commutative_operation.cpp @@ -0,0 +1,28 @@ +#include + +namespace Poincare { + +void CommutativeOperation::sort() { + // First, sort every child + for (int i = 0; i < m_numberOfOperands; i++) { + m_operands[i]->sort(); + } + // Second, sort all children together + // TODO: use a heap sort instead of a buble sort + for (int i = m_numberOfOperands-1; i > 0; i--) { + bool isSorted = true; + for (int j = 0; j < m_numberOfOperands-1; j++) { + if (m_operands[j]->isGreaterThan(m_operands[j+1])) { + Expression * temp = m_operands[j]; + m_operands[j] = m_operands[j+1]; + m_operands[j+1] = temp; + isSorted = false; + } + } + if (isSorted) { + return; + } + } +} + +} diff --git a/poincare/src/complex.cpp b/poincare/src/complex.cpp index 7cad41caa..cdfa5eba6 100644 --- a/poincare/src/complex.cpp +++ b/poincare/src/complex.cpp @@ -6,7 +6,6 @@ extern "C" { #include } #include -#include #include "layout/string_layout.h" #include "layout/baseline_relative_layout.h" #include @@ -70,7 +69,20 @@ Complex Complex::Polar(T r, T th) { } template -static inline T setSign(T f, bool negative) { +Complex::Complex(const Complex & other) { + m_a = other.m_a; + m_b = other.m_b; +} + +template +Complex & Complex::operator=(const Complex& other) { + m_a = other.m_a; + m_b = other.m_b; + return *this; +} + +template +static inline T privateFloatSetSign(T f, bool negative) { if (negative) { return -f; } @@ -98,56 +110,12 @@ Complex::Complex(const char * integralPart, int integralPartLength, bool inte const char * exponent, int exponentLength, bool exponentNegative) { T i = digitsToFloat(integralPart, integralPartLength); T j = digitsToFloat(fractionalPart, fractionalPartLength); - T l = setSign(digitsToFloat(exponent, exponentLength), exponentNegative); + T l = privateFloatSetSign(digitsToFloat(exponent, exponentLength), exponentNegative); - m_a = setSign((i + j*std::pow(10, -std::ceil((T)fractionalPartLength)))* std::pow(10, l), integralNegative); + m_a = privateFloatSetSign((i + j*std::pow(10, -std::ceil((T)fractionalPartLength)))* std::pow(10, l), integralNegative); m_b = 0; } -template -T Complex::toScalar() const { - if (m_b != 0) { - return NAN; - } - return m_a; -} - -template -int Complex::numberOfRows() const { - return 1; -} - -template -int Complex::numberOfColumns() const { - return 1; -} - -template -Expression::Type Complex::type() const { - return Expression::Type::Complex; -} - -template -Complex * Complex::clone() const { - return new Complex(*this); -} - -template -Evaluation * Complex::cloneWithDifferentOperands(Expression** newOperands, - int numberOfOperands, bool cloneOperands) const { - return this->clone(); -} - -template -int Complex::writeTextInBuffer(char * buffer, int bufferSize) const { - return convertComplexToText(buffer, bufferSize, Preferences::sharedPreferences()->displayMode(), Preferences::sharedPreferences()->complexFormat()); -} - -template -Evaluation * Complex::createInverse() const { - return new Complex(Cartesian(1/m_a, -1/m_b)); -} - template T Complex::a() const { return m_a; @@ -212,6 +180,29 @@ Complex Complex::conjugate() const { return Cartesian(m_a, -m_b); } +template +Expression::Type Complex::type() const { + return Expression::Type::Complex; +} + +template +Complex * Complex::clone() const { + return new Complex(*this); +} + +template +T Complex::toScalar() const { + if (m_b != 0) { + return NAN; + } + return m_a; +} + +template +int Complex::writeTextInBuffer(char * buffer, int bufferSize) const { + return convertComplexToText(buffer, bufferSize, Preferences::sharedPreferences()->displayMode(), Preferences::sharedPreferences()->complexFormat()); +} + template int Complex::convertFloatToText(T f, char * buffer, int bufferSize, int numberOfSignificantDigits, Expression::FloatDisplayMode mode) { @@ -246,11 +237,6 @@ Complex::Complex(T a, T b) : { } -template -const Complex * Complex::complexOperand(int i) const { - return this; -} - template ExpressionLayout * Complex::privateCreateLayout(Expression::FloatDisplayMode floatDisplayMode, Expression::ComplexFormat complexFormat) const { assert(floatDisplayMode != Expression::FloatDisplayMode::Default); @@ -262,7 +248,7 @@ ExpressionLayout * Complex::privateCreateLayout(Expression::FloatDisplayMode template template -Evaluation * Complex::templatedEvaluate(Context& context, Expression::AngleUnit angleUnit) const { +Complex * Complex::templatedEvaluate(Context& context, Expression::AngleUnit angleUnit) const { return new Complex(Complex::Cartesian((U)m_a, (U)m_b)); } @@ -490,12 +476,12 @@ ExpressionLayout * Complex::createCartesianLayout(Expression::FloatDisplayMod return new StringLayout(buffer, numberOfChars); } -template class Poincare::Complex; -template class Poincare::Complex; -template Poincare::Evaluation* Poincare::Complex::templatedEvaluate(Poincare::Context&, Poincare::Expression::AngleUnit) const; -template Poincare::Evaluation* Poincare::Complex::templatedEvaluate(Poincare::Context&, Poincare::Expression::AngleUnit) const; -template Poincare::Evaluation* Poincare::Complex::templatedEvaluate(Poincare::Context&, Poincare::Expression::AngleUnit) const; -template Poincare::Evaluation* Poincare::Complex::templatedEvaluate(Poincare::Context&, Poincare::Expression::AngleUnit) const; +template class Complex; +template class Complex; +template Complex* Complex::templatedEvaluate(Context&, Expression::AngleUnit) const; +template Complex* Complex::templatedEvaluate(Context&, Expression::AngleUnit) const; +template Complex* Complex::templatedEvaluate(Context&, Expression::AngleUnit) const; +template Complex* Complex::templatedEvaluate(Context&, Expression::AngleUnit) const; } diff --git a/poincare/src/complex_argument.cpp b/poincare/src/complex_argument.cpp index 85a4c2505..99d0c4d4f 100644 --- a/poincare/src/complex_argument.cpp +++ b/poincare/src/complex_argument.cpp @@ -1,6 +1,6 @@ #include #include - +#include extern "C" { #include } @@ -8,25 +8,31 @@ extern "C" { namespace Poincare { -ComplexArgument::ComplexArgument() : - Function("arg") -{ -} - Expression::Type ComplexArgument::type() const { return Type::ComplexArgument; } -Expression * ComplexArgument::cloneWithDifferentOperands(Expression** newOperands, - int numberOfOperands, bool cloneOperands) const { - assert(newOperands != nullptr); - ComplexArgument * ca = new ComplexArgument(); - ca->setArgument(newOperands, numberOfOperands, cloneOperands); - return ca; +Expression * ComplexArgument::clone() const { + ComplexArgument * a = new ComplexArgument(m_operands, true); + return a; +} + +Expression * ComplexArgument::shallowReduce(Context& context, AngleUnit angleUnit) { + Expression * e = Expression::shallowReduce(context, angleUnit); + if (e != this) { + return e; + } +#if MATRIX_EXACT_REDUCING + Expression * op = editableOperand(0); + if (op->type() == Type::Matrix) { + return SimplificationEngine::map(this, context, angleUnit); + } +#endif + return this; } template -Complex ComplexArgument::templatedComputeComplex(const Complex c) const { +Complex ComplexArgument::computeOnComplex(const Complex c, AngleUnit angleUnit) { return Complex::Float(c.th()); } diff --git a/poincare/src/complex_matrix.cpp b/poincare/src/complex_matrix.cpp deleted file mode 100644 index 7fc368963..000000000 --- a/poincare/src/complex_matrix.cpp +++ /dev/null @@ -1,103 +0,0 @@ -extern "C" { -#include -#include -} -#include -#include -#include "layout/grid_layout.h" -#include "layout/bracket_layout.h" -#include -#include -#include - -namespace Poincare { - -template -ComplexMatrix::ComplexMatrix(const Complex * complexes, int numberOfRows, int numberOfColumns) : - m_numberOfRows(numberOfRows), - m_numberOfColumns(numberOfColumns) -{ - assert(complexes != nullptr); - m_values = new Complex[numberOfRows*numberOfColumns]; - for (int i = 0; i < numberOfRows*numberOfColumns; i++) { - m_values[i] = complexes[i]; - } -} - -template -ComplexMatrix::~ComplexMatrix() { - delete[] m_values; -} - -template -T ComplexMatrix::toScalar() const { - if (m_numberOfRows != 1 || m_numberOfColumns != 1) { - return NAN; - } - if (m_values[0].b() != 0) { - return NAN; - } - return m_values[0].a(); -} - -template -int ComplexMatrix::numberOfRows() const { - return m_numberOfRows; -} - -template -int ComplexMatrix::numberOfColumns() const { - return m_numberOfColumns; -} - -template -const Complex * ComplexMatrix::complexOperand(int i) const { - return &m_values[i]; -} - -template -ComplexMatrix * ComplexMatrix::clone() const { - return new ComplexMatrix(m_values, m_numberOfRows, m_numberOfColumns); -} - -template -ComplexMatrix * ComplexMatrix::cloneWithDifferentOperands(Expression** newOperands, - int numberOfOperands, bool cloneOperands) const { - assert(newOperands != nullptr); - return new ComplexMatrix((Complex *)newOperands[0], m_numberOfRows, m_numberOfColumns); -} - -template -Evaluation * ComplexMatrix::createIdentity(int dim) { - Complex * operands = new Complex [dim*dim]; - for (int i = 0; i < dim; i++) { - for (int j = 0; j < dim; j++) { - if (i == j) { - operands[i*dim+j] = Complex::Float(1.0); - } else { - operands[i*dim+j] = Complex::Float(0.0); - } - } - } - Evaluation * matrix = new ComplexMatrix(operands, dim, dim); - delete [] operands; - return matrix; -} - -template -template -Evaluation * ComplexMatrix::templatedEvaluate(Context& context, Expression::AngleUnit angleUnit) const { - Complex * values = new Complex[m_numberOfRows*m_numberOfColumns]; - for (int i = 0; i < m_numberOfRows*m_numberOfColumns; i++) { - values[i] = Complex::Cartesian(m_values[i].a(), m_values[i].b()); - } - Evaluation * result = new ComplexMatrix(values, m_numberOfRows, m_numberOfColumns); - delete [] values; - return result; - -} - -template class Poincare::ComplexMatrix; -template class Poincare::ComplexMatrix; - -} diff --git a/poincare/src/confidence_interval.cpp b/poincare/src/confidence_interval.cpp index cbdffeabe..3ffc36764 100644 --- a/poincare/src/confidence_interval.cpp +++ b/poincare/src/confidence_interval.cpp @@ -1,6 +1,10 @@ #include #include -#include +#include +#include +#include +#include +#include extern "C" { #include } @@ -8,38 +12,70 @@ extern "C" { namespace Poincare { -ConfidenceInterval::ConfidenceInterval() : - Function("confidence", 2) -{ -} - Expression::Type ConfidenceInterval::type() const { return Type::ConfidenceInterval; } -Expression * ConfidenceInterval::cloneWithDifferentOperands(Expression** newOperands, - int numberOfOperands, bool cloneOperands) const { - assert(newOperands != nullptr); - ConfidenceInterval * ci = new ConfidenceInterval(); - ci->setArgument(newOperands, numberOfOperands, cloneOperands); - return ci; +Expression * ConfidenceInterval::clone() const { + ConfidenceInterval * a = new ConfidenceInterval(m_operands, true); + return a; +} + +Expression * ConfidenceInterval::shallowReduce(Context& context, AngleUnit angleUnit) { + Expression * e = Expression::shallowReduce(context, angleUnit); + if (e != this) { + return e; + } + Expression * op0 = editableOperand(0); + Expression * op1 = editableOperand(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->numerator().isNegative() || Integer::NaturalOrder(r0->numerator(), r0->denominator()) > 0) { + 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); + detachOperands(); + // Compute [r0-1/sqr(r1), r0+1/sqr(r1)] + Expression * sqr = new Power(r1, new Rational(-1, 2), false); + const Expression * newOperands[2] = {new Addition(r0, new Multiplication(new Rational(-1), sqr, false), false), new Addition(r0, sqr, true)}; + Expression * matrix = replaceWith(new Matrix(newOperands, 1, 2, false), true); + return matrix->deepReduce(context, angleUnit); } template -Evaluation * ConfidenceInterval::templatedEvaluate(Context& context, AngleUnit angleUnit) const { - Evaluation * fInput = m_args[0]->evaluate(context, angleUnit); - Evaluation * nInput = m_args[1]->evaluate(context, angleUnit); - T f = fInput->toScalar(); - T n = nInput->toScalar(); +Expression * ConfidenceInterval::templatedEvaluate(Context& context, AngleUnit angleUnit) const { + Expression * fInput = operand(0)->evaluate(context, angleUnit); + Expression * nInput = operand(1)->evaluate(context, angleUnit); + if (fInput->type() != Type::Complex || nInput->type() != Type::Complex) { + return new Complex(Complex::Float(NAN)); + } + T f = static_cast *>(fInput)->toScalar(); + T n = static_cast *>(nInput)->toScalar(); delete fInput; delete nInput; if (std::isnan(f) || std::isnan(n) || n != (int)n || n < 0 || f < 0 || f > 1) { return new Complex(Complex::Float(NAN)); } - Complex operands[2]; - operands[0] = Complex::Float(f - 1/std::sqrt(n)); - operands[1] = Complex::Float(f + 1/std::sqrt(n)); - return new ComplexMatrix(operands, 1, 2); + Expression * operands[2]; + operands[0] = new Complex(Complex::Float(f - 1/std::sqrt(n))); + operands[1] = new Complex(Complex::Float(f + 1/std::sqrt(n))); + return new Matrix(operands, 1, 2, false); } } diff --git a/poincare/src/conjugate.cpp b/poincare/src/conjugate.cpp index 33333cc7f..0944df809 100644 --- a/poincare/src/conjugate.cpp +++ b/poincare/src/conjugate.cpp @@ -1,5 +1,6 @@ #include #include +#include #include "layout/conjugate_layout.h" extern "C" { @@ -9,32 +10,41 @@ extern "C" { namespace Poincare { -Conjugate::Conjugate() : - Function("conj") -{ -} - Expression::Type Conjugate::type() const { return Type::Conjugate; } -Expression * Conjugate::cloneWithDifferentOperands(Expression** newOperands, - int numberOfOperands, bool cloneOperands) const { - assert(newOperands != nullptr); - Conjugate * c = new Conjugate(); - c->setArgument(newOperands, numberOfOperands, cloneOperands); - return c; -} - -template -Complex Conjugate::templatedComputeComplex(const Complex c) const { - return c.conjugate(); +Expression * Conjugate::clone() const { + Conjugate * a = new Conjugate(m_operands, true); + return a; } ExpressionLayout * Conjugate::privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const { assert(floatDisplayMode != FloatDisplayMode::Default); assert(complexFormat != ComplexFormat::Default); - return new ConjugateLayout(m_args[0]->createLayout(floatDisplayMode, complexFormat)); + return new ConjugateLayout(operand(0)->createLayout(floatDisplayMode, complexFormat)); +} + +Expression * Conjugate::shallowReduce(Context& context, AngleUnit angleUnit) { + Expression * e = Expression::shallowReduce(context, angleUnit); + if (e != this) { + return e; + } + Expression * op = editableOperand(0); +#if MATRIX_EXACT_REDUCING + if (op->type() == Type::Matrix) { + return SimplificationEngine::map(this, context, angleUnit); + } +#endif + if (op->type() == Type::Rational) { + return replaceWith(op, true); + } + return this; +} + +template +Complex Conjugate::computeOnComplex(const Complex c, AngleUnit angleUnit) { + return c.conjugate(); } } diff --git a/poincare/src/cosine.cpp b/poincare/src/cosine.cpp index 480e1dd33..38ce65d52 100644 --- a/poincare/src/cosine.cpp +++ b/poincare/src/cosine.cpp @@ -1,6 +1,11 @@ #include #include #include +#include +#include +#include +#include +#include extern "C" { #include } @@ -8,25 +13,31 @@ extern "C" { namespace Poincare { -Cosine::Cosine() : - Function("cos") -{ -} - Expression::Type Cosine::type() const { return Type::Cosine; } -Expression * Cosine::cloneWithDifferentOperands(Expression** newOperands, - int numberOfOperands, bool cloneOperands) const { - assert(newOperands != nullptr); - Cosine * c = new Cosine(); - c->setArgument(newOperands, numberOfOperands, cloneOperands); - return c; +Expression * Cosine::clone() const { + Cosine * a = new Cosine(m_operands, true); + return a; +} + +Expression * Cosine::shallowReduce(Context& context, AngleUnit angleUnit) { + Expression * e = Expression::shallowReduce(context, angleUnit); + if (e != this) { + return e; + } +#if MATRIX_EXACT_REDUCING + Expression * op = editableOperand(0); + if (op->type() == Type::Matrix) { + return SimplificationEngine::map(this, context, angleUnit); + } +#endif + return Trigonometry::shallowReduceDirectFunction(this, context, angleUnit); } template -Complex Cosine::compute(const Complex c, AngleUnit angleUnit) { +Complex Cosine::computeOnComplex(const Complex c, AngleUnit angleUnit) { assert(angleUnit != AngleUnit::Default); if (c.b() == 0) { T input = c.a(); @@ -47,7 +58,7 @@ Complex Cosine::compute(const Complex c, AngleUnit angleUnit) { return Complex::Float(result); } Complex arg = Complex::Cartesian(-c.b(), c.a()); - return HyperbolicCosine::compute(arg); + return HyperbolicCosine::computeOnComplex(arg, angleUnit); } } diff --git a/poincare/src/decimal.cpp b/poincare/src/decimal.cpp new file mode 100644 index 000000000..e7ac92254 --- /dev/null +++ b/poincare/src/decimal.cpp @@ -0,0 +1,274 @@ +#include +#include +#include +#include +#include +#include +#include +extern "C" { +#include +} + +#include "layout/string_layout.h" + +namespace Poincare { + +int Decimal::exponent(const char * integralPart, int integralPartLength, const char * fractionalPart, int fractionalPartLength, const char * exponent, int exponentLength, bool exponentNegative) { + int base = 10; + int exp = 0; + for (int i = 0; i < exponentLength; i++) { + exp *= base; + exp += *exponent-'0'; + exponent++; + } + if (exponentNegative) { + exp = -exp; + } + const char * integralPartEnd = integralPart + integralPartLength; + if (integralPart != nullptr) { + while (*integralPart == '0' && integralPart < integralPartEnd) { + integralPart++; + } + } + exp += integralPartEnd-integralPart-1; + if (integralPart == integralPartEnd) { + const char * fractionalPartEnd = fractionalPart + fractionalPartLength; + if (fractionalPart != nullptr) { + while (*fractionalPart == '0' && fractionalPart < fractionalPartEnd) { + fractionalPart++; + exp--; + } + } + if (fractionalPart == fractionalPartEnd) { + exp += fractionalPartLength+1; + } + } + return exp; +} + +void removeZeroAtTheEnd(Integer & i) { + if (i.isZero()) { + return; + } + Integer base = Integer(10); + IntegerDivision d = Integer::Division(i, base); + while (d.remainder.isZero()) { + i = d.quotient; + d = Integer::Division(i, base); + } +} + +Integer Decimal::mantissa(const char * integralPart, int integralPartLength, const char * fractionalPart, int fractionalPartLength, bool negative) { + Integer zero = Integer(0); + Integer base = Integer(10); + Integer numerator = Integer(integralPart, negative); + for (int i = 0; i < fractionalPartLength; i++) { + numerator = Integer::Multiplication(numerator, base); + numerator = Integer::Addition(numerator, Integer(*fractionalPart-'0')); + fractionalPart++; + } + removeZeroAtTheEnd(numerator); + return numerator; +} + +Decimal::Decimal(Integer mantissa, int exponent) : + m_mantissa(mantissa), + m_exponent(exponent) +{ +} + +Decimal::Decimal(double f) { + double logBase10 = f != 0 ? std::log10(std::fabs(f)) : 0; + int exponentInBase10 = std::floor(logBase10); + /* Correct the exponent in base 10: sometines the exact log10 of f is 6.999999 + * but is stored as 7 in hardware. We catch these cases here. */ + if (f != 0 && logBase10 == (int)logBase10 && std::fabs(f) < std::pow(10, logBase10)) { + exponentInBase10--; + } + double m = f*std::pow(10, (double)-exponentInBase10); // TODO: hangle exponentInBase10 is too big! mantissa is nan + m = m * std::pow(10, (double)(k_doublePrecision-1)); + int64_t integerMantissa = std::round(m); + /* If m > 999999999999999.5, the mantissa stored will be 1 (as we keep only + * 15 significative numbers from double. In that case, the exponent must be + * increment as well. */ + if (m >= k_biggestMantissaFromDouble+0.5) { + exponentInBase10++; + } + m_mantissa = Integer(integerMantissa); + removeZeroAtTheEnd(m_mantissa); + m_exponent = exponentInBase10; +} + +Expression::Type Decimal::type() const { + return Type::Decimal; +} + +Expression * Decimal::clone() const { + return new Decimal(m_mantissa, m_exponent); +} + +template Expression * Decimal::templatedEvaluate(Context& context, Expression::AngleUnit angleUnit) const { + T m = m_mantissa.approximate(); + int numberOfDigits = numberOfDigitsInMantissaWithoutSign(); + return new Complex(Complex::Float(m*std::pow((T)10.0, (T)(m_exponent-numberOfDigits+1)))); +} + +int Decimal::writeTextInBuffer(char * buffer, int bufferSize) const { + if (bufferSize == 0) { + return -1; + } + buffer[bufferSize-1] = 0; + int currentChar = 0; + if (currentChar >= bufferSize-1) { return bufferSize-1; } + if (m_mantissa.isZero()) { + buffer[currentChar++] = '0'; + buffer[currentChar] = 0; + return currentChar; + } + int nbOfDigitsInMantissaWithoutSign = numberOfDigitsInMantissaWithoutSign(); + int numberOfRequiredDigits = nbOfDigitsInMantissaWithoutSign > m_exponent ? nbOfDigitsInMantissaWithoutSign : m_exponent; + numberOfRequiredDigits = m_exponent < 0 ? 1+nbOfDigitsInMantissaWithoutSign-m_exponent : numberOfRequiredDigits; + /* Case 0: the number would be too long if we print it as a natural decimal */ + if (numberOfRequiredDigits > k_maxLength) { + if (nbOfDigitsInMantissaWithoutSign == 1) { + currentChar +=m_mantissa.writeTextInBuffer(buffer, bufferSize); + } else { + currentChar++; + if (currentChar >= bufferSize-1) { return bufferSize-1; } + currentChar += m_mantissa.writeTextInBuffer(buffer+currentChar, bufferSize-currentChar); + int decimalMarkerPosition = 1; + if (buffer[1] == '-') { + decimalMarkerPosition++; + buffer[0] = buffer[1]; + } + buffer[decimalMarkerPosition-1] = buffer[decimalMarkerPosition]; + buffer[decimalMarkerPosition] = '.'; + } + if (m_exponent == 0) { + return currentChar; + } + if (currentChar >= bufferSize-1) { return bufferSize-1; } + buffer[currentChar++] = Ion::Charset::Exponent; + currentChar += Integer(m_exponent).writeTextInBuffer(buffer+currentChar, bufferSize-currentChar); + return currentChar; + } + /* Case 2: Print a natural decimal number */ + if (m_mantissa.isNegative()) { + buffer[currentChar++] = '-'; + } + if (m_exponent < 0) { + for (int i = 0; i <= -m_exponent; i++) { + if (currentChar >= bufferSize-1) { return bufferSize-1; } + if (i == 1) { + buffer[currentChar++] = '.'; + continue; + } + buffer[currentChar++] = '0'; + } + } + /* If mantissa is negative, m_mantissa.writeTextInBuffer is going to add an + * unwanted '-' in place of the temp char. We store it to replace it back + * after calling m_mantissa.writeTextInBuffer. */ + char tempChar; + int tempCharPosition; + if (m_mantissa.isNegative()) { + currentChar--; + tempChar = buffer[currentChar]; + tempCharPosition = currentChar; + } + currentChar += m_mantissa.writeTextInBuffer(buffer+currentChar, bufferSize-currentChar); + if (m_mantissa.isNegative()) { // replace the temp char back + buffer[tempCharPosition] = tempChar; + } + int currentExponent = m_mantissa.isNegative() ? currentChar-2 : currentChar-1; + if (m_exponent >= 0 && m_exponent < currentExponent) { + if (currentChar+1 >= bufferSize-1) { return bufferSize-1; } + int decimalMarkerPosition = m_mantissa.isNegative() ? m_exponent +1 : m_exponent; + for (int i = currentChar-1; i > decimalMarkerPosition; i--) { + buffer[i+1] = buffer[i]; + } + buffer[decimalMarkerPosition+1] = '.'; + currentChar++; + } + if (m_exponent >= 0 && m_exponent > currentExponent) { + int decimalMarkerPosition = m_mantissa.isNegative() ? m_exponent+1 : m_exponent; + for (int i = currentChar-1; i < decimalMarkerPosition; i++) { + if (currentChar+1 >= bufferSize-1) { return bufferSize-1; } + buffer[currentChar++] = '0'; + } + } + buffer[currentChar] = 0; + return currentChar; +} + +ExpressionLayout * Decimal::privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const { + char buffer[255]; + int numberOfChars = writeTextInBuffer(buffer, 255); + return new StringLayout(buffer, numberOfChars); +} + +Expression * Decimal::shallowReduce(Context& context, AngleUnit angleUnit) { + Expression * e = Expression::shallowReduce(context, angleUnit); + if (e != this) { + return e; + } + // Do not reduce decimal to rational if the exponent is too big or too small. + if (m_exponent > k_maxDoubleExponent || m_exponent < -k_maxDoubleExponent) { + return this; // TODO: return new Infinite() ? new Rational(0) ? + } + int numberOfDigits = numberOfDigitsInMantissaWithoutSign(); + Integer numerator = m_mantissa; + Integer denominator = Integer(1); + if (m_exponent >= numberOfDigits-1) { + numerator = Integer::Multiplication(m_mantissa, Integer::Power(Integer(10), Integer(m_exponent-numberOfDigits+1))); + } else { + denominator = Integer::Power(Integer(10), Integer(numberOfDigits-1-m_exponent)); + } + return replaceWith(new Rational(numerator, denominator), true); +} + +Expression * Decimal::shallowBeautify(Context & context, AngleUnit angleUnit) { + if (m_mantissa.isNegative()) { + m_mantissa.setNegative(false); + Opposite * o = new Opposite(this, true); + return replaceWith(o, true); + } + return this; +} + +int Decimal::simplificationOrderSameType(const Expression * e) const { + assert(e->type() == Type::Decimal); + const Decimal * other = static_cast(e); + if (sign() == Sign::Negative && other->sign() == Sign::Positive) { + return -1; + } + if (sign() == Sign::Positive && other->sign() == Sign::Negative) { + return 1; + } + assert(sign() == other->sign()); + int unsignedComparison = 0; + if (exponent() < other->exponent()) { + unsignedComparison = -1; + } else if (exponent() > other->exponent()) { + unsignedComparison = 1; + } else { + assert(exponent() == other->exponent()); + unsignedComparison = Integer::NaturalOrder(mantissa(), other->mantissa()); + } + return ((int)sign())*unsignedComparison; +} + +int Decimal::numberOfDigitsInMantissaWithoutSign() const { + int numberOfDigits = 1; + Integer mantissaCopy = m_mantissa; + mantissaCopy.setNegative(false); + IntegerDivision d = Integer::Division(mantissaCopy, Integer(10)); + while (!d.quotient.isZero()) { + mantissaCopy = d.quotient; + d = Integer::Division(mantissaCopy, Integer(10)); + numberOfDigits++; + } + return numberOfDigits; +} + +} diff --git a/poincare/src/derivative.cpp b/poincare/src/derivative.cpp index 525ac242e..cfea6e23e 100644 --- a/poincare/src/derivative.cpp +++ b/poincare/src/derivative.cpp @@ -1,6 +1,8 @@ #include #include #include +#include +#include #include extern "C" { #include @@ -9,41 +11,47 @@ extern "C" { namespace Poincare { -Derivative::Derivative() : - Function("diff", 2) -{ -} - Expression::Type Derivative::type() const { return Type::Derivative; } -Expression * Derivative::cloneWithDifferentOperands(Expression** newOperands, - int numberOfOperands, bool cloneOperands) const { - assert(newOperands != nullptr); - Derivative * d = new Derivative(); - d->setArgument(newOperands, numberOfOperands, cloneOperands); - return d; +Expression * Derivative::clone() const { + Derivative * a = new Derivative(m_operands, true); + return a; +} + +Expression * Derivative::shallowReduce(Context& context, AngleUnit angleUnit) { + Expression * e = Expression::shallowReduce(context, angleUnit); + if (e != this) { + return e; + } +#if MATRIX_EXACT_REDUCING + if (operand(0)->type() == Type::Matrix || operand(1)->type() == Type::Matrix) { + return replaceWith(new Undefined(), true); + } +#endif + // TODO: to be implemented diff(+) -> +diff() etc + return this; } template -Evaluation * Derivative::templatedEvaluate(Context& context, AngleUnit angleUnit) const { +Expression * Derivative::templatedEvaluate(Context& context, AngleUnit angleUnit) const { static T min = sizeof(T) == sizeof(double) ? DBL_MIN : FLT_MIN; static T max = sizeof(T) == sizeof(double) ? DBL_MAX : FLT_MAX; VariableContext xContext = VariableContext('x', &context); - Symbol xSymbol = Symbol('x'); - Evaluation * xInput = m_args[1]->evaluate(context, angleUnit); - T x = xInput->toScalar(); + Symbol xSymbol('x'); + Expression * xInput = operand(1)->evaluate(context, angleUnit); + T x = xInput->type() == Type::Complex ? static_cast *>(xInput)->toScalar() : NAN; delete xInput; Complex e = Complex::Float(x); - xContext.setExpressionForSymbolName(&e, &xSymbol); - Evaluation * fInput = m_args[1]->evaluate(xContext, angleUnit); - T functionValue = fInput->toScalar(); + xContext.setExpressionForSymbolName(&e, &xSymbol, xContext); + Expression * fInput = operand(0)->evaluate(xContext, angleUnit); + T functionValue = fInput->type() == Type::Complex ? static_cast *>(fInput)->toScalar() : NAN; delete fInput; // No complex/matrix version of Derivative if (std::isnan(x) || std::isnan(functionValue)) { - return new Complex(Complex::Float(NAN)); + return new Complex(Complex::Float(NAN)); } /* Ridders' Algorithm @@ -112,37 +120,37 @@ Evaluation * Derivative::templatedEvaluate(Context& context, AngleUnit angleU template T Derivative::growthRateAroundAbscissa(T x, T h, VariableContext xContext, AngleUnit angleUnit) const { - Symbol xSymbol = Symbol('x'); + Symbol xSymbol('x'); Complex e = Complex::Float(x + h); - xContext.setExpressionForSymbolName(&e, &xSymbol); - Evaluation * fInput = m_args[0]->evaluate(xContext, angleUnit); - T expressionPlus = fInput->toScalar(); + xContext.setExpressionForSymbolName(&e, &xSymbol, xContext); + Expression * fInput = operand(0)->evaluate(xContext, angleUnit); + T expressionPlus = fInput->type() == Type::Complex ? static_cast *>(fInput)->toScalar() : NAN; delete fInput; e = Complex::Float(x-h); - xContext.setExpressionForSymbolName(&e, &xSymbol); - fInput = m_args[0]->evaluate(xContext, angleUnit); - T expressionMinus = fInput->toScalar(); + xContext.setExpressionForSymbolName(&e, &xSymbol, xContext); + fInput = operand(0)->evaluate(xContext, angleUnit); + T expressionMinus = fInput->type() == Type::Complex ? static_cast *>(fInput)->toScalar() : NAN; delete fInput; return (expressionPlus - expressionMinus)/(2*h); } template T Derivative::approximateDerivate2(T x, T h, VariableContext xContext, AngleUnit angleUnit) const { - Symbol xSymbol = Symbol('x'); + Symbol xSymbol('x'); Complex e = Complex::Float(x + h); - xContext.setExpressionForSymbolName(&e, &xSymbol); - Evaluation * fInput = m_args[0]->evaluate(xContext, angleUnit); - T expressionPlus = fInput->toScalar(); + xContext.setExpressionForSymbolName(&e, &xSymbol, xContext); + Expression * fInput = operand(0)->evaluate(xContext, angleUnit); + T expressionPlus = fInput->type() == Type::Complex ? static_cast *>(fInput)->toScalar() : NAN; delete fInput; e = Complex::Float(x); - xContext.setExpressionForSymbolName(&e, &xSymbol); - fInput = m_args[0]->evaluate(xContext, angleUnit); - T expression = fInput->toScalar(); + xContext.setExpressionForSymbolName(&e, &xSymbol, xContext); + fInput = operand(0)->evaluate(xContext, angleUnit); + T expression = fInput->type() == Type::Complex ? static_cast *>(fInput)->toScalar() : NAN; delete fInput; e = Complex::Float(x-h); - xContext.setExpressionForSymbolName(&e, &xSymbol); - fInput = m_args[0]->evaluate(xContext, angleUnit); - T expressionMinus = fInput->toScalar(); + xContext.setExpressionForSymbolName(&e, &xSymbol, xContext); + fInput = operand(0)->evaluate(xContext, angleUnit); + T expressionMinus = fInput->type() == Type::Complex ? static_cast *>(fInput)->toScalar() : NAN; delete fInput; return expressionPlus - 2.0*expression + expressionMinus; } diff --git a/poincare/src/determinant.cpp b/poincare/src/determinant.cpp index 314bd4230..cf19a054a 100644 --- a/poincare/src/determinant.cpp +++ b/poincare/src/determinant.cpp @@ -7,28 +7,42 @@ extern "C" { namespace Poincare { -Determinant::Determinant() : - Function("det") -{ -} - Expression::Type Determinant::type() const { return Type::Determinant; } -Expression * Determinant::cloneWithDifferentOperands(Expression** newOperands, - int numberOfOperands, bool cloneOperands) const { - assert(newOperands != nullptr); - Determinant * d = new Determinant(); - d->setArgument(newOperands, numberOfOperands, cloneOperands); - return d; +Expression * Determinant::clone() const { + Determinant * a = new Determinant(m_operands, true); + return a; } -template -Evaluation * Determinant::templatedEvaluate(Context& context, AngleUnit angleUnit) const { +Expression * Determinant::shallowReduce(Context& context, AngleUnit angleUnit) { + Expression * e = Expression::shallowReduce(context, angleUnit); + if (e != this) { + return e; + } + Expression * op = editableOperand(0); +#if MATRIX_EXACT_REDUCING + if (!op->recursivelyMatches(Expression::IsMatrix)) { + return replaceWith(op, true); + } + return this; +#else + return replaceWith(op, true); +#endif +} - Evaluation * input = m_args[0]->evaluate(context, angleUnit); - Evaluation * result = input->createDeterminant(); +// TODO: handle this exactly in shallowReduce for small dimensions. +template +Expression * Determinant::templatedEvaluate(Context& context, AngleUnit angleUnit) const { + Expression * input = operand(0)->evaluate(context, angleUnit); + Expression * result = nullptr; + if (input->type() == Type::Complex) { + result = input->clone(); + } else { + assert(input->type() == Type::Matrix); + result = static_cast(input)->createDeterminant(); + } delete input; return result; } diff --git a/poincare/src/fraction.cpp b/poincare/src/division.cpp similarity index 58% rename from poincare/src/fraction.cpp rename to poincare/src/division.cpp index 36572da98..8d03c7509 100644 --- a/poincare/src/fraction.cpp +++ b/poincare/src/division.cpp @@ -3,32 +3,41 @@ extern "C" { #include #include } -#include -#include + +#include +#include +#include +#include #include +#include #include "layout/fraction_layout.h" +#include namespace Poincare { -Expression * Fraction::cloneWithDifferentOperands(Expression** newOperands, - int numberOfOperands, bool cloneOperands) const { - assert(numberOfOperands == 2); - assert(newOperands != nullptr); - return new Fraction(newOperands, cloneOperands); +Expression::Type Division::type() const { + return Type::Division; } -ExpressionLayout * Fraction::privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const { - assert(floatDisplayMode != FloatDisplayMode::Default); - assert(complexFormat != ComplexFormat::Default); - return new FractionLayout(m_operands[0]->createLayout(floatDisplayMode, complexFormat), m_operands[1]->createLayout(floatDisplayMode, complexFormat)); +Expression * Division::clone() const { + return new Division(m_operands, true); } -Expression::Type Fraction::type() const { - return Type::Fraction; +Expression * Division::shallowReduce(Context& context, AngleUnit angleUnit) { + Expression * e = Expression::shallowReduce(context, angleUnit); + if (e != this) { + return e; + } + Power * p = new Power(operand(1), new Rational(-1), false); + Multiplication * m = new Multiplication(operand(0), p, false); + detachOperands(); + p->deepReduce(context, angleUnit); + replaceWith(m, true); + return m->shallowReduce(context, angleUnit); } template -Complex Fraction::compute(const Complex c, const Complex d) { +Complex Division::compute(const Complex c, const Complex d) { // We want to avoid multiplies in the middle of the calculation that could overflow. // aa, ab, ba, bb, min, max = |d.a| <= |d.b| ? (c.a, c.b, -c.a, c.b, d.a, d.b) : (c.b, c.a, c.b, -c.a, d.b, d.a) // c c.a+c.b*i d.a-d.b*i 1/max (c.a+c.b*i) * (d.a-d.b*i) / max @@ -72,24 +81,35 @@ Complex Fraction::compute(const Complex c, const Complex d) { return Complex::Cartesian((temp*aa + ab) / norm, (temp*bb + ba) / norm); } -template Evaluation * Fraction::templatedComputeOnComplexAndComplexMatrix(const Complex * c, Evaluation * n) const { - Evaluation * inverse = n->createInverse(); - Evaluation * result = Multiplication::computeOnComplexAndMatrix(c, inverse); +ExpressionLayout * Division::privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const { + assert(floatDisplayMode != FloatDisplayMode::Default); + assert(complexFormat != ComplexFormat::Default); + const Expression * numerator = operand(0)->type() == Type::Parenthesis ? operand(0)->operand(0) : operand(0); + const Expression * denominator = operand(1)->type() == Type::Parenthesis ? operand(1)->operand(0) : operand(1); + return new FractionLayout(numerator->createLayout(floatDisplayMode, complexFormat), denominator->createLayout(floatDisplayMode, complexFormat)); +} + +template Matrix * Division::computeOnComplexAndMatrix(const Complex * c, const Matrix * n) { + Matrix * inverse = n->createInverse(); + if (inverse == nullptr) { + return nullptr; + } + Matrix * result = Multiplication::computeOnComplexAndMatrix(c, inverse); delete inverse; return result; } -template Evaluation * Fraction::templatedComputeOnComplexMatrices(Evaluation * m, Evaluation * n) const { +template Matrix * Division::computeOnMatrices(const Matrix * m, const Matrix * n) { if (m->numberOfColumns() != n->numberOfColumns()) { return nullptr; } - Evaluation * inverse = n->createInverse(); - Evaluation * result = Multiplication::computeOnMatrices(m, inverse); + Matrix * inverse = n->createInverse(); + if (inverse == nullptr) { + return nullptr; + } + Matrix * result = Multiplication::computeOnMatrices(m, inverse); delete inverse; return result; } -template Complex Fraction::compute(const Complex, const Complex); -template Complex Fraction::compute(const Complex, const Complex); - } diff --git a/poincare/src/division_quotient.cpp b/poincare/src/division_quotient.cpp index 2673a042f..938299b14 100644 --- a/poincare/src/division_quotient.cpp +++ b/poincare/src/division_quotient.cpp @@ -1,4 +1,6 @@ #include +#include +#include extern "C" { #include @@ -7,29 +9,60 @@ extern "C" { namespace Poincare { -DivisionQuotient::DivisionQuotient() : - Function("quo", 2) -{ -} - Expression::Type DivisionQuotient::type() const { return Type::DivisionQuotient; } -Expression * DivisionQuotient::cloneWithDifferentOperands(Expression** newOperands, - int numberOfOperands, bool cloneOperands) const { - assert(newOperands != nullptr); - DivisionQuotient * dq = new DivisionQuotient(); - dq->setArgument(newOperands, numberOfOperands, cloneOperands); - return dq; +Expression * DivisionQuotient::clone() const { + DivisionQuotient * a = new DivisionQuotient(m_operands, true); + return a; +} + +Expression * DivisionQuotient::shallowReduce(Context& context, AngleUnit angleUnit) { + Expression * e = Expression::shallowReduce(context, angleUnit); + if (e != this) { + return e; + } + Expression * op0 = editableOperand(0); + Expression * op1 = editableOperand(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()) { + return replaceWith(new Undefined(), true); + } + } + if (op1->type() == Type::Rational) { + Rational * r1 = static_cast(op1); + if (!r1->denominator().isOne()) { + 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 a = r0->numerator(); + Integer b = r1->numerator(); + if (b.isZero()) { + return replaceWith(new Undefined(), true); // TODO: new Infinite(a.isNegative()) + } + Integer result = Integer::Division(a, b).quotient; + return replaceWith(new Rational(result), true); } template -Evaluation * DivisionQuotient::templatedEvaluate(Context& context, AngleUnit angleUnit) const { - Evaluation * f1Input = m_args[0]->evaluate(context, angleUnit); - Evaluation * f2Input = m_args[1]->evaluate(context, angleUnit); - T f1 = f1Input->toScalar(); - T f2 = f2Input->toScalar(); +Complex * DivisionQuotient::templatedEvaluate(Context& context, AngleUnit angleUnit) const { + Expression * f1Input = operand(0)->evaluate(context, angleUnit); + Expression * f2Input = operand(1)->evaluate(context, angleUnit); + T f1 = f1Input->type() == Type::Complex ? static_cast *>(f1Input)->toScalar() : NAN; + T f2 = f2Input->type() == Type::Complex ? static_cast *>(f2Input)->toScalar() : NAN; delete f1Input; delete f2Input; if (std::isnan(f1) || std::isnan(f2) || f1 != (int)f1 || f2 != (int)f2) { diff --git a/poincare/src/division_remainder.cpp b/poincare/src/division_remainder.cpp index 0721024ec..ab631b010 100644 --- a/poincare/src/division_remainder.cpp +++ b/poincare/src/division_remainder.cpp @@ -1,4 +1,6 @@ #include +#include +#include extern "C" { #include @@ -7,29 +9,60 @@ extern "C" { namespace Poincare { -DivisionRemainder::DivisionRemainder() : - Function("rem", 2) -{ -} - Expression::Type DivisionRemainder::type() const { return Type::DivisionRemainder; } -Expression * DivisionRemainder::cloneWithDifferentOperands(Expression** newOperands, - int numberOfOperands, bool cloneOperands) const { - assert(newOperands != nullptr); - DivisionRemainder * dr = new DivisionRemainder(); - dr->setArgument(newOperands, numberOfOperands, cloneOperands); - return dr; +Expression * DivisionRemainder::clone() const { + DivisionRemainder * a = new DivisionRemainder(m_operands, true); + return a; +} + +Expression * DivisionRemainder::shallowReduce(Context& context, AngleUnit angleUnit) { + Expression * e = Expression::shallowReduce(context, angleUnit); + if (e != this) { + return e; + } + Expression * op0 = editableOperand(0); + Expression * op1 = editableOperand(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()) { + return replaceWith(new Undefined(), true); + } + } + if (op1->type() == Type::Rational) { + Rational * r1 = static_cast(op1); + if (!r1->denominator().isOne()) { + 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 a = r0->numerator(); + Integer b = r1->numerator(); + if (b.isZero()) { + return replaceWith(new Undefined(), true); // TODO: new Infinite(a.isNegative()) + } + Integer result = Integer::Division(a, b).remainder; + return replaceWith(new Rational(result), true); } template -Evaluation * DivisionRemainder::templatedEvaluate(Context& context, AngleUnit angleUnit) const { - Evaluation * f1Input = m_args[0]->evaluate(context, angleUnit); - Evaluation * f2Input = m_args[1]->evaluate(context, angleUnit); - T f1 = f1Input->toScalar(); - T f2 = f2Input->toScalar(); +Complex * DivisionRemainder::templatedEvaluate(Context& context, AngleUnit angleUnit) const { + Expression * f1Input = operand(0)->evaluate(context, angleUnit); + Expression * f2Input = operand(1)->evaluate(context, angleUnit); + T f1 = f1Input->type() == Type::Complex ? static_cast *>(f1Input)->toScalar() : NAN; + T f2 = f2Input->type() == Type::Complex ? static_cast *>(f2Input)->toScalar() : NAN; delete f1Input; delete f2Input; if (std::isnan(f1) || std::isnan(f2) || f1 != (int)f1 || f2 != (int)f2) { diff --git a/poincare/src/dynamic_hierarchy.cpp b/poincare/src/dynamic_hierarchy.cpp new file mode 100644 index 000000000..cb60ca350 --- /dev/null +++ b/poincare/src/dynamic_hierarchy.cpp @@ -0,0 +1,174 @@ +#include +extern "C" { +#include +#include +} + +namespace Poincare { + +DynamicHierarchy::DynamicHierarchy() : + Hierarchy(), + m_operands(nullptr), + m_numberOfOperands(0) +{ +} + +DynamicHierarchy::DynamicHierarchy(const Expression * const * operands, int numberOfOperands, bool cloneOperands) : + Hierarchy(), + m_numberOfOperands(numberOfOperands) +{ + assert(operands != nullptr); + m_operands = new const Expression * [numberOfOperands]; + for (int i=0; iclone(); + } else { + m_operands[i] = operands[i]; + } + const_cast(m_operands[i])->setParent(this); + } +} + +DynamicHierarchy::~DynamicHierarchy() { + if (m_operands != nullptr) { + for (int i = 0; i < m_numberOfOperands; i++) { + if (m_operands[i] != nullptr) { + delete m_operands[i]; + } + } + } + delete[] m_operands; +} + +void DynamicHierarchy::addOperands(const Expression * const * operands, int numberOfOperands) { + assert(numberOfOperands > 0); + const Expression ** newOperands = new const Expression * [m_numberOfOperands+numberOfOperands]; + for (int i=0; i(operands[i])->setParent(this); + newOperands[i+m_numberOfOperands] = operands[i]; + } + delete[] m_operands; + m_operands = newOperands; + m_numberOfOperands += numberOfOperands; +} + +void DynamicHierarchy::mergeOperands(DynamicHierarchy * d) { + removeOperand(d, false); + addOperands(d->operands(), d->numberOfOperands()); + d->detachOperands(); + delete d; +} + +void DynamicHierarchy::addOperand(Expression * operand) { + addOperandAtIndex(operand, m_numberOfOperands); +} + +void DynamicHierarchy::addOperandAtIndex(Expression * operand, int index) { + assert(index >= 0 && index <= m_numberOfOperands); + const Expression ** newOperands = new const Expression * [m_numberOfOperands+1]; + int j = 0; + for (int i=0; i<=m_numberOfOperands; i++) { + if (i == index) { + operand->setParent(this); + newOperands[i] = operand; + } else { + newOperands[i] = m_operands[j++]; + } + } + delete[] m_operands; + m_operands = newOperands; + m_numberOfOperands += 1; +} + +void DynamicHierarchy::removeOperand(const Expression * e, bool deleteAfterRemoval) { + for (int i=0; i 0; i--) { + bool isSorted = true; + for (int j = 0; j < numberOfOperands()-1; j++) { + /* Warning: Matrix operations are not always commutative (ie, + * multiplication) so we never swap 2 matrices. */ + if (order(operand(j), operand(j+1)) > 0 && (!operand(j)->recursivelyMatches(Expression::IsMatrix) || !operand(j+1)->recursivelyMatches(Expression::IsMatrix))) { + swapOperands(j, j+1); + isSorted = false; + } + } + if (isSorted) { + return; + } + } +} + +Expression * DynamicHierarchy::squashUnaryHierarchy() { + if (numberOfOperands() == 1) { + assert(parent() != nullptr); + Expression * o = editableOperand(0); + replaceWith(o, true); + return o; + } + return this; +} + +// Private + +void DynamicHierarchy::removeOperandAtIndex(int i, bool deleteAfterRemoval) { + if (deleteAfterRemoval) { + delete m_operands[i]; + } else { + const_cast(m_operands[i])->setParent(nullptr); + } + m_numberOfOperands--; + for (int j=i; jnumberOfOperands(); + int n = e->numberOfOperands(); + for (int i = 1; i <= m; i++) { + // The NULL node is the least node type. + if (n < i) { + return 1; + } + if (SimplificationOrder(this->operand(m-i), e->operand(n-i)) != 0) { + return SimplificationOrder(this->operand(m-i), e->operand(n-i)); + } + } + // The NULL node is the least node type. + if (n > m) { + return -1; + } + return 0; +} + +int DynamicHierarchy::simplificationOrderGreaterType(const Expression * e) const { + int m = numberOfOperands(); + if (m == 0) { + return -1; + } + /* Compare e to last term of hierarchy. */ + if (SimplificationOrder(operand(m-1), e) != 0) { + return SimplificationOrder(operand(m-1), e); + } + if (m > 1) { + return 1; + } + return 0; +} + +} diff --git a/poincare/src/evaluation.cpp b/poincare/src/evaluation.cpp deleted file mode 100644 index 24fad4d16..000000000 --- a/poincare/src/evaluation.cpp +++ /dev/null @@ -1,201 +0,0 @@ -#include -extern "C" { -#include -#include -} -#include -#include -#include -#include "layout/grid_layout.h" -#include "layout/bracket_layout.h" -#include -#include -#include - -namespace Poincare { - -template -Expression::Type Evaluation::type() const { - return Type::Evaluation; -} - -template -bool Evaluation::hasValidNumberOfArguments() const { - return true; -} - -template -const Expression * Evaluation::operand(int i) const { - return complexOperand(i); -} - -template -Evaluation * Evaluation::createTrace() const { - if (numberOfRows() != numberOfColumns()) { - return new Complex(Complex::Float(NAN)); - } - int dim = numberOfRows(); - Complex c = Complex::Float(0); - for (int i = 0; i < dim; i++) { - c = Addition::compute(c, *complexOperand(i*dim+i)); - } - return new Complex(c); -} - -template -// TODO: implement determinant for complex matrix? -Evaluation * Evaluation::createDeterminant() const { - if (numberOfRows() != numberOfColumns()) { - return new Complex(Complex::Float(NAN)); - } - int dim = numberOfRows(); - T ** tempMat = new T*[dim]; - for (int i = 0; i < dim; i++) { - tempMat[i] = new T[dim]; - } - T det = 1; - /* Copy the matrix */ - for (int i = 0; i < dim; i++) { - for (int j = 0; j < dim; j++) { - tempMat[i][j] = complexOperand(i*dim+ j)->toScalar(); - } - } - - /* Main Loop: Gauss pivot */ - for (int i = 0; i < dim-1; i++) { - /* Search for pivot */ - int rowWithPivot = i; - for (int row = i+1; row < dim; row++) { - if (std::fabs(tempMat[rowWithPivot][i]) < std::fabs(tempMat[row][i])) { - rowWithPivot = row; - } - } - T valuePivot = tempMat[rowWithPivot][i]; - /* if the pivot is null, det = 0. */ - if (std::fabs(valuePivot) <= FLT_EPSILON) { - for (int i = 0; i < dim; i++) { - free(tempMat[i]); - } - free(tempMat); - return new Complex(Complex::Float(0.0f)); - } - /* Switch rows to have the pivot row as first row */ - if (rowWithPivot != i) { - for (int col = i; col < dim; col++) { - T temp = tempMat[i][col]; - tempMat[i][col] = tempMat[rowWithPivot][col]; - tempMat[rowWithPivot][col] = temp; - } - det *= -1; - } - det *= valuePivot; - /* Set to 0 all A[][i] by linear combination */ - for (int row = i+1; row < dim; row++) { - T factor = tempMat[row][i]/valuePivot; - for (int col = i; col < dim; col++) { - tempMat[row][col] -= factor*tempMat[i][col]; - } - } - } - det *= tempMat[dim-1][dim-1]; - for (int i = 0; i < dim; i++) { - delete[] tempMat[i]; - } - delete[] tempMat; - return new Complex(Complex::Float(det)); -} - -template -Evaluation * Evaluation::createInverse() const { - if (numberOfRows() != numberOfColumns()) { - return new Complex(Complex::Float(NAN)); - } - int dim = numberOfRows(); - /* Create the matrix inv = (A|I) with A the input matrix and I the dim identity matrix */ - T ** inv = new T*[dim]; - for (int i = 0; i < dim; i++) { - inv[i] = new T [2*dim]; - } - for (int i = 0; i < dim; i++) { - for (int j = 0; j < dim; j++) { - inv[i][j] = complexOperand(i*dim+j)->toScalar(); - } - for (int j = dim; j < 2*dim; j++) { - inv[i][j] = (i+dim == j); - } - } - /* Main Loop: Gauss pivot */ - for (int i = 0; i < dim; i++) { - /* Search for pivot */ - int rowWithPivot = i; - for (int row = i+1; row < dim; row++) { - if (std::fabs(inv[rowWithPivot][i]) < std::fabs(inv[row][i])) { - rowWithPivot = row; - } - } - T valuePivot = inv[rowWithPivot][i]; - /* if the pivot is null, the matrix in not invertible. */ - if (std::fabs(valuePivot) <= FLT_EPSILON) { - for (int i = 0; i < dim; i++) { - free(inv[i]); - } - free(inv); - return new Complex(Complex::Float(NAN)); - } - /* Switch rows to have the pivot row as first row */ - if (rowWithPivot != i) { - for (int col = i; col < 2*dim; col++) { - T temp = inv[i][col]; - inv[i][col] = inv[rowWithPivot][col]; - inv[rowWithPivot][col] = temp; - } - } - /* A[pivot][] = A[pivot][]/valuePivot */ - for (int col = 0; col < 2*dim; col++) { - inv[i][col] /= valuePivot; - } - /* Set to 0 all A[][row] by linear combination */ - for (int row = 0; row < dim; row++) { - if (row == i) { - continue; - } - T factor = inv[row][i]; - for (int col = 0; col < 2*dim; col++) { - inv[row][col] -= factor*inv[i][col]; - } - } - } - Complex * operands = new Complex[numberOfOperands()]; - for (int i = 0; i < dim; i++) { - for (int j = 0; j < dim; j++) { - operands[i*dim+j] = Complex(Complex::Float(inv[i][j+dim])); - } - } - for (int i = 0; i < dim; i++) { - delete[] inv[i]; - } - delete[] inv; - // Intentionally swapping dimensions for inverse, although it doesn't make a difference because it is square - Evaluation * matrix = new ComplexMatrix(operands, numberOfColumns(), numberOfRows()); - delete[] operands; - return matrix; -} - -template -Evaluation * Evaluation::createTranspose() const { - Complex * operands = new Complex[numberOfOperands()]; - for (int i = 0; i < numberOfRows(); i++) { - for (int j = 0; j < numberOfColumns(); j++) { - operands[j*numberOfRows()+i] = *(complexOperand(i*numberOfColumns()+j)); - } - } - // Intentionally swapping dimensions for transpose - Evaluation * matrix = new ComplexMatrix(operands, numberOfColumns(), numberOfRows()); - delete[] operands; - return matrix; -} - -template class Poincare::Evaluation; -template class Poincare::Evaluation; - -} diff --git a/poincare/src/evaluation_engine.cpp b/poincare/src/evaluation_engine.cpp new file mode 100644 index 000000000..ddab709b7 --- /dev/null +++ b/poincare/src/evaluation_engine.cpp @@ -0,0 +1,104 @@ +#include +#include +#include +extern "C" { +#include +} + +namespace Poincare { + +template Expression * EvaluationEngine::map(const Expression * expression, Context& context, Expression::AngleUnit angleUnit, ComplexCompute compute) { + assert(expression->numberOfOperands() == 1); + Expression * input = expression->operand(0)->evaluate(context, angleUnit); + Expression * result = nullptr; + if (input->type() == Expression::Type::Complex) { + Complex * c = static_cast *>(input); + result = new Complex(compute(*c, angleUnit)); + } else { + assert(input->type() == Expression::Type::Matrix); + Expression ** operands = new Expression * [input->numberOfOperands()]; + for (int i = 0; i < input->numberOfOperands(); i++) { + assert(input->operand(i)->type() == Expression::Type::Complex); + const Complex * c = static_cast *>(input->operand(i)); + operands[i] = new Complex(compute(*c, angleUnit)); + } + result = new Matrix(operands, static_cast(input)->numberOfRows(), static_cast(input)->numberOfColumns(), false); + delete[] operands; + } + delete input; + return result; +} + +template Expression * EvaluationEngine::mapReduce(const Expression * expression, Context& context, Expression::AngleUnit angleUnit, ComplexAndComplexReduction computeOnComplexes, ComplexAndMatrixReduction computeOnComplexAndMatrix, MatrixAndComplexReduction computeOnMatrixAndComplex, MatrixAndMatrixReduction computeOnMatrices) { + Expression * result = expression->operand(0)->evaluate(context, angleUnit); + for (int i = 1; i < expression->numberOfOperands(); i++) { + Expression * intermediateResult = nullptr; + Expression * nextOperandEvaluation = expression->operand(i)->evaluate(context, angleUnit); + if (result->type() == Expression::Type::Complex && nextOperandEvaluation->type() == Expression::Type::Complex) { + const Complex * c = static_cast *>(result); + const Complex * d = static_cast *>(nextOperandEvaluation); + intermediateResult = new Complex(computeOnComplexes(*c, *d)); + } else if (result->type() == Expression::Type::Complex) { + const Complex * c = static_cast *>(result); + assert(nextOperandEvaluation->type() == Expression::Type::Matrix); + const Matrix * n = static_cast(nextOperandEvaluation); + intermediateResult = computeOnComplexAndMatrix(c, n); + } else if (nextOperandEvaluation->type() == Expression::Type::Complex) { + assert(result->type() == Expression::Type::Matrix); + const Matrix * m = static_cast(result); + const Complex * d = static_cast *>(nextOperandEvaluation); + intermediateResult = computeOnMatrixAndComplex(m, d); + } else { + assert(result->type() == Expression::Type::Matrix); + const Matrix * m = static_cast(result); + assert(nextOperandEvaluation->type() == Expression::Type::Matrix); + const Matrix * n = static_cast(nextOperandEvaluation); + intermediateResult = computeOnMatrices(m, n); + } + delete result; + delete nextOperandEvaluation; + result = intermediateResult; + if (result == nullptr) { + return new Complex(Complex::Float(NAN)); + } + } + return result; +} + +template Matrix * EvaluationEngine::elementWiseOnComplexAndComplexMatrix(const Complex * c, const Matrix * m, ComplexAndComplexReduction computeOnComplexes) { + Expression ** operands = new Expression * [m->numberOfRows()*m->numberOfColumns()]; + for (int i = 0; i < m->numberOfOperands(); i++) { + const Complex * d = static_cast *>(m->operand(i)); + operands[i] = new Complex(computeOnComplexes(*d, *c)); + } + Matrix * result = new Matrix(operands, m->numberOfRows(), m->numberOfColumns(), false); + delete[] operands; + return result; +} + +template Matrix * EvaluationEngine::elementWiseOnComplexMatrices(const Matrix * m, const Matrix * n, ComplexAndComplexReduction computeOnComplexes) { + if (m->numberOfRows() != n->numberOfRows() || m->numberOfColumns() != n->numberOfColumns()) { + return nullptr; + } + Expression ** operands = new Expression * [m->numberOfRows()*m->numberOfColumns()]; + for (int i = 0; i < m->numberOfOperands(); i++) { + const Complex * c = static_cast *>(m->operand(i)); + const Complex * d = static_cast *>(n->operand(i)); + operands[i] = new Complex(computeOnComplexes(*c, *d)); + } + Matrix * result = new Matrix(operands, m->numberOfRows(), m->numberOfColumns(), false); + delete[] operands; + return result; +} + +template Poincare::Expression * Poincare::EvaluationEngine::map(const Poincare::Expression * expression, Poincare::Context& context, Poincare::Expression::AngleUnit angleUnit, Poincare::EvaluationEngine::ComplexCompute compute); +template Poincare::Expression * Poincare::EvaluationEngine::map(const Poincare::Expression * expression, Poincare::Context& context, Poincare::Expression::AngleUnit angleUnit, Poincare::EvaluationEngine::ComplexCompute compute); +template Poincare::Expression * Poincare::EvaluationEngine::mapReduce(const Poincare::Expression * expression, Poincare::Context& context, Poincare::Expression::AngleUnit angleUnit, Poincare::EvaluationEngine::ComplexAndComplexReduction computeOnComplexes, Poincare::EvaluationEngine::ComplexAndMatrixReduction computeOnComplexAndMatrix, Poincare::EvaluationEngine::MatrixAndComplexReduction computeOnMatrixAndComplex, Poincare::EvaluationEngine::MatrixAndMatrixReduction computeOnMatrices); +template Poincare::Expression * Poincare::EvaluationEngine::mapReduce(const Poincare::Expression * expression, Poincare::Context& context, Poincare::Expression::AngleUnit angleUnit, Poincare::EvaluationEngine::ComplexAndComplexReduction computeOnComplexes, Poincare::EvaluationEngine::ComplexAndMatrixReduction computeOnComplexAndMatrix, Poincare::EvaluationEngine::MatrixAndComplexReduction computeOnMatrixAndComplex, Poincare::EvaluationEngine::MatrixAndMatrixReduction computeOnMatrices); +template Poincare::Matrix * Poincare::EvaluationEngine::elementWiseOnComplexAndComplexMatrix(Poincare::Complex const*, const Poincare::Matrix *, Poincare::Complex (*)(Poincare::Complex, Poincare::Complex)); +template Poincare::Matrix* Poincare::EvaluationEngine::elementWiseOnComplexAndComplexMatrix(Poincare::Complex const*, const Poincare::Matrix*, Poincare::Complex (*)(Poincare::Complex, Poincare::Complex)); +template Poincare::Matrix* Poincare::EvaluationEngine::elementWiseOnComplexMatrices(const Poincare::Matrix*, const Poincare::Matrix*, Poincare::Complex (*)(Poincare::Complex, Poincare::Complex)); +template Poincare::Matrix* Poincare::EvaluationEngine::elementWiseOnComplexMatrices(const Poincare::Matrix*, const Poincare::Matrix*, Poincare::Complex (*)(Poincare::Complex, Poincare::Complex)); + + +} diff --git a/poincare/src/expression.cpp b/poincare/src/expression.cpp index 1ce6953b5..06e605079 100644 --- a/poincare/src/expression.cpp +++ b/poincare/src/expression.cpp @@ -1,27 +1,27 @@ #include #include -#include #include +#include +#include #include #include -#include +#include +#include +#include +#include +#include #include #include "expression_parser.hpp" #include "expression_lexer.hpp" -extern "C" { -#include -} - -#include "simplify/rules.h" int poincare_expression_yyparse(Poincare::Expression ** expressionOutput); namespace Poincare { -static Expression::CircuitBreaker sCircuitBreaker = nullptr; - #include +/* Constructor & Destructor */ + Expression * Expression::parse(char const * string) { if (string[0] == 0) { return nullptr; @@ -40,6 +40,72 @@ Expression * Expression::parse(char const * string) { return expression; } +/* Circuit breaker */ + +static Expression::CircuitBreaker sCircuitBreaker = nullptr; + +void Expression::setCircuitBreaker(CircuitBreaker cb) { + sCircuitBreaker = cb; +} + +bool Expression::shouldStopProcessing() { + if (sCircuitBreaker == nullptr) { + return false; + } + return sCircuitBreaker(); +} + +/* Hierarchy */ + +bool Expression::hasAncestor(const Expression * e) const { + assert(m_parent != this); + if (m_parent == e) { + return true; + } + if (m_parent == nullptr) { + return false; + } + return m_parent->hasAncestor(e); +} + +Expression * Expression::replaceWith(Expression * newOperand, bool deleteAfterReplace) { + assert(m_parent != nullptr); + m_parent->replaceOperand(this, newOperand, deleteAfterReplace); + return newOperand; +} + +/* Properties */ + +bool Expression::recursivelyMatches(ExpressionTest test) const { + if (test(this)) { + return true; + } + for (int i = 0; i < numberOfOperands(); i++) { + if (operand(i)->recursivelyMatches(test)) { + return true; + } + } + return false; +} + +bool Expression::IsMatrix(const Expression * e) { + return e->type() == Type::Matrix || e->type() == Type::ConfidenceInterval || e->type() == Type::MatrixDimension || e->type() == Type::PredictionInterval || e->type() == Type::MatrixInverse || e->type() == Type::MatrixTranspose; +} + +/* Comparison */ + +int Expression::SimplificationOrder(const Expression * e1, const Expression * e2) { + if (e1->type() > e2->type()) { + return -(e2->simplificationOrderGreaterType(e1)); + } else if (e1->type() == e2->type()) { + return e1->simplificationOrderSameType(e2); + } else { + return e1->simplificationOrderGreaterType(e2); + } +} + +/* Layout */ + ExpressionLayout * Expression::createLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const { switch (floatDisplayMode) { case FloatDisplayMode::Default: @@ -59,7 +125,64 @@ ExpressionLayout * Expression::createLayout(FloatDisplayMode floatDisplayMode, C } } -template Evaluation * Expression::evaluate(Context& context, AngleUnit angleUnit) const { +/* Simplification */ + +void Expression::Simplify(Expression ** expressionAddress, Context & context, AngleUnit angleUnit) { + if (angleUnit == AngleUnit::Default) { + angleUnit = Preferences::sharedPreferences()->angleUnit(); + } +#if MATRIX_EXACT_REDUCING +#else + if ((*expressionAddress)->recursivelyMatches(IsMatrix)) { + return; + } +#endif + SimplificationRoot root(*expressionAddress); + root.editableOperand(0)->deepReduce(context, angleUnit); + root.editableOperand(0)->deepBeautify(context, angleUnit); + *expressionAddress = root.editableOperand(0); +} + + +void Expression::Reduce(Expression ** expressionAddress, Context & context, AngleUnit angleUnit, bool recursively) { + SimplificationRoot root(*expressionAddress); + if (recursively) { + root.editableOperand(0)->deepReduce(context, angleUnit); + } else { + root.editableOperand(0)->shallowReduce(context,angleUnit); + } + *expressionAddress = root.editableOperand(0); +} + +Expression * Expression::deepReduce(Context & context, AngleUnit angleUnit) { + assert(parent() != nullptr); + for (int i = 0; i < numberOfOperands(); i++) { + editableOperand(i)->deepReduce(context, angleUnit); + } + return shallowReduce(context, angleUnit); +} + +Expression * Expression::shallowReduce(Context & context, AngleUnit angleUnit) { + for (int i = 0; i < numberOfOperands(); i++) { + if (editableOperand(i)->type() == Type::Undefined && this->type() != Type::SimplificationRoot) { + return replaceWith(new Undefined(), true); + } + } + return this; +} + +Expression * Expression::deepBeautify(Context & context, AngleUnit angleUnit) { + assert(parent() != nullptr); + Expression * e = shallowBeautify(context, angleUnit); + for (int i = 0; i < e->numberOfOperands(); i++) { + e->editableOperand(i)->deepBeautify(context, angleUnit); + } + return e; +} + +/* Evaluation */ + +template Expression * Expression::evaluate(Context& context, AngleUnit angleUnit) const { switch (angleUnit) { case AngleUnit::Default: return privateEvaluate(T(), context, Preferences::sharedPreferences()->angleUnit()); @@ -69,21 +192,25 @@ template Evaluation * Expression::evaluate(Context& context, Angl } template T Expression::approximate(Context& context, AngleUnit angleUnit) const { - Evaluation * evaluation = evaluate(context, angleUnit); - T result = evaluation->toScalar(); + Expression * evaluation = evaluate(context, angleUnit); + assert(evaluation->type() == Type::Complex || evaluation->type() == Type::Matrix); + T result = NAN; + if (evaluation->type() == Type::Complex) { + result = static_cast *>(evaluation)->toScalar(); + } + /*if (evaluation->type() == Type::Matrix) { + if (numberOfOperands() == 1) { + result = static_cast *>(operand(0))->toScalar(); + } + }*/ delete evaluation; return result; } template T Expression::approximate(const char * text, Context& context, AngleUnit angleUnit) { Expression * exp = parse(text); - if (exp == nullptr) { - return NAN; - } - Evaluation * evaluation = exp->evaluate(context, angleUnit); + T result = exp->approximate(context, angleUnit); delete exp; - T result = evaluation->toScalar(); - delete evaluation; return result; } @@ -92,173 +219,10 @@ template T Expression::epsilon() { return epsilon; } -#if POINCARE_SIMPLIFY -Expression * Expression::simplify() const { - /* We make sure that the simplification is deletable. - * Indeed, we don't want an expression with some parts deletable and some not - */ - - // If we have a leaf node nothing can be simplified. - if (this->numberOfOperands()==0) { - return this->clone(); - } - - Expression * result = this->clone(); - Expression * tmp = nullptr; - - bool simplification_pass_was_useful = true; - while (simplification_pass_was_useful) { - /* We recursively simplify the children expressions. - * Note that we are sure to get the samne number of children as we had before - */ - Expression ** simplifiedOperands = new Expression * [result->numberOfOperands()]; - for (int i = 0; i < result->numberOfOperands(); i++) { - simplifiedOperands[i] = result->operand(i)->simplify(); - } - - /* Note that we don't need to clone the simplified children because they are - * already cloned before. */ - tmp = result->cloneWithDifferentOperands(simplifiedOperands, result->numberOfOperands(), false); - delete result; - result = tmp; - - // The table is no longer needed. - delete [] simplifiedOperands; - - simplification_pass_was_useful = false; - for (int i=0; isimplify(result); - if (simplified != nullptr) { - simplification_pass_was_useful = true; - delete result; - result = simplified; - break; - } - } - } - - return result; -} -#endif - -bool Expression::sequentialOperandsIdentity(const Expression * e) const { - /* Here we simply test all operands for identity in the order they are defined - * in. */ - for (int i=0; inumberOfOperands(); i++) { - if (!this->operand(i)->isIdenticalTo(e->operand(i))) { - return false; - } - } - return true; } -bool Expression::combinatoryCommutativeOperandsIdentity(const Expression * e, - bool * operandMatched, int leftToMatch) const { - if (leftToMatch == 0) { - return true; - } - - // We try to test for equality the i-th operand of our first expression. - int i = this->numberOfOperands() - leftToMatch; - for (int j = 0; jnumberOfOperands(); j++) { - /* If the operand of the second expression has already been associated with - * a previous operand we skip it */ - if (operandMatched[j]) { - continue; - } - if (this->operand(i)->isIdenticalTo(e->operand(j))) { - // We managed to match this operand. - operandMatched[j] = true; - /* We check that we can match the rest in this configuration, if so we - * are good. */ - if (this->combinatoryCommutativeOperandsIdentity(e, operandMatched, leftToMatch - 1)) { - return true; - } - // Otherwise we backtrack. - operandMatched[j] = false; - } - } - - return false; -} - -bool Expression::commutativeOperandsIdentity(const Expression * e) const { - int leftToMatch = this->numberOfOperands(); - - /* We create a table allowing us to know which operands of the second - * expression have been associated with one of the operands of the first - * expression */ - bool * operandMatched = new bool [this->numberOfOperands()]; - for (int i(0); inumberOfOperands(); i++) { - operandMatched[i] = false; - } - - // We call our recursive helper. - bool commutativelyIdentical = this->combinatoryCommutativeOperandsIdentity(e, operandMatched, leftToMatch); - - delete [] operandMatched; - return commutativelyIdentical; -} - -bool Expression::isIdenticalTo(const Expression * e) const { - if (e->type() != this->type() || e->numberOfOperands() != this->numberOfOperands()) { - return false; - } - if (this->isCommutative()) { - if (!this->commutativeOperandsIdentity(e)) { - return false; - } - } else { - if (!this->sequentialOperandsIdentity(e)) { - return false; - } - } - return this->valueEquals(e); -} - -#if POINCARE_SIMPLIFY -bool Expression::isEquivalentTo(Expression * e) const { - Expression * a = this->simplify(); - Expression * b = e->simplify(); - bool result = a->isIdenticalTo(b); - delete a; - delete b; - return result; -} -#endif - -bool Expression::valueEquals(const Expression * e) const { - assert(this->type() == e->type()); - /* This behavior makes sense for value-less nodes (addition, product, fraction - * power, etc… For nodes with a value (Integer, Float), this must be over- - * -riden. */ - return true; -} - -bool Expression::isCommutative() const { - return false; -} - -int Expression::writeTextInBuffer(char * buffer, int bufferSize) const { - return 0; -} - -void Expression::setCircuitBreaker(CircuitBreaker cb) { - sCircuitBreaker = cb; -} - -bool Expression::shouldStopProcessing() const { - if (sCircuitBreaker == nullptr) { - return false; - } - return sCircuitBreaker(this); -} - -} - -template Poincare::Evaluation * Poincare::Expression::evaluate(Context& context, AngleUnit angleUnit) const; -template Poincare::Evaluation * Poincare::Expression::evaluate(Context& context, AngleUnit angleUnit) const; +template Poincare::Expression * Poincare::Expression::evaluate(Context& context, AngleUnit angleUnit) const; +template Poincare::Expression * Poincare::Expression::evaluate(Context& context, AngleUnit angleUnit) const; template double Poincare::Expression::approximate(char const*, Poincare::Context&, Poincare::Expression::AngleUnit); template float Poincare::Expression::approximate(char const*, Poincare::Context&, Poincare::Expression::AngleUnit); template double Poincare::Expression::approximate(Poincare::Context&, Poincare::Expression::AngleUnit) const; diff --git a/poincare/src/expression_debug.cpp b/poincare/src/expression_debug.cpp index 4ba3717dd..527317f2f 100644 --- a/poincare/src/expression_debug.cpp +++ b/poincare/src/expression_debug.cpp @@ -1,8 +1,14 @@ -#include +#include #include #include +#include +#include +#include +#include #include +namespace Poincare { + void print_expression(const Expression * e, int indentationLevel) { if (indentationLevel>0) { for (int i=0; itype()) { + case Expression::Type::AbsoluteValue: + std::cout << "AbsoluteValue"; + break; case Expression::Type::Addition: std::cout << "Addition"; break; + case Expression::Type::ArcCosine: + std::cout << "ArcCosine"; + break; + case Expression::Type::ArcSine: + std::cout << "ArcSine"; + break; + case Expression::Type::ArcTangent: + std::cout << "ArcTangent"; + break; + case Expression::Type::BinomialCoefficient: + std::cout << "BinomialCoefficient"; + break; + case Expression::Type::Ceiling: + std::cout << "Ceiling"; + break; + case Expression::Type::Complex: + std::cout << "Complex("; + std::cout << static_cast *>(e)->a(); + std::cout << ", "; + std::cout << static_cast *>(e)->b(); + std::cout << ")"; + break; + case Expression::Type::ComplexArgument: + std::cout << "ComplexArgument"; + break; + case Expression::Type::ConfidenceInterval: + std::cout << "ConfidenceInterval"; + break; + case Expression::Type::Conjugate: + std::cout << "Conjugate"; + break; case Expression::Type::Cosine: std::cout << "Cosine"; break; - case Expression::Type::Float: - std::cout << "Float()"; - break; - case Expression::Type::Integer: - std::cout << "Integer("; - std::cout << e->approximate(context); + case Expression::Type::Decimal: + std::cout << "Decimal("; + std::cout << e->approximate(context, Expression::AngleUnit::Radian); std::cout << ")"; break; - case Expression::Type::Fraction: - std::cout << "Fraction"; + case Expression::Type::Derivative: + std::cout << "Derivative"; + break; + case Expression::Type::Determinant: + std::cout << "Determinant"; + break; + case Expression::Type::Division: + std::cout << "Division"; + break; + case Expression::Type::DivisionQuotient: + std::cout << "DivisionQuotient"; + break; + case Expression::Type::DivisionRemainder: + std::cout << "DivisionRemainder"; + break; + case Expression::Type::Factorial: + std::cout << "Factorial"; + break; + case Expression::Type::Floor: + std::cout << "Floor"; + break; + case Expression::Type::FracPart: + std::cout << "FracPart"; + break; + case Expression::Type::GreatCommonDivisor: + std::cout << "GreatCommonDivisor"; + break; + case Expression::Type::HyperbolicArcCosine: + std::cout << "HyperbolicArcCosine"; + break; + case Expression::Type::HyperbolicArcSine: + std::cout << "HyperbolicArcSine"; + break; + case Expression::Type::HyperbolicArcTangent: + std::cout << "HyperbolicArcTangent"; + break; + case Expression::Type::HyperbolicCosine: + std::cout << "HyperbolicCosine"; + break; + case Expression::Type::HyperbolicSine: + std::cout << "HyperbolicSine"; + break; + case Expression::Type::HyperbolicTangent: + std::cout << "HyperbolicTangent"; + break; + case Expression::Type::ImaginaryPart: + std::cout << "ImaginaryPart"; + break; + case Expression::Type::Integral: + std::cout << "Integral"; + break; + case Expression::Type::LeastCommonMultiple: + std::cout << "LeastCommonMultiple"; + break; + case Expression::Type::Logarithm: + std::cout << "Logarithm"; break; case Expression::Type::Matrix: - std::cout << "Matrix"; + std::cout << "Matrix(Rows: "; + std::cout << static_cast(e)->numberOfRows(); + std::cout << ", Columns: "; + std::cout << static_cast(e)->numberOfColumns(); + std::cout << ")"; + break; + case Expression::Type::MatrixDimension: + std::cout << "MatrixDimension"; + break; + case Expression::Type::MatrixInverse: + std::cout << "MatrixInverse"; + break; + case Expression::Type::MatrixTrace: + std::cout << "MatrixTrace"; + break; + case Expression::Type::MatrixTranspose: + std::cout << "MatrixTranspose"; + break; + case Expression::Type::Multiplication: + std::cout << "Multiplication"; + break; + case Expression::Type::NaperianLogarithm: + std::cout << "NaperianLogarithm"; + break; + case Expression::Type::NthRoot: + std::cout << "NthRoot"; + break; + case Expression::Type::Opposite: + std::cout << "Opposite"; break; case Expression::Type::Parenthesis: std::cout << "Parenthesis"; break; + case Expression::Type::PermuteCoefficient: + std::cout << "PermuteCoefficient"; + break; + case Expression::Type::PredictionInterval: + std::cout << "PredictionInterval"; + break; case Expression::Type::Power: std::cout << "Power"; break; case Expression::Type::Product: std::cout << "Product"; break; + case Expression::Type::Rational: + std::cout << "Rational("; + std::cout << static_cast(e)->numerator().approximate(); + std::cout << ", "; + std::cout << static_cast(e)->denominator().approximate(); + std::cout << ")"; + break; + case Expression::Type::RealPart: + std::cout << "RealPart"; + break; + case Expression::Type::Round: + std::cout << "Round"; + break; + case Expression::Type::SimplificationRoot: + std::cout << "SimplificationRoot"; + break; case Expression::Type::Sine: std::cout << "Sine"; break; + case Expression::Type::SquareRoot: + std::cout << "SquareRoot"; + break; + case Expression::Type::Store: + std::cout << "Store"; + break; case Expression::Type::Subtraction: std::cout << "Subtraction"; break; + case Expression::Type::Sum: + std::cout << "Sum"; + break; case Expression::Type::Symbol: - std::cout << "Symbol(" << ((Symbol*)e)->name() << ")"; + std::cout << "Symbol("; + switch (((Symbol*)e)->name()) { + case Ion::Charset::SmallPi: + std::cout << "PI"; + break; + case Ion::Charset::IComplex: + std::cout << "i"; + break; + case Ion::Charset::Exponential: + std::cout << "e"; + break; + default: + std::cout << ((Symbol*)e)->name(); + } + std::cout << ")"; break; case Expression::Type::Tangent: std::cout << "Tangent"; break; + case Expression::Type::Undefined: + std::cout << "Undefined"; + break; } - std::cout << std::endl; + std::cout << " at " << (void *)e << " with parent " << (void *)(e->parent()) << std::endl; for (int i=0; inumberOfOperands(); i++) { print_expression(e->operand(i), indentationLevel+1); } } + +void print_prime_factorization(Integer * outputFactors, Integer * outputCoefficients, int outputLength) { + for (int index = 0; index < outputLength; index++) { + if (outputCoefficients[index].isEqualTo(Integer(0))) { + break; + } + std::cout << outputFactors[index].approximate(); + std::cout << "^"; + std::cout << outputCoefficients[index].approximate(); + std::cout << "+"; + } + std::cout <<" "<< std::endl; +} + +} diff --git a/poincare/src/expression_debug.h b/poincare/src/expression_debug.h index 5c28c9901..51264e945 100644 --- a/poincare/src/expression_debug.h +++ b/poincare/src/expression_debug.h @@ -2,7 +2,13 @@ #define POICARE_EXPRESSION_DEBUG_H #include +#include + +namespace Poincare { void print_expression(const Expression * e, int indentationLevel = 0); +void print_prime_factorization(Integer * outputFactors, Integer * outputCoefficients, int outputLength); + +} #endif // POICARE_EXPRESSION_DEBUG_H diff --git a/poincare/src/expression_lexer.l b/poincare/src/expression_lexer.l index 2f3ce2e51..9c5de104b 100644 --- a/poincare/src/expression_lexer.l +++ b/poincare/src/expression_lexer.l @@ -135,16 +135,17 @@ tan { poincare_expression_yylval.expression = new Tangent(); return FUNCTION; } tanh { poincare_expression_yylval.expression = new HyperbolicTangent(); return FUNCTION; } trace { poincare_expression_yylval.expression = new MatrixTrace(); return FUNCTION; } transpose { poincare_expression_yylval.expression = new MatrixTranspose(); return FUNCTION; } -undef { poincare_expression_yylval.expression = new Complex(Complex::Float(NAN)); return UNDEFINED; } -inf { poincare_expression_yylval.expression = new Complex(Complex::Float(INFINITY)); return UNDEFINED; } +undef { poincare_expression_yylval.expression = new Undefined(); return UNDEFINED; } +inf { poincare_expression_yylval.expression = new Undefined(); return UNDEFINED; } \x89 { poincare_expression_yylval.character = yytext[0]; return SYMBOL; } \x8c { return EE; } \x90 { poincare_expression_yylval.expression = new SquareRoot(); return FUNCTION; } -\x8b { return ICOMPLEX; } +\x8b { poincare_expression_yylval.character = yytext[0]; return SYMBOL; } \x8e { poincare_expression_yylval.character = yytext[0]; return SYMBOL; } \x8f { return STO; } \+ { return PLUS; } \- { return MINUS; } +\x93 { return MULTIPLY; } \* { return MULTIPLY; } \/ { return DIVIDE; } \^ { return POW; } diff --git a/poincare/src/expression_matrix.cpp b/poincare/src/expression_matrix.cpp deleted file mode 100644 index 6945d144c..000000000 --- a/poincare/src/expression_matrix.cpp +++ /dev/null @@ -1,83 +0,0 @@ -extern "C" { -#include -#include -} -#include -#include -#include -#include -#include -#include - -namespace Poincare { - -ExpressionMatrix::ExpressionMatrix(MatrixData * matrixData) : - m_matrixData(matrixData) -{ -} - -ExpressionMatrix::ExpressionMatrix(Expression ** newOperands, int numberOfOperands, int numberOfRows, int numberOfColumns, bool cloneOperands) -{ - m_matrixData = new MatrixData(newOperands, numberOfOperands, numberOfRows, numberOfColumns, cloneOperands); -} - -ExpressionMatrix::~ExpressionMatrix() { - delete m_matrixData; -} - -bool ExpressionMatrix::hasValidNumberOfArguments() const { - for (int i = 0; i < numberOfOperands(); i++) { - if (!operand(i)->hasValidNumberOfArguments()) { - return false; - } - } - return true; -} - - -int ExpressionMatrix::numberOfRows() const { - return m_matrixData->numberOfRows(); -} - -int ExpressionMatrix::numberOfColumns() const { - return m_matrixData->numberOfColumns(); -} - -const Expression * ExpressionMatrix::operand(int i) const { - assert(i >= 0); - assert(i < numberOfOperands()); - return m_matrixData->operands()[i]; -} - -Expression * ExpressionMatrix::clone() const { - return this->cloneWithDifferentOperands(m_matrixData->operands(), numberOfOperands(), true); -} - -Expression::Type ExpressionMatrix::type() const { - return Type::ExpressionMatrix; -} - -Expression * ExpressionMatrix::cloneWithDifferentOperands(Expression** newOperands, - int numberOfOperands, bool cloneOperands) const { - assert(newOperands != nullptr); - return new ExpressionMatrix(newOperands, numberOfOperands, numberOfRows(), numberOfColumns(), cloneOperands); -} - -template -Evaluation * ExpressionMatrix::templatedEvaluate(Context& context, AngleUnit angleUnit) const { - Complex * operands = new Complex[numberOfOperands()]; - for (int i = 0; i < numberOfOperands(); i++) { - Evaluation * operandEvaluation = operand(i)->evaluate(context, angleUnit); - if (operandEvaluation->numberOfOperands() != 1) { - operands[i] = Complex::Float(NAN); - } else { - operands[i] = *(operandEvaluation->complexOperand(0)); - } - delete operandEvaluation; - } - Evaluation * matrix = new ComplexMatrix(operands, numberOfRows(), numberOfColumns()); - delete[] operands; - return matrix; -} - -} diff --git a/poincare/src/expression_parser.y b/poincare/src/expression_parser.y index acfa871ba..4c989fa9c 100644 --- a/poincare/src/expression_parser.y +++ b/poincare/src/expression_parser.y @@ -25,18 +25,18 @@ void poincare_expression_yyerror(Poincare::Expression ** expressionOutput, char /* All symbols (both terminals and non-terminals) may have a value associated * with them. In our case, it's going to be either an Expression (for example, - * when parsing (a/b) we want to create a new Fraction), or a string (this will + * when parsing (a/b) we want to create a new Division), or a string (this will * be useful to retrieve the value of Integers for example). */ %union { Poincare::Expression * expression; Poincare::Symbol * symbol; Poincare::ListData * listData; Poincare::MatrixData * matrixData; - Poincare::Function * function; + Poincare::StaticHierarchy<0> * function; /* Caution: all the const char * are NOT guaranteed to be NULL-terminated! * While Flex guarantees that yytext is NULL-terminated when building tokens, * it does so by temporarily swapping in a NULL terminated in the input text. - * Of course that hack has vanished when the pointer is fed into Bison. + * Of course that hack has vanished by the time the pointer is fed into Bison. * We thus record the length of the char fed into Flex in a structure and give * it to the object constructor called by Bison along with the char *. */ struct { @@ -143,19 +143,19 @@ lstData: | lstData COMMA exp { $$ = $1; $$->pushExpression($3); } /* MATRICES_ARE_DEFINED */ mtxData: - LEFT_BRACKET lstData RIGHT_BRACKET { $$ = new Poincare::MatrixData($2, true); delete $2; } - | mtxData LEFT_BRACKET lstData RIGHT_BRACKET { $$ = $1; $$->pushListData($3, true); delete $3; } + LEFT_BRACKET lstData RIGHT_BRACKET { $$ = new Poincare::MatrixData($2, false); $2->detachOperands(); delete $2; } + | mtxData LEFT_BRACKET lstData RIGHT_BRACKET { $$ = $1; $$->pushListData($3, false); $3->detachOperands(); delete $3; } number: - DIGITS { $$ = new Poincare::Integer($1.address, false); } - | DOT DIGITS { $$ = new Poincare::Complex(nullptr, 0, false, $2.address, $2.length, nullptr, 0, false); } - | DIGITS DOT DIGITS { $$ = new Poincare::Complex($1.address, $1.length, false, $3.address, $3.length, nullptr, 0, false); } - | DOT DIGITS EE DIGITS { $$ = new Poincare::Complex(nullptr, 0, false, $2.address, $2.length, $4.address, $4.length, false); } - | DIGITS DOT DIGITS EE DIGITS { $$ = new Poincare::Complex($1.address, $1.length, false, $3.address, $3.length, $5.address, $5.length, false); } - | DIGITS EE DIGITS { $$ = new Poincare::Complex($1.address, $1.length, false, nullptr, 0, $3.address, $3.length, false); } - | DOT DIGITS EE MINUS DIGITS { $$ = new Poincare::Complex(nullptr, 0, false, $2.address, $2.length, $5.address, $5.length, true); } - | DIGITS DOT DIGITS EE MINUS DIGITS { $$ = new Poincare::Complex($1.address, $1.length, false, $3.address, $3.length, $6.address, $6.length, true); } - | DIGITS EE MINUS DIGITS { $$ = new Poincare::Complex($1.address, $1.length, false, nullptr, 0, $4.address, $4.length, true); } + DIGITS { $$ = new Poincare::Rational(Poincare::Integer($1.address, false)); } + | DOT DIGITS { $$ = new Poincare::Decimal(Poincare::Decimal::mantissa(nullptr, 0, $2.address, $2.length, false), Poincare::Decimal::exponent(nullptr, 0, $2.address, $2.length, nullptr, 0, false)); } + | DIGITS DOT DIGITS { $$ = new Poincare::Decimal(Poincare::Decimal::mantissa($1.address, $1.length, $3.address, $3.length, false), Poincare::Decimal::exponent($1.address, $1.length, $3.address, $3.length, nullptr, 0, false)); } + | DOT DIGITS EE DIGITS { if ($4.length > 9) { YYERROR; }; $$ = new Poincare::Decimal(Poincare::Decimal::mantissa(nullptr, 0, $2.address, $2.length, false), Poincare::Decimal::exponent(nullptr, 0, $2.address, $2.length, $4.address, $4.length, false)); } + | DIGITS DOT DIGITS EE DIGITS { if ($5.length > 9) { YYERROR; }; $$ = new Poincare::Decimal(Poincare::Decimal::mantissa($1.address, $1.length, $3.address, $3.length, false), Poincare::Decimal::exponent($1.address, $1.length, $3.address, $3.length, $5.address, $5.length, false)); } + | DIGITS EE DIGITS { if ($3.length > 9) { YYERROR; }; $$ = new Poincare::Decimal(Poincare::Decimal::mantissa($1.address, $1.length, nullptr, 0, false), Poincare::Decimal::exponent($1.address, $1.length, nullptr, 0, $3.address, $3.length, false)); } + | DOT DIGITS EE MINUS DIGITS { if ($5.length > 9) { YYERROR; }; $$ = new Poincare::Decimal(Poincare::Decimal::mantissa(nullptr, 0, $2.address, $2.length, false), Poincare::Decimal::exponent(nullptr, 0, $2.address, $2.length, $5.address, $5.length, true)); } + | DIGITS DOT DIGITS EE MINUS DIGITS { if ($6.length > 9) { YYERROR; }; $$ = new Poincare::Decimal(Poincare::Decimal::mantissa($1.address, $1.length, $3.address, $3.length, false), Poincare::Decimal::exponent($1.address, $1.length, $3.address, $3.length, $6.address, $6.length, true)); } + | DIGITS EE MINUS DIGITS { if ($4.length > 9) { YYERROR; }; $$ = new Poincare::Decimal(Poincare::Decimal::mantissa($1.address, $1.length, nullptr, 0, false), Poincare::Decimal::exponent($1.address, $1.length, nullptr, 0, $4.address, $4.length, true)); } symb: SYMBOL { $$ = new Poincare::Symbol($1); } @@ -169,25 +169,25 @@ exp: UNDEFINED { $$ = $1; } | exp BANG { $$ = new Poincare::Factorial($1, false); } | number { $$ = $1; } - | ICOMPLEX { $$ = new Poincare::Complex(Poincare::Complex::Cartesian(0.0f, 1.0f)); } | symb { $$ = $1; } - | exp PLUS exp { Poincare::Expression * terms[2] = {$1,$3}; $$ = new Poincare::Addition(terms, false); } + | exp PLUS exp { Poincare::Expression * terms[2] = {$1,$3}; $$ = new Poincare::Addition(terms, 2, false); } | exp MINUS exp { Poincare::Expression * terms[2] = {$1,$3}; $$ = new Poincare::Subtraction(terms, false); } - | exp MULTIPLY exp { Poincare::Expression * terms[2] = {$1,$3}; $$ = new Poincare::Multiplication(terms, false); } - | exp exp %prec IMPLICIT_MULTIPLY { Poincare::Expression * terms[2] = {$1,$2}; $$ = new Poincare::Multiplication(terms, false); } - | exp DIVIDE exp { Poincare::Expression * terms[2] = {$1,$3}; $$ = new Poincare::Fraction(terms, false); } + | exp MULTIPLY exp { Poincare::Expression * terms[2] = {$1,$3}; $$ = new Poincare::Multiplication(terms, 2, false); } + | exp exp %prec IMPLICIT_MULTIPLY { Poincare::Expression * terms[2] = {$1,$2}; $$ = new Poincare::Multiplication(terms, 2, false); } + | exp DIVIDE exp { Poincare::Expression * terms[2] = {$1,$3}; $$ = new Poincare::Division(terms, false); } | exp POW exp { Poincare::Expression * terms[2] = {$1,$3}; $$ = new Poincare::Power(terms, false); } - | MINUS exp %prec UNARY_MINUS { $$ = new Poincare::Opposite($2, false); } - | LEFT_PARENTHESIS exp RIGHT_PARENTHESIS { $$ = new Poincare::Parenthesis($2, false); } + | MINUS exp %prec UNARY_MINUS { Poincare::Expression * terms[1] = {$2}; $$ = new Poincare::Opposite(terms, false); } + | LEFT_PARENTHESIS exp RIGHT_PARENTHESIS { Poincare::Expression * terms[1] = {$2}; $$ = new Poincare::Parenthesis(terms, false); } /* MATRICES_ARE_DEFINED */ - | LEFT_BRACKET mtxData RIGHT_BRACKET { $$ = new Poincare::ExpressionMatrix($2); } - | FUNCTION LEFT_PARENTHESIS lstData RIGHT_PARENTHESIS { $$ = $1; $1->setArgument($3, true); delete $3; } + | LEFT_BRACKET mtxData RIGHT_BRACKET { $$ = new Poincare::Matrix($2); delete $2; } + | FUNCTION LEFT_PARENTHESIS lstData RIGHT_PARENTHESIS { $$ = $1; if (!$1->hasValidNumberOfOperands($3->numberOfOperands())) { delete $1; delete $3; YYERROR; } ; $1->setArgument($3, $3->numberOfOperands(), false); $3->detachOperands(); delete $3; } final_exp: exp { $$ = $1; } - | exp STO symb { $$ = new Poincare::Store($3, $1, false); }; + | exp STO symb { Poincare::Expression * terms[2] = {$1,$3}; $$ = new Poincare::Store(terms, false); }; %% -void poincare_expression_yyerror(Poincare::Expression ** expressionOutput, char const *msg) { +void poincare_expression_yyerror(Poincare::Expression ** expressionOutput, const char * msg) { // Handle the error! + // TODO: handle explicitely different type of errors (division by 0, missing parenthesis). This should call back the container to display a pop up with a message corresponding to the error? } diff --git a/poincare/src/factorial.cpp b/poincare/src/factorial.cpp index af89cf860..b751e361b 100644 --- a/poincare/src/factorial.cpp +++ b/poincare/src/factorial.cpp @@ -1,6 +1,11 @@ #include #include "layout/string_layout.h" #include "layout/horizontal_layout.h" +#include +#include +#include +#include +#include extern "C" { #include } @@ -8,25 +13,52 @@ extern "C" { namespace Poincare { -Factorial::Factorial(Expression * argument, bool clone) : - Function("fact") +Factorial::Factorial(const Expression * argument, bool clone) : + StaticHierarchy<1>(&argument, clone) { - setArgument(&argument, 1, clone); } Expression::Type Factorial::type() const { return Type::Factorial; } -Expression * Factorial::cloneWithDifferentOperands(Expression** newOperands, - int numberOfOperands, bool cloneOperands) const { - assert(newOperands != nullptr); - Factorial * f = new Factorial(newOperands[0], cloneOperands); - return f; +Expression * Factorial::clone() const { + Factorial * a = new Factorial(m_operands[0], true); + return a; +} + +Expression * Factorial::shallowReduce(Context& context, AngleUnit angleUnit) { + Expression * e = Expression::shallowReduce(context, angleUnit); + if (e != this) { + return e; + } +#if MATRIX_EXACT_REDUCING + if (operand(0)->type() == Type::Matrix) { + return SimplificationEngine::map(this, context, angleUnit); + } +#endif + if (operand(0)->type() == Type::Rational) { + Rational * r = static_cast(editableOperand(0)); + if (!r->denominator().isOne()) { + return replaceWith(new Undefined(), true); + } + if (Integer(k_maxOperandValue).isLowerThan(r->numerator())) { + return this; + } + Rational * fact = new Rational(Integer::Factorial(r->numerator())); + return replaceWith(fact, true); + } + if (operand(0)->type() == Type::Symbol) { + Symbol * s = static_cast(editableOperand(0)); + if (s->name() == Ion::Charset::SmallPi || s->name() == Ion::Charset::Exponential) { + return replaceWith(new Undefined(), true); + } + } + return this; } template -Complex Factorial::templatedComputeComplex(const Complex c) const { +Complex Factorial::computeOnComplex(const Complex c, AngleUnit angleUnit) { T n = c.a(); if (c.b() != 0 || std::isnan(n) || n != (int)n || n < 0) { return Complex::Float(NAN); @@ -45,9 +77,34 @@ ExpressionLayout * Factorial::privateCreateLayout(FloatDisplayMode floatDisplayM assert(floatDisplayMode != FloatDisplayMode::Default); assert(complexFormat != ComplexFormat::Default); ExpressionLayout * childrenLayouts[2]; - childrenLayouts[0] = m_args[0]->createLayout(floatDisplayMode, complexFormat); + childrenLayouts[0] = operand(0)->createLayout(floatDisplayMode, complexFormat); childrenLayouts[1] = new StringLayout("!", 1); return new HorizontalLayout(childrenLayouts, 2); } +int Factorial::simplificationOrderGreaterType(const Expression * e) const { + if (SimplificationOrder(operand(0),e) == 0) { + return 1; + } + return SimplificationOrder(operand(0), e); +} + +int Factorial::writeTextInBuffer(char * buffer, int bufferSize) const { + if (bufferSize == 0) { + return -1; + } + buffer[bufferSize-1] = 0; + int numberOfChar = operand(0)->writeTextInBuffer(buffer, bufferSize); + if (numberOfChar >= bufferSize-1) { + return numberOfChar; + } + buffer[numberOfChar++] = '!'; + buffer[numberOfChar] = 0; + return numberOfChar; +} + +int Factorial::simplificationOrderSameType(const Expression * e) const { + return SimplificationOrder(operand(0), e->operand(0)); +} + } diff --git a/poincare/src/floor.cpp b/poincare/src/floor.cpp index b82b247e5..48892fe5d 100644 --- a/poincare/src/floor.cpp +++ b/poincare/src/floor.cpp @@ -1,6 +1,9 @@ #include #include "layout/floor_layout.h" - +#include +#include +#include +#include extern "C" { #include } @@ -8,25 +11,45 @@ extern "C" { namespace Poincare { -Floor::Floor() : - Function("floor") -{ -} - Expression::Type Floor::type() const { return Type::Floor; } -Expression * Floor::cloneWithDifferentOperands(Expression** newOperands, - int numberOfOperands, bool cloneOperands) const { - assert(newOperands != nullptr); - Floor * f = new Floor(); - f->setArgument(newOperands, numberOfOperands, cloneOperands); - return f; +Expression * Floor::clone() const { + Floor * c = new Floor(m_operands, true); + return c; +} + +Expression * Floor::shallowReduce(Context& context, AngleUnit angleUnit) { + Expression * e = Expression::shallowReduce(context, angleUnit); + if (e != this) { + return e; + } + Expression * op = editableOperand(0); +#if MATRIX_EXACT_REDUCING + if (op->type() == Type::Matrix) { + return SimplificationEngine::map(this, context, angleUnit); + } +#endif + if (op->type() == Type::Symbol) { + Symbol * s = static_cast(op); + if (s->name() == Ion::Charset::SmallPi) { + return replaceWith(new Rational(3), true); + } + if (s->name() == Ion::Charset::Exponential) { + return replaceWith(new Rational(2), true); + } + } + if (op->type() != Type::Rational) { + return this; + } + Rational * r = static_cast(op); + IntegerDivision div = Integer::Division(r->numerator(), r->denominator()); + return replaceWith(new Rational(div.quotient), true); } template -Complex Floor::templatedComputeComplex(const Complex c) const { +Complex Floor::computeOnComplex(const Complex c, AngleUnit angleUnit) { if (c.b() != 0) { return Complex::Float(NAN); } @@ -36,7 +59,7 @@ Complex Floor::templatedComputeComplex(const Complex c) const { ExpressionLayout * Floor::privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const { assert(floatDisplayMode != FloatDisplayMode::Default); assert(complexFormat != ComplexFormat::Default); - return new FloorLayout(m_args[0]->createLayout(floatDisplayMode, complexFormat)); + return new FloorLayout(m_operands[0]->createLayout(floatDisplayMode, complexFormat)); } } diff --git a/poincare/src/frac_part.cpp b/poincare/src/frac_part.cpp index 554151598..0231b4e75 100644 --- a/poincare/src/frac_part.cpp +++ b/poincare/src/frac_part.cpp @@ -1,5 +1,6 @@ #include - +#include +#include extern "C" { #include } @@ -7,25 +8,36 @@ extern "C" { namespace Poincare { -FracPart::FracPart() : - Function("frac") -{ -} - Expression::Type FracPart::type() const { return Type::FracPart; } -Expression * FracPart::cloneWithDifferentOperands(Expression** newOperands, - int numberOfOperands, bool cloneOperands) const { - assert(newOperands != nullptr); - FracPart * fp = new FracPart(); - fp->setArgument(newOperands, numberOfOperands, cloneOperands); - return fp; +Expression * FracPart::clone() const { + FracPart * c = new FracPart(m_operands, true); + return c; +} + +Expression * FracPart::shallowReduce(Context& context, AngleUnit angleUnit) { + Expression * e = Expression::shallowReduce(context, angleUnit); + if (e != this) { + return e; + } + Expression * op = editableOperand(0); +#if MATRIX_EXACT_REDUCING + if (op->type() == Type::Matrix) { + return SimplificationEngine::map(this, context, angleUnit); + } +#endif + if (op->type() != Type::Rational) { + return this; + } + Rational * r = static_cast(op); + IntegerDivision div = Integer::Division(r->numerator(), r->denominator()); + return replaceWith(new Rational(div.remainder, r->denominator()), true); } template -Complex FracPart::templatedComputeComplex(const Complex c) const { +Complex FracPart::computeOnComplex(const Complex c, AngleUnit angleUnit) { if (c.b() != 0) { return Complex::Float(NAN); } diff --git a/poincare/src/function.cpp b/poincare/src/function.cpp deleted file mode 100644 index b7b905686..000000000 --- a/poincare/src/function.cpp +++ /dev/null @@ -1,121 +0,0 @@ -extern "C" { -#include -#include -} -#include -#include -#include -#include "layout/horizontal_layout.h" -#include "layout/parenthesis_layout.h" -#include "layout/string_layout.h" - -namespace Poincare { - -Function::Function(const char * name, int requiredNumberOfArguments) : - m_args(nullptr), - m_numberOfArguments(0), - m_requiredNumberOfArguments(requiredNumberOfArguments), - m_name(name) -{ -} - -void Function::setArgument(Expression ** args, int numberOfArguments, bool clone) { - build(args, numberOfArguments, clone); -} - -void Function::setArgument(ListData * listData, bool clone) { - build(listData->operands(), listData->numberOfOperands(), clone); -} - -Function::~Function() { - clean(); -} - -bool Function::hasValidNumberOfArguments() const { - if (m_numberOfArguments != m_requiredNumberOfArguments) { - return false; - } - for (int i = 0; i < m_requiredNumberOfArguments; i++) { - if (!m_args[i]->hasValidNumberOfArguments()) { - return false; - } - } - return true; -} - -const Expression * Function::operand(int i) const { - assert(i >= 0 && i < m_numberOfArguments); - return m_args[i]; -} - -int Function::numberOfOperands() const { - return m_numberOfArguments; -} - -Expression * Function::clone() const { - return this->cloneWithDifferentOperands(m_args, m_numberOfArguments, true); -} - -template -Evaluation * Function::templatedEvaluate(Context& context, AngleUnit angleUnit) const { - if (m_numberOfArguments != 1) { - return new Complex(Complex::Float(NAN)); - } - Evaluation * input = m_args[0]->evaluate(context, angleUnit); - Complex * operands = new Complex[input->numberOfRows()*input->numberOfColumns()]; - for (int i = 0; i < input->numberOfOperands(); i++) { - operands[i] = computeComplex(*input->complexOperand(i), angleUnit); - } - Evaluation * result = nullptr; - if (input->numberOfOperands() == 1) { - result = new Complex(operands[0]); - } else { - result = new ComplexMatrix(operands, input->numberOfRows(), input->numberOfColumns()); - } - delete input; - delete[] operands; - return result; -} - -ExpressionLayout * Function::privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const { - assert(floatDisplayMode != FloatDisplayMode::Default); - assert(complexFormat != ComplexFormat::Default); - ExpressionLayout ** grandChildrenLayouts = new ExpressionLayout *[2*m_numberOfArguments-1]; - int layoutIndex = 0; - grandChildrenLayouts[layoutIndex++] = m_args[0]->createLayout(floatDisplayMode, complexFormat); - for (int i = 1; i < m_numberOfArguments; i++) { - grandChildrenLayouts[layoutIndex++] = new StringLayout(",", 1); - grandChildrenLayouts[layoutIndex++] = m_args[i]->createLayout(floatDisplayMode, complexFormat); - } - ExpressionLayout * argumentLayouts = new HorizontalLayout(grandChildrenLayouts, 2*m_numberOfArguments-1); - delete [] grandChildrenLayouts; - ExpressionLayout * childrenLayouts[2]; - childrenLayouts[0] = new StringLayout(m_name, strlen(m_name)); - childrenLayouts[1] = new ParenthesisLayout(argumentLayouts); - return new HorizontalLayout(childrenLayouts, 2); -} - -void Function::build(Expression ** args, int numberOfArguments, bool clone) { - clean(); - m_numberOfArguments = numberOfArguments; - m_args = new Expression * [numberOfArguments]; - for (int i = 0; i < numberOfArguments; i++) { - assert(args[i] != nullptr); - if (clone) { - m_args[i] = args[i]->clone(); - } else { - m_args[i] = args[i]; - } - } -} - -void Function::clean() { - if (m_args != nullptr) { - for (int i = 0; i < m_numberOfArguments; i++) { - delete m_args[i]; - } - delete[] m_args; - } -} - -} diff --git a/poincare/src/global_context.cpp b/poincare/src/global_context.cpp index d7e4d5ad0..4400804e9 100644 --- a/poincare/src/global_context.cpp +++ b/poincare/src/global_context.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -8,7 +9,8 @@ namespace Poincare { GlobalContext::GlobalContext() : m_pi(Complex::Float(M_PI)), - m_e(Complex::Float(M_E)) + m_e(Complex::Float(M_E)), + m_i(Complex::Cartesian(0.0, 1.0)) { for (int i = 0; i < k_maxNumberOfScalarExpressions; i++) { m_expressions[i] = nullptr; @@ -33,6 +35,10 @@ GlobalContext::~GlobalContext() { } } +/* TODO: so far, symbols are not replaced in expression at simplification. So, + * right now, it is not an issue that multiple symbols are replaced by the same + * objet at evaluation (defaultExpression). However, when we will replace + * symbols in simplification, we will have to have an expression per symbol!! */ Complex * GlobalContext::defaultExpression() { static Complex * defaultExpression = new Complex(Complex::Float(0.0)); return defaultExpression; @@ -43,13 +49,16 @@ int GlobalContext::symbolIndex(const Symbol * symbol) const { return index; } -const Evaluation * GlobalContext::evaluationForSymbol(const Symbol * symbol) { +const Expression * GlobalContext::expressionForSymbol(const Symbol * symbol) { if (symbol->name() == Ion::Charset::SmallPi) { return &m_pi; } if (symbol->name() == Ion::Charset::Exponential) { return &m_e; } + if (symbol->name() == Ion::Charset::IComplex) { + return &m_i; + } if (symbol->isMatrixSymbol()) { int indexMatrix = symbol->name() - (char)Symbol::SpecialSymbols::M0; return m_matrixExpressions[indexMatrix]; @@ -64,7 +73,7 @@ const Evaluation * GlobalContext::evaluationForSymbol(const Symbol * sym return m_expressions[index]; } -void GlobalContext::setExpressionForSymbolName(Expression * expression, const Symbol * symbol) { +void GlobalContext::setExpressionForSymbolName(const Expression * expression, const Symbol * symbol, Context & context) { if (symbol->isMatrixSymbol()) { int indexMatrix = symbol->name() - (char)Symbol::SpecialSymbols::M0; assert(indexMatrix >= 0 && indexMatrix < k_maxNumberOfMatrixExpressions); @@ -73,12 +82,11 @@ void GlobalContext::setExpressionForSymbolName(Expression * expression, const Sy m_matrixExpressions[indexMatrix] = nullptr; } if (expression != nullptr) { - Evaluation * evaluation = expression->evaluate(*this); - if (evaluation->numberOfOperands() == 1) { - m_matrixExpressions[indexMatrix] = new ComplexMatrix(evaluation->complexOperand(0), 1, 1); - delete evaluation; + Expression * evaluation = expression->evaluate(context); + if (evaluation->type() == Expression::Type::Complex) { + m_matrixExpressions[indexMatrix] = new Matrix(&evaluation, 1, 1, false); } else { - m_matrixExpressions[indexMatrix] = (ComplexMatrix *)evaluation; + m_matrixExpressions[indexMatrix] = static_cast(evaluation); } } return; @@ -94,13 +102,13 @@ void GlobalContext::setExpressionForSymbolName(Expression * expression, const Sy if (expression == nullptr) { return; } - Evaluation * evaluation = expression->evaluate(*this); - if (evaluation->numberOfOperands() == 1) { - m_expressions[index] = new Complex(*(evaluation->complexOperand(0))); + Expression * evaluation = expression->evaluate(context); + if (evaluation->type() == Expression::Type::Complex) { + m_expressions[index] = static_cast *>(evaluation); } else { m_expressions[index] = new Complex(Complex::Float(NAN)); + delete evaluation; } - delete evaluation; } } diff --git a/poincare/src/great_common_divisor.cpp b/poincare/src/great_common_divisor.cpp index 8ad27ed6f..53494e623 100644 --- a/poincare/src/great_common_divisor.cpp +++ b/poincare/src/great_common_divisor.cpp @@ -1,5 +1,8 @@ #include #include +#include +#include +#include extern "C" { #include @@ -8,29 +11,57 @@ extern "C" { namespace Poincare { -GreatCommonDivisor::GreatCommonDivisor() : - Function("gcd", 2) -{ -} - Expression::Type GreatCommonDivisor::type() const { return Type::GreatCommonDivisor; } -Expression * GreatCommonDivisor::cloneWithDifferentOperands(Expression** newOperands, - int numberOfOperands, bool cloneOperands) const { - assert(newOperands != nullptr); - GreatCommonDivisor * gcd = new GreatCommonDivisor(); - gcd->setArgument(newOperands, numberOfOperands, cloneOperands); - return gcd; +Expression * GreatCommonDivisor::clone() const { + GreatCommonDivisor * a = new GreatCommonDivisor(m_operands, true); + return a; +} + +Expression * GreatCommonDivisor::shallowReduce(Context& context, AngleUnit angleUnit) { + Expression * e = Expression::shallowReduce(context, angleUnit); + if (e != this) { + return e; + } + Expression * op0 = editableOperand(0); + Expression * op1 = editableOperand(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()) { + return replaceWith(new Undefined(), true); + } + } + if (op1->type() == Type::Rational) { + Rational * r1 = static_cast(op1); + if (!r1->denominator().isOne()) { + 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 a = r0->numerator(); + Integer b = r1->numerator(); + Integer gcd = Arithmetic::GCD(&a, &b); + return replaceWith(new Rational(gcd), true); } template -Evaluation * GreatCommonDivisor::templatedEvaluate(Context& context, AngleUnit angleUnit) const { - Evaluation * f1Input = m_args[0]->evaluate(context, angleUnit); - Evaluation * f2Input = m_args[1]->evaluate(context, angleUnit); - T f1 = f1Input->toScalar(); - T f2 = f2Input->toScalar(); +Complex * GreatCommonDivisor::templatedEvaluate(Context& context, AngleUnit angleUnit) const { + Expression * f1Input = operand(0)->evaluate(context, angleUnit); + Expression * f2Input = operand(1)->evaluate(context, angleUnit); + T f1 = f1Input->type() == Type::Complex ? static_cast *>(f1Input)->toScalar() : NAN; + T f2 = f2Input->type() == Type::Complex ? static_cast *>(f2Input)->toScalar() : NAN; delete f1Input; delete f2Input; if (std::isnan(f1) || std::isnan(f2) || f1 != (int)f1 || f2 != (int)f2) { diff --git a/poincare/src/hierarchy.cpp b/poincare/src/hierarchy.cpp new file mode 100644 index 000000000..a0adebaa6 --- /dev/null +++ b/poincare/src/hierarchy.cpp @@ -0,0 +1,79 @@ +#include +extern "C" { +#include +} + +namespace Poincare { + +const Expression * Hierarchy::operand(int i) const { + assert(i >= 0); + assert(i < numberOfOperands()); + assert(operands()[i]->parent() == nullptr || operands()[i]->parent() == this); + return operands()[i]; +} + +void Hierarchy::swapOperands(int i, int j) { + assert(i >= 0 && i < numberOfOperands()); + assert(j >= 0 && j < numberOfOperands()); + Expression ** op = const_cast(operands()); + Expression * temp = op[i]; + op[i] = op[j]; + op[j] = temp; +} + +void Hierarchy::detachOperand(const Expression * e) { + Expression ** op = const_cast(operands()); + for (int i=0; ihasAncestor(this)) { + static_cast(newOperand->parent())->detachOperand(newOperand); + } + Expression ** op = const_cast(operands()); + for (int i=0; iparent() == this) { + const_cast(oldOperand)->setParent(nullptr); + } + if (deleteOldOperand) { + delete oldOperand; + } + if (newOperand != nullptr) { + const_cast(newOperand)->setParent(this); + } + op[i] = newOperand; + break; + } + } +} + +const Expression * const * Hierarchy::ExpressionArray(const Expression * e1, const Expression * e2) { + static const Expression * result[2] = {nullptr, nullptr}; + result[0] = e1; + result[1] = e2; + return result; +} + +void Hierarchy::detachOperandAtIndex(int i) { + Expression ** op = const_cast(operands()); + // When detachOperands is called, it's very likely that said operands have been stolen + if (op[i] != nullptr && op[i]->parent() == this) { + const_cast(op[i])->setParent(nullptr); + } + op[i] = nullptr; +} + +} diff --git a/poincare/src/hyperbolic_arc_cosine.cpp b/poincare/src/hyperbolic_arc_cosine.cpp index 6c6698cef..0d5fbb7c3 100644 --- a/poincare/src/hyperbolic_arc_cosine.cpp +++ b/poincare/src/hyperbolic_arc_cosine.cpp @@ -1,4 +1,5 @@ #include +#include extern "C" { #include } @@ -6,25 +7,31 @@ extern "C" { namespace Poincare { -HyperbolicArcCosine::HyperbolicArcCosine() : - Function("acosh") -{ -} - Expression::Type HyperbolicArcCosine::type() const { return Type::HyperbolicArcCosine; } -Expression * HyperbolicArcCosine::cloneWithDifferentOperands(Expression** newOperands, - int numberOfOperands, bool cloneOperands) const { - assert(newOperands != nullptr); - HyperbolicArcCosine * c = new HyperbolicArcCosine(); - c->setArgument(newOperands, numberOfOperands, cloneOperands); - return c; +Expression * HyperbolicArcCosine::clone() const { + HyperbolicArcCosine * a = new HyperbolicArcCosine(m_operands, true); + return a; +} + +Expression * HyperbolicArcCosine::shallowReduce(Context& context, AngleUnit angleUnit) { + Expression * e = Expression::shallowReduce(context, angleUnit); + if (e != this) { + return e; + } +#if MATRIX_EXACT_REDUCING + Expression * op = editableOperand(0); + if (op->type() == Type::Matrix) { + return SimplificationEngine::map(this, context, angleUnit); + } +#endif + return this; } template -Complex HyperbolicArcCosine::templatedComputeComplex(const Complex c) const { +Complex HyperbolicArcCosine::computeOnComplex(const Complex c, AngleUnit angleUnit) { if (c.b() != 0) { return Complex::Float(NAN); } diff --git a/poincare/src/hyperbolic_arc_sine.cpp b/poincare/src/hyperbolic_arc_sine.cpp index a55395231..6f68f1f21 100644 --- a/poincare/src/hyperbolic_arc_sine.cpp +++ b/poincare/src/hyperbolic_arc_sine.cpp @@ -1,4 +1,5 @@ #include +#include extern "C" { #include } @@ -6,25 +7,31 @@ extern "C" { namespace Poincare { -HyperbolicArcSine::HyperbolicArcSine() : - Function("asinh") -{ -} - Expression::Type HyperbolicArcSine::type() const { return Type::HyperbolicArcSine; } -Expression * HyperbolicArcSine::cloneWithDifferentOperands(Expression** newOperands, - int numberOfOperands, bool cloneOperands) const { - assert(newOperands != nullptr); - HyperbolicArcSine * s = new HyperbolicArcSine(); - s->setArgument(newOperands, numberOfOperands, cloneOperands); - return s; +Expression * HyperbolicArcSine::clone() const { + HyperbolicArcSine * a = new HyperbolicArcSine(m_operands, true); + return a; +} + +Expression * HyperbolicArcSine::shallowReduce(Context& context, AngleUnit angleUnit) { + Expression * e = Expression::shallowReduce(context, angleUnit); + if (e != this) { + return e; + } +#if MATRIX_EXACT_REDUCING + Expression * op = editableOperand(0); + if (op->type() == Type::Matrix) { + return SimplificationEngine::map(this, context, angleUnit); + } +#endif + return this; } template -Complex HyperbolicArcSine::templatedComputeComplex(const Complex c) const { +Complex HyperbolicArcSine::computeOnComplex(const Complex c, AngleUnit angleUnit) { if (c.b() != 0) { return Complex::Float(NAN); } diff --git a/poincare/src/hyperbolic_arc_tangent.cpp b/poincare/src/hyperbolic_arc_tangent.cpp index c2d9a68d7..6717cedb0 100644 --- a/poincare/src/hyperbolic_arc_tangent.cpp +++ b/poincare/src/hyperbolic_arc_tangent.cpp @@ -1,4 +1,5 @@ #include +#include extern "C" { #include } @@ -6,25 +7,31 @@ extern "C" { namespace Poincare { -HyperbolicArcTangent::HyperbolicArcTangent() : - Function("atanh") -{ -} - Expression::Type HyperbolicArcTangent::type() const { return Type::HyperbolicArcTangent; } -Expression * HyperbolicArcTangent::cloneWithDifferentOperands(Expression** newOperands, - int numberOfOperands, bool cloneOperands) const { - assert(newOperands != nullptr); - HyperbolicArcTangent * t = new HyperbolicArcTangent(); - t->setArgument(newOperands, numberOfOperands, cloneOperands); - return t; +Expression * HyperbolicArcTangent::clone() const { + HyperbolicArcTangent * a = new HyperbolicArcTangent(m_operands, true); + return a; +} + +Expression * HyperbolicArcTangent::shallowReduce(Context& context, AngleUnit angleUnit) { + Expression * e = Expression::shallowReduce(context, angleUnit); + if (e != this) { + return e; + } +#if MATRIX_EXACT_REDUCING + Expression * op = editableOperand(0); + if (op->type() == Type::Matrix) { + return SimplificationEngine::map(this, context, angleUnit); + } +#endif + return this; } template -Complex HyperbolicArcTangent::templatedComputeComplex(const Complex c) const { +Complex HyperbolicArcTangent::computeOnComplex(const Complex c, AngleUnit angleUnit) { if (c.b() != 0) { return Complex::Float(NAN); } diff --git a/poincare/src/hyperbolic_cosine.cpp b/poincare/src/hyperbolic_cosine.cpp index 283275325..a0e7d7c5e 100644 --- a/poincare/src/hyperbolic_cosine.cpp +++ b/poincare/src/hyperbolic_cosine.cpp @@ -2,8 +2,9 @@ #include #include #include -#include +#include #include +#include extern "C" { #include } @@ -11,25 +12,31 @@ extern "C" { namespace Poincare { -HyperbolicCosine::HyperbolicCosine() : - Function("cosh") -{ -} - Expression::Type HyperbolicCosine::type() const { return Type::HyperbolicCosine; } -Expression * HyperbolicCosine::cloneWithDifferentOperands(Expression** newOperands, - int numberOfOperands, bool cloneOperands) const { - assert(newOperands != nullptr); - HyperbolicCosine * hc = new HyperbolicCosine(); - hc->setArgument(newOperands, numberOfOperands, cloneOperands); - return hc; +Expression * HyperbolicCosine::clone() const { + HyperbolicCosine * a = new HyperbolicCosine(m_operands, true); + return a; +} + +Expression * HyperbolicCosine::shallowReduce(Context& context, AngleUnit angleUnit) { + Expression * e = Expression::shallowReduce(context, angleUnit); + if (e != this) { + return e; + } +#if MATRIX_EXACT_REDUCING + Expression * op = editableOperand(0); + if (op->type() == Type::Matrix) { + return SimplificationEngine::map(this, context, angleUnit); + } +#endif + return this; } template -Complex HyperbolicCosine::compute(const Complex c) { +Complex HyperbolicCosine::computeOnComplex(const Complex c, AngleUnit angleUnit) { if (c.b() == 0) { return Complex::Float(std::cosh(c.a())); } @@ -37,7 +44,7 @@ Complex HyperbolicCosine::compute(const Complex c) { Complex exp1 = Power::compute(e, c); Complex exp2 = Power::compute(e, Complex::Cartesian(-c.a(), -c.b())); Complex sum = Addition::compute(exp1, exp2); - return Fraction::compute(sum, Complex::Float(2)); + return Division::compute(sum, Complex::Float(2)); } } diff --git a/poincare/src/hyperbolic_sine.cpp b/poincare/src/hyperbolic_sine.cpp index 91ea1efdc..1b5ce48c9 100644 --- a/poincare/src/hyperbolic_sine.cpp +++ b/poincare/src/hyperbolic_sine.cpp @@ -2,8 +2,9 @@ #include #include #include -#include +#include #include +#include extern "C" { #include } @@ -11,25 +12,31 @@ extern "C" { namespace Poincare { -HyperbolicSine::HyperbolicSine() : - Function("sinh") -{ -} - Expression::Type HyperbolicSine::type() const { return Type::HyperbolicSine; } -Expression * HyperbolicSine::cloneWithDifferentOperands(Expression** newOperands, - int numberOfOperands, bool cloneOperands) const { - assert(newOperands != nullptr); - HyperbolicSine * hs = new HyperbolicSine(); - hs->setArgument(newOperands, numberOfOperands, cloneOperands); - return hs; +Expression * HyperbolicSine::clone() const { + HyperbolicSine * a = new HyperbolicSine(m_operands, true); + return a; +} + +Expression * HyperbolicSine::shallowReduce(Context& context, AngleUnit angleUnit) { + Expression * e = Expression::shallowReduce(context, angleUnit); + if (e != this) { + return e; + } +#if MATRIX_EXACT_REDUCING + Expression * op = editableOperand(0); + if (op->type() == Type::Matrix) { + return SimplificationEngine::map(this, context, angleUnit); + } +#endif + return this; } template -Complex HyperbolicSine::compute(const Complex c) { +Complex HyperbolicSine::computeOnComplex(const Complex c, AngleUnit angleUnit) { if (c.b() == 0) { return Complex::Float(std::sinh(c.a())); } @@ -37,7 +44,7 @@ Complex HyperbolicSine::compute(const Complex c) { Complex exp1 = Power::compute(e, c); Complex exp2 = Power::compute(e, Complex::Cartesian(-c.a(), -c.b())); Complex sub = Subtraction::compute(exp1, exp2); - return Fraction::compute(sub, Complex::Float(2)); + return Division::compute(sub, Complex::Float(2)); } } diff --git a/poincare/src/hyperbolic_tangent.cpp b/poincare/src/hyperbolic_tangent.cpp index 4be35f1d3..96ccbb6ae 100644 --- a/poincare/src/hyperbolic_tangent.cpp +++ b/poincare/src/hyperbolic_tangent.cpp @@ -2,7 +2,8 @@ #include #include #include -#include +#include +#include extern "C" { #include } @@ -10,30 +11,37 @@ extern "C" { namespace Poincare { -HyperbolicTangent::HyperbolicTangent() : - Function("tanh") -{ -} - Expression::Type HyperbolicTangent::type() const { return Type::HyperbolicTangent; } -Expression * HyperbolicTangent::cloneWithDifferentOperands(Expression** newOperands, - int numberOfOperands, bool cloneOperands) const { - HyperbolicTangent * ht = new HyperbolicTangent(); - ht->setArgument(newOperands, numberOfOperands, cloneOperands); - return ht; +Expression * HyperbolicTangent::clone() const { + HyperbolicTangent * a = new HyperbolicTangent(m_operands, true); + return a; +} + +Expression * HyperbolicTangent::shallowReduce(Context& context, AngleUnit angleUnit) { + Expression * e = Expression::shallowReduce(context, angleUnit); + if (e != this) { + return e; + } +#if MATRIX_EXACT_REDUCING + Expression * op = editableOperand(0); + if (op->type() == Type::Matrix) { + return SimplificationEngine::map(this, context, angleUnit); + } +#endif + return this; } template -Complex HyperbolicTangent::compute(const Complex c) { +Complex HyperbolicTangent::computeOnComplex(const Complex c, AngleUnit angleUnit) { if (c.b() == 0) { return Complex::Float(std::tanh(c.a())); } - Complex arg1 = HyperbolicSine::compute(c); - Complex arg2 = HyperbolicCosine::compute(c); - return Fraction::compute(arg1, arg2); + Complex arg1 = HyperbolicSine::computeOnComplex(c, angleUnit); + Complex arg2 = HyperbolicCosine::computeOnComplex(c, angleUnit); + return Division::compute(arg1, arg2); } } diff --git a/poincare/src/imaginary_part.cpp b/poincare/src/imaginary_part.cpp index b68f44b4b..506141ef3 100644 --- a/poincare/src/imaginary_part.cpp +++ b/poincare/src/imaginary_part.cpp @@ -1,5 +1,7 @@ #include #include +#include +#include #include extern "C" { #include @@ -7,25 +9,35 @@ extern "C" { namespace Poincare { -ImaginaryPart::ImaginaryPart() : - Function("im") -{ -} - Expression::Type ImaginaryPart::type() const { return Type::ImaginaryPart; } -Expression * ImaginaryPart::cloneWithDifferentOperands(Expression** newOperands, - int numberOfOperands, bool cloneOperands) const { - assert(newOperands != nullptr); - ImaginaryPart * ip = new ImaginaryPart(); - ip->setArgument(newOperands, numberOfOperands, cloneOperands); - return ip; +Expression * ImaginaryPart::clone() const { + ImaginaryPart * a = new ImaginaryPart(m_operands, true); + return a; +} + + +Expression * ImaginaryPart::shallowReduce(Context& context, AngleUnit angleUnit) { + Expression * e = Expression::shallowReduce(context, angleUnit); + if (e != this) { + return e; + } + Expression * op = editableOperand(0); +#if MATRIX_EXACT_REDUCING + if (op->type() == Type::Matrix) { + return SimplificationEngine::map(this, context, angleUnit); + } +#endif + if (op->type() == Type::Rational) { + return replaceWith(new Rational(0), true); + } + return this; } template -Complex ImaginaryPart::templatedComputeComplex(const Complex c) const { +Complex ImaginaryPart::computeOnComplex(const Complex c, AngleUnit angleUnit) { return Complex::Float(c.b()); } diff --git a/poincare/src/integer.cpp b/poincare/src/integer.cpp index 133ec3f49..8a49f1dab 100644 --- a/poincare/src/integer.cpp +++ b/poincare/src/integer.cpp @@ -7,52 +7,54 @@ extern "C" { #include #include #include "layout/string_layout.h" - -#define MAX(a,b) ((a)>(b)?a:b) -#define NATIVE_UINT_BIT_COUNT (8*sizeof(native_uint_t)) +#include namespace Poincare { -uint8_t log2(native_uint_t v) { - assert(NATIVE_UINT_BIT_COUNT < 256); // Otherwise uint8_t isn't OK - for (uint8_t i=0; iy ? x : y); } + +uint8_t log2(Integer::native_uint_t v) { + constexpr int nativeUnsignedIntegerBitCount = 8*sizeof(Integer::native_uint_t); + static_assert(nativeUnsignedIntegerBitCount < 256, "uint8_t cannot contain the log2 of a native_uint_t"); + for (uint8_t i=0; i0 ? i : -i); +// Constructors + +Integer::Integer(double_native_int_t i) { + double_native_uint_t j = i < 0 ? -i : i; + if (j <= 0xFFFFFFFF) { + m_digit = j; + m_numberOfDigits = 1; + } else { + native_uint_t * digits = new native_uint_t [2]; + digits[0] = j & 0xFFFFFFFF; + digits[1] = (j >> 32) & 0xFFFFFFFF; + m_digits = digits; + m_numberOfDigits = 2; + } + m_negative = i < 0; } /* Caution: string is NOT guaranteed to be NULL-terminated! */ -Integer::Integer(const char * digits, bool negative) { - m_negative = negative; - +Integer::Integer(const char * digits, bool negative) : + Integer(0) +{ if (digits != nullptr && digits[0] == '-') { - m_negative = true; + negative = true; digits++; } @@ -61,160 +63,195 @@ Integer::Integer(const char * digits, bool negative) { if (digits != nullptr) { Integer base = Integer(10); while (*digits >= '0' && *digits <= '9') { - result = result.multiply_by(base); - result = result.add(Integer(*digits-'0')); + result = Multiplication(result, base); + result = Addition(result, Integer(*digits-'0')); digits++; } } - // Pilfer v's ivars - m_numberOfDigits = result.m_numberOfDigits; - m_digits = result.m_digits; + *this = std::move(result); - // Zero-out v - result.m_numberOfDigits = 0; - result.m_digits = NULL; + if (isZero()) { + negative = false; + } + m_negative = negative; +} + +Integer Integer::exponent(int fractionalPartLength, const char * exponent, int exponentLength, bool exponentNegative) { + Integer base = Integer(10); + Integer power = Integer(0); + for (int i = 0; i < exponentLength; i++) { + power = Multiplication(power, base); + power = Addition(power, Integer(*exponent-'0')); + exponent++; + } + if (exponentNegative) { + power.setNegative(true); + } + return Subtraction(Integer(fractionalPartLength), power); +} + +Integer Integer::numerator(const char * integralPart, int integralPartLength, const char * fractionalPart, int fractionalPartLength, bool negative, Integer * exponent) { + Integer base = Integer(10); + Integer numerator = Integer(integralPart, negative); + for (int i = 0; i < fractionalPartLength; i++) { + numerator = Multiplication(numerator, base); + numerator = Addition(numerator, Integer(*fractionalPart-'0')); + fractionalPart++; + } + if (exponent->isNegative()) { + while (!exponent->isEqualTo(Integer(0))) { + numerator = Multiplication(numerator, base); + *exponent = Addition(*exponent, Integer(1)); + } + } + return numerator; +} + +Integer Integer::denominator(Integer * exponent) { + Integer base = Integer(10); + Integer denominator = Integer(1); + if (!exponent->isNegative()) { + while (!exponent->isEqualTo(Integer(0))) { + denominator = Multiplication(denominator, base); + *exponent = Subtraction(*exponent, Integer(1)); + } + } + return denominator; } Integer::~Integer() { - if (m_digits) { - delete[] m_digits; - } + releaseDynamicIvars(); } -// Private methods - -Integer::Integer(native_uint_t * digits, uint16_t numberOfDigits, bool negative) : - m_digits(digits), - m_numberOfDigits(numberOfDigits), - m_negative(negative) { -} - -int8_t Integer::ucmp(const Integer &other) const { - if (m_numberOfDigits < other.m_numberOfDigits) { - return -1; - } else if (other.m_numberOfDigits < m_numberOfDigits) { - return 1; - } - for (uint16_t i = 0; i < m_numberOfDigits; i++) { - // Digits are stored most-significant last - native_uint_t digit = m_digits[m_numberOfDigits-i-1]; - native_uint_t otherDigit = other.m_digits[m_numberOfDigits-i-1]; - if (digit < otherDigit) { - return -1; - } else if (otherDigit < digit) { - return 1; - } - } - return 0; -} - -static inline int8_t sign(bool negative) { - return 1 - 2*(int8_t)negative; -} - -bool Integer::operator<(const Integer &other) const { - if (m_negative != other.m_negative) { - return m_negative; - } - return (sign(m_negative)*ucmp(other) < 0); -} - -bool Integer::operator==(const Integer &other) const { - if (m_negative != other.m_negative) { - return false; - } - return (ucmp(other) == 0); -} - -Integer& Integer::operator=(Integer&& other) { - if (this != &other) { - // Release our ivars - m_negative = 0; - m_numberOfDigits = 0; - delete[] m_digits; - - // Pilfer other's ivars - m_numberOfDigits = other.m_numberOfDigits; +Integer::Integer(Integer && other) { + // Pilfer other's data + if (other.usesImmediateDigit()) { + m_digit = other.m_digit; + } else { m_digits = other.m_digits; + } + m_numberOfDigits = other.m_numberOfDigits; + m_negative = other.m_negative; + + // Reset other + other.m_digit = 0; + other.m_numberOfDigits = 1; + other.m_negative = 0; +} + +Integer::Integer(const Integer& other) { + // Copy other's data + if (other.usesImmediateDigit()) { + m_digit = other.m_digit; + } else { + native_uint_t * digits = new native_uint_t [other.m_numberOfDigits]; + for (int i=0; iabs(other) : s = sign*udiff(this, other) - * 2/abs(other)>abs(this) : s = sign*udiff(other, this) - * sign? sign of the greater! */ - if (ucmp(other) >= 0) { - return usum(other, true, m_negative); +Integer& Integer::operator=(const Integer& other) { + if (this != &other) { + releaseDynamicIvars(); + // Copy other's ivars + if (other.usesImmediateDigit()) { + m_digit = other.m_digit; } else { - return other.usum(*this, true, other_negative); + native_uint_t * digits = new native_uint_t [other.m_numberOfDigits]; + for (int i=0; i= m_numberOfDigits ? 0 : m_digits[i]); - native_uint_t b = (i >= other.m_numberOfDigits ? 0 : other.m_digits[i]); - native_uint_t result = (subtract ? a - b - carry : a + b + carry); - digits[i] = result; - carry = (subtract ? (aresult)||(b>result))); // There's been an underflow or overflow - } - while (digits[size-1] == 0 && size>1) { - size--; - // We could realloc digits to a smaller size. Probably not worth the trouble. - } - return Integer(digits, size, output_negative); +bool Integer::isEqualTo(const Integer & other) const { + return (NaturalOrder(*this, other) == 0); } -Integer Integer::multiply_by(const Integer &other) const { +bool Integer::isLowerThan(const Integer & other) const { + return (NaturalOrder(*this, other) < 0); +} + +// Arithmetic + +Integer Integer::Addition(const Integer & a, const Integer & b) { + return addition(a, b, false); +} + +Integer Integer::Subtraction(const Integer & a, const Integer & b) { + return addition(a, b, true); +} + +Integer Integer::Multiplication(const Integer & a, const Integer & b) { assert(sizeof(double_native_uint_t) == 2*sizeof(native_uint_t)); - uint16_t productSize = other.m_numberOfDigits + m_numberOfDigits; + uint16_t productSize = a.m_numberOfDigits + b.m_numberOfDigits; native_uint_t * digits = new native_uint_t [productSize]; memset(digits, 0, productSize*sizeof(native_uint_t)); double_native_uint_t carry = 0; - for (uint16_t i=0; i1) { @@ -222,224 +259,346 @@ Integer Integer::multiply_by(const Integer &other) const { /* At this point we could realloc m_digits to a smaller size. */ } - return Integer(digits, productSize, m_negative != other.m_negative); + return Integer(digits, productSize, a.m_negative != b.m_negative); } -Division::Division(const Integer &numerator, const Integer &denominator) : -m_quotient(Integer((native_int_t)0)), -m_remainder(Integer((native_int_t)0)) { - // FIXME: First, test if denominator is zero. - - if (numerator < denominator) { - m_quotient = Integer((native_int_t)0); - m_remainder = numerator.add(Integer((native_int_t)0)); - // FIXME: This is a ugly way to bypass creating a copy constructor! - return; +Integer Integer::Factorial(const Integer & i) { + Integer j = Integer(2); + Integer result = Integer(1); + while (!i.isLowerThan(j)) { + result = Multiplication(j, result); + j = Addition(j, Integer(1)); } + return result; +} - // Recursive case - *this = Division(numerator, denominator.add(denominator)); - m_quotient = m_quotient.add(m_quotient); - if (!(m_remainder < denominator)) { - m_remainder = m_remainder.subtract(denominator); - m_quotient = m_quotient.add(Integer(1)); +IntegerDivision Integer::Division(const Integer & numerator, const Integer & denominator) { + if (!numerator.isNegative() && !denominator.isNegative()) { + return udiv(numerator, denominator); + } + Integer absNumerator = numerator; + absNumerator.setNegative(false); + Integer absDenominator = denominator; + absDenominator.setNegative(false); + IntegerDivision usignedDiv = udiv(absNumerator, absDenominator); + if (usignedDiv.remainder.isEqualTo(Integer(0))) { + if (!numerator.isNegative() || !denominator.isNegative()) { + usignedDiv.quotient.setNegative(true); + } + return usignedDiv; + } + if (numerator.isNegative()) { + if (denominator.isNegative()) { + usignedDiv.remainder.setNegative(true); + usignedDiv.quotient = Addition(usignedDiv.quotient, Integer(1)); + usignedDiv.remainder = Integer::Subtraction(usignedDiv.remainder, denominator); + } else { + usignedDiv.quotient.setNegative(true); + usignedDiv.quotient = Subtraction(usignedDiv.quotient, Integer(1)); + usignedDiv.remainder = Integer::Subtraction(denominator, usignedDiv.remainder); + } + } else { + assert(denominator.isNegative()); + usignedDiv.quotient.setNegative(true); + } + return usignedDiv; +} + +Integer Integer::Power(const Integer & i, const Integer & j) { + // TODO: optimize with dichotomia + assert(!j.isNegative()); + Integer index = j; + Integer result = Integer(1); + while (!index.isEqualTo(Integer(0))) { + result = Multiplication(result, i); + index = Subtraction(index, Integer(1)); + } + return result; +} + +// Private methods + + /* WARNING: This constructor takes ownership of the digits array! */ +Integer::Integer(const native_uint_t * digits, uint16_t numberOfDigits, bool negative) : + m_numberOfDigits(numberOfDigits), + m_negative(negative) +{ + assert(digits != nullptr); + if (numberOfDigits == 1) { + m_digit = digits[0]; + delete[] digits; + if (isZero()) { + // Normalize zero + m_negative = false; + } + } else { + assert(numberOfDigits > 1); + m_digits = digits; } } -Integer Integer::divide_by(const Integer &other) const { - return Division(*this, other).m_quotient; -} - -Expression * Integer::clone() const { - Integer * clone = new Integer((native_int_t)0); - clone->m_numberOfDigits = m_numberOfDigits; - clone->m_negative = m_negative; - delete[] clone->m_digits; - clone->m_digits = new native_uint_t [m_numberOfDigits]; - for (unsigned int i=0;im_digits[i] = m_digits[i]; +void Integer::releaseDynamicIvars() { + if (!usesImmediateDigit()) { + assert(m_digits != nullptr); + delete[] m_digits; } - return clone; } -Evaluation * Integer::privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const { +int8_t Integer::ucmp(const Integer & a, const Integer & b) { + if (a.m_numberOfDigits < b.m_numberOfDigits) { + return -1; + } else if (a.m_numberOfDigits > b.m_numberOfDigits) { + return 1; + } + for (uint16_t i = 0; i < a.m_numberOfDigits; i++) { + // Digits are stored most-significant last + native_uint_t aDigit = a.digit(a.m_numberOfDigits-i-1); + native_uint_t bDigit = b.digit(b.m_numberOfDigits-i-1); + if (aDigit < bDigit) { + return -1; + } else if (aDigit > bDigit) { + return 1; + } + } + return 0; +} + +Integer Integer::usum(const Integer & a, const Integer & b, bool subtract, bool outputNegative) { + uint16_t size = max(a.m_numberOfDigits, b.m_numberOfDigits); + if (!subtract) { + // Addition can overflow + size += 1; + } + native_uint_t * digits = new native_uint_t [size]; + bool carry = false; + for (uint16_t i = 0; i= a.m_numberOfDigits ? 0 : a.digit(i)); + native_uint_t bDigit = (i >= b.m_numberOfDigits ? 0 : b.digit(i)); + native_uint_t result = (subtract ? aDigit - bDigit - carry : aDigit + bDigit + carry); + digits[i] = result; + carry = (subtract ? (aDigitresult)||(bDigit>result))); // There's been an underflow or overflow + } + while (digits[size-1] == 0 && size>1) { + size--; + // We could realloc digits to a smaller size. Probably not worth the trouble. + } + return Integer(digits, size, outputNegative); +} + + +Integer Integer::addition(const Integer & a, const Integer & b, bool inverseBNegative) { + bool bNegative = (inverseBNegative ? !b.m_negative : b.m_negative); + if (a.m_negative == bNegative) { + return usum(a, b, false, a.m_negative); + } else { + /* The signs are different, this is in fact a subtraction + * s = a+b = (abs(a)-abs(b) OR abs(b)-abs(a)) + * 1/abs(a)>abs(b) : s = sign*udiff(a, b) + * 2/abs(b)>abs(a) : s = sign*udiff(b, a) + * sign? sign of the greater! */ + if (ucmp(a, b) >= 0) { + return usum(a, b, true, a.m_negative); + } else { + return usum(b, a, true, bNegative); + } + } +} + +Integer Integer::IntegerWithHalfDigitAtIndex(half_native_uint_t halfDigit, int index) { + assert(halfDigit != 0); + half_native_uint_t * digits = (half_native_uint_t *)new native_uint_t [(index+1)/2]; + memset(digits, 0, (index+1)/2*sizeof(native_uint_t)); + digits[index-1] = halfDigit; + int indexInBase32 = index%2 == 1 ? index/2+1 : index/2; + return Integer((native_uint_t *)digits, indexInBase32, false); +} + +IntegerDivision Integer::udiv(const Integer & numerator, const Integer & denominator) { + /* Modern Computer Arithmetic, Richard P. Brent and Paul Zimmermann + * (Algorithm 1.6) */ + assert(!denominator.isZero()); + if (numerator.isLowerThan(denominator)) { + IntegerDivision div = {.quotient = 0, .remainder = numerator}; + return div; + } + + Integer A = numerator; + Integer B = denominator; + native_int_t base = (double_native_uint_t)1 << 16; + // TODO: optimize by just swifting digit and finding 2^kB that makes B normalized + native_int_t d = base/(native_int_t)(B.halfDigit(B.numberOfHalfDigits()-1)+1); + A = Multiplication(Integer(d), A); + B = Multiplication(Integer(d), B); + + int n = B.numberOfHalfDigits(); + int m = A.numberOfHalfDigits()-n; + half_native_uint_t * qDigits = (half_native_uint_t *)new native_uint_t [m/2+1]; + memset(qDigits, 0, (m/2+1)*sizeof(native_uint_t)); + Integer betam = IntegerWithHalfDigitAtIndex(1, m+1); + Integer betaMB = Multiplication(betam, B); // TODO: can swift all digits by m! B.swift16(mg) + if (!A.isLowerThan(betaMB)) { + qDigits[m] = 1; + A = Subtraction(A, betaMB); + } + for (int j = m-1; j >= 0; j--) { + native_uint_t base = 1 << 16; + native_uint_t qj2 = ((native_uint_t)A.halfDigit(n+j)*base+(native_uint_t)A.halfDigit(n+j-1))/(native_uint_t)B.halfDigit(n-1); + half_native_uint_t baseMinus1 = (1 << 16) -1; + qDigits[j] = qj2 < (native_uint_t)baseMinus1 ? (half_native_uint_t)qj2 : baseMinus1; + Integer factor = qDigits[j] > 0 ? IntegerWithHalfDigitAtIndex(qDigits[j], j+1) : Integer(0); + A = Subtraction(A, Multiplication(factor, B)); + Integer m = Multiplication(IntegerWithHalfDigitAtIndex(1, j+1), B); + while (A.isLowerThan(Integer(0))) { + qDigits[j] = qDigits[j]-1; + A = Addition(A, m); + } + } + int qNumberOfDigits = m+1; + while (qDigits[qNumberOfDigits-1] == 0 && qNumberOfDigits > 1) { + qNumberOfDigits--; + // We could realloc digits to a smaller size. Probably not worth the trouble. + } + int qNumberOfDigitsInBase32 = qNumberOfDigits%2 == 1 ? qNumberOfDigits/2+1 : qNumberOfDigits/2; + IntegerDivision div = {.quotient = Integer((native_uint_t *)qDigits, qNumberOfDigitsInBase32, false), .remainder = A}; + if (d != 1 && !div.remainder.isZero()) { + div.remainder = udiv(div.remainder, Integer(d)).quotient; + } + return div; +} + +template +T Integer::approximate() const { union { - uint32_t uint_result; - float float_result; - }; - assert(sizeof(float) == 4); - /* We're generating an IEEE 754 compliant float. - * Theses numbers are 32-bit values, stored as follow: - * sign (1 bit) - * exponent (8 bits) - * mantissa (23 bits) + uint64_t uint_result; + T float_result; + } u; + assert(sizeof(T) == 4 || sizeof(T) == 8); + /* We're generating an IEEE 754 compliant float(double). + * Theses numbers are 32(64)-bit values, stored as follow: + * sign: 1 bit (1 bit) + * exponent: 8 bits (11 bits) + * mantissa: 23 bits (52 bits) * * We can tell that: * - the sign is going to be 0 for now, we only handle positive numbers - * - the exponent is the length of our BigInt, in bits - 1 + 127; + * - the exponent is the length of our BigInt, in bits - 1 + 127 (-1+1023); * - the mantissa is the beginning of our BigInt, discarding the first bit */ - native_uint_t lastDigit = m_digits[m_numberOfDigits-1]; + native_uint_t lastDigit = digit(m_numberOfDigits-1); uint8_t numberOfBitsInLastDigit = log2(lastDigit); bool sign = m_negative; - uint8_t exponent = 126; - /* if the exponent is bigger then 255, it cannot be stored as a uint8. Also, - * the integer whose 2-exponent is bigger than 255 cannot be stored as a - * float (IEEE 754 floating point). The approximation is thus INFINITY. */ - if ((int)exponent + (m_numberOfDigits-1)*32 +numberOfBitsInLastDigit> 255) { - return new Complex(Complex::Float(INFINITY)); - } - exponent += (m_numberOfDigits-1)*32; - exponent += numberOfBitsInLastDigit; - - uint32_t mantissa = 0; - mantissa |= (lastDigit << (32-numberOfBitsInLastDigit)); - if (m_numberOfDigits >= 2) { - native_uint_t beforeLastDigit = m_digits[m_numberOfDigits-2]; - mantissa |= (beforeLastDigit >> numberOfBitsInLastDigit); -} - - if ((m_numberOfDigits==1) && (m_digits[0]==0)) { - /* This special case for 0 is needed, because the current algorithm assumes - * that the big integer is non zero, thus puts the exponent to 126 (integer - * area), the issue is that when the mantissa is 0, a "shadow bit" is - * assumed to be there, thus 126 0x000000 is equal to 0.5 and not zero. - */ - float result = m_negative ? -0.0f : 0.0f; - return new Complex(Complex::Float(result)); - } - - uint_result = 0; - uint_result |= (sign << 31); - uint_result |= (exponent << 23); - uint_result |= (mantissa >> (32-23-1) & 0x7FFFFF); - - /* If exponent is 255 and the float is undefined, we have exceed IEEE 754 - * representable float. */ - if (exponent == 255 && std::isnan(float_result)) { - return new Complex(Complex::Float(INFINITY)); - } - - return new Complex(Complex::Float(float_result)); -} - -Evaluation * Integer::privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const { - union { - uint64_t uint_result; - double double_result; - }; - assert(sizeof(double) == 8); - /* We're generating an IEEE 754 compliant double. - * Theses numbers are 64-bit values, stored as follow: - * sign (1 bit) - * exponent (11 bits) - * mantissa (52 bits) - * - * We can tell that: - * - the exponent is the length of our BigInt, in bits - 1 + 1023; - * - the mantissa is the beginning of our BigInt, discarding the first bit - */ - native_uint_t lastDigit = m_digits[m_numberOfDigits-1]; - uint8_t numberOfBitsInLastDigit = log2(lastDigit); - - bool sign = m_negative; - - uint16_t exponent = 1022; - /* if the exponent is bigger then 2047, it cannot be stored as a uint11. Also, - * the integer whose 2-exponent is bigger than 2047 cannot be stored as a - * double (IEEE 754 double point). The approximation is thus INFINITY. */ - if ((int)exponent + (m_numberOfDigits-1)*32 +numberOfBitsInLastDigit> 2047) { - return new Complex(Complex::Float(INFINITY)); + int signNbBit = 1; + int exponentNbBit = sizeof(T) == sizeof(float) ? 8 : 11; + int mantissaNbBit = sizeof(T) == sizeof(float) ? 23 : 52; + int totalNumberOfBits = signNbBit + exponentNbBit + mantissaNbBit; + uint16_t exponent = (1 << (exponentNbBit-1)) -2; + /* if the exponent is bigger then 255 (2047), it cannot be stored as a + * uint8(uint11). Also, the integer whose 2-exponent is bigger than 255(2047) + * cannot be stored as a float (IEEE 754 floating(double) point). The + * approximation is thus INFINITY. */ + int maxExponent = (1 << exponentNbBit) -1; + if ((int)exponent + (m_numberOfDigits-1)*32 +numberOfBitsInLastDigit> maxExponent) { + return INFINITY; } exponent += (m_numberOfDigits-1)*32; exponent += numberOfBitsInLastDigit; uint64_t mantissa = 0; - mantissa |= ((uint64_t)lastDigit << (64-numberOfBitsInLastDigit)); + mantissa |= ((uint64_t)lastDigit << (totalNumberOfBits-numberOfBitsInLastDigit)); int digitIndex = 2; int numberOfBits = log2(lastDigit); while (m_numberOfDigits >= digitIndex) { - lastDigit = m_digits[m_numberOfDigits-digitIndex]; + lastDigit = digit(m_numberOfDigits-digitIndex); numberOfBits += 32; - if (64 > numberOfBits) { - mantissa |= ((uint64_t)lastDigit << (64-numberOfBits)); + if (totalNumberOfBits > numberOfBits) { + mantissa |= ((uint64_t)lastDigit << (totalNumberOfBits-numberOfBits)); } else { - mantissa |= ((uint64_t)lastDigit >> (numberOfBits-64)); + mantissa |= ((uint64_t)lastDigit >> (numberOfBits-totalNumberOfBits)); } digitIndex++; } - if ((m_numberOfDigits==1) && (m_digits[0]==0)) { + if (isZero()) { /* This special case for 0 is needed, because the current algorithm assumes * that the big integer is non zero, thus puts the exponent to 126 (integer * area), the issue is that when the mantissa is 0, a "shadow bit" is * assumed to be there, thus 126 0x000000 is equal to 0.5 and not zero. */ - float result = m_negative ? -0.0f : 0.0f; - return new Complex(Complex::Float(result)); + T result = m_negative ? -0.0 : 0.0; + return result; } - uint_result = 0; - uint_result |= ((uint64_t)sign << 63); - uint_result |= ((uint64_t)exponent << 52); - uint_result |= ((uint64_t)mantissa >> (64-52-1) & 0xFFFFFFFFFFFFF); + u.uint_result = 0; + u.uint_result |= ((uint64_t)sign << (totalNumberOfBits-1)); + u.uint_result |= ((uint64_t)exponent << mantissaNbBit); + uint64_t oneOnMantissaBits = mantissaNbBit == 23 ? 0x7FFFFF : 0xFFFFFFFFFFFFF; + u.uint_result |= ((uint64_t)mantissa >> (totalNumberOfBits-mantissaNbBit-1) & oneOnMantissaBits); - /* If exponent is 2047 and the double is undefined, we have exceed IEEE 754 - * representable double. */ - if (exponent == 2047 && std::isnan(double_result)) { - return new Complex(Complex::Float(INFINITY)); + /* If exponent is 255 and the float is undefined, we have exceed IEEE 754 + * representable float. */ + if (exponent == maxExponent && std::isnan(u.float_result)) { + return INFINITY; } - return new Complex(Complex::Float(double_result)); + return u.float_result; } -Expression::Type Integer::type() const { - return Type::Integer; -} - -ExpressionLayout * Integer::privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const { - assert(floatDisplayMode != FloatDisplayMode::Default); - assert(complexFormat != ComplexFormat::Default); +int Integer::writeTextInBuffer(char * buffer, int bufferSize) const { + if (bufferSize == 0) { + return -1; + } + buffer[bufferSize-1] = 0; /* If the integer is too long, this method may overflow the stack. * Experimentally, we can display at most integer whose number of digits is * around 7. However, to avoid crashing when the stack is already half full, * we decide not to display integers whose number of digits > 5. */ - if (m_numberOfDigits > 5) { - return new StringLayout("inf", 3); - } - - char buffer[255]; + /*if (m_numberOfDigits > 12) { + return strlcpy(buffer, "inf", 4); + }*/ Integer base = Integer(10); - Division d = Division(*this, base); + Integer abs = *this; + abs.setNegative(false); + IntegerDivision d = udiv(abs, base); int size = 0; - if (*this == Integer((native_int_t)0)) { - buffer[size++] = '0'; + if (bufferSize == 1) { + return 0; } - while (!(d.m_remainder == Integer((native_int_t)0) && - d.m_quotient == Integer((native_int_t)0))) { - assert(size<255); //TODO: malloc an extra buffer - char c = char_from_digit(d.m_remainder.m_digits[0]); + if (isEqualTo(Integer(0))) { + buffer[size++] = '0'; + } else if (isNegative()) { + buffer[size++] = '-'; + } + while (!(d.remainder.isEqualTo(Integer(0)) && + d.quotient.isEqualTo(Integer(0)))) { + char c = char_from_digit(d.remainder.digit(0)); + if (size >= bufferSize-1) { + return strlcpy(buffer, "inf", bufferSize); + } buffer[size++] = c; - d = Division(d.m_quotient, base); + d = Division(d.quotient, base); } buffer[size] = 0; // Flip the string - for (int i=0, j=size-1 ; i < j ; i++, j--) { + int startChar = isNegative() ? 1 : 0; + for (int i=startChar, j=size-1 ; i < j ; i++, j--) { char c = buffer[i]; buffer[i] = buffer[j]; buffer[j] = c; } - - return new StringLayout(buffer, size); + return size; } -bool Integer::valueEquals(const Expression * e) const { - assert(e->type() == Type::Integer); - return (*this == *(Integer *)e); // FIXME: Remove operator overloading +ExpressionLayout * Integer::createLayout() const { + char buffer[255]; + int numberOfChars = writeTextInBuffer(buffer, 255); + return new StringLayout(buffer, numberOfChars); } +template float Poincare::Integer::approximate() const; +template double Poincare::Integer::approximate() const; + } diff --git a/poincare/src/integral.cpp b/poincare/src/integral.cpp index 245e78422..9d02e67ef 100644 --- a/poincare/src/integral.cpp +++ b/poincare/src/integral.cpp @@ -1,44 +1,49 @@ #include #include -#include #include +#include #include extern "C" { #include #include #include } -#include "layout/horizontal_layout.h" #include "layout/string_layout.h" #include "layout/integral_layout.h" +#include "layout/horizontal_layout.h" namespace Poincare { -Integral::Integral() : - Function("int", 3) -{ -} - Expression::Type Integral::type() const { return Type::Integral; } -Expression * Integral::cloneWithDifferentOperands(Expression** newOperands, - int numberOfOperands, bool cloneOperands) const { - assert(newOperands != nullptr); - Integral * i = new Integral(); - i->setArgument(newOperands, numberOfOperands, cloneOperands); - return i; +Expression * Integral::clone() const { + Integral * a = new Integral(m_operands, true); + return a; +} + +Expression * Integral::shallowReduce(Context& context, AngleUnit angleUnit) { + Expression * e = Expression::shallowReduce(context, angleUnit); + if (e != this) { + return e; + } +#if MATRIX_EXACT_REDUCING + if (operand(0)->type() == Type::Matrix || operand(1)->type() == Type::Matrix || operand(2)->type() == Type::Matrix) { + return replaceWith(new Undefined(), true); + } +#endif + return this; } template -Evaluation * Integral::templatedEvaluate(Context & context, AngleUnit angleUnit) const { +Complex * Integral::templatedEvaluate(Context & context, AngleUnit angleUnit) const { VariableContext xContext = VariableContext('x', &context); - Evaluation * aInput = m_args[1]->evaluate(context, angleUnit); - T a = aInput->toScalar(); + Expression * aInput = operand(1)->evaluate(context, angleUnit); + T a = aInput->type() == Type::Complex ? static_cast *>(aInput)->toScalar() : NAN; delete aInput; - Evaluation * bInput = m_args[2]->evaluate(context, angleUnit); - T b = bInput->toScalar(); + Expression * bInput = operand(2)->evaluate(context, angleUnit); + T b = bInput->type() == Type::Complex ? static_cast *>(bInput)->toScalar() : NAN; delete bInput; if (std::isnan(a) || std::isnan(b)) { return new Complex(Complex::Float(NAN)); @@ -55,18 +60,18 @@ ExpressionLayout * Integral::privateCreateLayout(FloatDisplayMode floatDisplayMo assert(floatDisplayMode != FloatDisplayMode::Default); assert(complexFormat != ComplexFormat::Default); ExpressionLayout * childrenLayouts[2]; - childrenLayouts[0] = m_args[0]->createLayout(floatDisplayMode, complexFormat); + childrenLayouts[0] = operand(0)->createLayout(floatDisplayMode, complexFormat); childrenLayouts[1] = new StringLayout("dx", 2); - return new IntegralLayout(m_args[1]->createLayout(floatDisplayMode, complexFormat), m_args[2]->createLayout(floatDisplayMode, complexFormat), new HorizontalLayout(childrenLayouts, 2)); + return new IntegralLayout(operand(1)->createLayout(floatDisplayMode, complexFormat), operand(2)->createLayout(floatDisplayMode, complexFormat), new HorizontalLayout(childrenLayouts, 2)); } template T Integral::functionValueAtAbscissa(T x, VariableContext xContext, AngleUnit angleUnit) const { Complex e = Complex::Float(x); - Symbol xSymbol = Symbol('x'); - xContext.setExpressionForSymbolName(&e, &xSymbol); - Evaluation * f = m_args[0]->evaluate(xContext, angleUnit); - T result = f->toScalar(); + Symbol xSymbol('x'); + xContext.setExpressionForSymbolName(&e, &xSymbol, xContext); + Expression * f = operand(0)->evaluate(xContext, angleUnit); + T result = f->type() == Type::Complex ? static_cast *>(f)->toScalar() : NAN; delete f; return result; } diff --git a/poincare/src/layout_engine.cpp b/poincare/src/layout_engine.cpp new file mode 100644 index 000000000..f5d6896b8 --- /dev/null +++ b/poincare/src/layout_engine.cpp @@ -0,0 +1,112 @@ +#include +#include "layout/string_layout.h" +#include "layout/parenthesis_layout.h" +#include "layout/horizontal_layout.h" +extern "C" { +#include +} + +namespace Poincare { + +ExpressionLayout * LayoutEngine::createInfixLayout(const Expression * expression, Expression::FloatDisplayMode floatDisplayMode, Expression::ComplexFormat complexFormat, const char * operatorName) { + assert(floatDisplayMode != Expression::FloatDisplayMode::Default); + assert(complexFormat != Expression::ComplexFormat::Default); + int numberOfOperands = expression->numberOfOperands(); + assert(numberOfOperands > 1); + ExpressionLayout** children_layouts = new ExpressionLayout * [2*numberOfOperands-1]; + children_layouts[0] = expression->operand(0)->createLayout(); + for (int i=1; ioperand(i)->type() == Expression::Type::Opposite ? new ParenthesisLayout(expression->operand(i)->createLayout(floatDisplayMode, complexFormat)) : expression->operand(i)->createLayout(floatDisplayMode, complexFormat); + } + /* HorizontalLayout holds the children layouts so they do not need to be + * deleted here. */ + ExpressionLayout * layout = new HorizontalLayout(children_layouts, 2*numberOfOperands-1); + delete[] children_layouts; + return layout; +} + +ExpressionLayout * LayoutEngine::createPrefixLayout(const Expression * expression, Expression::FloatDisplayMode floatDisplayMode, Expression::ComplexFormat complexFormat, const char * operatorName) { + assert(floatDisplayMode != Expression::FloatDisplayMode::Default); + assert(complexFormat != Expression::ComplexFormat::Default); + int numberOfOperands = expression->numberOfOperands(); + ExpressionLayout ** grandChildrenLayouts = new ExpressionLayout *[2*numberOfOperands-1]; + int layoutIndex = 0; + grandChildrenLayouts[layoutIndex++] = expression->operand(0)->createLayout(floatDisplayMode, complexFormat); + for (int i = 1; i < numberOfOperands; i++) { + grandChildrenLayouts[layoutIndex++] = new StringLayout(",", 1); + grandChildrenLayouts[layoutIndex++] = expression->operand(i)->createLayout(floatDisplayMode, complexFormat); + } + /* HorizontalLayout holds the grand children layouts so they do not need to + * be deleted */ + ExpressionLayout * argumentLayouts = new HorizontalLayout(grandChildrenLayouts, 2*numberOfOperands-1); + delete [] grandChildrenLayouts; + ExpressionLayout * childrenLayouts[2]; + childrenLayouts[0] = new StringLayout(operatorName, strlen(operatorName)); + childrenLayouts[1] = new ParenthesisLayout(argumentLayouts); + /* Same comment as above */ + return new HorizontalLayout(childrenLayouts, 2); +} + +int LayoutEngine::writeInfixExpressionTextInBuffer(const Expression * expression, char * buffer, int bufferSize, const char * operatorName, OperandNeedParenthesis operandNeedParenthesis) { + if (bufferSize == 0) { + return -1; + } + buffer[bufferSize-1] = 0; + int numberOfChar = 0; + int numberOfOperands = expression->numberOfOperands(); + assert(numberOfOperands > 1); + if (numberOfChar >= bufferSize-1) { return bufferSize-1; } + if (operandNeedParenthesis(expression->operand(0))) { + buffer[numberOfChar++] = '('; + if (numberOfChar >= bufferSize-1) { return bufferSize-1; } + } + numberOfChar += expression->operand(0)->writeTextInBuffer(buffer+numberOfChar, bufferSize-numberOfChar); + if (operandNeedParenthesis(expression->operand(0))) { + buffer[numberOfChar++] = ')'; + if (numberOfChar >= bufferSize-1) { return bufferSize-1; } + } + for (int i=1; i= bufferSize-1) { return bufferSize-1; } + numberOfChar += strlcpy(buffer+numberOfChar, operatorName, bufferSize-numberOfChar); + if (numberOfChar >= bufferSize-1) { return bufferSize-1; } + if (operandNeedParenthesis(expression->operand(i))) { + buffer[numberOfChar++] = '('; + if (numberOfChar >= bufferSize-1) { return bufferSize-1; } + } + numberOfChar += expression->operand(i)->writeTextInBuffer(buffer+numberOfChar, bufferSize-numberOfChar); + if (operandNeedParenthesis(expression->operand(i))) { + buffer[numberOfChar++] = ')'; + if (numberOfChar >= bufferSize-1) { return bufferSize-1; } + } + } + buffer[numberOfChar] = 0; + return numberOfChar; +} + +int LayoutEngine::writePrefixExpressionTextInBuffer(const Expression * expression, char * buffer, int bufferSize, const char * operatorName) { + if (bufferSize == 0) { + return -1; + } + buffer[bufferSize-1] = 0; + int numberOfChar = 0; + numberOfChar += strlcpy(buffer, operatorName, bufferSize); + if (numberOfChar >= bufferSize-1) { return bufferSize-1; } + buffer[numberOfChar++] = '('; + if (numberOfChar >= bufferSize-1) { return bufferSize-1; } + int numberOfOperands = expression->numberOfOperands(); + assert(numberOfOperands > 0); + numberOfChar += expression->operand(0)->writeTextInBuffer(buffer+numberOfChar, bufferSize-numberOfChar); + for (int i = 1; i < numberOfOperands; i++) { + if (numberOfChar >= bufferSize-1) { return bufferSize-1; } + buffer[numberOfChar++] = ','; + if (numberOfChar >= bufferSize-1) { return bufferSize-1; } + numberOfChar += expression->operand(i)->writeTextInBuffer(buffer+numberOfChar, bufferSize-numberOfChar); + } + if (numberOfChar >= bufferSize-1) { return bufferSize-1; } + buffer[numberOfChar++] = ')'; + buffer[numberOfChar] = 0; + return numberOfChar; +} + +} diff --git a/poincare/src/leaf_expression.cpp b/poincare/src/leaf_expression.cpp deleted file mode 100644 index e1a37d118..000000000 --- a/poincare/src/leaf_expression.cpp +++ /dev/null @@ -1,28 +0,0 @@ -#include -extern "C" { -#include -#include -} - -namespace Poincare { - -bool LeafExpression::hasValidNumberOfArguments() const { - return true; -} - -int LeafExpression::numberOfOperands() const { - return 0; -} - -const Expression * LeafExpression::operand(int i) const { - assert(false); - return nullptr; -} - -Expression * LeafExpression::cloneWithDifferentOperands(Expression** newOperands, - int numberOfOperands, bool cloneOperands) const { - assert(numberOfOperands == 0); - return this->clone(); -} - -} diff --git a/poincare/src/least_common_multiple.cpp b/poincare/src/least_common_multiple.cpp index cc23f5929..f55d410dd 100644 --- a/poincare/src/least_common_multiple.cpp +++ b/poincare/src/least_common_multiple.cpp @@ -1,5 +1,8 @@ #include #include +#include +#include +#include extern "C" { #include @@ -8,34 +11,65 @@ extern "C" { namespace Poincare { -LeastCommonMultiple::LeastCommonMultiple() : - Function("lcm", 2) -{ -} - Expression::Type LeastCommonMultiple::type() const { return Type::LeastCommonMultiple; } -Expression * LeastCommonMultiple::cloneWithDifferentOperands(Expression** newOperands, - int numberOfOperands, bool cloneOperands) const { - assert(newOperands != nullptr); - LeastCommonMultiple * lcm = new LeastCommonMultiple(); - lcm->setArgument(newOperands, numberOfOperands, cloneOperands); - return lcm; +Expression * LeastCommonMultiple::clone() const { + LeastCommonMultiple * a = new LeastCommonMultiple(m_operands, true); + return a; +} + +Expression * LeastCommonMultiple::shallowReduce(Context& context, AngleUnit angleUnit) { + Expression * e = Expression::shallowReduce(context, angleUnit); + if (e != this) { + return e; + } + Expression * op0 = editableOperand(0); + Expression * op1 = editableOperand(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()) { + return replaceWith(new Undefined(), true); + } + } + if (op1->type() == Type::Rational) { + Rational * r1 = static_cast(op1); + if (!r1->denominator().isOne()) { + 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 a = r0->numerator(); + Integer b = r1->numerator(); + Integer lcm = Arithmetic::LCM(&a, &b); + return replaceWith(new Rational(lcm), true); } template -Evaluation * LeastCommonMultiple::templatedEvaluate(Context& context, AngleUnit angleUnit) const { - Evaluation * f1Input = m_args[0]->evaluate(context, angleUnit); - Evaluation * f2Input = m_args[1]->evaluate(context, angleUnit); - T f1 = f1Input->toScalar(); - T f2 = f2Input->toScalar(); +Complex * LeastCommonMultiple::templatedEvaluate(Context& context, AngleUnit angleUnit) const { + Expression * f1Input = operand(0)->evaluate(context, angleUnit); + Expression * f2Input = operand(1)->evaluate(context, angleUnit); + T f1 = f1Input->type() == Type::Complex ? static_cast *>(f1Input)->toScalar() : NAN; + T f2 = f2Input->type() == Type::Complex ? static_cast *>(f2Input)->toScalar() : NAN; delete f1Input; delete f2Input; - if (std::isnan(f1) || std::isnan(f2) || f1 != (int)f1 || f2 != (int)f2 || f1 == 0.0f || f2 == 0.0f) { + if (std::isnan(f1) || std::isnan(f2) || f1 != (int)f1 || f2 != (int)f2) { return new Complex(Complex::Float(NAN)); } + if (f1 == 0.0f || f2 == 0.0f) { + return new Complex(Complex::Float(0)); + } int a = (int)f2; int b = (int)f1; if (f1 > f2) { diff --git a/poincare/src/list_data.cpp b/poincare/src/list_data.cpp index 82e29b43c..110d6163c 100644 --- a/poincare/src/list_data.cpp +++ b/poincare/src/list_data.cpp @@ -16,7 +16,9 @@ ListData::ListData(Expression * operand) : ListData::~ListData() { for (int i=0; i -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include +#include extern "C" { #include #include @@ -12,54 +23,185 @@ extern "C" { namespace Poincare { -Logarithm::Logarithm() : - Function("log", 2) -{ -} - -bool Logarithm::hasValidNumberOfArguments() const { - if (m_numberOfArguments != 1 && m_numberOfArguments != 2) { - return false; - } - for (int i = 0; i < m_numberOfArguments; i++) { - if (!m_args[i]->hasValidNumberOfArguments()) { - return false; - } - } - return true; -} - Expression::Type Logarithm::type() const { return Type::Logarithm; } -Expression * Logarithm::cloneWithDifferentOperands(Expression** newOperands, - int numberOfOperands, bool cloneOperands) const { - assert(newOperands != nullptr); - Logarithm * l = new Logarithm(); - l->setArgument(newOperands, numberOfOperands, cloneOperands); - return l; +Expression * Logarithm::clone() const { + return new Logarithm(operands(), numberOfOperands(), true); } template -Complex Logarithm::templatedComputeComplex(const Complex c) const { +Complex Logarithm::computeOnComplex(const Complex c, AngleUnit angleUnit) { if (c.b() != 0) { return Complex::Float(NAN); } return Complex::Float(std::log10(c.a())); } +Expression * Logarithm::shallowReduce(Context& context, AngleUnit angleUnit) { + Expression * e = Expression::shallowReduce(context, angleUnit); + if (e != this) { + return e; + } + Expression * op = editableOperand(0); +#if MATRIX_EXACT_REDUCING + if (numberOfOperands() == 1 && op->type() == Type::Matrix) { + return SimplificationEngine::map(this, context, angleUnit); + } + if (numberOfOperands() == 2 && (op->type() == Type::Matrix || operand(1)->type() == Type::Matrix)) { + return replaceWith(new Undefined(), true); + } +#endif + if (op->sign() == Sign::Negative || (numberOfOperands() == 2 && operand(1)->sign() == Sign::Negative)) { + return replaceWith(new Undefined(), true); + } + // log(x,x)->1 + if (numberOfOperands() == 2 && op->isIdenticalTo(operand(1))) { + return replaceWith(new Rational(1), true); + } + /* We do not apply some rules if the parent node is a power of b. In this + * case there is a simplication of form e^ln(3^(1/2))->3^(1/2) */ + bool letLogAtRoot = parentIsAPowerOfSameBase(); + // log(x^y, b)->y*log(x, b) if x>0 + if (!letLogAtRoot && op->type() == Type::Power && op->operand(0)->sign() == Sign::Positive) { + Power * p = static_cast(op); + Expression * x = p->editableOperand(0); + Expression * y = p->editableOperand(1); + p->detachOperands(); + replaceOperand(p, x, true); + Expression * newLog = shallowReduce(context, angleUnit); + newLog = newLog->replaceWith(new Multiplication(y, newLog->clone(), false), true); + return newLog->shallowReduce(context, angleUnit); + } + // log(x*y, b)->log(x,b)+log(y, b) if x,y>0 + if (!letLogAtRoot && op->type() == Type::Multiplication) { + Addition * a = new Addition(); + for (int i = 0; inumberOfOperands()-1; i++) { + Expression * factor = op->editableOperand(i); + if (factor->sign() == Sign::Positive) { + Expression * newLog = clone(); + static_cast(op)->removeOperand(factor, false); + newLog->replaceOperand(newLog->editableOperand(0), factor, true); + a->addOperand(newLog); + newLog->shallowReduce(context, angleUnit); + } + } + if (a->numberOfOperands() > 0) { + op->shallowReduce(context, angleUnit); + Expression * reducedLastLog = shallowReduce(context, angleUnit); + reducedLastLog->replaceWith(a, false); + a->addOperand(reducedLastLog); + return a->shallowReduce(context, angleUnit); + } else { + delete a; + } + } + + if (op->type() == Type::Rational) { + const Rational * r = static_cast(operand(0)); + // log(0) = undef + if (r->isZero()) { + return replaceWith(new Undefined(), true); + } + // log(1) = 0; + if (r->isOne()) { + return replaceWith(new Rational(0), true); + } + // log(10) ->1 + if (numberOfOperands() == 1 && r->isTen()) { + return replaceWith(new Rational(1), true); + } + // log(r) = a0log(p0)+a1log(p1)+... with r = p0^a0*p1^a1*... (Prime decomposition) + if (!letLogAtRoot) { + Expression * n = splitInteger(r->numerator(), false, context, angleUnit); + Expression * d = splitInteger(r->denominator(), true, context, angleUnit); + Addition * a = new Addition(n, d, false); + replaceWith(a, true); + return a->shallowReduce(context, angleUnit); + } + } + return this; +} + +bool Logarithm::parentIsAPowerOfSameBase() const { + if (parent()->type() == Type::Power && parent()->operand(1) == this) { + const Expression * powerOperand0 = parent()->operand(0); + if (numberOfOperands() == 1) { + if (powerOperand0->type() == Type::Rational && static_cast(powerOperand0)->isTen()) { + return true; + } + } + if (numberOfOperands() == 2) { + if (powerOperand0->isIdenticalTo(operand(1))){ + return true; + } + } + } + return false; +} + +Expression * Logarithm::splitInteger(Integer i, bool isDenominator, Context & context, AngleUnit angleUnit) { + assert(!i.isZero()); + assert(!i.isNegative()); + if (i.isOne()) { + return new Rational(0); + } + assert(!i.isOne()); + if (Arithmetic::k_primorial32.isLowerThan(i)) { + // We do not want to break i in prime factor because it might be take too many factors... More than k_maxNumberOfPrimeFactors. + Expression * e = clone(); + e->replaceOperand(operand(0), new Rational(i), true); + if (!isDenominator) { + return e; + } + Multiplication * m = new Multiplication(new Rational(-1), e, false); + return m; + } + Integer factors[Arithmetic::k_maxNumberOfPrimeFactors]; + Integer coefficients[Arithmetic::k_maxNumberOfPrimeFactors]; + Arithmetic::PrimeFactorization(&i, factors, coefficients, Arithmetic::k_maxNumberOfPrimeFactors); + Addition * a = new Addition(); + int index = 0; + while (!coefficients[index].isZero() && index < Arithmetic::k_maxNumberOfPrimeFactors) { + if (isDenominator) { + coefficients[index].setNegative(true); + } + Expression * e = clone(); + e->replaceOperand(e->operand(0), new Rational(factors[index]), true); + Multiplication * m = new Multiplication(new Rational(coefficients[index]), e, false); + a->addOperand(m); + m->shallowReduce(context, angleUnit); + index++; + } + return a; +} + +Expression * Logarithm::shallowBeautify(Context & context, AngleUnit angleUnit) { + Symbol e = Symbol(Ion::Charset::Exponential); + const Expression * op = operand(0); + Rational one(1); + if (numberOfOperands() == 2 && (operand(1)->isIdenticalTo(&e) || operand(1)->isIdenticalTo(&one))) { + detachOperand(op); + Expression * nl = operand(1)->isIdenticalTo(&e) ? static_cast(new NaperianLogarithm(op, false)) : static_cast (new Logarithm(op, false)); + return replaceWith(nl, true); + } + return this; +} + template -Evaluation * Logarithm::templatedEvaluate(Context& context, AngleUnit angleUnit) const { - if (m_numberOfArguments == 1) { - return Function::templatedEvaluate(context, angleUnit); +Expression * Logarithm::templatedEvaluate(Context& context, AngleUnit angleUnit) const { + if (numberOfOperands() == 1) { + return EvaluationEngine::map(this, context, angleUnit, computeOnComplex); } - Evaluation * x = m_args[0]->evaluate(context, angleUnit); - Evaluation * n = m_args[1]->evaluate(context, angleUnit); - if (x->numberOfRows() != 1 || x->numberOfColumns() != 1 || n->numberOfRows() != 1 || n->numberOfColumns() != 1) { - return new Complex(Complex::Float(NAN)); + Expression * x = operand(0)->evaluate(context, angleUnit); + Expression * n = operand(1)->evaluate(context, angleUnit); + Complex result = Complex::Float(NAN); + if (x->type() == Type::Complex && n->type() == Type::Complex) { + Complex * xc = static_cast *>(x); + Complex * nc = static_cast *>(n); + result = Division::compute(computeOnComplex(*nc, angleUnit), computeOnComplex(*xc, angleUnit)); } - Complex result = Fraction::compute(templatedComputeComplex(*(n->complexOperand(0))), templatedComputeComplex(*(x->complexOperand(0)))); delete x; delete n; return new Complex(result); @@ -68,12 +210,12 @@ Evaluation * Logarithm::templatedEvaluate(Context& context, AngleUnit angleUn ExpressionLayout * Logarithm::privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const { assert(floatDisplayMode != FloatDisplayMode::Default); assert(complexFormat != ComplexFormat::Default); - if (m_numberOfArguments == 1) { - return Function::privateCreateLayout(floatDisplayMode, complexFormat); + if (numberOfOperands() == 1) { + return LayoutEngine::createPrefixLayout(this, floatDisplayMode, complexFormat, "log"); } ExpressionLayout * childrenLayouts[2]; - childrenLayouts[0] = new BaselineRelativeLayout(new StringLayout(m_name, strlen(m_name)), m_args[0]->createLayout(floatDisplayMode, complexFormat), BaselineRelativeLayout::Type::Subscript); - childrenLayouts[1] = new ParenthesisLayout(m_args[1]->createLayout(floatDisplayMode, complexFormat)); + childrenLayouts[0] = new BaselineRelativeLayout(new StringLayout("log", strlen("log")), operand(1)->createLayout(floatDisplayMode, complexFormat), BaselineRelativeLayout::Type::Subscript); + childrenLayouts[1] = new ParenthesisLayout(operand(0)->createLayout(floatDisplayMode, complexFormat)); return new HorizontalLayout(childrenLayouts, 2); } diff --git a/poincare/src/matrix.cpp b/poincare/src/matrix.cpp index 4e2b9ca28..8e23e7753 100644 --- a/poincare/src/matrix.cpp +++ b/poincare/src/matrix.cpp @@ -4,6 +4,9 @@ extern "C" { } #include #include +#include +#include +#include #include "layout/grid_layout.h" #include "layout/bracket_layout.h" #include @@ -12,8 +15,84 @@ extern "C" { namespace Poincare { -int Matrix::numberOfOperands() const { - return numberOfRows() * numberOfColumns(); +Matrix::Matrix(MatrixData * matrixData) : + DynamicHierarchy() +{ + assert(matrixData != nullptr); + m_numberOfOperands = matrixData->numberOfRows()*matrixData->numberOfColumns(); + m_numberOfRows = matrixData->numberOfRows(); + matrixData->pilferOperands(&m_operands); + for (int i = 0; i < m_numberOfOperands; i++) { + const_cast(m_operands[i])->setParent(this); + } +} + +Matrix::Matrix(const Expression * const * operands, int numberOfRows, int numberOfColumns, bool cloneOperands) : + DynamicHierarchy(operands, numberOfRows*numberOfColumns, cloneOperands), + m_numberOfRows(numberOfRows) +{ +} + +int Matrix::numberOfRows() const { + return m_numberOfRows; +} + +int Matrix::numberOfColumns() const { + return numberOfOperands()/m_numberOfRows; +} + +Expression::Type Matrix::type() const { + return Type::Matrix; +} + +Expression * Matrix::clone() const { + return new Matrix(m_operands, numberOfRows(), numberOfColumns(), true); +} + +int Matrix::writeTextInBuffer(char * buffer, int bufferSize) const { + if (bufferSize == 0) { + return -1; + } + buffer[bufferSize-1] = 0; + int currentChar = 0; + if (currentChar >= bufferSize-1) { + return 0; + } + buffer[currentChar++] = '['; + if (currentChar >= bufferSize-1) { + return currentChar; + } + for (int i = 0; i < numberOfRows(); i++) { + buffer[currentChar++] = '['; + if (currentChar >= bufferSize-1) { + return currentChar; + } + currentChar += operand(i*numberOfColumns())->writeTextInBuffer(buffer+currentChar, bufferSize-currentChar); + if (currentChar >= bufferSize-1) { + return currentChar; + } + for (int j = 1; j < numberOfColumns(); j++) { + buffer[currentChar++] = ','; + if (currentChar >= bufferSize-1) { + return currentChar; + } + currentChar += operand(i*numberOfColumns()+j)->writeTextInBuffer(buffer+currentChar, bufferSize-currentChar); + if (currentChar >= bufferSize-1) { + return currentChar; + } + } + currentChar = strlen(buffer); + if (currentChar >= bufferSize-1) { + return currentChar; + } + buffer[currentChar++] = ']'; + if (currentChar >= bufferSize-1) { + return currentChar; + } + } + buffer[currentChar++] = ']'; + buffer[currentChar] = 0; + return currentChar; } ExpressionLayout * Matrix::privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const { @@ -28,50 +107,233 @@ ExpressionLayout * Matrix::privateCreateLayout(FloatDisplayMode floatDisplayMode return layout; } -int Matrix::writeTextInBuffer(char * buffer, int bufferSize) const { - buffer[bufferSize-1] = 0; - int currentChar = 0; - if (currentChar >= bufferSize) { - return 0; +template +Complex * Matrix::createTrace() const { + if (numberOfRows() != numberOfColumns()) { + return new Complex(Complex::Float(NAN)); } - buffer[currentChar++] = '['; - if (currentChar >= bufferSize) { - return currentChar; + int dim = numberOfRows(); + Complex c = Complex::Float(0); + for (int i = 0; i < dim; i++) { + assert(operand(i*dim+i)->type() == Type::Complex); + c = Addition::compute(c, *(static_cast *>(operand(i*dim+i)))); } - for (int i = 0; i < numberOfRows(); i++) { - buffer[currentChar++] = '['; - if (currentChar >= bufferSize) { - return currentChar; - } - currentChar += operand(i*numberOfColumns())->writeTextInBuffer(buffer+currentChar, bufferSize-currentChar); - if (currentChar >= bufferSize) { - return currentChar; - } - for (int j = 1; j < numberOfColumns(); j++) { - buffer[currentChar++] = ','; - if (currentChar >= bufferSize) { - return currentChar; - } - currentChar += operand(i*numberOfColumns()+j)->writeTextInBuffer(buffer+currentChar, bufferSize-currentChar); - if (currentChar >= bufferSize) { - return currentChar; - } - } - currentChar = strlen(buffer); - if (currentChar >= bufferSize) { - return currentChar; - } - buffer[currentChar++] = ']'; - if (currentChar >= bufferSize) { - return currentChar; - } - } - buffer[currentChar++] = ']'; - if (currentChar >= bufferSize) { - return currentChar; - } - buffer[currentChar] = 0; - return currentChar; + return new Complex(c); } +// TODO: 1. implement determinant/inverse for complex matrix +// TODO: 2. implement determinant/inverse for any expression (do not evaluate first) +template +Complex * Matrix::createDeterminant() const { + if (numberOfRows() != numberOfColumns()) { + return new Complex(Complex::Float(NAN)); + } + int dim = numberOfRows(); + T ** tempMat = new T*[dim]; + for (int i = 0; i < dim; i++) { + tempMat[i] = new T[dim]; + } + T det = 1; + /* Copy the matrix */ + for (int i = 0; i < dim; i++) { + for (int j = 0; j < dim; j++) { + const Expression * op = operand(i*dim+j); + assert(op->type() == Type::Complex); + tempMat[i][j] = static_cast *>(op)->toScalar(); // TODO: keep complex + } + } + + /* Main Loop: Gauss pivot */ + for (int i = 0; i < dim-1; i++) { + /* Search for pivot */ + int rowWithPivot = i; + for (int row = i+1; row < dim; row++) { + if (std::fabs(tempMat[rowWithPivot][i]) < std::fabs(tempMat[row][i])) { + rowWithPivot = row; + } + } + T valuePivot = tempMat[rowWithPivot][i]; + /* if the pivot is null, det = 0. */ + if (std::fabs(valuePivot) <= (sizeof(T) == sizeof(float) ? FLT_EPSILON : DBL_EPSILON)) { + for (int i = 0; i < dim; i++) { + free(tempMat[i]); + } + free(tempMat); + return new Complex(Complex::Float(0.0)); + } + /* Switch rows to have the pivot row as first row */ + if (rowWithPivot != i) { + for (int col = i; col < dim; col++) { + T temp = tempMat[i][col]; + tempMat[i][col] = tempMat[rowWithPivot][col]; + tempMat[rowWithPivot][col] = temp; + } + det *= -1; + } + det *= valuePivot; + /* Set to 0 all A[][i] by linear combination */ + for (int row = i+1; row < dim; row++) { + T factor = tempMat[row][i]/valuePivot; + for (int col = i; col < dim; col++) { + tempMat[row][col] -= factor*tempMat[i][col]; + } + } + } + det *= tempMat[dim-1][dim-1]; + for (int i = 0; i < dim; i++) { + delete[] tempMat[i]; + } + delete[] tempMat; + return new Complex(Complex::Float(det)); +} + +template +Matrix * Matrix::createInverse() const { + if (numberOfRows() != numberOfColumns()) { + return nullptr; + } + int dim = numberOfRows(); + /* Create the matrix inv = (A|I) with A the input matrix and I the dim identity matrix */ + T ** inv = new T*[dim]; + for (int i = 0; i < dim; i++) { + inv[i] = new T [2*dim]; + } + for (int i = 0; i < dim; i++) { + for (int j = 0; j < dim; j++) { + const Expression * op = operand(i*dim+j); + assert(op->type() == Type::Complex); + inv[i][j] = static_cast *>(op)->toScalar(); // TODO: keep complex + } + for (int j = dim; j < 2*dim; j++) { + inv[i][j] = (i+dim == j); + } + } + /* Main Loop: Gauss pivot */ + for (int i = 0; i < dim; i++) { + /* Search for pivot */ + int rowWithPivot = i; + for (int row = i+1; row < dim; row++) { + if (std::fabs(inv[rowWithPivot][i]) < std::fabs(inv[row][i])) { + rowWithPivot = row; + } + } + T valuePivot = inv[rowWithPivot][i]; + /* if the pivot is null, the matrix in not invertible. */ + if (std::fabs(valuePivot) <= (sizeof(T) == sizeof(float) ? FLT_EPSILON : DBL_EPSILON)) { + for (int i = 0; i < dim; i++) { + free(inv[i]); + } + free(inv); + return nullptr; + } + /* Switch rows to have the pivot row as first row */ + if (rowWithPivot != i) { + for (int col = i; col < 2*dim; col++) { + T temp = inv[i][col]; + inv[i][col] = inv[rowWithPivot][col]; + inv[rowWithPivot][col] = temp; + } + } + /* A[pivot][] = A[pivot][]/valuePivot */ + for (int col = 0; col < 2*dim; col++) { + inv[i][col] /= valuePivot; + } + /* Set to 0 all A[][row] by linear combination */ + for (int row = 0; row < dim; row++) { + if (row == i) { + continue; + } + T factor = inv[row][i]; + for (int col = 0; col < 2*dim; col++) { + inv[row][col] -= factor*inv[i][col]; + } + } + } + const Expression ** operands = new const Expression * [numberOfOperands()]; + for (int i = 0; i < dim; i++) { + for (int j = 0; j < dim; j++) { + operands[i*dim+j] = new Complex(Complex::Float(inv[i][j+dim])); + } + } + for (int i = 0; i < dim; i++) { + delete[] inv[i]; + } + delete[] inv; + // Intentionally swapping dimensions for inverse, although it doesn't make a difference because it is square + Matrix * matrix = new Matrix(operands, numberOfColumns(), numberOfRows(), false); + delete[] operands; + return matrix; +} + +Matrix * Matrix::createTranspose() const { + const Expression ** operands = new const Expression * [numberOfOperands()]; + for (int i = 0; i < numberOfRows(); i++) { + for (int j = 0; j < numberOfColumns(); j++) { + operands[j*numberOfRows()+i] = operand(i*numberOfColumns()+j); + } + } + // Intentionally swapping dimensions for transpose + Matrix * matrix = new Matrix(operands, numberOfColumns(), numberOfRows(), true); + delete[] operands; + return matrix; +} + +Matrix * Matrix::createIdentity(int dim) { + Expression ** operands = new Expression * [dim*dim]; + for (int i = 0; i < dim; i++) { + for (int j = 0; j < dim; j++) { + if (i == j) { + operands[i*dim+j] = new Rational(1); + } else { + operands[i*dim+j] = new Rational(0); + } + } + } + Matrix * matrix = new Matrix(operands, dim, dim, false); + delete [] operands; + return matrix; +} + +template +Matrix * Matrix::createApproximateIdentity(int dim) { + Expression ** operands = new Expression * [dim*dim]; + for (int i = 0; i < dim; i++) { + for (int j = 0; j < dim; j++) { + if (i == j) { + operands[i*dim+j] = new Complex(Complex::Float(1)); + } else { + operands[i*dim+j] = new Complex(Complex::Float(0)); + } + } + } + Matrix * matrix = new Matrix(operands, dim, dim, false); + delete [] operands; + return matrix; +} + +template +Expression * Matrix::templatedEvaluate(Context& context, AngleUnit angleUnit) const { + Expression ** operands = new Expression * [numberOfOperands()]; + for (int i = 0; i < numberOfOperands(); i++) { + Expression * operandEvaluation = operand(i)->evaluate(context, angleUnit); + if (operandEvaluation->type() != Type::Complex) { + operands[i] = new Complex(Complex::Float(NAN)); + delete operandEvaluation; + } else { + operands[i] = operandEvaluation; + } + } + Expression * matrix = new Matrix(operands, numberOfRows(), numberOfColumns(), false); + delete[] operands; + return matrix; +} + +template Matrix* Matrix::createApproximateIdentity(int); +template Matrix* Matrix::createApproximateIdentity(int); +template Complex* Matrix::createTrace() const; +template Complex* Matrix::createTrace() const; +template Matrix* Matrix::createInverse() const; +template Matrix* Matrix::createInverse() const; +template Complex* Matrix::createDeterminant() const; +template Complex* Matrix::createDeterminant() const; } diff --git a/poincare/src/matrix_data.cpp b/poincare/src/matrix_data.cpp index 0489429be..8c1e31a3c 100644 --- a/poincare/src/matrix_data.cpp +++ b/poincare/src/matrix_data.cpp @@ -13,57 +13,40 @@ MatrixData::MatrixData(ListData * listData, bool clone) : { assert(listData != nullptr); m_numberOfColumns = listData->numberOfOperands(); - m_operands = new Expression *[m_numberOfColumns]; + m_operands = new const Expression *[m_numberOfColumns]; for (int i = 0; i < m_numberOfColumns; i++) { if (clone) { - m_operands[i] = (Expression *)listData->operand(i)->clone(); + m_operands[i] = listData->operand(i)->clone(); } else { - m_operands[i] = (Expression *)listData->operand(i); - } - } -} - -MatrixData::MatrixData(Expression ** newOperands, int numberOfOperands, int numberOfRows, int numberOfColumns, bool cloneOperands) : - m_numberOfRows(numberOfRows), - m_numberOfColumns(numberOfColumns) -{ - assert(newOperands != nullptr); - m_operands = new Expression *[m_numberOfRows*m_numberOfColumns]; - for (int i = 0; i < m_numberOfRows*m_numberOfColumns; i++) { - if (cloneOperands) { - m_operands[i] = i < numberOfOperands ? newOperands[i]->clone() : defaultExpression(); - } else { - m_operands[i] = i < numberOfOperands ? newOperands[i] : defaultExpression(); + m_operands[i] = listData->operand(i); } } } Complex * MatrixData::defaultExpression() { - static Complex * defaultExpression = new Complex(Complex::Float(0.0)); - return defaultExpression; + return new Complex(Complex::Float(0.0)); } MatrixData::~MatrixData() { - for (int i=0; inumberOfOperands(); if (clone) { - newOperands[m_numberOfRows*m_numberOfColumns+i] = i < max ? (Expression *)listData->operand(i)->clone() : defaultExpression(); + newOperands[m_numberOfRows*m_numberOfColumns+i] = i < max ? listData->operand(i)->clone() : defaultExpression(); } else { - newOperands[m_numberOfRows*m_numberOfColumns+i] = i < max ? (Expression *)listData->operand(i) : defaultExpression(); + newOperands[m_numberOfRows*m_numberOfColumns+i] = i < max ? listData->operand(i) : defaultExpression(); } } delete[] m_operands; @@ -79,8 +62,9 @@ int MatrixData::numberOfColumns() { return m_numberOfColumns; } -Expression ** MatrixData::operands() const { - return m_operands; +void MatrixData::pilferOperands(const Expression *** newStorage) { + *newStorage = m_operands; + m_operands = nullptr; } } diff --git a/poincare/src/matrix_dimension.cpp b/poincare/src/matrix_dimension.cpp index ee3a78893..8bbb5fb74 100644 --- a/poincare/src/matrix_dimension.cpp +++ b/poincare/src/matrix_dimension.cpp @@ -7,32 +7,53 @@ extern "C" { namespace Poincare { -MatrixDimension::MatrixDimension() : - Function("dimension") -{ -} - Expression::Type MatrixDimension::type() const { return Type::MatrixDimension; } -Expression * MatrixDimension::cloneWithDifferentOperands(Expression** newOperands, - int numberOfOperands, bool cloneOperands) const { - assert(newOperands != nullptr); - MatrixDimension * md = new MatrixDimension(); - md->setArgument(newOperands, numberOfOperands, cloneOperands); - return md; +Expression * MatrixDimension::clone() const { + MatrixDimension * a = new MatrixDimension(m_operands, true); + return a; +} + +Expression * MatrixDimension::shallowReduce(Context& context, AngleUnit angleUnit) { + Expression * e = Expression::shallowReduce(context, angleUnit); + if (e != this) { + return e; + } +#if MATRIX_EXACT_REDUCING + Expression * op = editableOperand(0); + if (op->type() == Type::Matrix) { + Matrix * m = static_cast(op); + const Expression * newOperands[2] = {new Rational(m->numberOfRows()), new Rational(m->numberOfColumns())}; + return replaceWith(new Matrix(newOperands, 1, 2, false), true); + } + if (!op->recursivelyMatches(Expression::IsMatrix)) { + const Expression * newOperands[2] = {new Rational(1), new Rational(1)}; + return replaceWith(new Matrix(newOperands, 1, 2, false), true); + } + return this; +#else + const Expression * newOperands[2] = {new Rational(1), new Rational(1)}; + return replaceWith(new Matrix(newOperands, 1, 2, false), true); +#endif } template -Evaluation * MatrixDimension::templatedEvaluate(Context& context, AngleUnit angleUnit) const { - Evaluation * input = m_args[0]->evaluate(context, angleUnit); - Complex operands[2]; - operands[0] = Complex::Float((T)input->numberOfRows()); - operands[1] = Complex::Float((T)input->numberOfColumns()); +Expression * MatrixDimension::templatedEvaluate(Context& context, AngleUnit angleUnit) const { + Expression * input = operand(0)->evaluate(context, angleUnit); + Expression * operands[2]; + if (input->type() == Type::Matrix) { + operands[0] = new Complex(Complex::Float((T)static_cast(input)->numberOfRows())); + operands[1] = new Complex(Complex::Float((T)static_cast(input)->numberOfColumns())); + } else { + operands[0] = new Complex(Complex::Float(1.0)); + operands[1] = new Complex(Complex::Float(1.0)); + } delete input; - return new ComplexMatrix(operands, 1, 2); + return new Matrix(operands, 1, 2, false); } + } diff --git a/poincare/src/matrix_inverse.cpp b/poincare/src/matrix_inverse.cpp index a5edefaa5..1d5271be5 100644 --- a/poincare/src/matrix_inverse.cpp +++ b/poincare/src/matrix_inverse.cpp @@ -1,7 +1,9 @@ #include #include #include -#include +#include +#include +#include extern "C" { #include } @@ -9,27 +11,54 @@ extern "C" { namespace Poincare { -MatrixInverse::MatrixInverse() : - Function("inverse") -{ -} - Expression::Type MatrixInverse::type() const { return Type::MatrixInverse; } -Expression * MatrixInverse::cloneWithDifferentOperands(Expression** newOperands, - int numberOfOperands, bool cloneOperands) const { - assert(newOperands != nullptr); - MatrixInverse * i = new MatrixInverse(); - i->setArgument(newOperands, numberOfOperands, cloneOperands); - return i; +Expression * MatrixInverse::clone() const { + MatrixInverse * a = new MatrixInverse(m_operands, true); + return a; } +Expression * MatrixInverse::shallowReduce(Context& context, AngleUnit angleUnit) { + Expression * e = Expression::shallowReduce(context, angleUnit); + if (e != this) { + return e; + } + Expression * op = editableOperand(0); +#if MATRIX_EXACT_REDUCING + if (!op->recursivelyMatches(Expression::IsMatrix)) { + detachOperand(op); + return replaceWith(new Power(op, new Rational(-1), false), true)->shallowReduce(context, angleUnit); + } + if (op->type() == Type::Matrix) { + Matrix * mat = static_cast(op); + if (mat->numberOfRows() != mat->numberOfColumns()) { + return replaceWith(new Undefined(), true); + } + } + return this; +#else + detachOperand(op); + return replaceWith(new Power(op, new Rational(-1), false), true)->shallowReduce(context, angleUnit); +#endif +} + +// TODO: handle this exactly in shallowReduce for small dimensions. template -Evaluation * MatrixInverse::templatedEvaluate(Context& context, AngleUnit angleUnit) const { - Evaluation * input = m_args[0]->evaluate(context, angleUnit); - Evaluation * result = input->createInverse(); +Expression * MatrixInverse::templatedEvaluate(Context& context, AngleUnit angleUnit) const { + Expression * input = operand(0)->evaluate(context, angleUnit); + Expression * result = nullptr; + if (input->type() == Type::Complex) { + Complex * c = static_cast *>(input); + result = new Complex(Complex::Cartesian(1/c->a(), -1/c->b())); + } else { + assert(input->type() == Type::Matrix); + result = static_cast(input)->createInverse(); + } + if (result == nullptr) { + result = new Complex(Complex::Float(NAN)); + } delete input; return result; } diff --git a/poincare/src/matrix_trace.cpp b/poincare/src/matrix_trace.cpp index 48c6780eb..4ae4ebfab 100644 --- a/poincare/src/matrix_trace.cpp +++ b/poincare/src/matrix_trace.cpp @@ -1,5 +1,7 @@ #include #include +#include +#include extern "C" { #include } @@ -7,27 +9,55 @@ extern "C" { namespace Poincare { -MatrixTrace::MatrixTrace() : - Function("trace") -{ -} - Expression::Type MatrixTrace::type() const { return Type::MatrixTrace; } -Expression * MatrixTrace::cloneWithDifferentOperands(Expression** newOperands, - int numberOfOperands, bool cloneOperands) const { - assert(newOperands != nullptr); - MatrixTrace * t = new MatrixTrace(); - t->setArgument(newOperands, numberOfOperands, cloneOperands); - return t; +Expression * MatrixTrace::clone() const { + MatrixTrace * a = new MatrixTrace(m_operands, true); + return a; +} + +Expression * MatrixTrace::shallowReduce(Context& context, AngleUnit angleUnit) { + Expression * e = Expression::shallowReduce(context, angleUnit); + if (e != this) { + return e; + } + Expression * op = editableOperand(0); +#if MATRIX_EXACT_REDUCING + if (op->type() == Type::Matrix) { + Matrix * m = static_cast(op); + if (m->numberOfRows() != m->numberOfColumns()) { + return replaceWith(new Undefined(), true); + } + int n = m->numberOfRows(); + Addition * a = new Addition(); + for (int i = 0; i < n; i++) { + Expression * diagEntry = m->editableOperand(i+n*i); + m->detachOperand(diagEntry); + a->addOperand(diagEntry); + } + return replaceWith(a, true)->shallowReduce(context, angleUnit); + } + if (!op->recursivelyMatches(Expression::IsMatrix)) { + return replaceWith(op, true); + } + return this; +#else + return replaceWith(op, true); +#endif } template -Evaluation * MatrixTrace::templatedEvaluate(Context& context, AngleUnit angleUnit) const { - Evaluation * input = m_args[0]->evaluate(context, angleUnit); - Evaluation * result = input->createTrace(); +Expression * MatrixTrace::templatedEvaluate(Context& context, AngleUnit angleUnit) const { + Expression * input = operand(0)->evaluate(context, angleUnit); + Expression * result = nullptr; + if (input->type() == Type::Complex) { + result = input->clone(); + } else { + assert(input->type() == Type::Matrix); + result = static_cast(input)->createTrace(); + } delete input; return result; } diff --git a/poincare/src/matrix_transpose.cpp b/poincare/src/matrix_transpose.cpp index 4b02d701d..59bb4ec3f 100644 --- a/poincare/src/matrix_transpose.cpp +++ b/poincare/src/matrix_transpose.cpp @@ -1,7 +1,7 @@ #include #include #include -#include +#include extern "C" { #include } @@ -9,30 +9,47 @@ extern "C" { namespace Poincare { -MatrixTranspose::MatrixTranspose() : - Function("transpose") -{ -} - Expression::Type MatrixTranspose::type() const { return Type::MatrixTranspose; } -Expression * MatrixTranspose::cloneWithDifferentOperands(Expression** newOperands, - int numberOfOperands, bool cloneOperands) const { - assert(newOperands != nullptr); - MatrixTranspose * t = new MatrixTranspose(); - t->setArgument(newOperands, numberOfOperands, cloneOperands); - return t; +Expression * MatrixTranspose::clone() const { + MatrixTranspose * a = new MatrixTranspose(m_operands, true); + return a; +} + +Expression * MatrixTranspose::shallowReduce(Context& context, AngleUnit angleUnit) { + Expression * e = Expression::shallowReduce(context, angleUnit); + if (e != this) { + return e; + } + Expression * op = editableOperand(0); +#if MATRIX_EXACT_REDUCING + if (op->type() == Type::Matrix) { + Matrix * transpose = static_cast(op)->createTranspose(); + return replaceWith(transpose, true); + } + if (!op->recursivelyMatches(Expression::IsMatrix)) { + return replaceWith(op, true); + } + return this; +#else + return replaceWith(op, true); +#endif } template -Evaluation * MatrixTranspose::templatedEvaluate(Context& context, AngleUnit angleUnit) const { - Evaluation * input = m_args[0]->evaluate(context, angleUnit); - Evaluation * result = input->createTranspose(); +Expression * MatrixTranspose::templatedEvaluate(Context& context, AngleUnit angleUnit) const { + Expression * input = operand(0)->evaluate(context, angleUnit); + Expression * result = nullptr; + if (input->type() == Type::Complex) { + result = input->clone(); + } else { + assert(input->type() == Type::Matrix); + result = static_cast(input)->createTranspose(); + } delete input; return result; } } - diff --git a/poincare/src/multiplication.cpp b/poincare/src/multiplication.cpp index f2ddd211e..5b6207f0f 100644 --- a/poincare/src/multiplication.cpp +++ b/poincare/src/multiplication.cpp @@ -5,6 +5,19 @@ extern "C" { #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include "layout/string_layout.h" #include "layout/horizontal_layout.h" #include "layout/parenthesis_layout.h" @@ -15,21 +28,39 @@ Expression::Type Multiplication::type() const { return Expression::Type::Multiplication; } -ExpressionLayout * Multiplication::privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const { - assert(floatDisplayMode != FloatDisplayMode::Default); - assert(complexFormat != ComplexFormat::Default); - ExpressionLayout * children_layouts[3]; - children_layouts[0] = m_operands[0]->createLayout(floatDisplayMode, complexFormat); - children_layouts[1] = new StringLayout("*", 1); - children_layouts[2] = m_operands[1]->type() == Type::Opposite ? new ParenthesisLayout(m_operands[1]->createLayout(floatDisplayMode, complexFormat)) : m_operands[1]->createLayout(floatDisplayMode, complexFormat); - return new HorizontalLayout(children_layouts, 3); +Expression * Multiplication::clone() const { + if (numberOfOperands() == 0) { + return new Multiplication(); + } + return new Multiplication(operands(), numberOfOperands(), true); } -Expression * Multiplication::cloneWithDifferentOperands(Expression** newOperands, - int numberOfOperands, bool cloneOperands) const { - assert(numberOfOperands == 2); - assert(newOperands != nullptr); - return new Multiplication(newOperands, cloneOperands); +ExpressionLayout * Multiplication::privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const { + const char middleDotString[] = {Ion::Charset::MiddleDot, 0}; + return LayoutEngine::createInfixLayout(this, floatDisplayMode, complexFormat, middleDotString); +} + +int Multiplication::writeTextInBuffer(char * buffer, int bufferSize) const { + const char multiplicationString[] = {Ion::Charset::MultiplicationSign, 0}; + return LayoutEngine::writeInfixExpressionTextInBuffer(this, buffer, bufferSize, multiplicationString); +} + +Expression::Sign Multiplication::sign() const { + int sign = 1; + for (int i = 0; i < numberOfOperands(); i++) { + sign *= (int)operand(i)->sign(); + } + return (Sign)sign; +} + +Expression * Multiplication::setSign(Sign s, Context & context, AngleUnit angleUnit) { + assert(s == Sign::Positive); + for (int i = 0; i < numberOfOperands(); i++) { + if (operand(i)->sign() == Sign::Negative) { + editableOperand(i)->setSign(s, context, angleUnit); + } + } + return shallowReduce(context, angleUnit); } template @@ -38,37 +69,583 @@ Complex Multiplication::compute(const Complex c, const Complex d) { } template -Evaluation * Multiplication::computeOnComplexAndMatrix(const Complex * c, Evaluation * m) { - Multiplication mul; - return mul.computeOnComplexAndComplexMatrix(c, m); -} - -template -Evaluation * Multiplication::computeOnMatrices(Evaluation * m, Evaluation * n) { +Matrix * Multiplication::computeOnMatrices(const Matrix * m, const Matrix * n) { if (m->numberOfColumns() != n->numberOfRows()) { - return new Complex(Complex::Float(NAN)); + return nullptr; } - Complex * operands = new Complex[m->numberOfRows()*n->numberOfColumns()]; + Expression ** operands = new Expression * [m->numberOfRows()*n->numberOfColumns()]; for (int i = 0; i < m->numberOfRows(); i++) { for (int j = 0; j < n->numberOfColumns(); j++) { T a = 0.0f; T b = 0.0f; for (int k = 0; k < m->numberOfColumns(); k++) { - Complex mEntry = *(m->complexOperand(i*m->numberOfColumns()+k)); - Complex nEntry = *(n->complexOperand(k*n->numberOfColumns()+j)); - a += mEntry.a()*nEntry.a() - mEntry.b()*nEntry.b(); - b += mEntry.b()*nEntry.a() + mEntry.a()*nEntry.b(); + const Complex * mEntry = static_cast *>(m->operand(i*m->numberOfColumns()+k)); + const Complex * nEntry = static_cast *>(n->operand(k*n->numberOfColumns()+j)); + a += mEntry->a()*nEntry->a() - mEntry->b()*nEntry->b(); + b += mEntry->b()*nEntry->a() + mEntry->a()*nEntry->b(); } - operands[i*n->numberOfColumns()+j] = Complex::Cartesian(a, b); + operands[i*n->numberOfColumns()+j] = new Complex(Complex::Cartesian(a, b)); } } - Evaluation * result = new ComplexMatrix(operands, m->numberOfRows(), n->numberOfColumns()); + Matrix * result = new Matrix(operands, m->numberOfRows(), n->numberOfColumns(), false); delete[] operands; return result; } -template Poincare::Evaluation* Poincare::Multiplication::computeOnComplexAndMatrix(Poincare::Complex const*, Poincare::Evaluation*); -template Poincare::Evaluation* Poincare::Multiplication::computeOnComplexAndMatrix(Poincare::Complex const*, Poincare::Evaluation*); +bool Multiplication::HaveSameNonRationalFactors(const Expression * e1, const Expression * e2) { + int numberOfNonRationalFactors1 = e1->operand(0)->type() == Type::Rational ? e1->numberOfOperands()-1 : e1->numberOfOperands(); + int numberOfNonRationalFactors2 = e2->operand(0)->type() == Type::Rational ? e2->numberOfOperands()-1 : e2->numberOfOperands(); + if (numberOfNonRationalFactors1 != numberOfNonRationalFactors2) { + return false; + } + int firstNonRationalOperand1 = e1->operand(0)->type() == Type::Rational ? 1 : 0; + int firstNonRationalOperand2 = e2->operand(0)->type() == Type::Rational ? 1 : 0; + for (int i = 0; i < numberOfNonRationalFactors1; i++) { + if (!(e1->operand(firstNonRationalOperand1+i)->isIdenticalTo(e2->operand(firstNonRationalOperand2+i)))) { + return false; + } + } + return true; +} + +static inline const Expression * Base(const Expression * e) { + if (e->type() == Expression::Type::Power) { + return e->operand(0); + } + return e; +} + +Expression * Multiplication::shallowReduce(Context& context, AngleUnit angleUnit) { + return privateShallowReduce(context, angleUnit, true); +} + +Expression * Multiplication::privateShallowReduce(Context & context, AngleUnit angleUnit, bool shouldExpand) { + Expression * e = Expression::shallowReduce(context, angleUnit); + if (e != this) { + return e; + } + /* Step 1: Multiplication is associative, so let's start by merging children + * which also are multiplications themselves. */ + int i = 0; + int initialNumberOfOperands = numberOfOperands(); + while (i < initialNumberOfOperands) { + Expression * o = editableOperand(i); + if (o->type() == Type::Multiplication) { + mergeOperands(static_cast(o)); // TODO: ensure that matrix operands are not swapped to implement MATRIX_EXACT_REDUCING + continue; + } + i++; + } + + /* Step 2: If any of the operand is zero, the multiplication result is zero */ + for (int i = 0; i < numberOfOperands(); i++) { + const Expression * o = operand(i); + if (o->type() == Type::Rational && static_cast(o)->isZero()) { + return replaceWith(new Rational(0), true); + } + } + + // Step 3: Sort the operands + sortOperands(SimplificationOrder); + +#if MATRIX_EXACT_REDUCING + /* Step 3bis: get rid of matrix */ + int n = 1; + int m = 1; + /* All operands have been simplified so if any operand contains a matrix, it + * is at the root node of the operand. Moreover, thanks to the simplification + * order, all matrix operands (if any) are the last operands. */ + Expression * lastOperand = editableOperand(numberOfOperands()-1); + if (lastOperand->type() == Type::Matrix) { + Matrix * resultMatrix = static_cast(lastOperand); + // Use the last matrix operand as the final matrix + n = resultMatrix->numberOfRows(); + m = resultMatrix->numberOfColumns(); + /* Scan accross the multiplication operands to find any other matrix: + * (the last operand is the result matrix so we start at + * numberOfOperands()-2)*/ + int k = numberOfOperands()-2; + while (k >= 0 && operand(k)->type() == Type::Matrix) { + Matrix * currentMatrix = static_cast(editableOperand(k)); + int on = currentMatrix->numberOfRows(); + int om = currentMatrix->numberOfColumns(); + if (om != n) { + return replaceWith(new Undefined(), true); + } + // Create the matrix resulting of the multiplication of the current matrix and the result matrix + /* resultMatrix + * i2= 0..m + * +-+-+-+-+-+ + * | | | | | | + * +-+-+-+-+-+ + * j=0..n | | | | | | + * +-+-+-+-+-+ + * | | | | | | + * +-+-+-+-+-+ + * currentMatrix + * j=0..om + * +---+---+---+ +-+-+-+-+-+ + * |  |   |  | | | | | | | + * +---+---+---+ +-+-+-+-+-+ + *i1=0..on |  |   |  | | |e| | | | + * +---+---+---+ +-+-+-+-+-+ + * |   |   | | | | | | | | + * +---+---+---+ +-+-+-+-+-+ + * */ + Expression ** newMatrixOperands = new Expression * [on*m]; + for (int e = 0; e < on*m; e++) { + newMatrixOperands[e] = new Addition(); + int i2 = e%m; + int i1 = e/m; + for (int j = 0; j < n; j++) { + Expression * mult = new Multiplication(currentMatrix->editableOperand(j+om*i1), resultMatrix->editableOperand(j*m+i2), true); + static_cast(newMatrixOperands[e])->addOperand(mult); + mult->shallowReduce(context, angleUnit); + } + Reduce(&newMatrixOperands[e], context, angleUnit, false); + } + n = on; + removeOperand(currentMatrix, true); + resultMatrix = static_cast(resultMatrix->replaceWith(new Matrix(newMatrixOperands, n, m, false), true)); + k--; + } + removeOperand(resultMatrix, false); + // Distribute the remaining multiplication on matrix operands + for (int i = 0; i < n*m; i++) { + Multiplication * m = static_cast(clone()); + Expression * entryI = resultMatrix->editableOperand(i); + resultMatrix->replaceOperand(entryI, m, false); + m->addOperand(entryI); + m->shallowReduce(context, angleUnit); + } + return replaceWith(resultMatrix, true)->shallowReduce(context, angleUnit); + } +#endif + + /* Step 4: Gather like terms. For example, turn pi^2*pi^3 into pi^5. Thanks to + * the simplification order, such terms are guaranteed to be next to each + * other. */ + i = 0; + while (i < numberOfOperands()-1) { + Expression * oi = editableOperand(i); + Expression * oi1 = editableOperand(i+1); + if (oi->type() == Type::Rational && oi1->type() == Type::Rational) { + Rational a = Rational::Multiplication(*(static_cast(oi)), *(static_cast(oi1))); + replaceOperand(oi, new Rational(a), true); + removeOperand(oi1, true); + continue; + } else if (TermsHaveIdenticalBase(oi, oi1)) { + bool shouldFactorizeBase = true; + if (TermHasRationalBase(oi)) { + /* Combining powers of a given rational isn't straightforward. Indeed, + * there are two cases we want to deal with: + * - 2*2^(1/2) or 2*2^pi, we want to keep as-is + * - 2^(1/2)*2^(3/2) we want to combine. */ + shouldFactorizeBase = !TermHasIntegerExponent(oi) && !TermHasIntegerExponent(oi1); + } + if (shouldFactorizeBase) { + factorizeBase(oi, oi1, context, angleUnit); + continue; + } + } else if (TermHasRationalBase(oi) && TermHasRationalBase(oi1) && TermsHaveIdenticalExponent(oi, oi1)) { + factorizeExponent(oi, oi1, context, angleUnit); + continue; + } + i++; + } + + /* Step 5: We look for terms of form sin(x)^p*cos(x)^q with p, q rational of + *opposite signs. We replace them by either: + * - tan(x)^p*cos(x)^(p+q) if |p|<|q| + * - tan(x)^(-q)*sin(x)^(p+q) otherwise */ + for (int i = 0; i < numberOfOperands(); i++) { + Expression * o1 = editableOperand(i); + if (Base(o1)->type() == Type::Sine && TermHasRationalExponent(o1)) { + const Expression * x = Base(o1)->operand(0); + /* Thanks to the SimplificationOrder, Cosine-base factors are after + * Sine-base factors */ + for (int j = i+1; j < numberOfOperands(); j++) { + Expression * o2 = editableOperand(j); + if (Base(o2)->type() == Type::Cosine && TermHasRationalExponent(o2) && Base(o2)->operand(0)->isIdenticalTo(x)) { + factorizeSineAndCosine(o1, o2, context, angleUnit); + } + } + } + } + /* Replacing sin/cos by tan factors may have mixed factors and factors are + * guaranteed to be sorted (according ot SimplificationOrder) at the end of + * shallowReduce */ + sortOperands(SimplificationOrder); + + /* Step 6: Let's remove ones if there's any. It's important to do this after + * having factorized because factorization can lead to new ones. For example + * pi^(-1)*pi. We don't remove the last one if it's the only operand left + * though. + * Same comment for -1 that can appear when reducing i*i. */ + i = 0; + while (i < numberOfOperands()) { + Expression * o = editableOperand(i); + if (o->type() == Type::Rational && static_cast(o)->isOne() && numberOfOperands() > 1) { + removeOperand(o, true); + continue; + } + if (o->type() == Type::Rational && static_cast(o)->isMinusOne() && numberOfOperands() > 1 && i > 0) { + removeOperand(o, operand(0)->type() == Type::Rational); + if (operand(0)->type() == Type::Rational) { + Rational * r = static_cast(editableOperand(0)); + r->setSign(r->sign() == Sign::Positive ? Sign::Negative : Sign::Positive); + } else { + addOperandAtIndex(o, 0); + } + continue; + } + i++; + } + + /* Step 7: Expand multiplication over addition operands if any. For example, + * turn (a+b)*c into a*c + b*c. We do not want to do this step right now if + * the parent is a multiplication to avoid missing factorization such as + * (x+y)^(-1)*((a+b)*(x+y)). + * Note: This step must be done after Step 4, otherwise we wouldn't be able to + * reduce expressions such as (x+y)^(-1)*(x+y)(a+b). */ + if (shouldExpand && parent()->type() != Type::Multiplication) { + for (int i=0; itype() == Type::Addition) { + return distributeOnOperandAtIndex(i, context, angleUnit); + } + } + } + + // Step 8: Let's remove the multiplication altogether if it has one operand + Expression * result = squashUnaryHierarchy(); + + return result; +} + +void Multiplication::factorizeSineAndCosine(Expression * o1, Expression * o2, Context & context, AngleUnit angleUnit) { + assert(o1->parent() == this && o2->parent() == this); + /* This function turn sin(x)^p * cos(x)^q into either: + * - tan(x)^p*cos(x)^(p+q) if |p|<|q| + * - tan(x)^(-q)*sin(x)^(p+q) otherwise */ + const Expression * x = Base(o1)->operand(0); + Rational p = o1->type() == Type::Power ? *(static_cast(o1->editableOperand(1))) : Rational(1); + Rational q = o2->type() == Type::Power ? *(static_cast(o2->editableOperand(1))) : Rational(1); + /* If p and q have the same sign, we cannot replace them by a tangent */ + if ((int)p.sign()*(int)q.sign() > 0) { + return; + } + Rational sumPQ = Rational::Addition(p, q); + Rational absP = p; + absP.setSign(Sign::Positive); + Rational absQ = q; + absQ.setSign(Sign::Positive); + Expression * tan = new Tangent(x, true); + if (Rational::NaturalOrder(absP, absQ) < 0) { + if (o1->type() == Type::Power) { + o1->replaceOperand(o1->operand(0), tan, true); + } else { + replaceOperand(o1, tan, true); + o1 = tan; + } + o1->shallowReduce(context, angleUnit); + if (o2->type() == Type::Power) { + o2->replaceOperand(o2->operand(1), new Rational(sumPQ), true); + } else { + Expression * newO2 = new Power(o2, new Rational(sumPQ), false); + replaceOperand(o2, newO2, false); + o2 = newO2; + } + o2->shallowReduce(context, angleUnit); + } else { + if (o2->type() == Type::Power) { + o2->replaceOperand(o2->operand(1), new Rational(Rational::Multiplication(q, Rational(-1))), true); + o2->replaceOperand(o2->operand(0), tan, true); + } else { + Expression * newO2 = new Power(tan, new Rational(-1), false); + replaceOperand(o2, newO2, true); + o2 = newO2; + } + o2->shallowReduce(context, angleUnit); + if (o1->type() == Type::Power) { + o1->replaceOperand(o1->operand(1), new Rational(sumPQ), true); + } else { + Expression * newO1 = new Power(o1, new Rational(sumPQ), false); + replaceOperand(o1, newO1, false); + o1 = newO1; + } + o1->shallowReduce(context, angleUnit); + } +} + +void Multiplication::factorizeBase(Expression * e1, Expression * e2, Context & context, AngleUnit angleUnit) { + /* This function factorizes two operands which have a common base. For example + * if this is Multiplication(pi^2, pi^3), then pi^2 and pi^3 could be merged + * and this turned into Multiplication(pi^5). */ + assert(TermsHaveIdenticalBase(e1, e2)); + + // Step 1: Find the new exponent + Expression * s = new Addition(CreateExponent(e1), CreateExponent(e2), false); + + // Step 2: Get rid of one of the operands + removeOperand(e2, true); + + // Step 3: Use the new exponent + Power * p = nullptr; + if (e1->type() == Type::Power) { + // If e1 is a power, replace the initial exponent with the new one + e1->replaceOperand(e1->operand(1), s, true); + p = static_cast(e1); + } else { + // Otherwise, create a new Power node + p = new Power(e1, s, false); + replaceOperand(e1, p, false); + } + + // Step 4: Reduce the new power + s->shallowReduce(context, angleUnit); // pi^2*pi^3 -> pi^(2+3) -> pi^5 + p->shallowReduce(context, angleUnit); // pi^2*pi^-2 -> pi^0 -> 1 +} + +void Multiplication::factorizeExponent(Expression * e1, Expression * e2, Context & context, AngleUnit angleUnit) { + /* This function factorizes operands which share a common exponent. For + * example, it turns Multiplication(2^x,3^x) into Multiplication(6^x). */ + assert(e1->parent() == this && e2->parent() == this); + + const Expression * base1 = e1->operand(0)->clone(); + const Expression * base2 = e2->operand(0); + // TODO: remove cast, everything is a hierarchy + static_cast(e2)->detachOperand(base2); + Expression * m = new Multiplication(base1, base2, false); + removeOperand(e2, true); + e1->replaceOperand(e1->operand(0), m, true); + + m->shallowReduce(context, angleUnit); // 2^x*3^x -> (2*3)^x -> 6^x + e1->shallowReduce(context, angleUnit); // 2^x*(1/2)^x -> (2*1/2)^x -> 1 +} + +Expression * Multiplication::distributeOnOperandAtIndex(int i, Context & context, AngleUnit angleUnit) { + // This function turns a*(b+c) into a*b + a*c + // We avoid deleting and creating a new addition + Addition * a = static_cast(editableOperand(i)); + for (int j = 0; j < a->numberOfOperands(); j++) { + Expression * termJ = a->editableOperand(j); + replaceOperand(operand(i), termJ->clone(), false); + Expression * m = clone(); + a->replaceOperand(termJ, m, true); + m->shallowReduce(context, angleUnit); // pi^(-1)*(pi + x) -> pi^(-1)*pi + pi^(-1)*x -> 1 + pi^(-1)*x + } + replaceWith(a, true); + return a->shallowReduce(context, angleUnit); // Order terms, put under a common denominator if needed +} + +const Expression * Multiplication::CreateExponent(Expression * e) { + return e->type() == Type::Power ? e->operand(1)->clone() : new Rational(1); +} + +bool Multiplication::TermsHaveIdenticalBase(const Expression * e1, const Expression * e2) { + return Base(e1)->isIdenticalTo(Base(e2)); +} + +bool Multiplication::TermsHaveIdenticalExponent(const Expression * e1, const Expression * e2) { + /* Note: We will return false for e1=2 and e2=Pi, even though one could argue + * that these have the same exponent whose value is 1. */ + return e1->type() == Type::Power && e2->type() == Type::Power && (e1->operand(1)->isIdenticalTo(e2->operand(1))); +} + +bool Multiplication::TermHasRationalBase(const Expression * e) { + return Base(e)->type() == Type::Rational; +} + +bool Multiplication::TermHasIntegerExponent(const Expression * e) { + if (e->type() != Type::Power) { + return true; + } + if (e->operand(1)->type() == Type::Rational) { + const Rational * r = static_cast(e->operand(1)); + return r->denominator().isOne(); + } + return false; +} + +bool Multiplication::TermHasRationalExponent(const Expression * e) { + if (e->type() != Type::Power) { + return true; + } + if (e->operand(1)->type() == Type::Rational) { + return true; + } + return false; +} + +Expression * Multiplication::shallowBeautify(Context & context, AngleUnit angleUnit) { + /* Beautifying a Multiplication consists in several possible operations: + * - Add Opposite ((-3)*x -> -(3*x), useful when printing fractions) + * - Adding parenthesis if needed (a*(b+c) is not a*b+c) + * - Creating a Division if there's either a term with a power of -1 (a.b^(-1) + * shall become a/b) or a non-integer rational term (3/2*a -> (3*a)/2). */ + + // Step 1: Turn -n*A into -(n*A) + if (operand(0)->type() == Type::Rational && operand(0)->sign() == Sign::Negative) { + if (static_cast(operand(0))->isMinusOne()) { + removeOperand(editableOperand(0), true); + } else { + editableOperand(0)->setSign(Sign::Positive, context, angleUnit); + } + Expression * e = squashUnaryHierarchy(); + Opposite * o = new Opposite(e, true); + e->replaceWith(o, true); + o->editableOperand(0)->shallowBeautify(context, angleUnit); + return o; + } + + /* Step 2: Merge negative powers: a*b^(-1)*c^(-pi)*d = a*(b*c^pi)^(-1) + * This also turns 2/3*a into 2*a*3^(-1) */ + Expression * e = mergeNegativePower(context, angleUnit); + if (e->type() == Type::Power) { + return e->shallowBeautify(context, angleUnit); + } + assert(e == this); + + // Step 3: Add Parenthesis if needed + for (int i = 0; i < numberOfOperands(); i++) { + const Expression * o = operand(i); + if (o->type() == Type::Addition ) { + Parenthesis * p = new Parenthesis(o, false); + replaceOperand(o, p, false); + } + } + + // Step 4: Create a Division if needed + for (int i = 0; i < numberOfOperands(); i++) { + if (!(operand(i)->type() == Type::Power && operand(i)->operand(1)->type() == Type::Rational && static_cast(operand(i)->operand(1))->isMinusOne())) { + continue; + } + + // Let's remove the denominator-to-be from this + Power * p = static_cast(editableOperand(i)); + Expression * denominatorOperand = p->editableOperand(0); + p->detachOperand(denominatorOperand); + removeOperand(p, true); + + Expression * numeratorOperand = shallowReduce(context, angleUnit); + // Delete parenthesis unnecessary on numerator + if (numeratorOperand->type() == Type::Parenthesis) { + numeratorOperand = numeratorOperand->replaceWith(numeratorOperand->editableOperand(0), true); + } + Expression * originalParent = numeratorOperand->parent(); + Division * d = new Division(numeratorOperand, denominatorOperand, false); + originalParent->replaceOperand(numeratorOperand, d, false); + return d->shallowBeautify(context, angleUnit); + } + + return this; +} + +Expression * Multiplication::cloneDenominator(Context & context, AngleUnit angleUnit) const { + // Merge negative power: a*b^-1*c^(-Pi)*d = a*(b*c^Pi)^-1 + // WARNING: we do not want to change the expression but to create a new one. + SimplificationRoot root(clone()); + Expression * e = ((Multiplication *)root.operand(0))->mergeNegativePower(context, angleUnit); + Expression * result = nullptr; + if (e->type() == Type::Power) { + result = static_cast(e)->cloneDenominator(context, angleUnit); + } else { + assert(e->type() == Type::Multiplication); + for (int i = 0; i < e->numberOfOperands(); i++) { + // a*b^(-1)*... -> a*.../b + if (e->operand(i)->type() == Type::Power && e->operand(i)->operand(1)->type() == Type::Rational && static_cast(e->operand(i)->operand(1))->isMinusOne()) { + Power * p = static_cast(e->editableOperand(i)); + result = p->editableOperand(0); + p->detachOperand((result)); + } + } + } + root.detachOperand(e); + delete e; + return result; +} + +Expression * Multiplication::mergeNegativePower(Context & context, AngleUnit angleUnit) { + Multiplication * m = new Multiplication(); + // Special case for rational p/q: if q != 1, q should be at denominator + if (operand(0)->type() == Type::Rational && !static_cast(operand(0))->denominator().isOne()) { + Rational * r = static_cast(editableOperand(0)); + m->addOperand(new Rational(r->denominator())); + if (r->numerator().isOne()) { + removeOperand(r, true); + } else { + replaceOperand(r, new Rational(r->numerator()), true); + } + } + int i = 0; + while (i < numberOfOperands()) { + if (operand(i)->type() == Type::Power && operand(i)->operand(1)->sign() == Sign::Negative) { + Expression * e = editableOperand(i); + e->editableOperand(1)->setSign(Sign::Positive, context, angleUnit); + removeOperand(e, false); + m->addOperand(e); + e->shallowReduce(context, angleUnit); + } else { + i++; + } + } + if (m->numberOfOperands() == 0) { + delete m; + return this; + } + Power * p = new Power(m, new Rational(-1), false); + m->sortOperands(SimplificationOrder); + m->squashUnaryHierarchy(); + addOperand(p); + sortOperands(SimplificationOrder); + return squashUnaryHierarchy(); +} + +void Multiplication::addMissingFactors(Expression * factor, Context & context, AngleUnit angleUnit) { + if (factor->type() == Type::Multiplication) { + for (int j = 0; j < factor->numberOfOperands(); j++) { + addMissingFactors(factor->editableOperand(j), context, angleUnit); + } + return; + } + /* Special case when factor is a Rational: if 'this' has already a rational + * operand, we replace it by its LCM with factor ; otherwise, we simply add + * factor as an operand. */ + if (numberOfOperands() > 0 && operand(0)->type() == Type::Rational && factor->type() == Type::Rational) { + Rational * f = static_cast(factor); + Rational * o = static_cast(editableOperand(0)); + assert(f->denominator().isOne()); + assert(o->denominator().isOne()); + Integer i = f->numerator(); + Integer j = o->numerator(); + return replaceOperand(o, new Rational(Arithmetic::LCM(&i, &j))); + } + if (factor->type() != Type::Rational) { + /* If factor is not a rational, we merge it with the operand of identical + * base if any. Otherwise, we add it as an new operand. */ + for (int i = 0; i < numberOfOperands(); i++) { + if (TermsHaveIdenticalBase(operand(i), factor)) { + Expression * sub = new Subtraction(CreateExponent(editableOperand(i)), CreateExponent(factor), false); + Reduce((Expression **)&sub, context, angleUnit); + if (sub->sign() == Sign::Negative) { // index[0] < index[1] + factor->replaceOperand(factor->editableOperand(1), new Opposite(sub, true), true); + factorizeBase(editableOperand(i), factor, context, angleUnit); + editableOperand(i)->shallowReduce(context, angleUnit); + } else if (sub->sign() == Sign::Unknown) { + factorizeBase(editableOperand(i), factor, context, angleUnit); + } else {} + delete sub; + return; + } + } + } + addOperand(factor->clone()); + sortOperands(SimplificationOrder); +} + +template Matrix * Multiplication::computeOnComplexAndMatrix(Complex const*, const Matrix*); +template Matrix * Multiplication::computeOnComplexAndMatrix(Complex const*, const Matrix*); +template Complex Multiplication::compute(Complex, Complex); +template Complex Multiplication::compute(Complex, Complex); + } -template Poincare::Complex Poincare::Multiplication::compute(Poincare::Complex, Poincare::Complex); -template Poincare::Complex Poincare::Multiplication::compute(Poincare::Complex, Poincare::Complex); diff --git a/poincare/src/naperian_logarithm.cpp b/poincare/src/naperian_logarithm.cpp index 7df7fa038..6b23eb695 100644 --- a/poincare/src/naperian_logarithm.cpp +++ b/poincare/src/naperian_logarithm.cpp @@ -1,8 +1,12 @@ #include +#include +#include +#include extern "C" { #include #include } +#include #include #include "layout/horizontal_layout.h" #include "layout/parenthesis_layout.h" @@ -10,25 +14,33 @@ extern "C" { namespace Poincare { -NaperianLogarithm::NaperianLogarithm() : - Function("ln") -{ -} - Expression::Type NaperianLogarithm::type() const { return Type::NaperianLogarithm; } -Expression * NaperianLogarithm::cloneWithDifferentOperands(Expression** newOperands, - int numberOfOperands, bool cloneOperands) const { - assert(newOperands != nullptr); - NaperianLogarithm * l = new NaperianLogarithm(); - l->setArgument(newOperands, numberOfOperands, cloneOperands); - return l; +Expression * NaperianLogarithm::clone() const { + NaperianLogarithm * a = new NaperianLogarithm(m_operands, true); + return a; +} + +Expression * NaperianLogarithm::shallowReduce(Context& context, AngleUnit angleUnit) { + Expression * e = Expression::shallowReduce(context, angleUnit); + if (e != this) { + return e; + } +#if MATRIX_EXACT_REDUCING + if (operand(0)->type() == Type::Matrix) { + return SimplificationEngine::map(this, context, angleUnit); + } +#endif + const Expression * logOperands[2] = {operand(0)->clone(), new Symbol(Ion::Charset::Exponential)}; + Logarithm * l = new Logarithm(logOperands, 2, false); + replaceWith(l, true); + return l->shallowReduce(context, angleUnit); } template -Complex NaperianLogarithm::templatedComputeComplex(const Complex c) const { +Complex NaperianLogarithm::computeOnComplex(const Complex c, AngleUnit angleUnit) { if (c.b() != 0) { return Complex::Float(NAN); } diff --git a/poincare/src/nth_root.cpp b/poincare/src/nth_root.cpp index 4bca282c6..030dc13ff 100644 --- a/poincare/src/nth_root.cpp +++ b/poincare/src/nth_root.cpp @@ -1,7 +1,8 @@ #include #include -#include +#include #include +#include #include "layout/nth_root_layout.h" extern "C" { @@ -11,49 +12,60 @@ extern "C" { namespace Poincare { -NthRoot::NthRoot() : - Function("root", 2) -{ -} - Expression::Type NthRoot::type() const { return Type::NthRoot; } -Expression * NthRoot::cloneWithDifferentOperands(Expression** newOperands, - int numberOfOperands, bool cloneOperands) const { - assert(newOperands != nullptr); - NthRoot * r = new NthRoot(); - r->setArgument(newOperands, numberOfOperands, cloneOperands); - return r; +Expression * NthRoot::clone() const { + NthRoot * a = new NthRoot(m_operands, true); return a; +} + +Expression * NthRoot::shallowReduce(Context& context, AngleUnit angleUnit) { + Expression * e = Expression::shallowReduce(context, angleUnit); + if (e != this) { + return e; + } +#if MATRIX_EXACT_REDUCING + if (operand(0)->type() == Type::Matrix || operand(1)->type() == Type::Matrix) { + return replaceWith(new Undefined(), true); + } +#endif + Power * invIndex = new Power(operand(1), new Rational(-1), false); + Power * p = new Power(operand(0), invIndex, false); + detachOperands(); + invIndex->shallowReduce(context, angleUnit); + replaceWith(p, true); + return p->shallowReduce(context, angleUnit); } ExpressionLayout * NthRoot::privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const { assert(floatDisplayMode != FloatDisplayMode::Default); assert(complexFormat != ComplexFormat::Default); - return new NthRootLayout(m_args[0]->createLayout(floatDisplayMode, complexFormat), m_args[1]->createLayout(floatDisplayMode, complexFormat)); + return new NthRootLayout(operand(0)->createLayout(floatDisplayMode, complexFormat), operand(1)->createLayout(floatDisplayMode, complexFormat)); } template -Evaluation * NthRoot::templatedEvaluate(Context& context, AngleUnit angleUnit) const { - Evaluation * base = m_args[0]->evaluate(context, angleUnit); - Evaluation * index = m_args[1]->evaluate(context, angleUnit); +Complex NthRoot::compute(const Complex c, const Complex d) { + if (c.a() >= 0 && c.b() == 0 && d.b() == 0) { + return Complex::Float(std::pow(c.a(), 1/d.a())); + } + Complex invIndex = Division::compute(Complex::Float(1), d); + return Power::compute(c, invIndex); +} + +template +Expression * NthRoot::templatedEvaluate(Context& context, AngleUnit angleUnit) const { + Expression * base = operand(0)->evaluate(context, angleUnit); + Expression * index = operand(1)->evaluate(context, angleUnit); Complex result = Complex::Float(NAN); - if (base->numberOfOperands() == 1 || index->numberOfOperands() == 1) { - result = compute(*(base->complexOperand(0)), *(index->complexOperand(0))); + if (base->type() == Type::Complex && index->type() == Type::Complex) { + Complex * basec = static_cast *>(base); + Complex * indexc = static_cast *>(index); + result = compute(*basec, *indexc); } delete base; delete index; return new Complex(result); } -template -Complex NthRoot::compute(const Complex c, const Complex d) const { - if (c.a() >= 0 && c.b() == 0 && d.b() == 0) { - return Complex::Float(std::pow(c.a(), 1/d.a())); - } - Complex invIndex = Fraction::compute(Complex::Float(1), d); - return Power::compute(c, invIndex); -} - } diff --git a/poincare/src/opposite.cpp b/poincare/src/opposite.cpp index 602f8ea45..aad66e0cf 100644 --- a/poincare/src/opposite.cpp +++ b/poincare/src/opposite.cpp @@ -1,6 +1,8 @@ #include -#include #include +#include +#include +#include extern "C" { #include #include @@ -12,64 +14,35 @@ extern "C" { namespace Poincare { -Opposite::Opposite(Expression * operand, bool cloneOperands) { - assert(operand != nullptr); - if (cloneOperands) { - m_operand = operand->clone(); - } else { - m_operand = operand; - } -} - -Opposite::~Opposite() { - delete m_operand; -} - -bool Opposite::hasValidNumberOfArguments() const { - return m_operand->hasValidNumberOfArguments(); -} - -const Expression * Opposite::operand(int i) const { - assert(i == 0); - return m_operand; -} - -int Opposite::numberOfOperands() const { - return 1; +Expression::Type Opposite::type() const { + return Type::Opposite; } Expression * Opposite::clone() const { - return this->cloneWithDifferentOperands((Expression**)&m_operand, 1, true); + Opposite * o = new Opposite(m_operands, true); + return o; } template -Complex Opposite::compute(const Complex c) { +Complex Opposite::compute(const Complex c, AngleUnit angleUnit) { return Complex::Cartesian(-c.a(), -c.b()); } -template -Evaluation * Opposite::computeOnMatrix(Evaluation * m) { - Complex * operands = new Complex[m->numberOfRows() * m->numberOfColumns()]; - for (int i = 0; i < m->numberOfRows() * m->numberOfColumns(); i++) { - Complex entry = *(m->complexOperand(i)); - operands[i] = Complex::Cartesian(-entry.a(), -entry.b()); +Expression * Opposite::shallowReduce(Context& context, AngleUnit angleUnit) { + Expression * e = Expression::shallowReduce(context, angleUnit); + if (e != this) { + return e; } - Evaluation * matrix = new ComplexMatrix(operands, m->numberOfRows(), m->numberOfColumns()); - delete[] operands; - return matrix; -} - -template -Evaluation * Opposite::templatedEvaluate(Context& context, AngleUnit angleUnit) const { - Evaluation * operandEvalutation = m_operand->evaluate(context, angleUnit); - Evaluation * result = nullptr; - if (operandEvalutation->numberOfRows() == 1 && operandEvalutation->numberOfColumns() == 1) { - result = new Complex(compute(*(operandEvalutation->complexOperand(0)))); - } else { - result = computeOnMatrix(operandEvalutation); + const Expression * op = operand(0); +#if MATRIX_EXACT_REDUCING + if (op->type() == Type::Matrix) { + return SimplificationEngine::map(this, context, angleUnit); } - delete operandEvalutation; - return result; +#endif + detachOperand(op); + Multiplication * m = new Multiplication(new Rational(-1), op, false); + replaceWith(m, true); + return m->shallowReduce(context, angleUnit); } ExpressionLayout * Opposite::privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const { @@ -78,22 +51,24 @@ ExpressionLayout * Opposite::privateCreateLayout(FloatDisplayMode floatDisplayMo ExpressionLayout * children_layouts[2]; char string[2] = {'-', '\0'}; children_layouts[0] = new StringLayout(string, 1); - children_layouts[1] = m_operand->type() == Type::Opposite ? new ParenthesisLayout(m_operand->createLayout(floatDisplayMode, complexFormat)) : m_operand->createLayout(floatDisplayMode, complexFormat); + children_layouts[1] = operand(0)->type() == Type::Opposite ? new ParenthesisLayout(operand(0)->createLayout(floatDisplayMode, complexFormat)) : operand(0)->createLayout(floatDisplayMode, complexFormat); return new HorizontalLayout(children_layouts, 2); } -Expression::Type Opposite::type() const { - return Expression::Type::Opposite; -} - -Expression * Opposite::cloneWithDifferentOperands(Expression** newOperands, - int numberOfOperands, bool cloneOperands) const { - assert(newOperands != nullptr); - assert(numberOfOperands == 1); - return new Opposite(newOperands[0], cloneOperands); +int Opposite::writeTextInBuffer(char * buffer, int bufferSize) const { + if (bufferSize == 0) { + return -1; + } + buffer[bufferSize-1] = 0; + int numberOfChar = 0; + if (bufferSize == 1) { return 0; } + buffer[numberOfChar++] = '-'; + numberOfChar += operand(0)->writeTextInBuffer(buffer+numberOfChar, bufferSize-numberOfChar); + buffer[numberOfChar] = 0; + return numberOfChar; } } -template Poincare::Complex Poincare::Opposite::compute(Poincare::Complex); -template Poincare::Complex Poincare::Opposite::compute(Poincare::Complex); +template Poincare::Complex Poincare::Opposite::compute(Poincare::Complex, AngleUnit angleUnit); +template Poincare::Complex Poincare::Opposite::compute(Poincare::Complex, AngleUnit angleUnit); diff --git a/poincare/src/parenthesis.cpp b/poincare/src/parenthesis.cpp index eab6bcfcb..9cc9c0829 100644 --- a/poincare/src/parenthesis.cpp +++ b/poincare/src/parenthesis.cpp @@ -7,56 +7,33 @@ extern "C" { namespace Poincare { -Parenthesis::Parenthesis(Expression * operand, bool cloneOperands) { - assert(operand != nullptr); - if (cloneOperands) { - m_operand = operand->clone(); - } else { - m_operand = operand; - } -} - -Parenthesis::~Parenthesis() { - delete m_operand; -} - -bool Parenthesis::hasValidNumberOfArguments() const { - return m_operand->hasValidNumberOfArguments(); -} - -int Parenthesis::numberOfOperands() const { - return 1; -} - -const Expression * Parenthesis::operand(int i) const { - assert(i == 0); - return m_operand; -} - -Expression * Parenthesis::clone() const { - return this->cloneWithDifferentOperands((Expression**) &m_operand, 1, true); -} - -ExpressionLayout * Parenthesis::privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const { - assert(floatDisplayMode != FloatDisplayMode::Default); - assert(complexFormat != ComplexFormat::Default); - return new ParenthesisLayout(m_operand->createLayout(floatDisplayMode, complexFormat)); -} - -template -Evaluation * Parenthesis::templatedEvaluate(Context& context, AngleUnit angleUnit) const { - return m_operand->evaluate(context, angleUnit); -} Expression::Type Parenthesis::type() const { return Type::Parenthesis; } -Expression * Parenthesis::cloneWithDifferentOperands(Expression** newOperands, - int numberOfOperands, bool cloneOperands) const { - assert(numberOfOperands == 1); - assert(newOperands != nullptr); - return new Parenthesis(newOperands[0], cloneOperands); +Expression * Parenthesis::clone() const { + Parenthesis * o = new Parenthesis(m_operands, true); + return o; +} + +ExpressionLayout * Parenthesis::privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const { + assert(floatDisplayMode != FloatDisplayMode::Default); + assert(complexFormat != ComplexFormat::Default); + return new ParenthesisLayout(operand(0)->createLayout(floatDisplayMode, complexFormat)); +} + +Expression * Parenthesis::shallowReduce(Context& context, AngleUnit angleUnit) { + Expression * e = Expression::shallowReduce(context, angleUnit); + if (e != this) { + return e; + } + return replaceWith(editableOperand(0), true); +} + +template +Expression * Parenthesis::templatedEvaluate(Context& context, AngleUnit angleUnit) const { + return operand(0)->evaluate(context, angleUnit); } } diff --git a/poincare/src/permute_coefficient.cpp b/poincare/src/permute_coefficient.cpp index 3c555857b..f381173d4 100644 --- a/poincare/src/permute_coefficient.cpp +++ b/poincare/src/permute_coefficient.cpp @@ -1,5 +1,6 @@ #include -#include +#include +#include extern "C" { #include @@ -8,33 +9,76 @@ extern "C" { namespace Poincare { -PermuteCoefficient::PermuteCoefficient() : - Function("permute", 2) -{ -} - Expression::Type PermuteCoefficient::type() const { return Type::PermuteCoefficient; } -Expression * PermuteCoefficient::cloneWithDifferentOperands(Expression** newOperands, - int numberOfOperands, bool cloneOperands) const { - assert(newOperands != nullptr); - PermuteCoefficient * pc = new PermuteCoefficient(); - pc->setArgument(newOperands, numberOfOperands, cloneOperands); - return pc; +Expression * PermuteCoefficient::clone() const { + PermuteCoefficient * b = new PermuteCoefficient(m_operands, true); + return b; +} + +Expression * PermuteCoefficient::shallowReduce(Context& context, AngleUnit angleUnit) { + Expression * e = Expression::shallowReduce(context, angleUnit); + if (e != this) { + return e; + } + Expression * op0 = editableOperand(0); + Expression * op1 = editableOperand(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 Rational(0), true); + } + /* if n is too big, we do not reduce to avoid too long computation. + * The permute coefficient will be evaluate approximatively later */ + if (Integer(k_maxNValue).isLowerThan(n)) { + return this; + } + Integer result(1); + int clippedK = k.extractedInt(); // Authorized because k < n < k_maxNValue + for (int i = 0; i < clippedK; i++) { + Integer factor = Integer::Subtraction(n, Integer(i)); + result = Integer::Multiplication(result, factor); + } + return replaceWith(new Rational(result), true); } template -Evaluation * PermuteCoefficient::templatedEvaluate(Context& context, AngleUnit angleUnit) const { - Evaluation * nInput = m_args[0]->evaluate(context, angleUnit); - Evaluation * kInput = m_args[1]->evaluate(context, angleUnit); - T n = nInput->toScalar(); - T k = kInput->toScalar(); +Complex * PermuteCoefficient::templatedEvaluate(Context& context, AngleUnit angleUnit) const { + Expression * nInput = operand(0)->evaluate(context, angleUnit); + Expression * kInput = operand(1)->evaluate(context, angleUnit); + if (nInput->type() != Type::Complex || kInput->type() != Type::Complex) { + return new Complex(Complex::Float(NAN)); + } + T n = static_cast *>(nInput)->toScalar(); + T k = static_cast *>(kInput)->toScalar(); delete nInput; delete kInput; - if (std::isnan(n) || std::isnan(k) || n != (int)n || k != (int)k || n < 0.0f || k < 0.0f) { - + if (std::isnan(n) || std::isnan(k) || n != std::round(n) || k != std::round(k) || n < 0.0f || k < 0.0f) { return new Complex(Complex::Float(NAN)); } if (k > n) { @@ -43,6 +87,9 @@ Evaluation * PermuteCoefficient::templatedEvaluate(Context& context, AngleUni T result = 1; for (int i = (int)n-(int)k+1; i <= (int)n; i++) { result *= i; + if (std::isinf(result)) { + return new Complex(Complex::Float(result)); + } } return new Complex(Complex::Float(std::round(result))); } diff --git a/poincare/src/power.cpp b/poincare/src/power.cpp index 9855eebb4..ecbae6c98 100644 --- a/poincare/src/power.cpp +++ b/poincare/src/power.cpp @@ -3,9 +3,24 @@ extern "C" { #include } #include +#include +#include +#include +#include #include -#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include "layout/baseline_relative_layout.h" namespace Poincare { @@ -14,21 +29,32 @@ Expression::Type Power::type() const { return Type::Power; } -Expression * Power::cloneWithDifferentOperands(Expression** newOperands, - int numberOfOperands, bool cloneOperands) const { - assert(numberOfOperands == 2); - return new Power(newOperands, cloneOperands); +Expression * Power::clone() const { + return new Power(m_operands, true); } -ExpressionLayout * Power::privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const { - assert(floatDisplayMode != FloatDisplayMode::Default); - assert(complexFormat != ComplexFormat::Default); - Expression * indiceOperand = m_operands[1]; - // Delete eventual parentheses of the indice in the pretty print - if (m_operands[1]->type() == Type::Parenthesis) { - indiceOperand = (Expression *)m_operands[1]->operand(0); +Expression::Sign Power::sign() const { + if (operand(0)->sign() == Sign::Positive && operand(1)->sign() != Sign::Unknown) { + return Sign::Positive; } - return new BaselineRelativeLayout(m_operands[0]->createLayout(floatDisplayMode, complexFormat),indiceOperand->createLayout(floatDisplayMode, complexFormat), BaselineRelativeLayout::Type::Superscript); + if (operand(0)->sign() == Sign::Negative && operand(1)->type() == Type::Rational) { + const Rational * r = static_cast(operand(1)); + if (r->denominator().isOne()) { + if (Integer::Division(r->numerator(), Integer(2)).remainder.isZero()) { + return Sign::Positive; + } else { + return Sign::Negative; + } + } + } + return Sign::Unknown; +} + +Expression * Power::setSign(Sign s, Context & context, AngleUnit angleUnit) { + assert(s == Sign::Positive); + assert(operand(0)->sign() == Sign::Negative); + editableOperand(0)->setSign(Sign::Positive, context, angleUnit); + return this; } template @@ -52,40 +78,575 @@ Complex Power::compute(const Complex c, const Complex d) { return Complex::Polar(radius, theta); } -template Evaluation * Power::templatedComputeOnComplexMatrixAndComplex(Evaluation * m, const Complex * d) const { +template Matrix * Power::computeOnMatrixAndComplex(const Matrix * m, const Complex * d) { if (m->numberOfRows() != m->numberOfColumns()) { - return new Complex(Complex::Float(NAN)); + return nullptr; } T power = d->toScalar(); - if (std::isnan(power) || std::isinf(power) || power != (int)power || std::fabs(power) > k_maxNumberOfSteps) { - return new Complex(Complex::Float(NAN)); + if (std::isnan(power) || std::isinf(power) || power != (int)power || std::fabs(power) > k_maxApproximatePowerMatrix) { + return nullptr; } if (power < 0) { - Evaluation * inverse = m->createInverse(); - Complex minusC = Opposite::compute(*d); - Evaluation * result = Power::computeOnComplexMatrixAndComplex(inverse, &minusC); + Matrix * inverse = m->createInverse(); + if (inverse == nullptr) { + return nullptr; + } + Complex minusC = Opposite::compute(*d, AngleUnit::Default); + Matrix * result = Power::computeOnMatrixAndComplex(inverse, &minusC); delete inverse; return result; } - Evaluation * result = ComplexMatrix::createIdentity(m->numberOfRows()); + Matrix * result = Matrix::createApproximateIdentity(m->numberOfRows()); // TODO: implement a quick exponentiation for (int k = 0; k < (int)power; k++) { if (shouldStopProcessing()) { delete result; - return new Complex(Complex::Float(NAN)); + return nullptr; } - result = Multiplication::computeOnMatrices(result, m); + result = Multiplication::computeOnMatrices(result, m); } return result; } -template Evaluation * Power::templatedComputeOnComplexAndComplexMatrix(const Complex * c, Evaluation * n) const { - return new Complex(Complex::Float(NAN)); +ExpressionLayout * Power::privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const { + assert(floatDisplayMode != FloatDisplayMode::Default); + assert(complexFormat != ComplexFormat::Default); + const Expression * indiceOperand = m_operands[1]; + // Delete eventual parentheses of the indice in the pretty print + if (m_operands[1]->type() == Type::Parenthesis) { + indiceOperand = m_operands[1]->operand(0); + } + return new BaselineRelativeLayout(m_operands[0]->createLayout(floatDisplayMode, complexFormat),indiceOperand->createLayout(floatDisplayMode, complexFormat), BaselineRelativeLayout::Type::Superscript); } -template Evaluation * Power::templatedComputeOnComplexMatrices(Evaluation * m, Evaluation * n) const { +int Power::simplificationOrderSameType(const Expression * e) const { + int baseComparison = SimplificationOrder(operand(0), e->operand(0)); + if (baseComparison != 0) { + return baseComparison; + } + return SimplificationOrder(operand(1), e->operand(1)); +} - return new Complex(Complex::Float(NAN)); +int Power::simplificationOrderGreaterType(const Expression * e) const { + int baseComparison = SimplificationOrder(operand(0), e); + if (baseComparison != 0) { + return baseComparison; + } + Rational one(1); + return SimplificationOrder(operand(1), &one); +} + +Expression * Power::shallowReduce(Context& context, AngleUnit angleUnit) { + Expression * e = Expression::shallowReduce(context, angleUnit); + if (e != this) { + return e; + } +#if MATRIX_EXACT_REDUCING + /* Step 0: get rid of matrix */ + if (operand(1)->type() == Type::Matrix) { + return replaceWith(new Undefined(), true); + } + if (operand(0)->type() == Type::Matrix) { + Matrix * mat = static_cast(editableOperand(0)); + if (operand(1)->type() != Type::Rational || !static_cast(operand(1))->denominator().isOne()) { + return replaceWith(new Undefined(), true); + } + Integer exponent = static_cast(operand(1))->numerator(); + if (mat->numberOfRows() != mat->numberOfColumns()) { + return replaceWith(new Undefined(), true); + } + if (exponent.isNegative()) { + editableOperand(1)->setSign(Sign::Positive, context, angleUnit); + Expression * newMatrix = shallowReduce(context, angleUnit); + Expression * parent = newMatrix->parent(); + MatrixInverse * inv = new MatrixInverse(newMatrix, false); + parent->replaceOperand(newMatrix, inv, false); + return inv; + } + if (Integer::NaturalOrder(exponent, Integer(k_maxExactPowerMatrix)) > 0) { + return this; + } + int exp = exponent.extractedInt(); // Ok, because 0 < exponent < k_maxExactPowerMatrix + Matrix * id = Matrix::createIdentity(mat->numberOfRows()); + if (exp == 0) { + return replaceWith(id, true); + } + Multiplication * result = new Multiplication(id, mat->clone()); + // TODO: implement a quick exponentiation + for (int k = 1; k < exp; k++) { + result->addOperand(mat->clone()); + } + replaceWith(result, true); + return result->shallowReduce(context, angleUnit); + } +#endif + + /* Step 0: We look for square root and sum of square roots (two terms maximum + * so far) at the denominator and move them to the numerator. */ + Expression * r = removeSquareRootsFromDenominator(context, angleUnit); + if (r) { + return r; + } + + if (operand(1)->type() == Type::Rational) { + const Rational * b = static_cast(operand(1)); + // x^0 + if (b->isZero()) { + return replaceWith(new Rational(1), true); + } + // x^1 + if (b->isOne()) { + return replaceWith(editableOperand(0), true); + } + // i^(p/q) + if (operand(0)->type() == Type::Symbol && static_cast(operand(0))->name() == Ion::Charset::IComplex) { + Rational r = Rational::Multiplication(*b, Rational(1, 2)); + return replaceWith(CreateNthRootOfUnity(r))->shallowReduce(context, angleUnit); + } + } + bool letPowerAtRoot = parentIsALogarithmOfSameBase(); + if (operand(0)->type() == Type::Rational) { + Rational * a = static_cast(editableOperand(0)); + // 0^x + if (a->isZero()) { + if (operand(1)->sign() == Sign::Positive) { + return replaceWith(new Rational(0), true); + } + if (operand(1)->sign() == Sign::Negative) { + return replaceWith(new Undefined(), true); + } + } + // 1^x + if (a->isOne()) { + return replaceWith(new Rational(1), true); + } + // p^q with p, q rationals + if (!letPowerAtRoot && operand(1)->type() == Type::Rational) { + double p = a->approximate(context, angleUnit); + double q = operand(1)->approximate(context, angleUnit); + double approx = std::pow(std::fabs(p), q); + if (std::isinf(approx) || std::isnan(approx) || std::fabs(approx)> 1E100) { + return this; + } + return simplifyRationalRationalPower(this, a, static_cast(editableOperand(1)), context, angleUnit); + } + } + // e^(i*Pi*r) with r rational + if (!letPowerAtRoot && isNthRootOfUnity()) { + Expression * m = editableOperand(1); + detachOperand(m); + Expression * i = m->editableOperand(m->numberOfOperands()-1); + static_cast(m)->removeOperand(i, false); + if (angleUnit == AngleUnit::Degree) { + const Expression * pi = m->operand(m->numberOfOperands()-1); + m->replaceOperand(pi, new Rational(180), true); + } + Expression * cos = new Cosine(m, false); + m = m->shallowReduce(context, angleUnit); + Expression * sin = new Sine(m, true); + Expression * complexPart = new Multiplication(sin, i, false); + sin->shallowReduce(context, angleUnit); + Expression * a = new Addition(cos, complexPart, false); + cos->shallowReduce(context, angleUnit); + complexPart->shallowReduce(context, angleUnit); + return replaceWith(a, true)->shallowReduce(context, angleUnit); + } + // x^log(y,x)->y if y > 0 + if (operand(1)->type() == Type::Logarithm) { + if (operand(1)->numberOfOperands() == 2 && operand(0)->isIdenticalTo(operand(1)->operand(1))) { + // y > 0 + if (operand(1)->operand(0)->sign() == Sign::Positive) { + return replaceWith(editableOperand(1)->editableOperand(0), true); + } + } + // 10^log(y) + if (operand(1)->numberOfOperands() == 1 && operand(0)->type() == Type::Rational && static_cast(operand(0))->isTen()) { + return replaceWith(editableOperand(1)->editableOperand(0), true); + } + } + // (a^b)^c -> a^(b+c) if a > 0 or c is integer + if (operand(0)->type() == Type::Power) { + Power * p = static_cast(editableOperand(0)); + // Check is a > 0 or c is Integer + if (p->operand(0)->sign() == Sign::Positive || + (operand(1)->type() == Type::Rational && static_cast(editableOperand(1))->denominator().isOne())) { + return simplifyPowerPower(p, editableOperand(1), context, angleUnit); + } + } + // (a*b*c*...)^r ? + if (!letPowerAtRoot && operand(0)->type() == Type::Multiplication) { + Multiplication * m = static_cast(editableOperand(0)); + // (a*b*c*...)^n = a^n*b^n*c^n*... if n integer + if (operand(1)->type() == Type::Rational && static_cast(editableOperand(1))->denominator().isOne()) { + return simplifyPowerMultiplication(m, editableOperand(1), context, angleUnit); + } + // (a*b*...)^r -> |a|^r*(sign(a)*b*...)^r if a rational + for (int i = 0; i < m->numberOfOperands(); i++) { + // a is signed and a != -1 + if (m->operand(i)->sign() != Sign::Unknown && (m->operand(i)->type() != Type::Rational || !static_cast(m->operand(i))->isMinusOne())) { + //if (m->operand(i)->sign() == Sign::Positive || m->operand(i)->type() == Type::Rational) { + Expression * r = editableOperand(1); + Expression * rCopy = r->clone(); + Expression * factor = m->editableOperand(i); + if (factor->sign() == Sign::Negative) { + m->replaceOperand(factor, new Rational(-1), false); + factor->setSign(Sign::Positive, context, angleUnit); + } else { + m->removeOperand(factor, false); + } + m->shallowReduce(context, angleUnit); + Power * p = new Power(factor, rCopy, false); + Multiplication * root = new Multiplication(p, clone(), false); + p->shallowReduce(context, angleUnit); + root->editableOperand(1)->shallowReduce(context, angleUnit); + replaceWith(root, true); + return root->shallowReduce(context, angleUnit); + } + } + } + // a^(b+c) -> Rational(a^b)*a^c with a and b rational + if (!letPowerAtRoot && operand(0)->type() == Type::Rational && operand(1)->type() == Type::Addition) { + Addition * a = static_cast(editableOperand(1)); + // Check is b is rational + if (a->operand(0)->type() == Type::Rational) { + Power * p1 = static_cast(clone()); + replaceOperand(a, a->editableOperand(1), true); + Power * p2 = static_cast(clone()); + Multiplication * m = new Multiplication(p1, p2, false); + simplifyRationalRationalPower(p1, static_cast(p1->editableOperand(0)), static_cast(p1->editableOperand(1)->editableOperand(0)), context, angleUnit); + replaceWith(m, true); + return m->shallowReduce(context, angleUnit); + } + } + return this; +} + +bool Power::parentIsALogarithmOfSameBase() const { + if (parent()->type() == Type::Logarithm && parent()->operand(0) == this) { + // parent = log(10^x) + if (parent()->numberOfOperands() == 1) { + if (operand(0)->type() == Type::Rational && static_cast(operand(0))->isTen()) { + return true; + } + return false; + } + // parent = log(x^y,x) + if (operand(0)->isIdenticalTo(parent()->operand(1))) { + return true; + } + } + // parent = ln(e^x) + if (parent()->type() == Type::NaperianLogarithm && parent()->operand(0) == this && operand(0)->type() == Type::Symbol && static_cast(operand(0))->name() == Ion::Charset::Exponential) { + return true; + } + return false; +} + +Expression * Power::simplifyPowerPower(Power * p, Expression * e, Context& context, AngleUnit angleUnit) { + Expression * p0 = p->editableOperand(0); + Expression * p1 = p->editableOperand(1); + p->detachOperands(); + Multiplication * m = new Multiplication(p1, e, false); + replaceOperand(e, m, false); + replaceOperand(p, p0, true); + m->shallowReduce(context, angleUnit); + return shallowReduce(context, angleUnit); +} + +Expression * Power::simplifyPowerMultiplication(Multiplication * m, Expression * r, Context& context, AngleUnit angleUnit) { + for (int index = 0; index < m->numberOfOperands(); index++) { + Expression * factor = m->editableOperand(index); + Power * p = new Power(factor, r, true); // We copy r and factor to avoid inheritance issues + m->replaceOperand(factor, p, true); + p->shallowReduce(context, angleUnit); + } + detachOperand(m); + return replaceWith(m, true)->shallowReduce(context, angleUnit); // delete r +} + +Expression * Power::simplifyRationalRationalPower(Expression * result, Rational * a, Rational * b, Context& context, AngleUnit angleUnit) { + if (b->denominator().isOne()) { + Rational r = Rational::Power(*a, b->numerator()); + return result->replaceWith(new Rational(r),true); + } + Expression * n = nullptr; + Expression * d = nullptr; + if (b->sign() == Sign::Negative) { + b->setSign(Sign::Positive); + n = CreateSimplifiedIntegerRationalPower(a->denominator(), b, false, context, angleUnit); + d = CreateSimplifiedIntegerRationalPower(a->numerator(), b, true, context, angleUnit); + } else { + n = CreateSimplifiedIntegerRationalPower(a->numerator(), b, false, context, angleUnit); + d = CreateSimplifiedIntegerRationalPower(a->denominator(), b, true, context, angleUnit); + } + Multiplication * m = new Multiplication(n, d, false); + result->replaceWith(m, true); + return m->shallowReduce(context, angleUnit); +} + +Expression * Power::CreateSimplifiedIntegerRationalPower(Integer i, Rational * r, bool isDenominator, Context & context, AngleUnit angleUnit) { + assert(!i.isZero()); + assert(r->sign() == Sign::Positive); + if (i.isOne()) { + return new Rational(1); + } + if (Arithmetic::k_primorial32.isLowerThan(i)) { + r->setSign(isDenominator ? Sign::Negative : Sign::Positive); + // We do not want to break i in prime factor because it might be take too many factors... More than k_maxNumberOfPrimeFactors. + return new Power(new Rational(i), r->clone(), false); + } + Integer factors[Arithmetic::k_maxNumberOfPrimeFactors]; + Integer coefficients[Arithmetic::k_maxNumberOfPrimeFactors]; + Arithmetic::PrimeFactorization(&i, factors, coefficients, Arithmetic::k_maxNumberOfPrimeFactors); + + Integer r1(1); + Integer r2(1); + int index = 0; + while (!coefficients[index].isZero() && index < Arithmetic::k_maxNumberOfPrimeFactors) { + Integer n = Integer::Multiplication(coefficients[index], r->numerator()); + IntegerDivision div = Integer::Division(n, r->denominator()); + r1 = Integer::Multiplication(r1, Integer::Power(factors[index], div.quotient)); + r2 = Integer::Multiplication(r2, Integer::Power(factors[index], div.remainder)); + index++; + } + Rational * p1 = new Rational(r2); + Integer one = isDenominator ? Integer(-1) : Integer(1); + Rational * p2 = new Rational(one, r->denominator()); + Power * p = new Power(p1, p2, false); + if (r1.isEqualTo(Integer(1)) && !i.isNegative()) { + return p; + } + Rational * r3 = isDenominator ? new Rational(Integer(1), r1) : new Rational(r1); + Multiplication * m = new Multiplication(r3, p, false); + if (r2.isOne()) { + m->removeOperand(p); + } + if (i.isNegative()) { + Expression * nthRootOfUnity = CreateNthRootOfUnity(*r); + m->addOperand(nthRootOfUnity); + nthRootOfUnity->shallowReduce(context, angleUnit); + + } + m->sortOperands(SimplificationOrder); + return m; +} + +Expression * Power::CreateNthRootOfUnity(const Rational r) { + const Symbol * exp = new Symbol(Ion::Charset::Exponential); + const Symbol * iComplex = new Symbol(Ion::Charset::IComplex); + const Symbol * pi = new Symbol(Ion::Charset::SmallPi); + const Expression * multExpOperands[3] = {iComplex, pi, new Rational(r)}; + Multiplication * mExp = new Multiplication(multExpOperands, 3, false); + mExp->sortOperands(SimplificationOrder); + return new Power(exp, mExp, false); +#if 0 + const Symbol * iComplex = new Symbol(Ion::Charset::IComplex); + const Symbol * pi = new Symbol(Ion::Charset::SmallPi); + Expression * op = new Multiplication(pi, r->clone(), false); + Cosine * cos = new Cosine(op, false); + op = op->shallowReduce(context, angleUnit); + Sine * sin = new Sine(op, true); + Expression * m = new Multiplication(iComplex, sin, false); + sin->shallowReduce(context, angleUnit); + Expression * a = new Addition(cos, m, false); + cos->shallowReduce(context, angleUnit); + const Expression * multExpOperands[3] = {pi, r->clone()}; +#endif +} + +Expression * Power::shallowBeautify(Context& context, AngleUnit angleUnit) { + // X^-y -> 1/(X->shallowBeautify)^y + if (operand(1)->sign() == Sign::Negative) { + Expression * p = cloneDenominator(context, angleUnit); + Division * d = new Division(new Rational(1), p, false); + p->shallowReduce(context, angleUnit); + replaceWith(d, true); + return d->shallowBeautify(context, angleUnit); + } + if (operand(1)->type() == Type::Rational && static_cast(operand(1))->numerator().isOne()) { + Integer index = static_cast(operand(1))->denominator(); + if (index.isEqualTo(Integer(2))) { + const Expression * sqrtOperand[1] = {operand(0)}; + SquareRoot * sqr = new SquareRoot(sqrtOperand, true); + return replaceWith(sqr, true); + } + const Expression * rootOperand[2] = {operand(0)->clone(), new Rational(index)}; + NthRoot * nr = new NthRoot(rootOperand, false); + return replaceWith(nr, true); + } + // +(a,b)^c ->(+(a,b))^c + if (operand(0)->type() == Type::Addition || operand(0)->type() == Type::Multiplication) { + const Expression * o[1] = {operand(0)}; + Parenthesis * p = new Parenthesis(o, true); + replaceOperand(operand(0), p, true); + } + return this; +} + +Expression * Power::cloneDenominator(Context & context, AngleUnit angleUnit) const { + if (operand(1)->sign() == Sign::Negative) { + Expression * denominator = clone(); + Expression * newExponent = denominator->editableOperand(1)->setSign(Sign::Positive, context, angleUnit); + if (newExponent->type() == Type::Rational && static_cast(newExponent)->isOne()) { + delete denominator; + return operand(0)->clone(); + } + return denominator; + } + return nullptr; +} + +bool Power::TermIsARationalSquareRootOrRational(const Expression * e) { + if (e->type() == Type::Rational) { + return true; + } + if (e->type() == Type::Power && e->operand(0)->type() == Type::Rational && e->operand(1)->type() == Type::Rational && static_cast(e->operand(1))->isHalf()) { + return true; + } + if (e->type() == Type::Multiplication && e->numberOfOperands() == 2 && e->operand(0)->type() == Type::Rational && e->operand(1)->type() == Type::Power && e->operand(1)->operand(0)->type() == Type::Rational && e->operand(1)->operand(1)->type() == Type::Rational && static_cast(e->operand(1)->operand(1))->isHalf()) { + return true; + } + return false; +} + +const Rational * Power::RadicandInExpression(const Expression * e) { + if (e->type() == Type::Rational) { + return nullptr; + } else if (e->type() == Type::Power) { + assert(e->type() == Type::Power); + assert(e->operand(0)->type() == Type::Rational); + return static_cast(e->operand(0)); + } else { + assert(e->type() == Type::Multiplication); + assert(e->operand(1)->type() == Type::Power); + assert(e->operand(1)->operand(0)->type() == Type::Rational); + return static_cast(e->operand(1)->operand(0)); + } +} + +const Rational * Power::RationalFactorInExpression(const Expression * e) { + if (e->type() == Type::Rational) { + return static_cast(e); + } else if (e->type() == Type::Power) { + return nullptr; + } else { + assert(e->type() == Type::Multiplication); + assert(e->operand(0)->type() == Type::Rational); + return static_cast(e->operand(0)); + } +} + +Expression * Power::removeSquareRootsFromDenominator(Context & context, AngleUnit angleUnit) { + Expression * result = nullptr; + + if (operand(0)->type() == Type::Rational && operand(1)->type() == Type::Rational && (static_cast(operand(1))->isHalf() || static_cast(operand(1))->isMinusHalf())) { + /* We're considering a term of the form sqrt(p/q) (or 1/sqrt(p/q)), with + * p and q integers. + * We'll turn those into sqrt(p*q)/q (or sqrt(p*q)/p) . */ + Integer p = static_cast(operand(0))->numerator(); + Integer q = static_cast(operand(0))->denominator(); + // We do nothing for terms of the form sqrt(p) + if (!q.isOne() || static_cast(operand(1))->isMinusHalf()) { + Power * sqrt = new Power(new Rational(Integer::Multiplication(p, q)), new Rational(1, 2), false); + if (static_cast(operand(1))->isHalf()) { + result = new Multiplication(new Rational(Integer(1), q), sqrt, false); + } else { + result = new Multiplication(new Rational(Integer(1), p), sqrt, false); + } + sqrt->shallowReduce(context, angleUnit); + } + } else if (operand(1)->type() == Type::Rational && static_cast(operand(1))->isMinusOne() && operand(0)->type() == Type::Addition && operand(0)->numberOfOperands() == 2 && TermIsARationalSquareRootOrRational(operand(0)->operand(0)) && TermIsARationalSquareRootOrRational(operand(0)->operand(1))) { + /* We're considering a term of the form + * + * 1/(n1/d1*sqrt(p1/q1) + n2/d2*sqrt(p2/q2)) + * + * and we want to turn it into + * + * n1*q2*d1*d2^2*sqrt(p1*q1) - n2*q1*d2*d1^2*sqrt(p2*q2) + * ------------------------------------------------------- + * n1^2*d2^2*p1*q2 - n2^2*d1^2*p2*q1 + */ + const Rational * f1 = RationalFactorInExpression(operand(0)->operand(0)); + const Rational * f2 = RationalFactorInExpression(operand(0)->operand(1)); + const Rational * r1 = RadicandInExpression(operand(0)->operand(0)); + const Rational * r2 = RadicandInExpression(operand(0)->operand(1)); + Integer n1 = (f1 ? f1->numerator() : Integer(1)); + Integer d1 = (f1 ? f1->denominator() : Integer(1)); + Integer p1 = (r1 ? r1->numerator() : Integer(1)); + Integer q1 = (r1 ? r1->denominator() : Integer(1)); + Integer n2 = (f2 ? f2->numerator() : Integer(1)); + Integer d2 = (f2 ? f2->denominator() : Integer(1)); + Integer p2 = (r2 ? r2->numerator() : Integer(1)); + Integer q2 = (r2 ? r2->denominator() : Integer(1)); + + // Compute the denominator = n1^2*d2^2*p1*q2 - n2^2*d1^2*p2*q1 + Integer denominator = Integer::Subtraction( + Integer::Multiplication( + Integer::Multiplication( + Integer::Power(n1, Integer(2)), + Integer::Power(d2, Integer(2))), + Integer::Multiplication(p1, q2)), + Integer::Multiplication( + Integer::Multiplication( + Integer::Power(n2, Integer(2)), + Integer::Power(d1, Integer(2))), + Integer::Multiplication(p2, q1))); + + // Compute the numerator + Power * sqrt1 = new Power(new Rational(Integer::Multiplication(p1, q1)), new Rational(1, 2), false); + Power * sqrt2 = new Power(new Rational(Integer::Multiplication(p2, q2)), new Rational(1, 2), false); + Integer factor1 = Integer::Multiplication( + Integer::Multiplication(n1, d1), + Integer::Multiplication(Integer::Power(d2, Integer(2)), q2)); + Multiplication * m1 = new Multiplication(new Rational(factor1), sqrt1, false); + Integer factor2 = Integer::Multiplication( + Integer::Multiplication(n2, d2), + Integer::Multiplication(Integer::Power(d1, Integer(2)), q1)); + Multiplication * m2 = new Multiplication(new Rational(factor2), sqrt2, false); + Subtraction * numerator = nullptr; + if (denominator.isNegative()) { + numerator = new Subtraction(m2, m1, false); + denominator.setNegative(false); + } else { + numerator = new Subtraction(m1, m2, false); + } + + result = new Multiplication(numerator, new Rational(Integer(1), denominator), false); + numerator->deepReduce(context, angleUnit); + } + + if (result) { + replaceWith(result, true); + result = result->shallowReduce(context, angleUnit); + } + return result; +} + +bool Power::isNthRootOfUnity() const { + if (operand(0)->type() != Type::Symbol || static_cast(operand(0))->name() != Ion::Charset::Exponential) { + return false; + } + if (operand(1)->type() != Type::Multiplication) { + return false; + } + if (operand(1)->numberOfOperands() < 2 || operand(1)->numberOfOperands() > 3) { + return false; + } + const Expression * i = operand(1)->operand(operand(1)->numberOfOperands()-1); + if (i->type() != Type::Symbol || static_cast(i)->name() != Ion::Charset::IComplex) { + return false; + } + const Expression * pi = operand(1)->operand(operand(1)->numberOfOperands()-2); + if (pi->type() != Type::Symbol || static_cast(pi)->name() != Ion::Charset::SmallPi) { + return false; + } + if (numberOfOperands() == 2) { + return true; + } + if (operand(1)->operand(0)->type() == Type::Rational) { + return true; + } + return false; } template Complex Power::compute(Complex, Complex); diff --git a/poincare/src/prediction_interval.cpp b/poincare/src/prediction_interval.cpp index 6ad762042..42f33e734 100644 --- a/poincare/src/prediction_interval.cpp +++ b/poincare/src/prediction_interval.cpp @@ -1,5 +1,11 @@ #include #include +#include +#include +#include +#include +#include +#include extern "C" { #include } @@ -7,38 +13,78 @@ extern "C" { namespace Poincare { -PredictionInterval::PredictionInterval() : - Function("prediction95", 2) -{ -} - Expression::Type PredictionInterval::type() const { return Type::PredictionInterval; } -Expression * PredictionInterval::cloneWithDifferentOperands(Expression** newOperands, - int numberOfOperands, bool cloneOperands) const { - assert(newOperands != nullptr); - PredictionInterval * pi = new PredictionInterval(); - pi->setArgument(newOperands, numberOfOperands, cloneOperands); - return pi; +Expression * PredictionInterval::clone() const { + PredictionInterval * a = new PredictionInterval(m_operands, true); + return a; +} + +Expression * PredictionInterval::shallowReduce(Context& context, AngleUnit angleUnit) { + Expression * e = Expression::shallowReduce(context, angleUnit); + if (e != this) { + return e; + } + Expression * op0 = editableOperand(0); + Expression * op1 = editableOperand(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->numerator().isNegative() || Integer::NaturalOrder(r0->numerator(), r0->denominator()) > 0) { + 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); + if (!r1->denominator().isOne() || r1->numerator().isNegative() || r0->numerator().isNegative() || Integer::NaturalOrder(r0->numerator(), r0->denominator()) > 0) { + return replaceWith(new Undefined(), true); + } + detachOperand(r0); + detachOperand(r1); + /* [r0-1.96*sqrt(r0*(1-r0)/r1), r0+1.96*sqrt(r0*(1-r0)/r1)]*/ + // Compute numerator = r0*(1-r0) + Rational * numerator = new Rational(Rational::Multiplication(*r0, Rational(Integer::Subtraction(r0->denominator(), r0->numerator()), r0->denominator()))); + // Compute sqr = sqrt(r0*(1-r0)/r1) + Expression * sqr = new Power(new Division(numerator, r1, false), new Rational(1, 2), false); + Expression * m = new Multiplication(new Rational(196, 100), sqr, false); + const Expression * newOperands[2] = {new Addition(r0, new Multiplication(new Rational(-1), m, false), false), new Addition(r0, m, true),}; + Expression * matrix = replaceWith(new Matrix(newOperands, 1, 2, false), true); + return matrix->deepReduce(context, angleUnit); } template -Evaluation * PredictionInterval::templatedEvaluate(Context& context, AngleUnit angleUnit) const { - Evaluation * pInput = m_args[0]->evaluate(context, angleUnit); - Evaluation * nInput = m_args[1]->evaluate(context, angleUnit); - T p = pInput->toScalar(); - T n = nInput->toScalar(); +Expression * PredictionInterval::templatedEvaluate(Context& context, AngleUnit angleUnit) const { + Expression * pInput = operand(0)->evaluate(context, angleUnit); + Expression * nInput = operand(1)->evaluate(context, angleUnit); + if (pInput->type() != Type::Complex || nInput->type() != Type::Complex) { + return new Complex(Complex::Float(NAN)); + } + T p = static_cast *>(pInput)->toScalar(); + T n = static_cast *>(nInput)->toScalar(); delete pInput; delete nInput; if (std::isnan(p) || std::isnan(n) || n != (int)n || n < 0 || p < 0 || p > 1) { return new Complex(Complex::Float(NAN)); } - Complex operands[2]; - operands[0] = Complex::Float(p - 1.96*std::sqrt(p*(1.0-p))/std::sqrt(n)); - operands[1] = Complex::Float(p + 1.96*std::sqrt(p*(1.0-p))/std::sqrt(n)); - return new ComplexMatrix(operands, 1, 2); + Expression * operands[2]; + operands[0] = new Complex(Complex::Float(p - 1.96*std::sqrt(p*(1.0-p))/std::sqrt(n))); + operands[1] = new Complex(Complex::Float(p + 1.96*std::sqrt(p*(1.0-p))/std::sqrt(n))); + return new Matrix(operands, 1, 2, false); } } diff --git a/poincare/src/product.cpp b/poincare/src/product.cpp index bed5c923b..dcc8f40d0 100644 --- a/poincare/src/product.cpp +++ b/poincare/src/product.cpp @@ -9,21 +9,17 @@ extern "C" { namespace Poincare { -Product::Product() : - Sequence("product") -{ -} - Expression::Type Product::type() const { return Type::Product; } -Expression * Product::cloneWithDifferentOperands(Expression** newOperands, - int numberOfOperands, bool cloneOperands) const { - assert(newOperands != nullptr); - Product * p = new Product(); - p->setArgument(newOperands, numberOfOperands, cloneOperands); - return p; +Expression * Product::clone() const { + Product * a = new Product(m_operands, true); + return a; +} + +const char * Product::name() const { + return "product"; } int Product::emptySequenceValue() const { @@ -35,14 +31,23 @@ ExpressionLayout * Product::createSequenceLayoutWithArgumentLayouts(ExpressionLa } template -Evaluation * Product::templatedEvaluateWithNextTerm(Evaluation * a, Evaluation * b) const { - if (a->numberOfOperands() == 1 && b->numberOfOperands() == 1) { - return new Complex(Multiplication::compute(*(a->complexOperand(0)), *(b->complexOperand(0)))); +Expression * Product::templatedEvaluateWithNextTerm(Expression * a, Expression * b) const { + if (a->type() == Type::Complex && b->type() == Type::Complex) { + Complex * c = static_cast *>(a); + Complex * d = static_cast *>(b); + return new Complex(Multiplication::compute(*c, *d)); } - if (a->numberOfOperands() == 1) { - return Multiplication::computeOnComplexAndMatrix(a->complexOperand(0), b); + if (a->type() == Type::Complex) { + Complex * c = static_cast *>(a); + assert(b->type() == Type::Matrix); + Matrix * m = static_cast(b); + return Multiplication::computeOnComplexAndMatrix(c, m); } - return Multiplication::computeOnMatrices(a, b); + assert(a->type() == Type::Matrix); + assert(b->type() == Type::Matrix); + Matrix * m = static_cast(a); + Matrix * n = static_cast(b); + return Multiplication::computeOnMatrices(m, n); } } diff --git a/poincare/src/rational.cpp b/poincare/src/rational.cpp new file mode 100644 index 000000000..78accb430 --- /dev/null +++ b/poincare/src/rational.cpp @@ -0,0 +1,171 @@ +#include +extern "C" { +#include +#include +#include +#include +} +#include +#include +#include "layout/string_layout.h" +#include "layout/fraction_layout.h" + +namespace Poincare { + +// Constructors + +Rational::Rational(const Integer numerator, const Integer denominator) { + assert(!denominator.isZero()); + if (numerator.isOne() || denominator.isOne()) { + // Avoid computing GCD if possible + m_numerator = numerator; + m_denominator = denominator; + } else { + Integer gcd = Arithmetic::GCD(&numerator, &denominator); + m_numerator = Integer::Division(numerator, gcd).quotient; + m_denominator = Integer::Division(denominator, gcd).quotient; + } + if (m_numerator.isNegative() && m_denominator.isNegative()) { + m_numerator.setNegative(false); + m_denominator.setNegative(false); + } else if (m_denominator.isNegative()) { + m_numerator.setNegative(true); + m_denominator.setNegative(false); + } +} + +Rational::Rational(const Integer numerator) { + m_numerator = numerator; + m_denominator = Integer(1); +} + +Rational::Rational(const Rational & other) { + m_numerator = other.m_numerator; + m_denominator = other.m_denominator; +} + +Rational & Rational::operator=(const Rational & other) { + m_numerator = other.m_numerator; + m_numerator = other.m_numerator; + m_denominator = other.m_denominator; + return *this; +} + +// Getter +const Integer Rational::numerator() const { + return m_numerator; +} + +const Integer Rational::denominator() const { + return m_denominator; +} +// Expression subclassing + +Expression::Type Rational::type() const { + return Type::Rational; +} + +Expression * Rational::clone() const { + return new Rational(m_numerator, m_denominator); +} + +Expression::Sign Rational::sign() const { + if (m_numerator.isNegative()) { + return Sign::Negative; + } + return Sign::Positive; +} + +Expression * Rational::setSign(Sign s) { + assert(s != Sign::Unknown); + bool negative = s == Sign::Negative ? true : false; + m_numerator.setNegative(negative); + return this; +} + +Expression * Rational::shallowBeautify(Context & context, AngleUnit angleUnit) { + if (m_numerator.isNegative()) { + m_numerator.setNegative(false); + Opposite * o = new Opposite(this, true); + return replaceWith(o, true); + } + return this; +} + +Expression * Rational::cloneDenominator(Context & context, AngleUnit angleUnit) const { + if (m_denominator.isOne()) { + return nullptr; + } + return new Rational(m_denominator); +} + +// Basic operations + +Rational Rational::Addition(const Rational & i, const Rational & j) { + Integer newNumerator = Integer::Addition(Integer::Multiplication(i.numerator(), j.denominator()), Integer::Multiplication(j.numerator(), i.denominator())); + Integer newDenominator = Integer::Multiplication(i.denominator(), j.denominator()); + return Rational(newNumerator, newDenominator); +} + +Rational Rational::Multiplication(const Rational & i, const Rational & j) { + Integer newNumerator = Integer::Multiplication(i.numerator(), j.numerator()); + Integer newDenominator = Integer::Multiplication(i.denominator(), j.denominator()); + return Rational(newNumerator, newDenominator); +} + +Rational Rational::Power(const Rational & i, const Integer & j) { + Integer absJ = j; + absJ.setNegative(false); + Integer newNumerator = Integer::Power(i.numerator(), absJ); + Integer newDenominator = Integer::Power(i.denominator(), absJ); + if (j.isNegative()) { + return Rational(newDenominator, newNumerator); + } + return Rational(newNumerator, newDenominator); +} + +int Rational::NaturalOrder(const Rational & i, const Rational & j) { + Integer i1 = Integer::Multiplication(i.numerator(), j.denominator()); + Integer i2 = Integer::Multiplication(i.denominator(), j.numerator()); + return Integer::NaturalOrder(i1, i2); +} + +// Comparison + +int Rational::simplificationOrderSameType(const Expression * e) const { + assert(e->type() == Expression::Type::Rational); + const Rational * other = static_cast(e); + return NaturalOrder(*this, *other); +} + +template Complex * Rational::templatedEvaluate(Context& context, Expression::AngleUnit angleUnit) const { + T n = m_numerator.approximate(); + T d = m_denominator.approximate(); + return new Complex(Complex::Float(n/d)); +} + +ExpressionLayout * Rational::privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const { + ExpressionLayout * numeratorLayout = m_numerator.createLayout(); + if (m_denominator.isOne()) { + return numeratorLayout; + } + ExpressionLayout * denominatorLayout = m_denominator.createLayout(); + return new FractionLayout(numeratorLayout, denominatorLayout); +} + +int Rational::writeTextInBuffer(char * buffer, int bufferSize) const { + buffer[bufferSize-1] = 0; + int numberOfChar = m_numerator.writeTextInBuffer(buffer, bufferSize); + if (m_denominator.isOne()) { + return numberOfChar; + } + if (numberOfChar >= bufferSize-1) { + return numberOfChar; + } + buffer[numberOfChar++] = '/'; + numberOfChar += m_denominator.writeTextInBuffer(buffer+numberOfChar, bufferSize-numberOfChar); + return numberOfChar; +} + +} + diff --git a/poincare/src/real_part.cpp b/poincare/src/real_part.cpp index bbe2377a7..aa8d4904f 100644 --- a/poincare/src/real_part.cpp +++ b/poincare/src/real_part.cpp @@ -1,6 +1,6 @@ #include #include - +#include extern "C" { #include } @@ -8,25 +8,34 @@ extern "C" { namespace Poincare { -RealPart::RealPart() : - Function("re") -{ -} - Expression::Type RealPart::type() const { return Type::RealPart; } -Expression * RealPart::cloneWithDifferentOperands(Expression** newOperands, - int numberOfOperands, bool cloneOperands) const { - assert(newOperands != nullptr); - RealPart * rp = new RealPart(); - rp->setArgument(newOperands, numberOfOperands, cloneOperands); - return rp; +Expression * RealPart::clone() const { + RealPart * a = new RealPart(m_operands, true); + return a; +} + +Expression * RealPart::shallowReduce(Context& context, AngleUnit angleUnit) { + Expression * e = Expression::shallowReduce(context, angleUnit); + if (e != this) { + return e; + } + Expression * op = editableOperand(0); +#if MATRIX_EXACT_REDUCING + if (op->type() == Type::Matrix) { + return SimplificationEngine::map(this, context, angleUnit); + } +#endif + if (op->type() == Type::Rational) { + return replaceWith(op, true); + } + return this; } template -Complex RealPart::templatedComputeComplex(const Complex c) const { +Complex RealPart::computeOnComplex(const Complex c, AngleUnit angleUnit) { return Complex::Float(c.a()); } diff --git a/poincare/src/round.cpp b/poincare/src/round.cpp index 99239d2df..ac379508d 100644 --- a/poincare/src/round.cpp +++ b/poincare/src/round.cpp @@ -1,4 +1,5 @@ #include +#include extern "C" { #include @@ -7,32 +8,37 @@ extern "C" { namespace Poincare { -Round::Round() : - Function("round", 2) -{ -} - Expression::Type Round::type() const { return Type::Round; } -Expression * Round::cloneWithDifferentOperands(Expression** newOperands, - int numberOfOperands, bool cloneOperands) const { - assert(newOperands != nullptr); - Round * r = new Round(); - r->setArgument(newOperands, numberOfOperands, cloneOperands); - return r; +Expression * Round::clone() const { + Round * c = new Round(m_operands, true); + return c; +} + +Expression * Round::shallowReduce(Context& context, AngleUnit angleUnit) { + Expression * e = Expression::shallowReduce(context, angleUnit); + if (e != this) { + return e; + } +#if MATRIX_EXACT_REDUCING + if (operand(0)->type() == Type::Matrix || operand(1)->type() == Type::Matrix) { + return replaceWith(new Undefined(), true); + } +#endif + return this; // TODO: implement for rationals! } template -Evaluation * Round::templatedEvaluate(Context& context, AngleUnit angleUnit) const { - Evaluation * f1Entry = m_args[0]->evaluate(context, angleUnit); - Evaluation * f2Entry = m_args[1]->evaluate(context, angleUnit); - T f1 = f1Entry->toScalar(); - T f2 = f2Entry->toScalar(); - delete f1Entry; - delete f2Entry; - if (std::isnan(f2) || f2 != (int)f2) { +Complex * Round::templatedEvaluate(Context& context, AngleUnit angleUnit) const { + Expression * f1Input = operand(0)->evaluate(context, angleUnit); + Expression * f2Input = operand(1)->evaluate(context, angleUnit); + T f1 = f1Input->type() == Type::Complex ? static_cast *>(f1Input)->toScalar() : NAN; + T f2 = f2Input->type() == Type::Complex ? static_cast *>(f2Input)->toScalar() : NAN; + delete f1Input; + delete f2Input; + if (std::isnan(f2) || f2 != std::round(f2)) { return new Complex(Complex::Float(NAN)); } T err = std::pow(10, std::floor(f2)); diff --git a/poincare/src/sequence.cpp b/poincare/src/sequence.cpp index e7269bc5b..0b2b5bf1a 100644 --- a/poincare/src/sequence.cpp +++ b/poincare/src/sequence.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include "layout/string_layout.h" #include "layout/horizontal_layout.h" extern "C" { @@ -12,43 +13,38 @@ extern "C" { namespace Poincare { -Sequence::Sequence(const char * name) : - Function(name, 3) -{ -} - ExpressionLayout * Sequence::privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const { assert(floatDisplayMode != FloatDisplayMode::Default); assert(complexFormat != ComplexFormat::Default); ExpressionLayout * childrenLayouts[2]; childrenLayouts[0] = new StringLayout("n=", 2); - childrenLayouts[1] = m_args[1]->createLayout(floatDisplayMode, complexFormat); - return createSequenceLayoutWithArgumentLayouts(new HorizontalLayout(childrenLayouts, 2), m_args[2]->createLayout(floatDisplayMode, complexFormat), m_args[0]->createLayout(floatDisplayMode, complexFormat)); + childrenLayouts[1] = operand(1)->createLayout(floatDisplayMode, complexFormat); + return createSequenceLayoutWithArgumentLayouts(new HorizontalLayout(childrenLayouts, 2), operand(2)->createLayout(floatDisplayMode, complexFormat), operand(0)->createLayout(floatDisplayMode, complexFormat)); } template -Evaluation * Sequence::templatedEvaluate(Context& context, AngleUnit angleUnit) const { - Evaluation * aInput = m_args[1]->evaluate(context, angleUnit); - Evaluation * bInput = m_args[2]->evaluate(context, angleUnit); - T start = aInput->toScalar(); - T end = bInput->toScalar(); +Expression * Sequence::templatedEvaluate(Context& context, AngleUnit angleUnit) const { + Expression * aInput = operand(1)->evaluate(context, angleUnit); + Expression * bInput = operand(2)->evaluate(context, angleUnit); + T start = aInput->type() == Type::Complex ? static_cast *>(aInput)->toScalar() : NAN; + T end = bInput->type() == Type::Complex ? static_cast *>(bInput)->toScalar() : NAN; delete aInput; delete bInput; if (std::isnan(start) || std::isnan(end) || start != (int)start || end != (int)end || end - start > k_maxNumberOfSteps) { return new Complex(Complex::Float(NAN)); } VariableContext nContext = VariableContext('n', &context); - Symbol nSymbol = Symbol('n'); - Evaluation * result = new Complex(Complex::Float(emptySequenceValue())); + Symbol nSymbol('n'); + Expression * result = new Complex(Complex::Float(emptySequenceValue())); for (int i = (int)start; i <= (int)end; i++) { if (shouldStopProcessing()) { delete result; return new Complex(Complex::Float(NAN)); } Complex iExpression = Complex::Float(i); - nContext.setExpressionForSymbolName(&iExpression, &nSymbol); - Evaluation * expression = m_args[0]->evaluate(nContext, angleUnit); - Evaluation * newResult = evaluateWithNextTerm(result, expression); + nContext.setExpressionForSymbolName(&iExpression, &nSymbol, nContext); + Expression * expression = operand(0)->evaluate(nContext, angleUnit); + Expression * newResult = evaluateWithNextTerm(T(), result, expression); delete result; delete expression; result = newResult; diff --git a/poincare/src/simplification/Makefile b/poincare/src/simplification/Makefile new file mode 100644 index 000000000..35bdc9fa1 --- /dev/null +++ b/poincare/src/simplification/Makefile @@ -0,0 +1,36 @@ +include poincare/src/simplification/rulegen/Makefile + +prefix = poincare/src/simplification + +%.h: $(RULEGEN) %.prs + @echo "RULEGEN $@" + @$(RULEGEN) < $(word 2,$^) > $@ + +rulesets = $(addprefix $(prefix)/,\ + demo_ruleset.h \ +) +$(prefix)/expression_simplify.o: $(rulesets) +products += $(rulesets) + +objs += $(addprefix $(prefix)/,\ + expression_simplify.o \ + rule.o \ + selector/combination.o \ + selector/selector.o \ + selector/type_and_identifier_selector.o \ + selector/type_selector.o \ + transform/division_transform.o \ + transform/remove_child_transform.o \ + transform/remove_parenthesis_transform.o \ + transform/subtraction_transform.o \ + transform/integer_dynamic_hierarchy_transform.o \ + transform/integer_power_transform.o \ + transform/merge_dynamic_hierarchy_transform.o \ + transform/opposite_transform.o \ + transform/rational_addition_transform.o \ + transform/rational_reduction_transform.o \ + transform/rational_power_transform.o \ + transform/replace_with_transform.o \ + transform/root_transform.o \ +) + diff --git a/poincare/src/simplify/README.md b/poincare/src/simplification/README.md similarity index 96% rename from poincare/src/simplify/README.md rename to poincare/src/simplification/README.md index 5da55835f..81d464f9a 100644 --- a/poincare/src/simplify/README.md +++ b/poincare/src/simplification/README.md @@ -37,7 +37,7 @@ you need a wildcard", but "ln(a)" is allright because ln has only one child) Addition(Integer(0),...) -> Addition(...) Product(Integer(0),...) -> Integer(0) -Fraction(Fraction(a,b),c) -> Fraction(a,Product(b,c)) +Division(Division(a,b),c) -> Division(a,Product(b,c)) Build(type=addition) - integer(0) diff --git a/poincare/src/simplification/demo_ruleset.prs b/poincare/src/simplification/demo_ruleset.prs new file mode 100644 index 000000000..ccac70e30 --- /dev/null +++ b/poincare/src/simplification/demo_ruleset.prs @@ -0,0 +1,73 @@ +DemoRuleset + +// 0*a -> 0 +Multiplication.a(Integer.b[0]) -> ReplaceWithTransform(a,b); + +// 1*a -> a +Multiplication.a(Integer.b[1]) -> RemoveChildTransform(a,b); + +// a+0 -> a +Addition.a(Integer.b[0]) -> RemoveChildTransform(a,b); + +// a^1 -> a +Power.a(Any.b,Integer.c[1]) -> ReplaceWithTransform(a,b); + +// 1^a -> 1 +Power.a(Integer.b[1], Any) -> ReplaceWithTransform(a,b); + +//a^0 -> 1 + +// Remove parenthesis +Parenthesis.a -> RemoveParenthesisTransform(a); + +// a-b -> a+(-1)*b +Subtraction.a -> SubtractionTransform(a); + +// -a -> (-1)*a +Opposite.a -> OppositeTransform(a); + +// a/b -> a*b^-1 +Division.a -> DivisionTransform(a); + +// sqrt(a) -> a^(1/2) +SquareRoot.a -> SquareRootTransform(a); + +// root(a, b) -> a^(1/b) +NthRoot.a -> NthRootTransform(a); + + +// (a+b)+c -> a+b+c +Addition.a(Addition.b) -> MergeDynamicHierarchyTransform(a,b); + +// (a*b)*c -> a*b*c +Multiplication.a(Multiplication.b) -> MergeDynamicHierarchyTransform(a,b); + +// Int+Int -> Int +Addition.a(Integer.b,Integer.c) -> IntegerAdditionTransform(a,b,c); + +// Int*Int -> Int +Multiplication.a(Integer.b,Integer.c) -> IntegerMultiplicationTransform(a,b,c); + +// Int^Int -> Int || Int^(-1) +Power.a(Integer.b,Integer.c) -> IntegerPowerTransform(a, b, c); + +// (b^c)^d -> b^(c*d) +//Power.a(Power(b,c),d) -> PowerPowerTransform(a,b,c,d) + +// Int*Int^-1 -> Int.a*Int.b^-1 with gcd(a,b) = 1 +Multiplication.a(Power.c(Integer.d, Integer[-1]), Integer.b) -> RationalReductionTransform(a,b,c,d); + +// Int*Int^-1+Int*Int^-1 -> Int*Int^-1 +Addition(Multiplication!(Integer.a,Power(Integer.b,Integer[-1])), Multiplication!(Integer.c,Power(Integer.d,Integer[-1]))) -> RationalAdditionTransform(a,b,c,d); + +// Int*Int^-1 + Int -> Int*Int^-1 +Addition(Multiplication!(Integer.a,Power(Integer.b,Integer[-1])), Integer.c) -> IntegerRationalAdditionTransform(a,b,c); + +// Int*Int^-1 + Int^(-1) -> Int*Int^-1 +Addition(Multiplication!(Integer.a,Power(Integer.b,Integer[-1])), Power(Integer.c,Integer[-1])) -> InverseIntegerRationalAdditionTransform(a,b,c); + +// Int^-1 + Int^-1 -> Int^-1 +Addition(Power(Integer.a,Integer[-1]),Power(Integer.b,Integer[-1])) -> InverseIntegerAdditionTransform(a,b); + +// Int^(Int*Int^(-1)) -> Int*Int^(Int*Int^(-1))*exp(i*Pi*Int/Int) +Power.a(Integer.b, Multiplication.c(Integer.d, Power.e(Integer.f, Integer[-1]))) -> RationalPowerTransform(a,b,c,d,e,f); diff --git a/poincare/src/simplification/expression_simplify.cpp b/poincare/src/simplification/expression_simplify.cpp new file mode 100644 index 000000000..7ce61943c --- /dev/null +++ b/poincare/src/simplification/expression_simplify.cpp @@ -0,0 +1,51 @@ +#include +#include "../expression_debug.h" +#include +#include +#include "demo_ruleset.h" + +namespace Poincare { + +class SimplificationRoot : public StaticHierarchy<1> { +public: + SimplificationRoot(Expression * e) : StaticHierarchy<1>(&e, false) { + e->setParent(this); + } + ~SimplificationRoot() { + detachOperand(operand(0)); + /* We don't want to clone the expression provided at construction. + * So we don't want it to be deleted when we're destroyed (parent destructor). */ + } + bool isCommutative() const override { return false; } + Expression * clone() const override { return nullptr; } + Type type() const override { return Expression::Type::Undefined; } + ExpressionLayout * privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const override { + return nullptr; + } + Evaluation * privateEvaluate(SinglePrecision p, Context& context, AngleUnit angleUnit) const override { + return nullptr; + } + Evaluation * privateEvaluate(DoublePrecision p, Context& context, AngleUnit angleUnit) const override { + return nullptr; + } +}; + +void Expression::simplify(Expression ** e) { + SimplificationRoot root(*e); + root.sort(); + // Only unconditional simplify + int ruleIndex = 0; + while (ruleIndex < Simplification::DemoRuleset.numberOfRules()) { + const Simplification::Rule * rule = Simplification::DemoRuleset.ruleAtIndex(ruleIndex++); + if (rule->apply(&root)) { + root.sort(); + std::cout << "-----" << std::endl; + print_expression(root.operand(0), 0); + std::cout << "-----" << std::endl; + ruleIndex = 0; + } + } + *e = (Expression *)(root.operand(0)); +} + +} diff --git a/poincare/src/simplification/handwritten_rules.txt b/poincare/src/simplification/handwritten_rules.txt new file mode 100644 index 000000000..c30a268f2 --- /dev/null +++ b/poincare/src/simplification/handwritten_rules.txt @@ -0,0 +1 @@ +Addition(Integer.a,Integer.b) -> IntegerAddition diff --git a/poincare/src/simplification/rule.cpp b/poincare/src/simplification/rule.cpp new file mode 100644 index 000000000..6b9396a16 --- /dev/null +++ b/poincare/src/simplification/rule.cpp @@ -0,0 +1,31 @@ +#include "rule.h" + +namespace Poincare { +namespace Simplification { + +bool Rule::apply(Expression * e) const { + if (immediateApply(e)) { + return true; + } + + for (int i=0; inumberOfOperands(); i++) { + Expression * child = (Expression *)(e->operand(i)); + // FIXME: Remove const-ness from operand() + if (apply(child)) { + return true; + } + } + + return false; +} + +bool Rule::immediateApply(Expression * e) const { + Expression * captures[m_captureLength]; + if (m_selector->match(e, captures, m_captureLength)) { + return m_transform(captures); + } + return false; +} + +} +} diff --git a/poincare/src/simplification/rule.h b/poincare/src/simplification/rule.h new file mode 100644 index 000000000..db81bf040 --- /dev/null +++ b/poincare/src/simplification/rule.h @@ -0,0 +1,26 @@ +#ifndef POINCARE_SIMPLIFICATION_RULE_H +#define POINCARE_SIMPLIFICATION_RULE_H + +#include "selector/selector.h" +#include "transform/transform.h" + +namespace Poincare { +namespace Simplification { + +class Rule { +public: + constexpr Rule(const Selector * s, Transform t, int captureLength) : + m_selector(s), m_transform(t), m_captureLength(captureLength) { + }; + bool apply(Expression * e) const; +private: + bool immediateApply(Expression * e) const; + const Selector * m_selector; + Transform m_transform; + int m_captureLength; +}; + +} +} + +#endif diff --git a/poincare/src/simplification/rulegen/Makefile b/poincare/src/simplification/rulegen/Makefile new file mode 100644 index 000000000..b7f01de3f --- /dev/null +++ b/poincare/src/simplification/rulegen/Makefile @@ -0,0 +1,36 @@ +prefix = poincare/src/simplification/rulegen + +rulegen_objs := $(addprefix $(prefix)/,\ + rules_parser.o\ + rules_lexer.o\ + node.o\ + rule.o\ +) + +# Note: we cannot use $prefix in the receipe because Make expands it when the +# command is run, at which time prefix might have been replaced. +$(prefix)/rules_parser.cpp: $(prefix)/rules_parser.y + @echo "BISON $@" + @bison --defines=poincare/src/simplification/rulegen/rules_tokens.h $< -o $@ + +$(prefix)/rules_lexer.cpp: $(prefix)/rules_lexer.l $(prefix)/rules_parser.cpp + @echo "FLEX $@" + @flex -o $@ $< + +RULEGEN := $(prefix)/rulegen + +products += $(rulegen_objs) $(RULEGEN) $(addprefix $(prefix)/,\ + rules_tokens.h\ + rules_parser.cpp\ + rules_lexer.cpp\ +) + +GENERATOR_CXXFLAGS = -std=c++11 -Wno-deprecated-register -Iion/include + +$(rulegen_objs): %.o: %.cpp + @echo "HOSTCC $@" + @$(HOSTCXX) $(GENERATOR_CXXFLAGS) -c $< -o $@ + +$(RULEGEN): $(rulegen_objs) + @echo "HOSTLD $@" + @$(HOSTCXX) $(rulegen_objs) -o $@ diff --git a/poincare/src/simplify/rules_generation/node.cpp b/poincare/src/simplification/rulegen/node.cpp similarity index 50% rename from poincare/src/simplify/rules_generation/node.cpp rename to poincare/src/simplification/rulegen/node.cpp index 0755b8ddb..c1bfc8cb6 100644 --- a/poincare/src/simplify/rules_generation/node.cpp +++ b/poincare/src/simplification/rulegen/node.cpp @@ -3,35 +3,9 @@ #include #include #include - -Node::Node(Type type, std::string * typeName) : - m_type(type), - m_typeName(typeName), - m_referenceMode(ReferenceMode::None), - m_referenceName(nullptr), - m_value(nullptr), - m_parent(nullptr) -{ - m_children = new std::vector(); -} - -Node::~Node() { - delete m_children; -} - -void Node::setReference(ReferenceMode mode, std::string * referenceName) { - assert(m_referenceName == nullptr); - m_referenceName = referenceName; - m_referenceMode = mode; -} - -void Node::setValue(std::string * value) { - assert(m_value == nullptr); - m_value = value; -} +#include void Node::setChildren(std::vector * children) { - assert(m_children->size() == 0); delete m_children; m_children = children; for (Node * child : *m_children) { @@ -39,8 +13,205 @@ void Node::setChildren(std::vector * children) { } } +void Node::identifyAnonymousChildren(int * index) { + if (m_identifier == nullptr) { + m_identifier = new std::string("s" + std::to_string(*index)); + (*index)++; + } + for (Node * child : *m_children) { + child->identifyAnonymousChildren(index); + } +} + // Generation +void Node::generateSelector(Rule * rule) { + sort(); + int i = 0; + + for (Node * child : *m_children) { + child->generateSelector(rule); + } + + if (m_children->size() > 0) { + std::cout + << "constexpr const Selector * " + << identifier() << "Children[] = {"; + for (Node * child : *m_children) { + std::cout << "&" << child->identifier(); + if (child != m_children->back()) { + std::cout << ", "; + } + } + std::cout << "};" << std::endl; + } + std::cout << "constexpr "; + if (name().compare("Any") == 0) { + std::cout << "AnySelector " << identifier() << "("; + } else if (name().compare("SameAs") == 0) { + assert(value().compare("NOVALUE") != 0); + std::cout << "SameAsSelector " << identifier() << "(" << value() << ", "; + } else { + if (m_value) { + std::cout << "TypeAndIdentifierSelector " << identifier() << "(Expression::Type::" << name() << ", " << intValue() << ", "; + } else { + std::cout << "TypeSelector " << identifier() << "(Expression::Type::" << name() << ", "; + } + } + std::cout << rule->indexOfIdentifierInTransform(*m_identifier); + if (m_children->size() > 0) { + std::cout << ", " << identifier() <<"Children, " << m_children->size() << ", " << m_partialMatch; + } + std::cout << ");" << std::endl; +} + +int Node::indexOfChildrenWithIdentifier(std::string identifier) { + for (int i=0; isize(); i++) { + if (*(m_children->at(i)->m_identifier) == identifier) { + return i; + } + } + return -1; +} + +std::string Node::value() const { + if (m_value) { + return *m_value; + } + return "NOVALUE"; +} + +std::string Node::identifier() const { + if (m_identifier) { + return *m_identifier; + } + return "NOIDEA"; +} + +int Node::intValue() const { + if (m_value == nullptr) { + return -INT_MAX; + } + // TODO: handle m_value = "Pi", "e", "i" ... + if (m_value->compare("Pi") == 0) { + return Ion::Charset::SmallPi; + } + if (m_value->compare("i") == 0) { + return Ion::Charset::IComplex; + } + // read number + int result = 0; + int base = 1; + for (int i=m_value->size()-1; i>=0; i--) { + if (m_value->at(i) == '-') { + return -result; + } + int digit = m_value->at(i)-'0'; + result = result+base*digit; + base *= 10; + } + return result; +} + +int Node::compareTo(Node * n) const { + if (m_name->compare("Any") == 0) { + if (n->m_name->compare("Any") == 0) { + if (identifier().compare(n->identifier()) == 0) { + return 0; + } else if (identifier().compare(n->identifier()) > 0) { + return 1; + } else { + return -1; + } + } else if (n->m_name->compare("SameAs") == 0) { + if (identifier().compare(n->value()) == 0) { + return -1; + } else if (identifier().compare(n->value()) > 0) { + return 1; + } else { + return -1; + } + } else { + return 1; + } + } else if (m_name->compare("SameAs") == 0) { + if (n->m_name->compare("Any") == 0) { + if (value().compare(n->identifier()) == 0) { + return 1; + } else if (value().compare(n->identifier()) > 0) { + return 1; + } else { + return -1; + } + } else if (n->m_name->compare("SameAs") == 0) { + if (value().compare(n->value()) == 0) { + return 0; + } else if (value().compare(n->value()) > 0) { + return 1; + } else { + return -1; + } + } else { + return 1; + } + } else { + if (n->m_name->compare("Any") == 0) { + return -1; + } else if (n->m_name->compare("SameAs") == 0) { + return -1; + } else { + if (name().compare(n->name()) != 0) { + if (name().compare(n->name()) > 0) { + return 1; + } else { + return -1; + } + } else { + if (intValue() == n->intValue()) { + return 0; + } else if (intValue() > n->intValue()) { + return 1; + } else { + return -1; + } + } + } + } +} + +void Node::sort() { + if (m_children->size() == 0) { + return; + } + for (Node * child : *m_children) { + child->sort(); + } + if (!isCommutative()) { + return; + } + for (int i = m_children->size()-1; i > 0; i--) { + bool isSorted = true; + for (int j = 0; j < m_children->size()-1; j++) { + if (m_children->at(j)->compareTo(m_children->at(j+1)) > 0) { + std::swap(m_children->at(j), m_children->at(j+1)); + isSorted = false; + } + } + if (isSorted) { + return; + } + } +} + +bool Node::isCommutative() { + if (m_name->compare("Addition") == 0 || m_name->compare("Multiplication") == 0) { + return true; + } + return false; +} + +#if 0 + std::string Node::generateSelectorConstructor(Rule * context) { std::ostringstream result; switch (m_type) { @@ -191,3 +362,4 @@ int Node::generateTree(bool selector, Rule * context, int index, int indentation } return generatedCount; } +#endif diff --git a/poincare/src/simplification/rulegen/node.h b/poincare/src/simplification/rulegen/node.h new file mode 100644 index 000000000..0d8cbd056 --- /dev/null +++ b/poincare/src/simplification/rulegen/node.h @@ -0,0 +1,48 @@ +#ifndef POINCARE_SIMPLIFICATION_RULES_GENERATION_NODE_H +#define POINCARE_SIMPLIFICATION_RULES_GENERATION_NODE_H + +class Rule; + +#include +#include + +class Node { +public: + Node(std::string * name, std::string * identifier, std::string * value, bool partialMatch = true) : + m_name(name), + m_identifier(identifier), + m_value(value), + m_partialMatch(partialMatch), + m_children(new std::vector()), + m_parent(nullptr) { } + void setChildren(std::vector * children); + void identifyAnonymousChildren(int * index); + + void generateSelector(Rule * rule); + int indexOfChildrenWithIdentifier(std::string identifier); + std::string identifier() const; + std::string name() const { return *m_name; } + std::string value() const; + int intValue() const; + int numberOfChildren() { return m_children->size(); } + int compareTo(Node * node) const; + void sort(); +private: + int selectorCaptureIndexInRule(Rule * rule); + int selectorIndexInRule(Rule * rule); + bool isCommutative(); + /* + int generateTree(bool selector, Rule * context, int index, int indentationLevel); + std::string generateSelectorConstructor(Rule * context); + std::string generateBuilderConstructor(Rule * context); + */ + + std::string * m_name; + std::string * m_identifier; + std::string * m_value; + bool m_partialMatch; + std::vector * m_children; + Node * m_parent; +}; + +#endif diff --git a/poincare/src/simplification/rulegen/rule.cpp b/poincare/src/simplification/rulegen/rule.cpp new file mode 100644 index 000000000..a5cf2f163 --- /dev/null +++ b/poincare/src/simplification/rulegen/rule.cpp @@ -0,0 +1,29 @@ +#include "rule.h" +#include + +void Rule::generate(int index) { + std::cout << "namespace Rule" << index << " {" << std::endl; + int selectorIndex = 0; + m_selector->identifyAnonymousChildren(&selectorIndex); + m_selector->generateSelector(this); + std::cout << "constexpr Rule rule(&" + << m_selector->identifier() << ", " + << m_transform->name() << ", " + << m_transform->numberOfChildren() << ");" << std::endl; + std::cout << "}" << std::endl; +} + +int Rule::indexOfIdentifierInTransform(std::string identifier) { + return m_transform->indexOfChildrenWithIdentifier(identifier); +} +#if 0 +void Rule::generate(std::string rule_name) { + std::cout << "constexpr ExpressionSelector " << rule_name << "Selector[" << m_selector->totalDescendantCountIncludingSelf() << "] = {" << std::endl; + m_selector->generateSelectorTree(this); + std::cout << "};" << std::endl; + + std::cout << "constexpr ExpressionBuilder " << rule_name << "Builder[" << m_builder->totalDescendantCountIncludingSelf() << "] = {" << std::endl; + m_builder->generateBuilderTree(this); + std::cout << "};" << std::endl; +} +#endif diff --git a/poincare/src/simplification/rulegen/rule.h b/poincare/src/simplification/rulegen/rule.h new file mode 100644 index 000000000..4fda47876 --- /dev/null +++ b/poincare/src/simplification/rulegen/rule.h @@ -0,0 +1,18 @@ +#ifndef POINCARE_SIMPLIFICATION_RULES_GENERATION_RULE_H +#define POINCARE_SIMPLIFICATION_RULES_GENERATION_RULE_H + +#include "node.h" + +class Rule { +public: + Rule(Node * selector, Node * transform) : + m_selector(selector), m_transform(transform) {} + void generate(int index); + void identifyAnonymousSelectors(); + int indexOfIdentifierInTransform(std::string identifier); +private: + Node * m_selector; + Node * m_transform; +}; + +#endif diff --git a/poincare/src/simplify/rules_generation/rules_lexer.l b/poincare/src/simplification/rulegen/rules_lexer.l similarity index 97% rename from poincare/src/simplify/rules_generation/rules_lexer.l rename to poincare/src/simplification/rulegen/rules_lexer.l index 49ad03ea6..c09a78979 100644 --- a/poincare/src/simplify/rules_generation/rules_lexer.l +++ b/poincare/src/simplification/rulegen/rules_lexer.l @@ -22,6 +22,7 @@ \, { return(COMMA); } \; { return(SEMICOLON); } \. { return(PERIOD); } +\! { return BANG; } "//".* {} /* ignore single line comments */ [ \t\n] {} /* ignore line jump and whitespaces */ diff --git a/poincare/src/simplify/rules_generation/rules_parser.y b/poincare/src/simplification/rulegen/rules_parser.y similarity index 54% rename from poincare/src/simplify/rules_generation/rules_parser.y rename to poincare/src/simplification/rulegen/rules_parser.y index 02953108a..2fd0ec3e4 100644 --- a/poincare/src/simplify/rules_generation/rules_parser.y +++ b/poincare/src/simplification/rulegen/rules_parser.y @@ -3,21 +3,21 @@ /* When calling the parser, we will provide yyparse with an extra parameter : a * backpointer to the resulting expression. */ -%parse-param { std::vector ** rules } +%parse-param { ParserResult * result } %{ #include "rule.h" +struct ParserResult { + std::string * name; + std::vector * rules; +}; + int yylex(); -int yyparse(std::vector ** rules); -int yyerror(std::vector ** rules, const char *s); +int yyparse(ParserResult * result); +int yyerror(ParserResult * result, const char * s); %} - -/* All symbols (both terminals and non-terminals) may have a value associated - * with them. In our case, it's going to be either an Expression (for example, - * when parsing (a/b) we want to create a new Fraction), or a string (this will - * be useful to retrieve the value of Integers for example). */ %union { std::vector * rule_list; std::vector * node_list; @@ -43,6 +43,7 @@ int yyerror(std::vector ** rules, const char *s); %token COMMA %token SEMICOLON %token PERIOD +%token BANG %token DOLLAR %token ASTERISK @@ -55,7 +56,7 @@ int yyerror(std::vector ** rules, const char *s); %% root: - rule_list { *rules = $1; } + CAPITALIZED_IDENTIFIER rule_list { result->name = $1; result->rules = $2; } rule_list: rule { $$ = new std::vector(); $$->push_back($1); } @@ -66,26 +67,28 @@ rule: node: CAPITALIZED_IDENTIFIER { - $$ = new Node(Node::Type::Expression, $1); + $$ = new Node($1, nullptr, nullptr); } - | DOLLAR CAPITALIZED_IDENTIFIER { - $$ = new Node(Node::Type::Generator, $2); + | CAPITALIZED_IDENTIFIER PERIOD IDENTIFIER { + $$ = new Node($1, $3, nullptr); + } + | CAPITALIZED_IDENTIFIER LEFT_BRACKET VALUE RIGHT_BRACKET { + $$ = new Node($1, nullptr, $3); + } + | CAPITALIZED_IDENTIFIER PERIOD IDENTIFIER LEFT_BRACKET VALUE RIGHT_BRACKET { + $$ = new Node($1, $3, $5); + } + | CAPITALIZED_IDENTIFIER BANG { + $$ = new Node($1, nullptr, nullptr, false); + } + | CAPITALIZED_IDENTIFIER BANG PERIOD IDENTIFIER { + $$ = new Node($1, $4, nullptr, false); + } + | CAPITALIZED_IDENTIFIER BANG LEFT_BRACKET VALUE RIGHT_BRACKET { + $$ = new Node($1, nullptr, $4, false); } | IDENTIFIER { - $$ = new Node(Node::Type::Any); - $$->setReference(Node::ReferenceMode::SingleNode, $1); - } - | IDENTIFIER ASTERISK { - $$ = new Node(Node::Type::Any); - $$->setReference(Node::ReferenceMode::Wildcard, $1); - } - | node PERIOD IDENTIFIER { - $$ = $1; - $$->setReference(Node::ReferenceMode::SingleNode, $3); - } - | node LEFT_BRACKET VALUE RIGHT_BRACKET { - $$ = $1; - $$->setValue($3); + $$ = new Node(nullptr, $1, nullptr); } | node LEFT_PARENTHESIS node_list RIGHT_PARENTHESIS { $$ = $1; @@ -101,15 +104,43 @@ node_list: #include #include -int yyerror(std::vector ** rules, const char *s) { +int yyerror(ParserResult * result, const char *s) { printf("Error: %s\n",s); return 0; } int main(void) { - std::vector * rules = new std::vector(); + ParserResult result; + //std::vector * rules = new std::vector(); - yyparse(&rules); + yyparse(&result); + + std::cout << "#include \"ruleset.h\"" << std::endl << std::endl; + std::cout << "#include \"transform/transform.h\"" << std::endl << std::endl; + + std::cout << "namespace Poincare {" << std::endl; + std::cout << "namespace Simplification {" << std::endl << std::endl; + + std::cout << "namespace " << *(result.name) << "Rules {" << std::endl << std::endl; + + for (int i=0; isize(); i++) { + result.rules->at(i)->generate(i); + std::cout << std::endl; + } + + std::cout << "constexpr Rule rules[" << result.rules->size() << "] = {"; + for (int i=0; isize(); i++) { + std::cout << "Rule" << i << "::rule"; + if (i+1 != result.rules->size()) { + std::cout << ", "; + } + } + std::cout << "};" << std::endl; + std::cout << "};" << std::endl << std::endl; + std::cout << "constexpr Ruleset " << *(result.name) << "(" << *(result.name) << "Rules::rules, " << result.rules->size() << ");" << std::endl; + std::cout << std::endl << "}" << std::endl << "}" << std::endl; + +#if 0 std::cout << "#include \"rules.h\"" << std::endl; std::cout << "#include \"simplification_generator.h\"" << std::endl; @@ -135,6 +166,6 @@ std::cout << " Simplification((ExpressionSelector *)" << name.str() << "Selecto std::cout << std::endl; std::cout << "constexpr int Poincare::knumberOfSimplifications = " << rules->size() << ";" << std::endl; - delete rules; +#endif return 0; } diff --git a/poincare/src/simplify/rules.h b/poincare/src/simplification/rules.h similarity index 65% rename from poincare/src/simplify/rules.h rename to poincare/src/simplification/rules.h index 13a07203c..d1056f406 100644 --- a/poincare/src/simplify/rules.h +++ b/poincare/src/simplification/rules.h @@ -1,5 +1,5 @@ -#ifndef POINCARE_SIMPLIFY_RULES_H -#define POINCARE_SIMPLIFY_RULES_H +#ifndef POINCARE_SIMPLIFICATION_RULES_H +#define POINCARE_SIMPLIFICATION_RULES_H #include "simplification.h" diff --git a/poincare/src/simplification/ruleset.h b/poincare/src/simplification/ruleset.h new file mode 100644 index 000000000..5121bad66 --- /dev/null +++ b/poincare/src/simplification/ruleset.h @@ -0,0 +1,27 @@ +#ifndef POINCARE_SIMPLIFICATION_RULESET_H +#define POINCARE_SIMPLIFICATION_RULESET_H + +#include "rule.h" +#include "selector/any_selector.h" +#include "selector/type_selector.h" +#include "selector/type_and_identifier_selector.h" + +namespace Poincare { +namespace Simplification { + +class Ruleset { +public: + constexpr Ruleset(const Rule rules[], int numberOfRules) : + m_rules(rules), + m_numberOfRules(numberOfRules) {}; + const Rule * ruleAtIndex(int i) const { return m_rules+i; }; + int numberOfRules() const { return m_numberOfRules; }; +private: + const Rule * m_rules; + int m_numberOfRules; +}; + +} +} + +#endif diff --git a/poincare/src/simplification/selector/any_selector.h b/poincare/src/simplification/selector/any_selector.h new file mode 100644 index 000000000..50f5efc12 --- /dev/null +++ b/poincare/src/simplification/selector/any_selector.h @@ -0,0 +1,20 @@ +#ifndef POINCARE_SIMPLIFICATION_SELECTOR_ANY_SELECTOR_H +#define POINCARE_SIMPLIFICATION_SELECTOR_ANY_SELECTOR_H + +#include "selector.h" + +namespace Poincare { +namespace Simplification { + +class AnySelector : public Selector { +public: + using Selector::Selector; + bool immediateMatch(const Expression * e) const override { + return true; + } +}; + +} +} + +#endif diff --git a/poincare/src/simplification/selector/combination.cpp b/poincare/src/simplification/selector/combination.cpp new file mode 100644 index 000000000..6cd12a8bc --- /dev/null +++ b/poincare/src/simplification/selector/combination.cpp @@ -0,0 +1,70 @@ +#include "combination.h" +#include + +namespace Poincare { +namespace Simplification { + +Combination::Combination(const Selector * const * selectors, int numberOfSelectors, const Expression * rootExpression, bool enableCrossings) : + m_selectors(selectors), + m_numberOfSelectors(numberOfSelectors), + m_rootExpression(rootExpression), + m_firstIteration(true), + m_enableCrossings(enableCrossings) +{ + assert(m_numberOfSelectors < 255); +} + +#if 0 +void Combination::print() const { + std::cout << "("; + for (int i=0; i= m_rootExpression->numberOfOperands()) { + return false; + } + return m_selectors[digit]->acceptsLocationInCombination(this, digit); +} + +bool Combination::next() { + int digit = m_firstIteration ? 0 : m_numberOfSelectors-1; + resetExpressionIndexForSelectorIndex(digit); + bool forceOneIncrementation = !m_firstIteration; + m_firstIteration = false; + + while (digit >= 0) { + if (!forceOneIncrementation && expressionIndexForSelectorIndexIsValidAtDigit(digit)) { // Of course this is forwarded to Selectors/Expression + // Good, go "deeper" + if (digit < (m_numberOfSelectors-1)) { + digit++; + resetExpressionIndexForSelectorIndex(digit); + } else { + return true; + } + } else { + m_expressionIndexForSelectorIndex[digit]++; + forceOneIncrementation = false; + if (m_expressionIndexForSelectorIndex[digit] >= m_rootExpression->numberOfOperands()) { + resetExpressionIndexForSelectorIndex(digit); + digit--; + forceOneIncrementation = true; + } + } + } + return false; +} + +void Combination::resetExpressionIndexForSelectorIndex(int digit) { + m_expressionIndexForSelectorIndex[digit] = m_enableCrossings || digit < 1 ? 0 : m_expressionIndexForSelectorIndex[digit-1]+1; +} +} +} diff --git a/poincare/src/simplification/selector/combination.h b/poincare/src/simplification/selector/combination.h new file mode 100644 index 000000000..1b6f71478 --- /dev/null +++ b/poincare/src/simplification/selector/combination.h @@ -0,0 +1,45 @@ +#ifndef POINCARE_SIMPLIFICATION_SELECTOR_COMBINATION_H +#define POINCARE_SIMPLIFICATION_SELECTOR_COMBINATION_H + +#include +#include "selector.h" + +namespace Poincare { +namespace Simplification { + +/* Combination is the association of two tables: a selectors table and an expressions + * table. + * The aim is to find combination between both: + * + * Selectors: s1 s2 s3 s4 ... s10 + * | \ / \ / | + * | x x | + * | / \ / \ | + * Expressions: e1 e2 e3 e4 ... e10 + * + * Crossing between selectors and expressions can be enable or disable. + * */ + +class Combination { +public: + Combination(const Selector * const * selectors, int numberOfSelectors, const Expression * rootExpression, bool enableCrossings); + int expressionIndexForSelectorIndex(int i) const { return m_expressionIndexForSelectorIndex[i]; } + const Expression * expressionForSelectorIndex(int i) const { return m_rootExpression->operand(expressionIndexForSelectorIndex(i)); } + bool next(); + // TODO: delete this method created only for testing purposes + void print() const; +private: + bool expressionIndexForSelectorIndexIsValidAtDigit(int digit) const; + void resetExpressionIndexForSelectorIndex(int digit); + const Selector * const * m_selectors; + int m_numberOfSelectors; + const Expression * m_rootExpression; + int m_expressionIndexForSelectorIndex[255]; // Hard limit at compile-time. Much lower in fact. + bool m_firstIteration; + bool m_enableCrossings; +}; + +} +} + +#endif diff --git a/poincare/src/simplification/selector/same_as_selector.h b/poincare/src/simplification/selector/same_as_selector.h new file mode 100644 index 000000000..a77979274 --- /dev/null +++ b/poincare/src/simplification/selector/same_as_selector.h @@ -0,0 +1,21 @@ +#ifndef POINCARE_SIMPLIFICATION_SAME_AS_SELECTOR_H +#define POINCARE_SIMPLIFICATION_SAME_AS_SELECTOR_H + +#include "selector.h" + +namespace Poincare { +namespace Simplification { + +class SameAsSelector : public Selector { +public: + constexpr SameAsSelector(int originalIndex, int captureIndex = -1, Selector ** children = nullptr, int numberOfChildren = 0, bool childrenPartialMatch = true) : + Selector(captureIndex, children, numberOfChildren, childrenPartialMatch), m_originalIndex(originalIndex) {} + bool acceptsLocationInCombination(const Combination * combination, int location) const override; +private: + int m_originalIndex; +}; + +} +} + +#endif diff --git a/poincare/src/simplification/selector/selector.cpp b/poincare/src/simplification/selector/selector.cpp new file mode 100644 index 000000000..98d446a66 --- /dev/null +++ b/poincare/src/simplification/selector/selector.cpp @@ -0,0 +1,66 @@ +#include "selector.h" +#include "combination.h" +#include + +namespace Poincare { +namespace Simplification { + +bool Selector::acceptsLocationInCombination(const Combination * combination, int location) const { + // Yes, if no other slot in the combination before me is the same + for (int i=0; iexpressionIndexForSelectorIndex(i) == combination->expressionIndexForSelectorIndex(location)) { + return false; + } + } + return immediateMatch(combination->expressionForSelectorIndex(location)); +} + +bool Selector::match(const Expression * e, Expression ** captures, int captureLength) const { + // Test that root selector match root expression + if (!immediateMatch(e)) { + return false; + } + // Capture the expression if required + if (m_captureIndex >= 0) { + assert(m_captureIndex < captureLength); + //FIXME: No cast + captures[m_captureIndex] = (Expression *)e; + } + /* If the root selector matched the root expression, we still have to test + * that every selector's child can match an expression's child: we return + * true if a combination linking every selector child to an expression child + * does exist. To avoid combinatorial explosion, we rely on the fact that the + * expression AND the selector are sorted enabling us to skip many useless + * cases. */ + if (m_numberOfChildren == 0) { + return true; + } + /* If we want a complete match (meaning every expression's child are matched + * by a selector's child), the number of selector children should equal the + * number of expression children. And that is the only condition to add to + * get a total match! */ + + if (!m_childrenPartialMatch && m_numberOfChildren != e->numberOfOperands()) { + return false; + } + Combination combination(m_children, m_numberOfChildren, e, e->isCommutative()); + while (combination.next()){ + bool allChildrenMatched = true; + for (int i=0; imatch(expression, captures, captureLength)) { + allChildrenMatched = false; + break; + } + } + if (allChildrenMatched) { + return true; + } + } + + return false; +} + +} +} diff --git a/poincare/src/simplification/selector/selector.h b/poincare/src/simplification/selector/selector.h new file mode 100644 index 000000000..f503d71e0 --- /dev/null +++ b/poincare/src/simplification/selector/selector.h @@ -0,0 +1,28 @@ +#ifndef POINCARE_SIMPLIFICATION_SELECTOR_SELECTOR_H +#define POINCARE_SIMPLIFICATION_SELECTOR_SELECTOR_H + +#include + +namespace Poincare { +namespace Simplification { + +class Combination; + +class Selector { +public: + constexpr Selector(int captureIndex = -1, const Selector * const * children = nullptr, int numberOfChildren = 0, bool childrenPartialMatch = true) : + m_captureIndex(captureIndex), m_children(children), m_numberOfChildren(numberOfChildren), m_childrenPartialMatch(childrenPartialMatch) {} + virtual bool acceptsLocationInCombination(const Combination * combination, int location) const; + virtual bool immediateMatch(const Expression * e) const = 0; + bool match(const Expression * e, Expression ** captures, int captureLength) const; +private: + int m_captureIndex; + const Selector * const * m_children; + int m_numberOfChildren; + bool m_childrenPartialMatch; +}; + +} +} + +#endif diff --git a/poincare/src/simplification/selector/type_and_identifier_selector.cpp b/poincare/src/simplification/selector/type_and_identifier_selector.cpp new file mode 100644 index 000000000..fc89f0227 --- /dev/null +++ b/poincare/src/simplification/selector/type_and_identifier_selector.cpp @@ -0,0 +1,12 @@ +#include "type_and_identifier_selector.h" +#include "combination.h" + +namespace Poincare { +namespace Simplification { + +bool TypeAndIdentifierSelector::immediateMatch(const Expression * e) const { + return TypeSelector::immediateMatch(e) && (m_identifier == e->identifier()); +} + +} +} diff --git a/poincare/src/simplification/selector/type_and_identifier_selector.h b/poincare/src/simplification/selector/type_and_identifier_selector.h new file mode 100644 index 000000000..e59ed0bd0 --- /dev/null +++ b/poincare/src/simplification/selector/type_and_identifier_selector.h @@ -0,0 +1,20 @@ +#ifndef POINCARE_SIMPLIFICATION_SELECTOR_TYPE_AND_IDENTIFIER_SELECTOR_H +#define POINCARE_SIMPLIFICATION_SELECTOR_TYPE_AND_IDENTIFIER_SELECTOR_H + +#include "type_selector.h" + +namespace Poincare { +namespace Simplification { + +class TypeAndIdentifierSelector : public TypeSelector { +public: + constexpr TypeAndIdentifierSelector(Expression::Type type, int identifier, int captureIndex = -1, Selector ** children = nullptr, int numberOfChildren = 0, bool childrenPartialMatch = true) : TypeSelector(type, captureIndex, children, numberOfChildren, childrenPartialMatch), m_identifier(identifier) { } + bool immediateMatch(const Expression * e) const override; +private: + int m_identifier; +}; + +} +} + +#endif diff --git a/poincare/src/simplification/selector/type_selector.cpp b/poincare/src/simplification/selector/type_selector.cpp new file mode 100644 index 000000000..dbb646aab --- /dev/null +++ b/poincare/src/simplification/selector/type_selector.cpp @@ -0,0 +1,26 @@ +#include "type_selector.h" +#include "combination.h" + +namespace Poincare { +namespace Simplification { + +bool TypeSelector::immediateMatch(const Expression * e) const { + return (e->type() == m_type); +} + +bool TypeSelector::acceptsLocationInCombination(const Combination * combination, int location) const { + // Yes if all this is true: + // - The Expression matches matches my Class + // - The proposed location is strictly after the one for my previous brother, if I have one. + // -> This requires to SORT selectors, and put the "ANY"/"SameAs" at the end + // Code is stupid : m[locationInMatch] > m[locationInMatch-1]; + + if (location > 0 && (combination->expressionIndexForSelectorIndex(location) <= combination->expressionIndexForSelectorIndex(location-1))) { + return false; + } + + return Selector::acceptsLocationInCombination(combination, location); +} + +} +} diff --git a/poincare/src/simplification/selector/type_selector.h b/poincare/src/simplification/selector/type_selector.h new file mode 100644 index 000000000..89134fb29 --- /dev/null +++ b/poincare/src/simplification/selector/type_selector.h @@ -0,0 +1,23 @@ +#ifndef POINCARE_SIMPLIFICATION_SELECTOR_TYPE_SELECTOR_H +#define POINCARE_SIMPLIFICATION_SELECTOR_TYPE_SELECTOR_H + +#include "selector.h" + +namespace Poincare { +namespace Simplification { + +class TypeSelector : public Selector { +public: + constexpr TypeSelector(Expression::Type type, int captureIndex = -1, const Selector * const * children = nullptr, int numberOfChildren = 0, int childrenPartialMatch = true) : + Selector(captureIndex, children, numberOfChildren, childrenPartialMatch), m_type(type) {} + bool immediateMatch(const Expression * e) const override; + bool acceptsLocationInCombination(const Combination * combination, int location) const override; +private: + Expression::Type m_type; +}; + +} +} + +#endif + diff --git a/poincare/src/simplification/transform/division_transform.cpp b/poincare/src/simplification/transform/division_transform.cpp new file mode 100644 index 000000000..352eb2819 --- /dev/null +++ b/poincare/src/simplification/transform/division_transform.cpp @@ -0,0 +1,27 @@ +#include "transform.h" +#include +#include +#include +#include +#include +#include + +bool Poincare::Simplification::DivisionTransform(Expression * captures[]) { + assert(captures[0]->type() == Expression::Type::Division); + + Division * d = static_cast(captures[0]); + assert(d->numberOfOperands() == 2); + + const Integer * minusOne = new Integer(-1); + const Expression * powerOperands[2] = {d->operand(1), minusOne}; + const Power * p = new Power(powerOperands, false); + const Expression * multiplicationOperands[2] = {d->operand(0), p}; + Multiplication * m = new Multiplication(multiplicationOperands, 2, false); + + static_cast(d->parent())->replaceOperand(d, m, false); + + d->detachOperands(); + delete d; + + return true; +} diff --git a/poincare/src/simplification/transform/integer_dynamic_hierarchy_transform.cpp b/poincare/src/simplification/transform/integer_dynamic_hierarchy_transform.cpp new file mode 100644 index 000000000..fb1791fd7 --- /dev/null +++ b/poincare/src/simplification/transform/integer_dynamic_hierarchy_transform.cpp @@ -0,0 +1,34 @@ +#include "transform.h" +#include +#include +#include +#include +#include +#include + +namespace Poincare { + +typedef Integer (*Operation)(const Integer&, const Integer &); + +static bool IntegerDynamicHierarchyTransform(Expression * captures[], Operation operation) { + DynamicHierarchy * a = static_cast(captures[0]); + Integer * i1 = static_cast(captures[1]); + Integer * i2 = static_cast(captures[2]); + + Integer resultOnStack = operation(*i1, *i2); + Integer * r = new Integer(std::move(resultOnStack)); + + a->replaceOperand(i1, r); + a->removeOperand(i2); + return true; +} + +bool Simplification::IntegerAdditionTransform(Expression * captures[]) { + return IntegerDynamicHierarchyTransform(captures, Integer::Addition); +} + +bool Simplification::IntegerMultiplicationTransform(Expression * captures[]) { + return IntegerDynamicHierarchyTransform(captures, Integer::Multiplication); +} + +} diff --git a/poincare/src/simplification/transform/integer_power_transform.cpp b/poincare/src/simplification/transform/integer_power_transform.cpp new file mode 100644 index 000000000..5d2210659 --- /dev/null +++ b/poincare/src/simplification/transform/integer_power_transform.cpp @@ -0,0 +1,31 @@ +#include "transform.h" +#include +#include +#include +#include +#include +#include + +namespace Poincare { + +bool Simplification::IntegerPowerTransform(Expression * captures[]) { + Power * p = static_cast(captures[0]); + Integer * i1 = static_cast(captures[1]); + Integer * i2 = static_cast(captures[2]); + Expression * result = nullptr; + if (i2->isNegative()) { + if (i2->isEqualTo(Integer(-1))) { + return false; + } + Integer absI2 = *i2; + absI2.setNegative(false); + Expression * operands[2] = {new Integer(Integer::Power(*i1, absI2)), new Integer(-1)}; + result = new Power(operands, false); + } else { + result = new Integer(Integer::Power(*i1, *i2)); + } + static_cast(p->parent())->replaceOperand(p, result, true); + return true; +} + +} diff --git a/poincare/src/simplification/transform/merge_dynamic_hierarchy_transform.cpp b/poincare/src/simplification/transform/merge_dynamic_hierarchy_transform.cpp new file mode 100644 index 000000000..bcc31f88a --- /dev/null +++ b/poincare/src/simplification/transform/merge_dynamic_hierarchy_transform.cpp @@ -0,0 +1,14 @@ +#include "transform.h" +#include +#include + +bool Poincare::Simplification::MergeDynamicHierarchyTransform(Expression * captures[]) { + DynamicHierarchy * h0 = static_cast(captures[0]); + DynamicHierarchy * h1 = static_cast(captures[1]); + // TODO: because removeOperand squashes the hierarchy when it has only 1 operand, we have to add operands before removing them. However, it can be non-optimal as we alloc an extra space for an expressions pointer which is deleted right after... + h0->addOperands(h1->operands(), h1->numberOfOperands()); + h0->removeOperand(h1, false); + h1->detachOperands(); + delete h1; + return true; +} diff --git a/poincare/src/simplification/transform/opposite_transform.cpp b/poincare/src/simplification/transform/opposite_transform.cpp new file mode 100644 index 000000000..bed6a3f67 --- /dev/null +++ b/poincare/src/simplification/transform/opposite_transform.cpp @@ -0,0 +1,19 @@ +#include "transform.h" +#include +#include +#include +#include + +bool Poincare::Simplification::OppositeTransform(Expression * captures[]) { + Opposite * o = static_cast(captures[0]); + assert(o->type() == Expression::Type::Opposite); + + const Integer * minusOne = new Integer(-1); + const Expression * multiplicationOperands[2] = {o->operand(0), minusOne}; + Multiplication * m = new Multiplication(multiplicationOperands, 2, false); + + o->detachOperands(); + o->replaceWith(m); + + return true; +} diff --git a/poincare/src/simplification/transform/rational_addition_transform.cpp b/poincare/src/simplification/transform/rational_addition_transform.cpp new file mode 100644 index 000000000..6983cfa9e --- /dev/null +++ b/poincare/src/simplification/transform/rational_addition_transform.cpp @@ -0,0 +1,104 @@ +#include "transform.h" +#include +#include +#include +#include +#include +#include + +namespace Poincare { + +/* Those transforms compute the sum of rational numbers + * + * The strategy is rather simple: + * - Match an expression of the shape a/b + c/d (variables being Integers) + * - Replace it with (a*d+c*b)/(d*c) + * + * In practice, the initial expression nearly always already contains a division + * so we simply reuse it. */ + +static bool PrivateRationalAdditionTransform(Integer * n1, Integer * d1, Integer * n2, Integer * d2, Expression * operandToRemove) { + // n1 and d1 will be replaced, so they must NOT be a descendant of operandToDelete + assert(!n1->hasAncestor(operandToRemove)); + assert(!d1->hasAncestor(operandToRemove)); + assert(operandToRemove->parent()->type() == Expression::Type::Addition); + + Integer * newNumerator = new Integer(Integer::Addition(Integer::Multiplication(*n1, *d2), Integer::Multiplication(*n2, *d1))); + Integer * newDenominator = new Integer(Integer::Multiplication(*d1, *d2)); + + n1->replaceWith(newNumerator); + d1->replaceWith(newDenominator); + operandToRemove->removeFromParent(); + + return true; +} + +bool Simplification::RationalAdditionTransform(Expression * captures[]) { + // If we match a/b+c/d, we modify a and b and remove c/d. + Integer * n1 = static_cast(captures[0]); + Integer * d1 = static_cast(captures[1]); + Integer * n2 = static_cast(captures[2]); + Integer * d2 = static_cast(captures[3]); + Multiplication * m2 = static_cast(n2->parent()); + assert(n1->type() == Expression::Type::Integer); + assert(d1->type() == Expression::Type::Integer); + assert(n2->type() == Expression::Type::Integer); + assert(d2->type() == Expression::Type::Integer); + assert(m2->type() == Expression::Type::Multiplication); + return PrivateRationalAdditionTransform(n1, d1, n2, d2, m2); +} + +bool Simplification::IntegerRationalAdditionTransform(Expression * captures[]) { + // We matched a/b + c. + // We modify a and b, and remove c. + Integer * n1 = static_cast(captures[0]); + Integer * d1 = static_cast(captures[1]); + Integer * n2 = static_cast(captures[2]); + assert(n1->type() == Expression::Type::Integer); + assert(d1->type() == Expression::Type::Integer); + assert(n2->type() == Expression::Type::Integer); + Integer d2(1); + return PrivateRationalAdditionTransform(n1, d1, n2, &d2, n2); +} + +bool Simplification::InverseIntegerRationalAdditionTransform(Expression * captures[]) { + // We matched a/b + 1/c. + // We modify a and b, and remove 1/c. + Integer * n1 = static_cast(captures[0]); + Integer * d1 = static_cast(captures[1]); + Integer * d2 = static_cast(captures[2]); + assert(n1->type() == Expression::Type::Integer); + assert(d1->type() == Expression::Type::Integer); + assert(d2->type() == Expression::Type::Integer); + Integer n2 = Integer(1); + Expression * operandToRemove = d2->parent(); + assert(operandToRemove->type() == Expression::Type::Power); + + return PrivateRationalAdditionTransform(n1, d1, &n2, d2, operandToRemove); +} + +bool Simplification::InverseIntegerAdditionTransform(Expression * captures[]) { + // We matched a^-1 + b^-1 + // This case is a tad bit more annoying because there's no numerator. + // Let's just add one! + Integer * n1 = new Integer(1); + Integer * d1 = static_cast(captures[0]); + Integer n2(1); + Integer * d2 = static_cast(captures[1]); + + assert(d1->type() == Expression::Type::Integer); + assert(d2->type() == Expression::Type::Integer); + + Expression * p = d1->parent(); + assert(p->type() == Expression::Type::Power); + Expression * root = p->parent(); + assert(root != nullptr); + + Expression * multOperands[2] = {n1, p}; + Multiplication * m = new Multiplication(multOperands, 2, false); // Don't clone anyone + root->replaceOperand(p, m, false); // But don't delete p either! + + return PrivateRationalAdditionTransform(n1, d1, &n2, d2, d2->parent()); +} + +} diff --git a/poincare/src/simplification/transform/rational_power_transform.cpp b/poincare/src/simplification/transform/rational_power_transform.cpp new file mode 100644 index 000000000..983fa3765 --- /dev/null +++ b/poincare/src/simplification/transform/rational_power_transform.cpp @@ -0,0 +1,62 @@ +#include "transform.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +bool Poincare::Simplification::RationalPowerTransform(Expression * captures[]) { + Power * p0 = static_cast(captures[0]); + Integer * i0 = static_cast(captures[1]); + Multiplication * m = static_cast(captures[2]); + Integer * i1 = static_cast(captures[3]); + Power * p1 = static_cast(captures[4]); + Integer * i2 = static_cast(captures[5]); + + if (i0->isEqualTo(Integer(1)) || i0->isEqualTo(Integer(0)) || Arithmetic::k_primorial32.isLowerThan(*i0)) { + // We do not want to factorize number above + return false; + } + Integer factors[Arithmetic::k_maxNumberOfPrimeFactors]; + Integer coefficients[Arithmetic::k_maxNumberOfPrimeFactors]; + Arithmetic::PrimeFactorization(i0, factors, coefficients, Arithmetic::k_maxNumberOfPrimeFactors); + + Integer r1 = Integer(1); + Integer r2 = Integer(1); + int index = 0; + while (Integer(0).isLowerThan(coefficients[index])) { + Integer n = Integer::Multiplication(coefficients[index], *i1); + IntegerDivision div = Integer::Division(n, *i2); + if (div.remainder.isEqualTo(Integer(0))) { + r1 = Integer::Multiplication(r1, Integer::Power(factors[index], div.quotient)); + } else { + r2 = Integer::Multiplication(r2, Integer::Power(factors[index], n)); + } + index++; + } + if (r1.isEqualTo(Integer(1)) && !i0->isNegative()) { + return false; + } + if (i0->isNegative()) { + const Symbol * exp = new Symbol(Ion::Charset::Exponential); + const Symbol * iComplex = new Symbol(Ion::Charset::IComplex); + const Symbol * pi = new Symbol(Ion::Charset::SmallPi); + const Expression * multOperands[4] = {iComplex, pi, i1->clone(), p1->clone()}; + Multiplication * mExp = new Multiplication(multOperands, 4, false); + const Expression * powOperands[2] = {exp, mExp}; + const Power * pExp = new Power(powOperands, false); + const Expression * operand[1] = {pExp}; + m->addOperands(operand, 1); + } + m->replaceOperand(i1, new Integer(r1), true); + const Expression * powerOperands[2] = {new Integer(r2), p1}; + Power * p2 = new Power(powerOperands, false); + m->replaceOperand(p1, p2, false); + + static_cast(p0->parent())->replaceOperand(p0, m, true); + return true; +} diff --git a/poincare/src/simplification/transform/rational_reduction_transform.cpp b/poincare/src/simplification/transform/rational_reduction_transform.cpp new file mode 100644 index 000000000..030466b8b --- /dev/null +++ b/poincare/src/simplification/transform/rational_reduction_transform.cpp @@ -0,0 +1,23 @@ +#include "transform.h" +#include +#include +#include +#include +#include + +bool Poincare::Simplification::RationalReductionTransform(Expression * captures[]) { + Multiplication * m = static_cast(captures[0]); + Power * p = static_cast(captures[2]); + Integer * i1 = (Integer *)(captures[1]); + Integer * i2 = (Integer *)(captures[3]); + Integer gcd = Arithmetic::GCD(i1, i2); + Integer one(1); + if (gcd.compareTo(&one) == 0) { + return false; + } + Integer * r1 = new Integer(Integer::Division(*i1, gcd).quotient); + Integer * r2 = new Integer(Integer::Division(*i2, gcd).quotient); + m->replaceOperand(i1, r1, true); + p->replaceOperand(i2, r2, true); + return true; +} diff --git a/poincare/src/simplification/transform/remove_child_transform.cpp b/poincare/src/simplification/transform/remove_child_transform.cpp new file mode 100644 index 000000000..baa925f0c --- /dev/null +++ b/poincare/src/simplification/transform/remove_child_transform.cpp @@ -0,0 +1,15 @@ +#include "transform.h" +#include +#include + +bool Poincare::Simplification::RemoveChildTransform(Expression * captures[]) { + Expression * a = captures[0]; + Expression * b = captures[1]; + + assert(a != nullptr); + assert(b != nullptr); + + static_cast(a)->removeOperand(b, true); + + return true; +} diff --git a/poincare/src/simplification/transform/remove_parenthesis_transform.cpp b/poincare/src/simplification/transform/remove_parenthesis_transform.cpp new file mode 100644 index 000000000..4ea4ee129 --- /dev/null +++ b/poincare/src/simplification/transform/remove_parenthesis_transform.cpp @@ -0,0 +1,15 @@ +#include "transform.h" +#include +#include + +bool Poincare::Simplification::RemoveParenthesisTransform(Expression * captures[]) { + assert(captures[0]->type() == Expression::Type::Parenthesis); + + Parenthesis * p = static_cast(captures[0]); + assert(p->numberOfOperands() == 1); + + static_cast(p->parent())->replaceOperand(p, const_cast(p->operand(0)), false); + delete p; + + return true; +} diff --git a/poincare/src/simplification/transform/replace_with_transform.cpp b/poincare/src/simplification/transform/replace_with_transform.cpp new file mode 100644 index 000000000..1630e37b4 --- /dev/null +++ b/poincare/src/simplification/transform/replace_with_transform.cpp @@ -0,0 +1,16 @@ +#include "transform.h" +#include +#include + +bool Poincare::Simplification::ReplaceWithTransform(Expression * captures[]) { + Expression * a = captures[0]; + Expression * b = captures[1]; + + assert(a != nullptr); + assert(a->parent() != nullptr); + assert(b != nullptr); + + static_cast(a->parent())->replaceOperand(a, const_cast(b)); + + return true; +} diff --git a/poincare/src/simplification/transform/root_transform.cpp b/poincare/src/simplification/transform/root_transform.cpp new file mode 100644 index 000000000..2c7e9cc27 --- /dev/null +++ b/poincare/src/simplification/transform/root_transform.cpp @@ -0,0 +1,32 @@ +#include "transform.h" +#include +#include +#include +#include + +namespace Poincare { + +static bool privateRootTransform(Expression * captures[], const Expression * index) { + Hierarchy * h = static_cast(captures[0]); + const Integer * minusOne = new Integer(-1); + const Expression * power0Operands[2] = {index, minusOne}; + const Power * p0 = new Power(power0Operands, false); + const Expression * power1Operands[2] = {h->operand(0), p0}; + Power * p1 = new Power(power1Operands, false); + static_cast(h->parent())->replaceOperand(h, p1, false); + h->detachOperands(); + delete h; + return true; +} + +bool Simplification::SquareRootTransform(Expression * captures[]) { + const Integer * two = new Integer(2); + return privateRootTransform(captures, two); +} + +bool Simplification::NthRootTransform(Expression * captures[]) { + Hierarchy * h = static_cast(captures[0]); + return privateRootTransform(captures, h->operand(1)); +} + +} diff --git a/poincare/src/simplification/transform/subtraction_transform.cpp b/poincare/src/simplification/transform/subtraction_transform.cpp new file mode 100644 index 000000000..5b436ff4b --- /dev/null +++ b/poincare/src/simplification/transform/subtraction_transform.cpp @@ -0,0 +1,26 @@ +#include "transform.h" +#include +#include +#include +#include +#include + +bool Poincare::Simplification::SubtractionTransform(Expression * captures[]) { + assert(captures[0]->type() == Expression::Type::Subtraction); + + Subtraction * s = static_cast(captures[0]); + assert(s->numberOfOperands() == 2); + + const Integer * minusOne = new Integer(-1); + const Expression * multiplicationOperands[2] = {s->operand(1), minusOne}; + const Multiplication * m = new Multiplication(multiplicationOperands, 2, false); + const Expression * additionOperands[2] = {s->operand(0), m}; + Addition * a = new Addition(additionOperands, 2, false); + + static_cast(s->parent())->replaceOperand(s, a, false); + + s->detachOperands(); + delete s; + + return true; +} diff --git a/poincare/src/simplification/transform/transform.h b/poincare/src/simplification/transform/transform.h new file mode 100644 index 000000000..e0b41db3f --- /dev/null +++ b/poincare/src/simplification/transform/transform.h @@ -0,0 +1,35 @@ +#ifndef POINCARE_SIMPLIFICATION_TRANSFORM_TRANSFORM_H +#define POINCARE_SIMPLIFICATION_TRANSFORM_TRANSFORM_H + +#include + +namespace Poincare { +namespace Simplification { + +// The return value tells wether the transform could be applied +using Transform = bool (*)(Expression * captures[]); + +bool RemoveParenthesisTransform(Expression * captures[]); +bool SubtractionTransform(Expression * captures[]); +bool DivisionTransform(Expression * captures[]); +bool MergeDynamicHierarchyTransform(Expression * captures[]); +bool IntegerAdditionTransform(Expression * captures[]); +bool IntegerMultiplicationTransform(Expression * captures[]); +bool PowerPowerTransform(Expression * captures[]); +bool RationalReductionTransform(Expression * captures[]); +bool RationalAdditionTransform(Expression * captures[]); +bool IntegerRationalAdditionTransform(Expression * captures[]); +bool InverseIntegerRationalAdditionTransform(Expression * captures[]); +bool InverseIntegerAdditionTransform(Expression * captures[]); +bool IntegerPowerTransform(Expression * captures[]); +bool OppositeTransform(Expression * captures[]); +bool SquareRootTransform(Expression * captures[]); +bool NthRootTransform(Expression * captures[]); +bool RationalPowerTransform(Expression * captures[]); +bool ReplaceWithTransform(Expression * captures[]); +bool RemoveChildTransform(Expression * captures[]); + +} +} + +#endif diff --git a/poincare/src/simplification_engine.cpp b/poincare/src/simplification_engine.cpp new file mode 100644 index 000000000..b4e33d4d5 --- /dev/null +++ b/poincare/src/simplification_engine.cpp @@ -0,0 +1,22 @@ +#include + +#if MATRIX_EXACT_REDUCING + +namespace Poincare { + +Expression * SimplificationEngine::map(Expression * e, Context & context, Expression::AngleUnit angleUnit) { + assert(e->numberOfOperands() == 1 && e->operand(0)->type() == Expression::Type::Matrix); + Expression * op = e->editableOperand(0); + for (int i = 0; i < op->numberOfOperands(); i++) { + Expression * entry = op->editableOperand(i); + Expression * eCopy = e->clone(); + eCopy->replaceOperand(eCopy->editableOperand(0), entry, true); + op->replaceOperand(entry, eCopy, false); + eCopy->shallowReduce(context, angleUnit); + } + return e->replaceWith(op, true)->shallowReduce(context, angleUnit); +} + +} + +#endif diff --git a/poincare/src/simplify/Makefile b/poincare/src/simplify/Makefile deleted file mode 100644 index 15b521f6f..000000000 --- a/poincare/src/simplify/Makefile +++ /dev/null @@ -1,18 +0,0 @@ -include poincare/src/simplify/rules_generation/Makefile - -dir=poincare/src/simplify - -$(dir)/rules.cpp: $(RULEGEN) $(dir)/rules.pr - @echo "RULEGEN $@" - @$(RULEGEN) < $(dir)/rules.pr > $@ - -products += $(dir)/rules.cpp - -objs += $(addprefix $(dir)/,\ - expression_builder.o\ - expression_match.o\ - expression_selector.o\ - rules.o\ - simplification.o\ - simplification_generator.o\ -) diff --git a/poincare/src/simplify/expression_builder.cpp b/poincare/src/simplify/expression_builder.cpp deleted file mode 100644 index 06d8975dd..000000000 --- a/poincare/src/simplify/expression_builder.cpp +++ /dev/null @@ -1,90 +0,0 @@ -#include "expression_builder.h" -#include -#include -#include -#include -extern "C" { -#include -} - -namespace Poincare { - -Expression * ExpressionBuilder::build(ExpressionMatch matches[]) { - Expression * children_expressions[255]; // FIXME: <- The sized can be given by the compiler - //Expression * children_expressions = malloc; - // That malloc can be avoided: we can give an upper bound - // Afer we've finished processing the rules - // That upper bound is likely to be very small (3, currently) - int numberOfChildrenExpressions = 0; - - for (int i=0; ichild(i); - if (child->m_action == ExpressionBuilder::Action::BringUpWildcard) { - for (int j=0; jm_matchIndex].numberOfExpressions(); j++) { - children_expressions[numberOfChildrenExpressions++] = - matches[child->m_matchIndex].expression(j)->clone(); - } - } else { - children_expressions[numberOfChildrenExpressions++] = child->build(matches); - } - } - - Expression * result = nullptr; - switch(m_action) { - case ExpressionBuilder::Action::BuildFromType: - switch(m_expressionType) { - case Expression::Type::Addition: - /* The children do not need to be cloned as they already have been - * before. */ - result = new Addition(children_expressions, false); - break; - case Expression::Type::Multiplication: - /* The children do not need to be cloned as they already have been - * before. */ - result = new Multiplication(children_expressions, false); - break; - default: - assert(false); - break; - } - break; - case ExpressionBuilder::Action::BuildFromTypeAndValue: - switch(m_expressionType) { - case Expression::Type::Integer: - result = new Integer(m_integerValue); - break; - case Expression::Type::Symbol: - result = new Symbol(m_symbolName); - break; - default: - assert(false); - break; - } - break; - case ExpressionBuilder::Action::BringUpWildcard: - // Build should never be called on BringUpWildcard action directly - assert(false); - break; - case ExpressionBuilder::Action::Clone: - // It only makes sense to clone if the match has a single expression! - assert(matches[m_matchIndex].numberOfExpressions() == 1); - result = matches[m_matchIndex].expression(0)->clone(); - break; - case ExpressionBuilder::Action::CallExternalGenerator: - result = m_generator(children_expressions, numberOfChildrenExpressions); - break; - } - return result; -} - -ExpressionBuilder * ExpressionBuilder::child(int index) { - assert(index>=0 && indexchild(index-1); - return previousChild+previousChild->m_numberOfChildren+1; // Pointer arithm. - } -} - -} diff --git a/poincare/src/simplify/expression_builder.h b/poincare/src/simplify/expression_builder.h deleted file mode 100644 index 709e8b4a2..000000000 --- a/poincare/src/simplify/expression_builder.h +++ /dev/null @@ -1,131 +0,0 @@ -#ifndef POINCARE_SIMPLIFY_EXPRESSION_BUILDER_H -#define POINCARE_SIMPLIFY_EXPRESSION_BUILDER_H - -#include -#include "expression_match.h" -extern "C" { -#include -} - -namespace Poincare { - -class ExpressionBuilder { -public: - static constexpr ExpressionBuilder BuildFromType(Expression::Type type, uint8_t numberOfChildren); - static constexpr ExpressionBuilder BuildFromTypeAndValue(Expression::Type type, int32_t value, uint8_t numberOfChildren); - static constexpr ExpressionBuilder Clone(uint8_t matchIndex, uint8_t numberOfChildren); - static constexpr ExpressionBuilder BringUpWildcard(uint8_t matchIndex, uint8_t numberOfChildren); - typedef Expression * (ExternalGenerator)(Expression ** parameters, int numberOfParameters); - static constexpr ExpressionBuilder CallExternalGenerator(ExternalGenerator * generator, uint8_t numberOfChildren); -public: - Expression * build(ExpressionMatch matches[]); - -private: - enum class Action { - BuildFromType, - BuildFromTypeAndValue, - Clone, - BringUpWildcard, - CallExternalGenerator - }; - - constexpr ExpressionBuilder(Expression::Type type, uint8_t numberOfChildren); - constexpr ExpressionBuilder(Expression::Type type, int32_t integerValue, uint8_t numberOfChildren); - constexpr ExpressionBuilder(Action action, uint8_t matchIndex, uint8_t numberOfChildren); - constexpr ExpressionBuilder(ExternalGenerator * generator, uint8_t numberOfChildren); - ExpressionBuilder * child(int index); - - Action m_action; - union { - // m_action == BuildFromType and BuildFromTypeAndValue - struct { - Expression::Type m_expressionType; - union { - // m_expressionType == Integer - int32_t m_integerValue; - // m_expressionType == Symbol - char m_symbolName; - }; - }; - // m_action == Clone or BringUpWildcard - uint8_t m_matchIndex; - // m_action == CallExternalGenerator - ExternalGenerator * m_generator; - }; - uint8_t m_numberOfChildren; -}; - -/* Since they have to be evaluated at compile time, constexpr functions are - * implicitely defined inline. Therefore we have to provide their implementation - * in this header. */ - -constexpr ExpressionBuilder ExpressionBuilder::BuildFromType( - Expression::Type type, - uint8_t numberOfChildren) { - return ExpressionBuilder(type, numberOfChildren); -} - -constexpr ExpressionBuilder ExpressionBuilder::BuildFromTypeAndValue( - Expression::Type type, - int32_t value, - uint8_t numberOfChildren) { - return ExpressionBuilder(type, value, numberOfChildren); -} - -constexpr ExpressionBuilder ExpressionBuilder::Clone( - uint8_t matchIndex, - uint8_t numberOfChildren) { - return ExpressionBuilder(ExpressionBuilder::Action::Clone, matchIndex, numberOfChildren); -} - -constexpr ExpressionBuilder ExpressionBuilder::BringUpWildcard( - uint8_t matchIndex, - uint8_t numberOfChildren) { - return ExpressionBuilder(ExpressionBuilder::Action::BringUpWildcard, matchIndex, numberOfChildren); -} - -constexpr ExpressionBuilder ExpressionBuilder::CallExternalGenerator( - ExternalGenerator * generator, - uint8_t numberOfChildren) { - return ExpressionBuilder(generator, numberOfChildren); -} - -constexpr ExpressionBuilder::ExpressionBuilder(Expression::Type type, - uint8_t numberOfChildren) - : - m_action(ExpressionBuilder::Action::BuildFromType), - m_expressionType(type), - m_integerValue(0), - m_numberOfChildren(numberOfChildren) { -} - -constexpr ExpressionBuilder::ExpressionBuilder(Expression::Type type, - int32_t integerValue, - uint8_t numberOfChildren) - : - m_action(ExpressionBuilder::Action::BuildFromTypeAndValue), - m_expressionType(type), - m_integerValue(integerValue), - m_numberOfChildren(numberOfChildren) { -} - -constexpr ExpressionBuilder::ExpressionBuilder(Action action, - uint8_t matchIndex, - uint8_t numberOfChildren) - : - m_action(action), - m_matchIndex(matchIndex), - m_numberOfChildren(numberOfChildren) { -} - -constexpr ExpressionBuilder::ExpressionBuilder(ExternalGenerator * generator, - uint8_t numberOfChildren) - : - m_action(ExpressionBuilder::Action::CallExternalGenerator), - m_generator(generator), - m_numberOfChildren(numberOfChildren) { -} - -} - -#endif diff --git a/poincare/src/simplify/expression_match.cpp b/poincare/src/simplify/expression_match.cpp deleted file mode 100644 index a0ba47ee4..000000000 --- a/poincare/src/simplify/expression_match.cpp +++ /dev/null @@ -1,47 +0,0 @@ -#include "expression_match.h" - -extern "C" { -#include -#include -} - -namespace Poincare { - -ExpressionMatch::ExpressionMatch() { - m_numberOfExpressions = 0; - m_expressions = nullptr; -} - -ExpressionMatch::ExpressionMatch(const Expression ** expressions, int numberOfExpressions) { - m_numberOfExpressions = numberOfExpressions; - m_expressions = (const Expression**) malloc(m_numberOfExpressions * sizeof(Expression*)); - for (int i(0); i -extern "C" { -#include -} - -namespace Poincare { - -class ExpressionMatch { -public: - ExpressionMatch(); - ExpressionMatch(const Expression ** expressions, int numberOfExpressions); - ~ExpressionMatch(); - const Expression * expression(int i); - int numberOfExpressions(); - ExpressionMatch& operator=(ExpressionMatch&& other); -private: - const Expression ** m_expressions; - int m_numberOfExpressions; -}; - -} - -#endif diff --git a/poincare/src/simplify/expression_selector.cpp b/poincare/src/simplify/expression_selector.cpp deleted file mode 100644 index cb169c627..000000000 --- a/poincare/src/simplify/expression_selector.cpp +++ /dev/null @@ -1,332 +0,0 @@ -extern "C" { -#include -#include -} - -#include -#include -#include "expression_selector.h" - -namespace Poincare { - -const uint8_t kUnmatched = 0xff; - -int ExpressionSelector::numberOfNonWildcardChildren() { - bool hasWildcard = child(m_numberOfChildren-1)->m_match == ExpressionSelector::Match::Wildcard; - int numberOfChildren = m_numberOfChildren; - if (hasWildcard) { - numberOfChildren--; - } - return numberOfChildren; -} - -int ExpressionSelector::match(const Expression * e, ExpressionMatch * matches) { - return this->match(e, matches, 0); -} - -int ExpressionSelector::match(const Expression * e, ExpressionMatch * matches, int offset) { - int numberOfMatches = 0; - - // Does the current node match? - switch(m_match) { - case ExpressionSelector::Match::Any: - // Yes, it always does! - break; - case ExpressionSelector::Match::Type: - if (e->type() != m_expressionType) { - return 0; - } - break; - case ExpressionSelector::Match::TypeAndValue: - if (e->type() != m_expressionType) { - return 0; - } - switch(e->type()) { - case Expression::Type::Integer: - { - Integer integer = Integer(m_integerValue); - if (!e->valueEquals(&integer)) { - return 0; - } - } - break; - case Expression::Type::Symbol: - { - Symbol symbol = Symbol(m_symbolName); - if (!e->valueEquals(&symbol)) { - return 0; - } - } - break; - default: - // Symbol and Integer are the only expressions which should be matched - // with a value. - assert(false); - break; - } - break; - case ExpressionSelector::Match::Wildcard: - /* This should not happen as a wildcard should be matched _before_ */ - assert(false); - break; - case ExpressionSelector::Match::SameAs: - /* Here we assume that the match can only have a single child, as we - * don't want to match on wildcards. */ - assert(matches[m_integerValue].numberOfExpressions() == 1); - if (!e->isEquivalentTo((Expression *)matches[m_sameAsPosition].expression(0))) { - return 0; - } - break; - } - - // The current node does match. Let's add it to our matches - matches[offset + numberOfMatches++] = ExpressionMatch(&e, 1); - - if (m_numberOfChildren != 0) { - int numberOfChildMatches = 0; - if (!e->isCommutative()) { - numberOfChildMatches = sequentialMatch(e, matches, offset+numberOfMatches); - } else { - numberOfChildMatches = commutativeMatch(e, matches, offset+numberOfMatches); - } - // We check whether the children matched or not. - if (numberOfChildMatches == 0) { - return 0; - } else { - numberOfMatches += numberOfChildMatches; - } - } - - return numberOfMatches; -} - -/* This tries to match the children selector sequentialy */ -int ExpressionSelector::sequentialMatch(const Expression * e, - ExpressionMatch * matches, int offset) { - int numberOfMatches = 0; - for (int i=0; ioperand(i); - - if (childSelector->m_match == ExpressionSelector::Match::Wildcard) { - assert(false); // There should not be a wildcard for non commutative op. - } else { - int numberOfChildMatches = childSelector->match( - childExpression, - matches, - offset+numberOfMatches); - if (numberOfChildMatches == 0) { - return 0; - } else { - numberOfMatches += numberOfChildMatches; - } - } - } - return numberOfMatches; -} - -/* This iterates over the combinations of possible matches in the children of - * a selector and then writes the output ExpressionMatch to matches just like - * match would do. - */ -int ExpressionSelector::commutativeMatch(const Expression * e, ExpressionMatch * matches, int offset) { - // If we have more children to match than the expression has, we cannot match. - if (e->numberOfOperands() < m_numberOfChildren) { - return 0; - } - - bool hasWildcard = child(m_numberOfChildren-1)->m_match == ExpressionSelector::Match::Wildcard; - uint8_t * selectorMatched = (uint8_t *)malloc(e->numberOfOperands()*sizeof(uint8_t)); - - /* Initialize the selectors matched to unmatched (0xff), here we assume that - * we never have more than 255 direct children selector. */ - assert(this->m_numberOfChildrennumberOfOperands(); i++) { - selectorMatched[i] = kUnmatched; - } - - /* TODO: This one's size can be determined by the compiler as this is the - * maximum number of direct children selectors. */ - uint8_t expressionMatched[5]; - - /* If we have a wildcard we do not want to try to match it to an expression - * yet. */ - int numberOfChildren = this->numberOfNonWildcardChildren(); - - if (!canCommutativelyMatch(e, matches, selectorMatched, numberOfChildren, offset)) { - free(selectorMatched); - return 0; - } - - /* Note that we need this indirection because the order in which the selectors - * are defined is significant. - * - * We can see that in the following example: - * Here we try to match: - * + - * / \ - * + w - * / - * w - * Onto: - * + - * / \ - * 4 + - * / \ - * 2 3 - * - * Since + is commutative they do match which is nice. - * The issue here is that the matches are expected in a certain order by the - * builder, here the order expected is: - * 0 - * / \ - * 1 3 - * / - * 2 - * But The nodes of the expressions are matched in this order: - * 0 - * / \ - * 1 2 - * \ - * 3 - * Which is not what we want, thus remembering which selector matched which - * expression allows us to put the match in the proper order. - * We got an expression -> selector relationship, here we are just inversting - * it to get the selector -> expression relationship that we want. */ - for (int i = 0; inumberOfOperands(); i++) { - if (selectorMatched[i] != kUnmatched) { - expressionMatched[selectorMatched[i]] = i; - } - } - - /* Here we recursively write the matches of each selector in the matches - * table. - * - * Using the example in the previous comment we would write - * | + | + | (Integer(2),Integer(3)) | Integer(4) | - * - * The pointer arithmetic with numberOfMatches, allows us to know how many - * matches a selector has written. - * Using the previous example, the part with - * + - * | - * w - * would write two matches: - * + and (Integer(2), Integer(3)) - * whereas: - * w - * would only write one: - * Integer(4) */ - int numberOfMatches = 0; - for (int i(0); imatch( - e->operand(expressionMatched[i]), - matches, - offset + numberOfMatches); - assert(numberOfChildMatches > 0); - numberOfMatches += numberOfChildMatches; - } - - if (hasWildcard) { - /* We allocate a table of Expression* the size of the number of unmatched - * operands. */ - const Expression ** local_expr = (const Expression**) malloc((e->numberOfOperands() - numberOfChildren) * sizeof(Expression*)); - int j = 0; - for (int i(0); inumberOfOperands(); i++) { - // if the expression was not matched, give it to the wildcard. - if (selectorMatched[i] == kUnmatched) { - local_expr[j++] = e->operand(i); - } - } - matches[offset + numberOfMatches++] = ExpressionMatch(local_expr, j); - free(local_expr); - } - - free(selectorMatched); - return numberOfMatches; -} - -/* This helper function finds whether there is a commutative match or not. - * The output is the selector matched for each expression, written in the - * selectorMatched table. - * The matches table will be written to, but will not overwrite any previous - * data so this is ok. - * leftToMatch tells it how many selectors still have to be matched. - * Implementation detail: selectors are matched in ascending order. - */ -bool ExpressionSelector::canCommutativelyMatch(const Expression * e, - ExpressionMatch * matches, - uint8_t * selectorMatched, - int leftToMatch, - int offset) { - bool hasWildcard = child(m_numberOfChildren-1)->m_match == ExpressionSelector::Match::Wildcard; - - // This part is used to make sure that we stop once we matched everything. - if (leftToMatch == 0) { - if (hasWildcard) { - return true; - } else { - /* We check that we matched all the children expressions. - * This can happen if the selector does not have a wildcard and we matched all - * the selectors, but there are still unmatched children expression. - * An example is the expression: - * + - * / | \ - * 1 2 3 - * Which would otherwise be matched by - * + - * / \ - * / \ - * Int Int - */ - int matchedChildren = 0; - for (int i = 0; inumberOfOperands(); i++) { - if (selectorMatched[i] != kUnmatched) { - matchedChildren++; - } - } - if (matchedChildren == e->numberOfOperands()) { - return true; - } else { - return false; - } - } - } - - // We try to match the i-th child selector. - int i = numberOfNonWildcardChildren() - leftToMatch; - for (int j = 0; jnumberOfOperands(); j++) { - // If the child has already been matched, we skip it. - if (selectorMatched[j] != kUnmatched) { - continue; - } - int numberOfMatches = child(i)->match(e->operand(j), matches, offset); - if (numberOfMatches) { - // We managed to match this selector. - selectorMatched[j] = i; - /* We check that we can match the rest in this configuration, if so we - * are good. */ - if (this->canCommutativelyMatch(e, matches, selectorMatched, leftToMatch - 1, offset + numberOfMatches)) { - return true; - } - // Otherwise we backtrack. - selectorMatched[j] = kUnmatched; - } - } - - return false; -} - -/* Extrude in a class impossible otherwise ExpressionBuilder is not aggregate - * and cannot be initialized statically. */ -ExpressionSelector * ExpressionSelector::child(int index) { - assert(index>=0 && indexchild(index-1); - return previousChild+previousChild->m_numberOfChildren+1; // Pointer arithm. - } -} - -} diff --git a/poincare/src/simplify/expression_selector.h b/poincare/src/simplify/expression_selector.h deleted file mode 100644 index d4fdb701f..000000000 --- a/poincare/src/simplify/expression_selector.h +++ /dev/null @@ -1,104 +0,0 @@ -#ifndef POINCARE_SIMPLIFY_EXPRESSION_SELECTOR_H -#define POINCARE_SIMPLIFY_EXPRESSION_SELECTOR_H - -#include -#include "expression_match.h" -extern "C" { -#include -} - -namespace Poincare { - -class ExpressionSelector { -public: - static constexpr ExpressionSelector Any(uint8_t numberOfChildren); - static constexpr ExpressionSelector Wildcard(uint8_t numberOfChildren); - static constexpr ExpressionSelector SameAs(int index, uint8_t numberOfChildren); - static constexpr ExpressionSelector Type(Expression::Type type, uint8_t numberOfChildren); - static constexpr ExpressionSelector TypeAndValue(Expression::Type type, int32_t value, uint8_t numberOfChildren); - - /* The match function is interesting - * - It returns 0 if the selector didn't match the expression - * - Otherwise, it returns the number of captured matches and sets *matches - * Caution: This function *will* write to *matches even if the returned - * value is zero. - */ - int match(const Expression * e, ExpressionMatch * matches); -private: - enum class Match { - Any, - Type, - Wildcard, - TypeAndValue, - SameAs, - }; - - int match(const Expression * e, ExpressionMatch * matches, int offset); - - constexpr ExpressionSelector(Match match, Expression::Type type, int32_t integerValue, uint8_t numberOfChildren); - - int numberOfNonWildcardChildren(); - bool canCommutativelyMatch(const Expression * e, ExpressionMatch * matches, - uint8_t * selectorMatched, int leftToMatch, int offset); - int commutativeMatch(const Expression * e, ExpressionMatch * matches, int offset); - int sequentialMatch(const Expression * e, ExpressionMatch * matches, int offset); - ExpressionSelector * child(int index); - - Match m_match; - union { - // m_match == Any - // nothing! - // m_match == ExpressionType - struct { - Expression::Type m_expressionType; - union { - // m_expressionType == Integer - int32_t m_integerValue; - // m_expressionType == Symbol - char m_symbolName; - // Position of the other match we must be equal to. - int32_t m_sameAsPosition; - }; - }; - }; - uint8_t m_numberOfChildren; -}; - -/* Since they have to be evaluated at compile time, constexpr functions are - * implicitely defined inline. Therefore we have to provide their implementation - * in this header. */ - -constexpr ExpressionSelector ExpressionSelector::Any(uint8_t numberOfChildren) { - return ExpressionSelector(Match::Any, (Expression::Type)0, 0, numberOfChildren); -} - -constexpr ExpressionSelector ExpressionSelector::Wildcard(uint8_t numberOfChildren) { - return ExpressionSelector(Match::Wildcard, (Expression::Type)0, 0, numberOfChildren); -} - -constexpr ExpressionSelector ExpressionSelector::SameAs(int index, uint8_t numberOfChildren) { - return ExpressionSelector(Match::SameAs, (Expression::Type)0, index, numberOfChildren); -} - -constexpr ExpressionSelector ExpressionSelector::Type(Expression::Type type, uint8_t numberOfChildren) { - return ExpressionSelector(Match::Type, type, 0, numberOfChildren); -} - -constexpr ExpressionSelector ExpressionSelector::TypeAndValue(Expression::Type type, int32_t value, uint8_t numberOfChildren) { - return ExpressionSelector(Match::TypeAndValue, type, value, numberOfChildren); -} - -constexpr ExpressionSelector::ExpressionSelector(Match match, - Expression::Type type, - int32_t integerValue, - uint8_t numberOfChildren) - : - m_match(match), - m_expressionType(type), - m_integerValue(integerValue), - m_numberOfChildren(numberOfChildren) { -} - -} - -#endif diff --git a/poincare/src/simplify/rules.pr b/poincare/src/simplify/rules.pr deleted file mode 100644 index 36f342187..000000000 --- a/poincare/src/simplify/rules.pr +++ /dev/null @@ -1,20 +0,0 @@ -Addition(Addition(a*),b*)->Addition(a*,b*); -Addition(Integer.a,Integer.b)->$AddIntegers(a,b); -Addition(Integer.a,Integer.b,c*)->Addition($AddIntegers(a,b),c*); - -Subtraction(a,b)->Addition(a,Multiplication(b,Integer[-1])); -Addition(a, Multiplication(a,Integer[-1]))->Integer[0]; -Addition(a, Multiplication(a,Integer[-1]), b)->b; -Addition(a, Multiplication(a,Integer[-1]), b, c*)->Addition(b,c*); - -Addition(a,a,b*)->Addition(Multiplication(a,Integer[2]),b*); -Addition(a,Multiplication(a,b),c*)->Addition(Multiplication(a,Addition(b,Integer[1])),c*); -Addition(Multiplication(a,b),Multiplication(a,c),d*)->Addition(Multiplication(a,Addition(b,c)),d*); -Addition(a,a)->Multiplication(a,Integer[2]); -Addition(a,Multiplication(a,b))->Multiplication(a,Addition(b,Integer[1])); -Addition(Multiplication(a,b),Multiplication(a,c))->Multiplication(a,Addition(b,c)); - -Multiplication(Multiplication(a*),b*)->Multiplication(a*,b*); -Multiplication(Integer[0],a*)->Integer[0]; -Multiplication(Integer.a,Integer.b)->$MultiplyIntegers(a,b); -Multiplication(Integer.a,Integer.b,c*)->Multiplication($MultiplyIntegers(a,b),c*); diff --git a/poincare/src/simplify/rules_generation/Makefile b/poincare/src/simplify/rules_generation/Makefile deleted file mode 100644 index 9ac631d24..000000000 --- a/poincare/src/simplify/rules_generation/Makefile +++ /dev/null @@ -1,34 +0,0 @@ -dir=poincare/src/simplify/rules_generation - -rulegen_objs := $(addprefix $(dir)/,\ - rules_parser.o\ - rules_lexer.o\ - node.o\ - rule.o\ -) - -$(dir)/rules_parser.cpp: $(dir)/rules_parser.y - @echo "BISON $@" - @bison --defines=poincare/src/simplify/rules_generation/rules_tokens.h $< -o $@ - -$(dir)/rules_lexer.cpp: $(dir)/rules_lexer.l $(dir)/rules_parser.cpp - @echo "FLEX $@" - @flex -o $@ $< - -RULEGEN := $(dir)/rulegen - -products += $(rulegen_objs) $(RULEGEN) $(addprefix $(dir)/,\ - rules_tokens.h\ - rules_parser.cpp\ - rules_lexer.cpp\ -) - -GENERATOR_CXXFLAGS = -std=c++11 -Wno-deprecated-register - -$(rulegen_objs): %.o: %.cpp - @echo "HOSTCC $@" - @$(HOSTCXX) $(GENERATOR_CXXFLAGS) -c $< -o $@ - -$(RULEGEN): $(rulegen_objs) - @echo "HOSTLD $@" - @$(HOSTCXX) $(rulegen_objs) -o $@ diff --git a/poincare/src/simplify/rules_generation/node.h b/poincare/src/simplify/rules_generation/node.h deleted file mode 100644 index 08d8d1ee5..000000000 --- a/poincare/src/simplify/rules_generation/node.h +++ /dev/null @@ -1,49 +0,0 @@ -#ifndef POINCARE_SIMPLIFY_RULES_GENERATION_NODE_H -#define POINCARE_SIMPLIFY_RULES_GENERATION_NODE_H - -class Rule; - -#include -#include - -class Node { -public: - enum class Type { - Expression, - Generator, - Any - }; - enum class ReferenceMode { - None, - SingleNode, - Wildcard - }; - - // Creating Nodes - Node(Type type, std::string * typeName = nullptr); - ~Node(); - void setReference(ReferenceMode mode, std::string * referenceName); - void setValue(std::string * value); - void setChildren(std::vector * children); - - int totalDescendantCountIncludingSelf(); - int flatIndexOfChildNamed(std::string name); - int flatIndexOfChildRef(Node * node); - - void generateSelectorTree(Rule * context); - void generateBuilderTree(Rule * context); -private: - int generateTree(bool selector, Rule * context, int index, int indentationLevel); - std::string generateSelectorConstructor(Rule * context); - std::string generateBuilderConstructor(Rule * context); - - Type m_type; - std::string * m_typeName; - ReferenceMode m_referenceMode; - std::string * m_referenceName; - std::string * m_value; - std::vector * m_children; - Node * m_parent; -}; - -#endif diff --git a/poincare/src/simplify/rules_generation/rule.cpp b/poincare/src/simplify/rules_generation/rule.cpp deleted file mode 100644 index 176d3bde8..000000000 --- a/poincare/src/simplify/rules_generation/rule.cpp +++ /dev/null @@ -1,25 +0,0 @@ -#include "rule.h" -#include - -Rule::Rule(Node * selector, Node * builder) : - m_selector(selector), m_builder(builder) { -} - -Rule::~Rule() { - delete m_builder; - delete m_selector; -} - -Node * Rule::selector() { - return m_selector; -} - -void Rule::generate(std::string rule_name) { - std::cout << "constexpr ExpressionSelector " << rule_name << "Selector[" << m_selector->totalDescendantCountIncludingSelf() << "] = {" << std::endl; - m_selector->generateSelectorTree(this); - std::cout << "};" << std::endl; - - std::cout << "constexpr ExpressionBuilder " << rule_name << "Builder[" << m_builder->totalDescendantCountIncludingSelf() << "] = {" << std::endl; - m_builder->generateBuilderTree(this); - std::cout << "};" << std::endl; -} diff --git a/poincare/src/simplify/rules_generation/rule.h b/poincare/src/simplify/rules_generation/rule.h deleted file mode 100644 index f6c57ae6f..000000000 --- a/poincare/src/simplify/rules_generation/rule.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef POINCARE_SIMPLIFY_RULES_GENERATION_RULE_H -#define POINCARE_SIMPLIFY_RULES_GENERATION_RULE_H - -#include "node.h" - -class Rule { -public: - Rule(Node * selector, Node * builder); - ~Rule(); - void generate(std::string rule_name); - Node * selector(); -private: - Node * m_selector; - Node * m_builder; -}; - -#endif diff --git a/poincare/src/simplify/simplification.cpp b/poincare/src/simplify/simplification.cpp deleted file mode 100644 index f751b62c2..000000000 --- a/poincare/src/simplify/simplification.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include "simplification.h" - -namespace Poincare { - -Expression * Simplification::simplify(Expression * expression) const { - ExpressionMatch matches[255]; // FIXME: The size ca be given by our compiler - if (m_selector->match(expression, matches)) { - return m_builder->build(matches); - } else { - return nullptr; - } -} - -} diff --git a/poincare/src/simplify/simplification.h b/poincare/src/simplify/simplification.h deleted file mode 100644 index d17598dbe..000000000 --- a/poincare/src/simplify/simplification.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef POINCARE_SIMPLIFY_SIMPLIFICATION_H -#define POINCARE_SIMPLIFY_SIMPLIFICATION_H - -#include -#include "expression_selector.h" -#include "expression_builder.h" - -namespace Poincare { - -class Simplification { -public: - constexpr Simplification(ExpressionSelector * m_selector, ExpressionBuilder * m_builder); - Expression * simplify(Expression * expression) const; -private: - ExpressionSelector * m_selector; - ExpressionBuilder * m_builder; -}; - -constexpr Simplification::Simplification( - ExpressionSelector * selector, - ExpressionBuilder * builder) - : - m_selector(selector), - m_builder(builder) { -} - -} - -#endif diff --git a/poincare/src/simplify/simplification_generator.cpp b/poincare/src/simplify/simplification_generator.cpp deleted file mode 100644 index 5f58accd7..000000000 --- a/poincare/src/simplify/simplification_generator.cpp +++ /dev/null @@ -1,33 +0,0 @@ -#include "simplification_generator.h" -#include -extern "C" { -#include -} - -namespace Poincare { - -Expression * SimplificationGenerator::AddIntegers(Expression ** parameters, int numberOfParameters) { - Integer * result = new Integer((native_int_t)0); - for (int i=0; itype() == Expression::Type::Integer); - *result = result->add(*(Integer *)parameters[i]); - /* Note We have to delete the parameters as they have been cloned before and - * we are the last ones to use them here. */ - delete parameters[i]; - } - return result; -} - -Expression * SimplificationGenerator::MultiplyIntegers(Expression ** parameters, int numberOfParameters) { - Integer * result = new Integer((native_int_t)1); - for (int i=0; itype() == Expression::Type::Integer); - *result = result->multiply_by(*(Integer *)parameters[i]); - /* Note We have to delete the parameters as they have been cloned before and - * we are the last ones to use them here. */ - delete parameters[i]; - } - return result; -} - -} diff --git a/poincare/src/simplify/simplification_generator.h b/poincare/src/simplify/simplification_generator.h deleted file mode 100644 index e2f62e9fc..000000000 --- a/poincare/src/simplify/simplification_generator.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef POINCARE_SIMPLIFY_SIMPLIFICATION_GENERATOR_H -#define POINCARE_SIMPLIFY_SIMPLIFICATION_GENERATOR_H - -#include - -namespace Poincare { - -class SimplificationGenerator { -public: - static Expression * AddIntegers(Expression ** parameters, int numberOfParameters); - static Expression * MultiplyIntegers(Expression ** parameters, int numberOfParameters); -}; - -} - -#endif diff --git a/poincare/src/sine.cpp b/poincare/src/sine.cpp index bd3a6db68..5b45acd0e 100644 --- a/poincare/src/sine.cpp +++ b/poincare/src/sine.cpp @@ -1,7 +1,11 @@ #include +#include #include #include #include +#include +#include +#include extern "C" { #include } @@ -9,25 +13,31 @@ extern "C" { namespace Poincare { -Sine::Sine() : - Function("sin") -{ -} - Expression::Type Sine::type() const { return Expression::Type::Sine; } -Expression * Sine::cloneWithDifferentOperands(Expression** newOperands, - int numberOfOperands, bool cloneOperands) const { - assert(newOperands != nullptr); - Sine * s = new Sine(); - s->setArgument(newOperands, numberOfOperands, cloneOperands); - return s; +Expression * Sine::clone() const { + Sine * a = new Sine(m_operands, true); + return a; +} + +Expression * Sine::shallowReduce(Context& context, AngleUnit angleUnit) { + Expression * e = Expression::shallowReduce(context, angleUnit); + if (e != this) { + return e; + } +#if MATRIX_EXACT_REDUCING + Expression * op = editableOperand(0); + if (op->type() == Type::Matrix) { + return SimplificationEngine::map(this, context, angleUnit); + } +#endif + return Trigonometry::shallowReduceDirectFunction(this, context, angleUnit); } template -Complex Sine::compute(const Complex c, AngleUnit angleUnit) { +Complex Sine::computeOnComplex(const Complex c, AngleUnit angleUnit) { if (c.b() == 0) { T input = c.a(); if (angleUnit == AngleUnit::Degree) { @@ -42,7 +52,7 @@ Complex Sine::compute(const Complex c, AngleUnit angleUnit) { return Complex::Float(result); } Complex arg = Complex::Cartesian(-c.b(), c.a()); - Complex sinh = HyperbolicSine::compute(arg); + Complex sinh = HyperbolicSine::computeOnComplex(arg, angleUnit); return Multiplication::compute(Complex::Cartesian(0, -1), sinh); } diff --git a/poincare/src/square_root.cpp b/poincare/src/square_root.cpp index f40887383..5d67ad83a 100644 --- a/poincare/src/square_root.cpp +++ b/poincare/src/square_root.cpp @@ -1,43 +1,58 @@ #include #include #include +#include #include "layout/nth_root_layout.h" extern "C" { #include } #include +#include namespace Poincare { -SquareRoot::SquareRoot() : - Function("squareRoot") -{ -} - Expression::Type SquareRoot::type() const { return Type::SquareRoot; } -Expression * SquareRoot::cloneWithDifferentOperands(Expression** newOperands, - int numberOfOperands, bool cloneOperands) const { - assert(newOperands != nullptr); - SquareRoot * sr = new SquareRoot(); - sr->setArgument(newOperands, numberOfOperands, cloneOperands); - return sr; +Expression * SquareRoot::clone() const { + SquareRoot * a = new SquareRoot(m_operands, true); + return a; } -ExpressionLayout * SquareRoot::privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const { - assert(floatDisplayMode != FloatDisplayMode::Default); - assert(complexFormat != ComplexFormat::Default); - return new NthRootLayout(m_args[0]->createLayout(floatDisplayMode, complexFormat),nullptr); +static_assert('\x90' == Ion::Charset::Root, "Unicode error"); +int SquareRoot::writeTextInBuffer(char * buffer, int bufferSize) const { + return LayoutEngine::writePrefixExpressionTextInBuffer(this, buffer, bufferSize, "\x90"); } template -Complex SquareRoot::templatedComputeComplex(const Complex c) const { +Complex SquareRoot::computeOnComplex(const Complex c, AngleUnit angleUnit) { if (c.b() == 0 && c.a() >= 0) { return Complex::Float(std::sqrt(c.a())); } return Power::compute(c, Complex::Float(0.5)); } +Expression * SquareRoot::shallowReduce(Context& context, AngleUnit angleUnit) { + Expression * e = Expression::shallowReduce(context, angleUnit); + if (e != this) { + return e; + } +#if MATRIX_EXACT_REDUCING + if (operand(0)->type() == Type::Matrix) { + return SimplificationEngine::map(this, context, angleUnit); + } +#endif + Power * p = new Power(operand(0), new Rational(1, 2), false); + detachOperands(); + replaceWith(p, true); + return p->shallowReduce(context, angleUnit); +} + +ExpressionLayout * SquareRoot::privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const { + assert(floatDisplayMode != FloatDisplayMode::Default); + assert(complexFormat != ComplexFormat::Default); + return new NthRootLayout(operand(0)->createLayout(floatDisplayMode, complexFormat),nullptr); +} + } diff --git a/poincare/src/static_hierarchy.cpp b/poincare/src/static_hierarchy.cpp new file mode 100644 index 000000000..62ec51d82 --- /dev/null +++ b/poincare/src/static_hierarchy.cpp @@ -0,0 +1,91 @@ +#include +extern "C" { +#include +} + +namespace Poincare { + +template +StaticHierarchy::StaticHierarchy() : + Hierarchy(), + m_operands{} +{ +} + +template +StaticHierarchy::StaticHierarchy(const Expression * const * operands, bool cloneOperands) : + Hierarchy() +{ + build(operands, T, cloneOperands); +} + +template<> +StaticHierarchy<1>::StaticHierarchy(const Expression * e, bool cloneOperands) : + StaticHierarchy((Expression **)&e, cloneOperands) +{ +} + +template<> +StaticHierarchy<2>::StaticHierarchy(const Expression * e1, const Expression * e2, bool cloneOperands) : + StaticHierarchy(ExpressionArray(e1, e2), cloneOperands) +{ +} + +template +StaticHierarchy::~StaticHierarchy() { + for (int i = 0; i < T; i++) { + if (m_operands[i] != nullptr) { + delete m_operands[i]; + } + } +} + +template +void StaticHierarchy::setArgument(ListData * listData, int numberOfOperands, bool clone) { + build(listData->operands(), listData->numberOfOperands(), clone); +} + +template +bool StaticHierarchy::hasValidNumberOfOperands(int numberOfOperands) const { + return numberOfOperands == T; +} + +template +void StaticHierarchy::build(const Expression * const * operands, int numberOfOperands, bool cloneOperands) { + assert(operands != nullptr); + assert(numberOfOperands <= T); + for (int i=0; i < numberOfOperands; i++) { + assert(operands[i] != nullptr); + if (cloneOperands) { + m_operands[i] = operands[i]->clone(); + } else { + m_operands[i] = operands[i]; + } + const_cast(m_operands[i])->setParent(this); + } +} + +template +int StaticHierarchy::simplificationOrderSameType(const Expression * e) const { + for (int i = 0; i < this->numberOfOperands(); i++) { + // The NULL node is the least node type. + if (e->numberOfOperands() <= i) { + return 1; + } + if (SimplificationOrder(this->operand(i), e->operand(i)) != 0) { + return SimplificationOrder(this->operand(i), e->operand(i)); + } + } + // The NULL node is the least node type. + if (e->numberOfOperands() > numberOfOperands()) { + return -1; + } + return 0; +} + +template class Poincare::StaticHierarchy<0>; +template class Poincare::StaticHierarchy<1>; +template class Poincare::StaticHierarchy<2>; +template class Poincare::StaticHierarchy<3>; + +} diff --git a/poincare/src/store.cpp b/poincare/src/store.cpp index 533921598..ad930fac3 100644 --- a/poincare/src/store.cpp +++ b/poincare/src/store.cpp @@ -5,7 +5,6 @@ extern "C" { } #include #include -#include #include #include #include "layout/horizontal_layout.h" @@ -13,73 +12,37 @@ extern "C" { namespace Poincare { -Store::Store(Symbol * symbol, Expression * value, bool clone) : - m_symbol(symbol), - m_value(value) -{ - if (clone) { - m_symbol = (Symbol *)symbol->clone(); - m_value = value->clone(); - } -} - -Store::~Store() { - delete m_symbol; - delete m_value; -} - -bool Store::hasValidNumberOfArguments() const { - return m_value->hasValidNumberOfArguments(); -} - Expression::Type Store::type() const { return Type::Store; } -const Expression * Store::operand(int i) const { - if (i == 0) { - return m_symbol; - } - return m_value; -} - -int Store::numberOfOperands() const { - return 2; -} - Expression * Store::clone() const { - Expression * newOperands[2]; - newOperands[0] = m_symbol; - newOperands[1] = m_value; - return this->cloneWithDifferentOperands(newOperands, 2, true); + return new Store(operands(), true); } -Expression * Store::cloneWithDifferentOperands(Expression ** newOperands, int numberOfOperands, bool cloneOperands) const { - assert(numberOfOperands == 2); - assert(newOperands != nullptr); - assert(newOperands[0]->type() == Type::Symbol); - return new Store((Symbol *)newOperands[0], newOperands[1], cloneOperands); +static_assert('\x8F' == Ion::Charset::Sto, "Incorrect"); +int Store::writeTextInBuffer(char * buffer, int bufferSize) const { + return LayoutEngine::writeInfixExpressionTextInBuffer(this, buffer, bufferSize, "\x8F"); } ExpressionLayout * Store::privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const { assert(floatDisplayMode != FloatDisplayMode::Default); assert(complexFormat != ComplexFormat::Default); ExpressionLayout * childrenLayouts[3]; - childrenLayouts[0] = m_value->createLayout(floatDisplayMode, complexFormat); + childrenLayouts[0] = value()->createLayout(floatDisplayMode, complexFormat); const char stoSymbol[2] = {Ion::Charset::Sto, 0}; childrenLayouts[1] = new StringLayout(stoSymbol, 1); - childrenLayouts[2] = m_symbol->createLayout(floatDisplayMode, complexFormat); + childrenLayouts[2] = symbol()->createLayout(floatDisplayMode, complexFormat); return new HorizontalLayout(childrenLayouts, 3); } - template -Evaluation * Store::templatedEvaluate(Context& context, AngleUnit angleUnit) const { - Evaluation * valueEvaluation = m_value->evaluate(context, angleUnit); - context.setExpressionForSymbolName(valueEvaluation, m_symbol); +Expression * Store::templatedEvaluate(Context& context, AngleUnit angleUnit) const { + Expression * valueEvaluation = value()->evaluate(context, angleUnit); + context.setExpressionForSymbolName(valueEvaluation, symbol(), context); delete valueEvaluation; - if (context.expressionForSymbol(m_symbol) != nullptr) { - return context.expressionForSymbol(m_symbol)->evaluate(context, angleUnit); + if (context.expressionForSymbol(symbol()) != nullptr) { + return context.expressionForSymbol(symbol())->evaluate(context, angleUnit); } return new Complex(Complex::Float(NAN)); } diff --git a/poincare/src/subtraction.cpp b/poincare/src/subtraction.cpp index 4cbe68cd8..0d4ef8b45 100644 --- a/poincare/src/subtraction.cpp +++ b/poincare/src/subtraction.cpp @@ -4,33 +4,23 @@ extern "C" { } #include +#include +#include +#include #include +#include #include "layout/horizontal_layout.h" #include "layout/string_layout.h" #include "layout/parenthesis_layout.h" namespace Poincare { -Expression * Subtraction::cloneWithDifferentOperands(Expression** newOperands, - int numberOfOperands, bool cloneOperands) const { - assert(newOperands != nullptr); - assert(numberOfOperands == 2); - return new Subtraction(newOperands, cloneOperands); -} - Expression::Type Subtraction::type() const { return Expression::Type::Subtraction; } -ExpressionLayout * Subtraction::privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const { - assert(floatDisplayMode != FloatDisplayMode::Default); - assert(complexFormat != ComplexFormat::Default); - ExpressionLayout * children_layouts[3]; - children_layouts[0] = m_operands[0]->createLayout(floatDisplayMode, complexFormat); - char string[2] = {'-', '\0'}; - children_layouts[1] = new StringLayout(string, 1); - children_layouts[2] = m_operands[1]->type() == Type::Opposite ? new ParenthesisLayout(m_operands[1]->createLayout(floatDisplayMode, complexFormat)) : m_operands[1]->createLayout(floatDisplayMode, complexFormat); - return new HorizontalLayout(children_layouts, 3); +Expression * Subtraction::clone() const { + return new Subtraction(m_operands, true); } template @@ -38,14 +28,33 @@ Complex Subtraction::compute(const Complex c, const Complex d) { return Complex::Cartesian(c.a()-d.a(), c.b() - d.b()); } -template Evaluation * Subtraction::templatedComputeOnComplexAndComplexMatrix(const Complex * c, Evaluation * m) const { - Evaluation * operand = computeOnComplexMatrixAndComplex(m, c); - Evaluation * result = Opposite::computeOnMatrix(operand); - delete operand; +template Matrix * Subtraction::computeOnComplexAndMatrix(const Complex * c, const Matrix * m) { + Matrix * opposite = computeOnMatrixAndComplex(m, c); + if (opposite == nullptr) { + return nullptr; + } + Expression ** operands = new Expression * [opposite->numberOfRows() * opposite->numberOfColumns()]; + for (int i = 0; i < opposite->numberOfOperands(); i++) { + const Complex * entry = static_cast *>(opposite->operand(i)); + operands[i] = new Complex(Complex::Cartesian(-entry->a(), -entry->b())); + } + Matrix * result = new Matrix(operands, m->numberOfRows(), m->numberOfColumns(), false); + delete[] operands; + delete opposite; return result; } +Expression * Subtraction::shallowReduce(Context& context, AngleUnit angleUnit) { + Expression * e = Expression::shallowReduce(context, angleUnit); + if (e != this) { + return e; + } + Multiplication * m = new Multiplication(new Rational(-1), operand(1), false); + Addition * a = new Addition(operand(0), m, false); + detachOperands(); + m->shallowReduce(context, angleUnit); + replaceWith(a, true); + return a->shallowReduce(context, angleUnit); } -template Poincare::Complex Poincare::Subtraction::compute(Poincare::Complex, Poincare::Complex); -template Poincare::Complex Poincare::Subtraction::compute(Poincare::Complex, Poincare::Complex); +} diff --git a/poincare/src/sum.cpp b/poincare/src/sum.cpp index 87b432fc1..7e7e5c792 100644 --- a/poincare/src/sum.cpp +++ b/poincare/src/sum.cpp @@ -9,21 +9,17 @@ extern "C" { namespace Poincare { -Sum::Sum() : - Sequence("sum") -{ -} - Expression::Type Sum::type() const { return Type::Sum; } -Expression * Sum::cloneWithDifferentOperands(Expression** newOperands, - int numberOfOperands, bool cloneOperands) const { - assert(newOperands != nullptr); - Sum * s = new Sum(); - s->setArgument(newOperands, numberOfOperands, cloneOperands); - return s; +Expression * Sum::clone() const { + Sum * a = new Sum(m_operands, true); + return a; +} + +const char * Sum::name() const { + return "sum"; } int Sum::emptySequenceValue() const { @@ -35,14 +31,23 @@ ExpressionLayout * Sum::createSequenceLayoutWithArgumentLayouts(ExpressionLayout } template -Evaluation * Sum::templatedEvaluateWithNextTerm(Evaluation * a, Evaluation * b) const { - if (a->numberOfOperands() == 1 && b->numberOfOperands() == 1) { - return new Complex(Addition::compute(*(a->complexOperand(0)), *(b->complexOperand(0)))); +Expression * Sum::templatedEvaluateWithNextTerm(Expression * a, Expression * b) const { + if (a->type() == Type::Complex && b->type() == Type::Complex) { + Complex * c = static_cast *>(a); + Complex * d = static_cast *>(b); + return new Complex(Addition::compute(*c, *d)); } - if (a->numberOfOperands() == 1) { - return Addition::computeOnComplexAndMatrix(a->complexOperand(0), b); + if (a->type() == Type::Complex) { + Complex * c = static_cast *>(a); + assert(b->type() == Type::Matrix); + Matrix * m = static_cast(b); + return Addition::computeOnComplexAndMatrix(c, m); } - return Addition::computeOnMatrices(a, b); + assert(a->type() == Type::Matrix); + assert(b->type() == Type::Matrix); + Matrix * m = static_cast(a); + Matrix * n = static_cast(b); + return Addition::computeOnMatrices(m, n); } } diff --git a/poincare/src/symbol.cpp b/poincare/src/symbol.cpp index 019be58ad..440b9fdee 100644 --- a/poincare/src/symbol.cpp +++ b/poincare/src/symbol.cpp @@ -3,7 +3,7 @@ #include #include "layout/baseline_relative_layout.h" #include "layout/string_layout.h" -#include +#include extern "C" { #include } @@ -11,6 +11,49 @@ extern "C" { namespace Poincare { +const char * Symbol::textForSpecialSymbols(char name) const { + switch (name) { + case SpecialSymbols::Ans: + return "ans"; + case SpecialSymbols::un: + return "u(n)"; + case SpecialSymbols::un1: + return "u(n+1)"; + case SpecialSymbols::vn: + return "v(n)"; + case SpecialSymbols::vn1: + return "v(n+1)"; + case SpecialSymbols::wn: + return "w(n)"; + case SpecialSymbols::wn1: + return "w(n+1)"; + case SpecialSymbols::M0: + return "M0"; + case SpecialSymbols::M1: + return "M1"; + case SpecialSymbols::M2: + return "M2"; + case SpecialSymbols::M3: + return "M3"; + case SpecialSymbols::M4: + return "M4"; + case SpecialSymbols::M5: + return "M5"; + case SpecialSymbols::M6: + return "M6"; + case SpecialSymbols::M7: + return "M7"; + case SpecialSymbols::M8: + return "M8"; + case SpecialSymbols::M9: + return "M9"; + default: + assert(false); + return nullptr; + } +} + + Symbol::SpecialSymbols Symbol::matrixSymbol(char index) { switch (index - '0') { case 0: @@ -44,8 +87,31 @@ Symbol::Symbol(char name) : { } +Symbol::Symbol(Symbol&& other) : + m_name(other.m_name) +{ +} + +Expression * Symbol::clone() const { + return new Symbol(m_name); +} + +Expression::Sign Symbol::sign() const { + /* TODO: Maybe, we will want to know that from a context given in parameter: + if (context.expressionForSymbol(this) != nullptr) { + return context.expressionForSymbol(this)->sign(context); + }*/ + if (m_name == Ion::Charset::SmallPi) { + return Sign::Positive; + } + if (m_name == Ion::Charset::Exponential) { + return Sign::Positive; + } + return Sign::Unknown; +} + template -Evaluation * Symbol::templatedEvaluate(Context& context, AngleUnit angleUnit) const { +Expression * Symbol::templatedEvaluate(Context& context, AngleUnit angleUnit) const { if (context.expressionForSymbol(this) != nullptr) { return context.expressionForSymbol(this)->evaluate(context, angleUnit); } @@ -91,13 +157,21 @@ ExpressionLayout * Symbol::privateCreateLayout(FloatDisplayMode floatDisplayMode return new StringLayout(&m_name, 1); } -Expression * Symbol::clone() const { - return new Symbol(m_name); -} - -bool Symbol::valueEquals(const Expression * e) const { - assert(e->type() == Expression::Type::Symbol); - return (m_name == ((Symbol *)e)->m_name); +int Symbol::writeTextInBuffer(char * buffer, int bufferSize) const { + if (bufferSize == 0) { + return -1; + } + if (bufferSize == 1) { + buffer[bufferSize-1] = 0; + return 0; + } + /* Special cases for all special symbols */ + if (m_name >0 && m_name < 32) { + return strlcpy(buffer, textForSpecialSymbols(m_name), bufferSize); + } + buffer[0] = m_name; + buffer[1] = 0; + return 1; } bool Symbol::isMatrixSymbol() const { @@ -107,4 +181,15 @@ bool Symbol::isMatrixSymbol() const { return false; } +int Symbol::simplificationOrderSameType(const Expression * e) const { + assert(e->type() == Expression::Type::Symbol); + if (m_name == ((Symbol *)e)->m_name) { + return 0; + } + if ((m_name > ((Symbol *)e)->m_name)) { + return 1; + } + return -1; +} + } diff --git a/poincare/src/tangent.cpp b/poincare/src/tangent.cpp index 48109631f..c66cddbe5 100644 --- a/poincare/src/tangent.cpp +++ b/poincare/src/tangent.cpp @@ -2,9 +2,11 @@ #include #include #include -#include +#include #include +#include #include +#include extern "C" { #include } @@ -12,30 +14,45 @@ extern "C" { namespace Poincare { -Tangent::Tangent() : - Function("tan") -{ -} - -Expression * Tangent::cloneWithDifferentOperands(Expression** newOperands, - int numberOfOperands, bool cloneOperands) const { - assert(newOperands != nullptr); - Tangent * t = new Tangent(); - t->setArgument(newOperands, numberOfOperands, cloneOperands); - return t; -} - Expression::Type Tangent::type() const { return Expression::Type::Tangent; } +Expression * Tangent::clone() const { + Tangent * a = new Tangent(m_operands, true); + return a; +} + +Expression * Tangent::shallowReduce(Context& context, AngleUnit angleUnit) { + Expression * e = Expression::shallowReduce(context, angleUnit); + if (e != this) { + return e; + } +#if MATRIX_EXACT_REDUCING + Expression * op = editableOperand(0); + if (op->type() == Type::Matrix) { + return SimplificationEngine::map(this, context, angleUnit); + } +#endif + Expression * newExpression = Trigonometry::shallowReduceDirectFunction(this, context, angleUnit); + if (newExpression->type() == Type::Tangent) { + const Expression * op[1] = {newExpression->operand(0)}; + Sine * s = new Sine(op, true); + Cosine * c = new Cosine(op, true); + Division * d = new Division(s, c, false); + newExpression = newExpression->replaceWith(d, true); + return newExpression->shallowReduce(context, angleUnit); + } + return newExpression; +} + template -Complex Tangent::templatedComputeComplex(const Complex c, AngleUnit angleUnit) const { - Complex result = Fraction::compute(Sine::compute(c, angleUnit), Cosine::compute(c, angleUnit)); +Complex Tangent::computeOnComplex(const Complex c, AngleUnit angleUnit) { + Complex result = Division::compute(Sine::computeOnComplex(c, angleUnit), Cosine::computeOnComplex(c, angleUnit)); if (!std::isnan(result.a()) && !std::isnan(result.b())) { return result; } - Complex tanh = HyperbolicTangent::compute(Multiplication::compute(Complex::Cartesian(0, -1), c)); + Complex tanh = HyperbolicTangent::computeOnComplex(Multiplication::compute(Complex::Cartesian(0, -1), c), angleUnit); return Multiplication::compute(Complex::Cartesian(0, 1), tanh); } diff --git a/poincare/src/trigonometry.cpp b/poincare/src/trigonometry.cpp new file mode 100644 index 000000000..37b87d77c --- /dev/null +++ b/poincare/src/trigonometry.cpp @@ -0,0 +1,203 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +extern "C" { +#include +} +#include + +namespace Poincare { + +Expression * Trigonometry::shallowReduceDirectFunction(Expression * e, Context& context, Expression::AngleUnit angleUnit) { + assert(e->type() == Expression::Type::Sine || e->type() == Expression::Type::Cosine || e->type() == Expression::Type::Tangent); + Expression * lookup = Trigonometry::table(e->operand(0), e->type(), context, angleUnit); + if (lookup != nullptr) { + return e->replaceWith(lookup, true); + } + Expression::Type correspondingType = e->type() == Expression::Type::Cosine ? Expression::Type::ArcCosine : (e->type() == Expression::Type::Sine ? Expression::Type::ArcSine : Expression::Type::ArcTangent); + if (e->operand(0)->type() == correspondingType) { + float trigoOp = e->operand(0)->operand(0)->approximate(context, angleUnit); + if (e->type() == Expression::Type::Tangent || (trigoOp >= -1.0f && trigoOp <= 1.0f)) { + return e->replaceWith(e->editableOperand(0)->editableOperand(0), true); + } + } + if (e->operand(0)->sign() == Expression::Sign::Negative) { + Expression * op = e->editableOperand(0); + Expression * newOp = op->setSign(Expression::Sign::Positive, context, angleUnit); + newOp->shallowReduce(context, angleUnit); + if (e->type() == Expression::Type::Cosine) { + return e->shallowReduce(context, angleUnit); + } else { + Multiplication * m = new Multiplication(new Rational(-1), e->clone(), false); + m->editableOperand(1)->shallowReduce(context, angleUnit); + return e->replaceWith(m, true)->shallowReduce(context, angleUnit); + } + } + if ((angleUnit == Expression::AngleUnit::Radian && e->operand(0)->type() == Expression::Type::Multiplication && e->operand(0)->operand(1)->type() == Expression::Type::Symbol && static_cast(e->operand(0)->operand(1))->name() == Ion::Charset::SmallPi && e->operand(0)->operand(0)->type() == Expression::Type::Rational) || (angleUnit == Expression::AngleUnit::Degree && e->operand(0)->type() == Expression::Type::Rational)) { + Rational * r = angleUnit == Expression::AngleUnit::Radian ? static_cast(e->editableOperand(0)->editableOperand(0)) : static_cast(e->editableOperand(0)); + int unaryCoefficient = 1; // store 1 or -1 + // Replace argument in [0, Pi/2[ or [0, 90[ + Integer divisor = angleUnit == Expression::AngleUnit::Radian ? r->denominator() : Integer::Multiplication(r->denominator(), Integer(90)); + Integer dividand = angleUnit == Expression::AngleUnit::Radian ? Integer::Addition(r->numerator(), r->numerator()) : r->numerator(); + if (divisor.isLowerThan(dividand)) { + Integer piDivisor = angleUnit == Expression::AngleUnit::Radian ? r->denominator() : Integer::Multiplication(r->denominator(), Integer(180)); + IntegerDivision div = Integer::Division(r->numerator(), piDivisor); + dividand = angleUnit == Expression::AngleUnit::Radian ? Integer::Addition(div.remainder, div.remainder) : div.remainder; + if (divisor.isLowerThan(dividand)) { + div.remainder = Integer::Subtraction(piDivisor, div.remainder); + if (e->type() == Expression::Type::Cosine || e->type() == Expression::Type::Tangent) { + unaryCoefficient *= -1; + } + } + Rational * newR = new Rational(div.remainder, r->denominator()); + Expression * rationalParent = angleUnit == Expression::AngleUnit::Radian ? e->editableOperand(0) : e; + rationalParent->replaceOperand(r, newR, true); + e->editableOperand(0)->shallowReduce(context, angleUnit); + if (Integer::Division(div.quotient, Integer(2)).remainder.isOne() && e->type() != Expression::Type::Tangent) { + unaryCoefficient *= -1; + } + Expression * simplifiedCosine = e->shallowReduce(context, angleUnit); // recursive + Multiplication * m = new Multiplication(new Rational(unaryCoefficient), simplifiedCosine->clone(), false); + return simplifiedCosine->replaceWith(m, true)->shallowReduce(context, angleUnit); + } + assert(r->sign() == Expression::Sign::Positive); + assert(!divisor.isLowerThan(dividand)); + } + return e; +} + +bool Trigonometry::ExpressionIsEquivalentToTangent(const Expression * e) { + assert(Expression::Type::Power < Expression::Type::Sine); + if (e->type() == Expression::Type::Multiplication && e->operand(1)->type() == Expression::Type::Sine && e->operand(0)->type() == Expression::Type::Power && e->operand(0)->operand(0)->type() == Expression::Type::Cosine && e->operand(0)->operand(1)->type() == Expression::Type::Rational && static_cast(e->operand(0)->operand(1))->isMinusOne()) { + return true; + } + return false; +} + +Expression * Trigonometry::shallowReduceInverseFunction(Expression * e, Context& context, Expression::AngleUnit angleUnit) { + assert(e->type() == Expression::Type::ArcCosine || e->type() == Expression::Type::ArcSine || e->type() == Expression::Type::ArcTangent); + if (e->type() != Expression::Type::ArcTangent) { + float approxOp = e->operand(0)->approximate(context, angleUnit); + if (approxOp > 1.0f || approxOp < -1.0f) { + return e->replaceWith(new Undefined(), true); + } + } + Expression::Type correspondingType = e->type() == Expression::Type::ArcCosine ? Expression::Type::Cosine : (e->type() == Expression::Type::ArcSine ? Expression::Type::Sine : Expression::Type::Tangent); + float pi = angleUnit == Expression::AngleUnit::Radian ? M_PI : 180; + if (e->operand(0)->type() == correspondingType) { + float trigoOp = e->operand(0)->operand(0)->approximate(context, angleUnit); + if ((e->type() == Expression::Type::ArcCosine && trigoOp >= 0.0f && trigoOp <= pi) || + (e->type() == Expression::Type::ArcSine && trigoOp >= -pi/2.0f && trigoOp <= pi/2.0f) || + (e->type() == Expression::Type::ArcTangent && trigoOp >= -pi/2.0f && trigoOp <= pi/2.0f)) { + return e->replaceWith(e->editableOperand(0)->editableOperand(0), true); + } + } + // Special case for arctan(sin(x)/cos(x)) + if (e->type() == Expression::Type::ArcTangent && ExpressionIsEquivalentToTangent(e->operand(0))) { + float trigoOp = e->operand(0)->operand(1)->operand(0)->approximate(context, angleUnit); + if (trigoOp >= -pi/2.0f && trigoOp <= pi/2.0f) { + return e->replaceWith(e->editableOperand(0)->editableOperand(1)->editableOperand(0), true); + } + } + Expression * lookup = Trigonometry::table(e->operand(0), e->type(), context, angleUnit); + if (lookup != nullptr) { + return e->replaceWith(lookup, true); + } + // arccos(-x) = Pi-arcos(x), arcsin(-x) = -arcsin(x), arctan(-x)=-arctan(x) + if (e->operand(0)->sign() == Expression::Sign::Negative || (e->operand(0)->type() == Expression::Type::Multiplication && e->operand(0)->operand(0)->type() == Expression::Type::Rational && static_cast(e->operand(0)->operand(0))->isMinusOne())) { + Expression * op = e->editableOperand(0); + if (e->operand(0)->sign() == Expression::Sign::Negative) { + Expression * newOp = op->setSign(Expression::Sign::Positive, context, angleUnit); + newOp->shallowReduce(context, angleUnit); + } else { + ((Multiplication *)op)->removeOperand(op->editableOperand(0), true); + op->shallowReduce(context, angleUnit); + } + if (e->type() == Expression::Type::ArcCosine) { + Expression * pi = angleUnit == Expression::AngleUnit::Radian ? static_cast(new Symbol(Ion::Charset::SmallPi)) : static_cast(new Rational(180)); + Subtraction * s = new Subtraction(pi, e->clone(), false); + s->editableOperand(1)->shallowReduce(context, angleUnit); + return e->replaceWith(s, true)->shallowReduce(context, angleUnit); + } else { + Multiplication * m = new Multiplication(new Rational(-1), e->clone(), false); + m->editableOperand(1)->shallowReduce(context, angleUnit); + return e->replaceWith(m, true)->shallowReduce(context, angleUnit); + } + } + + return e; +} + +static_assert('\x89' == Ion::Charset::SmallPi, "Unicode error"); +constexpr const char * cheatTable[Trigonometry::k_numberOfEntries][5] = +{{"-90", "\x89*(-2)^(-1)", "", "-1", "undef"}, + {"-75", "\x89*(-5)*12^(-1)", "", "(-1)*6^(1/2)*4^(-1)-2^(1/2)*4^(-1)", "-(3^(1/2)+2)"}, + {"-72", "\x89*2*(-5)^(-1)", "", "-(5/8+5^(1/2)/8)^(1/2)", "-(5+2*5^(1/2))^(1/2)"}, + {"-135/2", "\x89*(-3)*8^(-1)", "", "-(2+2^(1/2))^(1/2)*2^(-1)", "-1-2^(1/2)"}, + {"-60", "\x89*(-3)^(-1)", "", "-3^(1/2)*2^(-1)", "-3^(1/2)"}, + {"-54", "\x89*(-3)*10^(-1)", "", "4^(-1)*(-1-5^(1/2))", "-(1+2*5^(-1/2))^(1/2)"}, + {"-45", "\x89*(-4)^(-1)", "", "(-1)*(2^(-1/2))", "-1"}, + {"-36", "\x89*(-5)^(-1)", "", "-(5/8-5^(1/2)/8)^(1/2)", "-(5-2*5^(1/2))^(1/2)"}, + {"-30", "\x89*(-6)^(-1)", "", "-0.5", "-3^(-1/2)"}, + {"-45/2", "\x89*(-8)^(-1)", "", "(2-2^(1/2))^(1/2)*(-2)^(-1)", "1-2^(1/2)"}, + {"-18", "\x89*(-10)^(-1)", "", "4^(-1)*(1-5^(1/2))", "-(1-2*5^(-1/2))^(1/2)"}, + {"-15", "\x89*(-12)^(-1)", "", "-6^(1/2)*4^(-1)+2^(1/2)*4^(-1)", "3^(1/2)-2"}, + {"0", "0", "1", "0", "0"}, + {"15", "\x89*12^(-1)", "6^(1/2)*4^(-1)+2^(1/2)*4^(-1)", "6^(1/2)*4^(-1)+2^(1/2)*(-4)^(-1)", "-(3^(1/2)-2)"}, + {"18", "\x89*10^(-1)", "(5/8+5^(1/2)/8)^(1/2)", "4^(-1)*(5^(1/2)-1)", "(1-2*5^(-1/2))^(1/2)"}, + {"45/2", "\x89*8^(-1)", "(2+2^(1/2))^(1/2)*2^(-1)", "(2-2^(1/2))^(1/2)*2^(-1)", "2^(1/2)-1"}, + {"30", "\x89*6^(-1)", "3^(1/2)*2^(-1)", "0.5", "3^(-1/2)"}, + {"36", "\x89*5^(-1)", "(5^(1/2)+1)*4^(-1)", "(5/8-5^(1/2)/8)^(1/2)", "(5-2*5^(1/2))^(1/2)"}, + {"45", "\x89*4^(-1)", "2^(-1/2)", "2^(-1/2)", "1"}, + {"54", "\x89*3*10^(-1)", "(5/8-5^(1/2)/8)^(1/2)", "4^(-1)*(5^(1/2)+1)", "(1+2*5^(-1/2))^(1/2)"}, + {"60", "\x89*3^(-1)", "0.5", "3^(1/2)*2^(-1)", "3^(1/2)"}, + {"135/2", "\x89*3*8^(-1)", "(2-2^(1/2))^(1/2)*2^(-1)", "(2+2^(1/2))^(1/2)*2^(-1)", "1+2^(1/2)"}, + {"72", "\x89*2*5^(-1)", "(5^(1/2)-1)*4^(-1)", "(5/8+5^(1/2)/8)^(1/2)", "(5+2*5^(1/2))^(1/2)"}, + {"75", "\x89*5*12^(-1)", "6^(1/2)*4^(-1)+2^(1/2)*(-4)^(-1)", "6^(1/2)*4^(-1)+2^(1/2)*4^(-1)", "3^(1/2)+2"}, + {"90", "\x89*2^(-1)", "0", "1", "undef"}, + {"105", "\x89*7*12^(-1)", "-6^(1/2)*4^(-1)+2^(1/2)*4^(-1)", "", ""}, + {"108", "\x89*3*5^(-1)", "(1-5^(1/2))*4^(-1)", "", ""}, + {"225/2", "\x89*5*8^(-1)", "(2-2^(1/2))^(1/2)*(-2)^(-1)", "", ""}, + {"120", "\x89*2*3^(-1)", "-0.5", "", ""}, + {"126", "\x89*7*10^(-1)", "-(5*8^(-1)-5^(1/2)*8^(-1))^(1/2)", "", ""}, + {"135", "\x89*3*4^(-1)", "(-1)*(2^(-1/2))", "", ""}, + {"144", "\x89*4*5^(-1)", "(-5^(1/2)-1)*4^(-1)", "", ""}, + {"150", "\x89*5*6^(-1)", "-3^(1/2)*2^(-1)", "", ""}, + {"315/2", "\x89*7*8^(-1)", "-(2+2^(1/2))^(1/2)*2^(-1)", "", ""}, + {"162", "\x89*9*10^(-1)", "-(5*8^(-1)+5^(1/2)*8^(-1))^(1/2)", "", ""}, + {"165", "\x89*11*12^(-1)", "(-1)*6^(1/2)*4^(-1)-2^(1/2)*4^(-1)", "", ""}, + {"180", "\x89", "-1", "0", "0"}}; + +Expression * Trigonometry::table(const Expression * e, Expression::Type type, Context & context, Expression::AngleUnit angleUnit) { + assert(type == Expression::Type::Sine || type == Expression::Type::Cosine || type == Expression::Type::Tangent || type == Expression::Type::ArcCosine || type == Expression::Type::ArcSine || type == Expression::Type::ArcTangent); + int angleUnitIndex = angleUnit == Expression::AngleUnit::Radian ? 1 : 0; + int trigonometricFunctionIndex = type == Expression::Type::Cosine || type == Expression::Type::ArcCosine ? 2 : (type == Expression::Type::Sine || type == Expression::Type::ArcSine ? 3 : 4); + int inputIndex = type == Expression::Type::ArcCosine || type == Expression::Type::ArcSine || type == Expression::Type::ArcTangent ? trigonometricFunctionIndex : angleUnitIndex; + int outputIndex = type == Expression::Type::ArcCosine || type == Expression::Type::ArcSine || type == Expression::Type::ArcTangent ? angleUnitIndex : trigonometricFunctionIndex; + for (int i = 0; i < k_numberOfEntries; i++) { + Expression * input = Expression::parse(cheatTable[i][inputIndex]); + if (input == nullptr) { + continue; + } + Expression::Reduce(&input, context, angleUnit); + bool rightInput = input->isIdenticalTo(e); + delete input; + if (rightInput) { + Expression * output = Expression::parse(cheatTable[i][outputIndex]); + if (output == nullptr) { + return nullptr; + } + Expression::Reduce(&output, context, angleUnit); + return output; + } + } + return nullptr; +} + +} diff --git a/poincare/src/undefined.cpp b/poincare/src/undefined.cpp new file mode 100644 index 000000000..c8da8aac4 --- /dev/null +++ b/poincare/src/undefined.cpp @@ -0,0 +1,35 @@ +#include +extern "C" { +#include +} +#include "layout/string_layout.h" + +namespace Poincare { + +Expression::Type Undefined::type() const { + return Type::Undefined; +} + +Expression * Undefined::clone() const { + return new Undefined(); +} + +template Complex * Undefined::templatedEvaluate(Context& context, AngleUnit angleUnit) const { + return new Complex(Complex::Float(NAN)); +} + +ExpressionLayout * Undefined::privateCreateLayout(FloatDisplayMode floatDisplayMode, ComplexFormat complexFormat) const { + char buffer[16]; + int numberOfChars = Complex::convertFloatToText(NAN, buffer, 16, 1, floatDisplayMode); + return new StringLayout(buffer, numberOfChars); +} + +int Undefined::writeTextInBuffer(char * buffer, int bufferSize) const { + if (bufferSize == 0) { + return -1; + } + return strlcpy(buffer, "undef", bufferSize); +} + +} + diff --git a/poincare/src/variable_context.cpp b/poincare/src/variable_context.cpp index ae6a58ad1..a85ff9072 100644 --- a/poincare/src/variable_context.cpp +++ b/poincare/src/variable_context.cpp @@ -14,17 +14,18 @@ VariableContext::VariableContext(char name, Context * parentContext) : } template -void VariableContext::setExpressionForSymbolName(Expression * expression, const Symbol * symbol) { +void VariableContext::setExpressionForSymbolName(const Expression * expression, const Symbol * symbol, Context & context) { if (symbol->name() == m_name) { if (expression == nullptr) { return; } - Evaluation * evaluation = expression->evaluate(*m_parentContext); - /* WARNING: We assume that the evaluation of expression is a real */ - m_value = Complex::Float(evaluation->toScalar()); - delete evaluation; + if (expression->type() == Expression::Type::Complex) { + m_value = Complex::Float(static_cast *>(expression)->toScalar()); + } else { + m_value = Complex::Float(NAN); + } } else { - m_parentContext->setExpressionForSymbolName(expression, symbol); + m_parentContext->setExpressionForSymbolName(expression, symbol, context); } } diff --git a/poincare/test/arithmetic.cpp b/poincare/test/arithmetic.cpp new file mode 100644 index 000000000..accd9529e --- /dev/null +++ b/poincare/test/arithmetic.cpp @@ -0,0 +1,88 @@ +#include +#include +#include +#include +#include + +#if POINCARE_TESTS_PRINT_EXPRESSIONS +#include "../src/expression_debug.h" +#include +using namespace std; +#endif + +using namespace Poincare; + +void assert_gcd_equals_to(Integer a, Integer b, Integer c) { +#if POINCARE_TESTS_PRINT_EXPRESSIONS + cout << "---- GCD ----" << endl; + cout << "gcd(" << a.approximate(); + cout << ", " << b.approximate() << ") = "; +#endif + Integer gcd = Arithmetic::GCD(&a, &b); +#if POINCARE_TESTS_PRINT_EXPRESSIONS + cout << gcd.approximate() << endl; +#endif + assert(gcd.isEqualTo(c)); +} + +void assert_lcm_equals_to(Integer a, Integer b, Integer c) { +#if POINCARE_TESTS_PRINT_EXPRESSIONS + cout << "---- LCM ----" << endl; + cout << "lcm(" << a.approximate(); + cout << ", " << b.approximate() << ") = "; +#endif + Integer lcm = Arithmetic::LCM(&a, &b); +#if POINCARE_TESTS_PRINT_EXPRESSIONS + cout << lcm.approximate() << endl; +#endif + assert(lcm.isEqualTo(c)); +} + +void assert_prime_factorization_equals_to(Integer a, int * factors, int * coefficients, int length) { + Integer outputFactors[1000]; + Integer outputCoefficients[1000]; +#if POINCARE_TESTS_PRINT_EXPRESSIONS + cout << "---- Primes factorization ----" << endl; + cout << "Decomp(" << a.approximate() << ") = "; +#endif + Arithmetic::PrimeFactorization(&a, outputFactors, outputCoefficients, 10); +#if POINCARE_TESTS_PRINT_EXPRESSIONS + print_prime_factorization(outputFactors, outputCoefficients, 10); +#endif + for (int index = 0; index < length; index++) { + if (outputCoefficients[index].isEqualTo(Integer(0))) { + break; + } + /* Cheat: instead of comparing to integers, we compare their approximations + * (the relation between integers and their approximation is a surjection, + * however different integers are really likely to have different + * approximations... */ + assert(outputFactors[index].approximate() == Integer(factors[index]).approximate()); + assert(outputCoefficients[index].approximate() == Integer(coefficients[index]).approximate()); + } +} + +QUIZ_CASE(poincare_arithmetic) { + assert_gcd_equals_to(Integer(11), Integer(121), Integer(11)); + assert_gcd_equals_to(Integer(-256), Integer(321), Integer(1)); + assert_gcd_equals_to(Integer(-8), Integer(-40), Integer(8)); + assert_gcd_equals_to(Integer("1234567899876543456", true), Integer("234567890098765445678"), Integer(2)); + assert_gcd_equals_to(Integer("45678998789"), Integer("1461727961248"), Integer("45678998789")); + assert_lcm_equals_to(Integer(11), Integer(121), Integer(121)); + assert_lcm_equals_to(Integer(-31), Integer(52), Integer(1612)); + assert_lcm_equals_to(Integer(-8), Integer(-40), Integer(40)); + assert_lcm_equals_to(Integer("1234567899876543456", true), Integer("234567890098765445678"), Integer("144794993728852353909143567804987191584")); + assert_lcm_equals_to(Integer("45678998789"), Integer("1461727961248"), Integer("1461727961248")); + int factors0[5] = {2,3,5,79,1319}; + int coefficients0[5] = {2,1,1,1,1}; + assert_prime_factorization_equals_to(Integer(6252060), factors0, coefficients0, 5); + int factors1[3] = {3,2969, 6907}; + int coefficients1[3] = {1,1,1}; + assert_prime_factorization_equals_to(Integer(61520649), factors1, coefficients1, 3); + int factors2[3] = {2,5, 7}; + int coefficients2[3] = {2,4,2}; + assert_prime_factorization_equals_to(Integer(122500), factors2, coefficients2, 3); + int factors3[8] = {3,7,11, 13, 19, 3607, 3803, 52579}; + int coefficients3[8] = {4,2,2,2,2,2,2,2}; + assert_prime_factorization_equals_to(Integer("15241578780673678515622620750190521"), factors3, coefficients3, 8); +} diff --git a/poincare/test/convert_expression_to_text.cpp b/poincare/test/convert_expression_to_text.cpp new file mode 100644 index 000000000..5f69ac26b --- /dev/null +++ b/poincare/test/convert_expression_to_text.cpp @@ -0,0 +1,97 @@ +#include +#include +#include +#include +#include +#include +#include + +using namespace Poincare; + +void assert_expression_prints_to(Expression * e, const char * result, int bufferSize = 100) { + quiz_print(result); + + char * buffer = new char[bufferSize]; + e->writeTextInBuffer(buffer, bufferSize); + + char * currentChar = buffer; + while (*currentChar != 0) { + if (*currentChar == Ion::Charset::Exponent) { + *currentChar = 'E'; + } + if (*currentChar == Ion::Charset::Exponential) { + *currentChar = 'e'; + } + if (*currentChar == Ion::Charset::IComplex) { + *currentChar = 'i'; + } + currentChar++; + } + + assert(strcmp(buffer, result) == 0); + + delete[] buffer; +} + +QUIZ_CASE(poincare_rational_to_text) { + Rational r(2,3); + assert_expression_prints_to(&r, "2/3"); +} + +QUIZ_CASE(poincare_decimal_to_text) { + Decimal d0(Integer("-123456789"),30); + assert_expression_prints_to(&d0, "-1.23456789E30"); + Decimal d1(Integer("123456789"),30); + assert_expression_prints_to(&d1, "1.23456789E30"); + Decimal d2(Integer("-123456789"),-30); + assert_expression_prints_to(&d2, "-1.23456789E-30"); + Decimal d3(Integer("-12345"),-3); + assert_expression_prints_to(&d3, "-0.0012345"); + Decimal d4(Integer("12345"),-3); + assert_expression_prints_to(&d4, "0.0012345"); + Decimal d5(Integer("12345"),3); + assert_expression_prints_to(&d5, "1234.5"); + Decimal d6(Integer("-12345"),3); + assert_expression_prints_to(&d6, "-1234.5"); + Decimal d7(Integer("12345"),6); + assert_expression_prints_to(&d7, "1234500"); + Decimal d8(Integer("-12345"),6); + assert_expression_prints_to(&d8, "-1234500"); + Decimal d9(Integer("-12345"),-1); + assert_expression_prints_to(&d9, "-0.12345"); + Decimal d10(Integer("12345"),-1); + assert_expression_prints_to(&d10, "0.12345"); + + Decimal e0(-1.23456789E30); + assert_expression_prints_to(&e0, "-1.23456789E30"); + Decimal e1(1.23456789E30); + assert_expression_prints_to(&e1, "1.23456789E30"); + Decimal e2(-1.23456789E-30); + assert_expression_prints_to(&e2, "-1.23456789E-30"); + Decimal e3(-1.2345E-3); + assert_expression_prints_to(&e3, "-0.0012345"); + Decimal e4(1.2345E-3); + assert_expression_prints_to(&e4, "0.0012345"); + Decimal e5(1.2345E3); + assert_expression_prints_to(&e5, "1234.5"); + Decimal e6(-1.2345E3); + assert_expression_prints_to(&e6, "-1234.5"); + Decimal e7(1.2345E6); + assert_expression_prints_to(&e7, "1234500"); + Decimal e8(-1.2345E6); + assert_expression_prints_to(&e8, "-1234500"); + Decimal e9(-1.2345E-1); + assert_expression_prints_to(&e9, "-0.12345"); + Decimal e10(1.2345E-1); + assert_expression_prints_to(&e10, "0.12345"); + Decimal e11(1); + assert_expression_prints_to(&e11, "1"); + Decimal e12(0.9999999999999995); + assert_expression_prints_to(&e12, "1"); + Decimal e13(0.999999999999995); + assert_expression_prints_to(&e13, "9.99999999999995E-1"); + Decimal e14(0.000000999999999999995); + assert_expression_prints_to(&e14, "9.99999999999995E-7"); + Decimal e15(0.0000009999999999999995); + assert_expression_prints_to(&e15, "0.000001"); +} diff --git a/poincare/test/fraction.cpp b/poincare/test/division.cpp similarity index 98% rename from poincare/test/fraction.cpp rename to poincare/test/division.cpp index 7f613f7d1..8ca7f1d87 100644 --- a/poincare/test/fraction.cpp +++ b/poincare/test/division.cpp @@ -7,7 +7,7 @@ using namespace Poincare; -QUIZ_CASE(poincare_fraction_evaluate) { +QUIZ_CASE(poincare_division_evaluate) { Complex a[1] = {Complex::Float(0.5f)}; assert_parsed_expression_evaluates_to("1/2", a); diff --git a/poincare/test/helper.cpp b/poincare/test/helper.cpp index a5549b465..279ec9931 100644 --- a/poincare/test/helper.cpp +++ b/poincare/test/helper.cpp @@ -8,7 +8,7 @@ using namespace Poincare; -static Expression * parse_expression(const char * expression) { +Expression * parse_expression(const char * expression) { quiz_print(expression); char buffer[200]; strlcpy(buffer, expression, sizeof(buffer)); @@ -32,29 +32,23 @@ void assert_parsed_expression_type(const char * expression, Poincare::Expression delete e; } -#if POINCARE_SIMPLIFY -void assert_parsed_simplified_expression_type(const char * expression, Poincare::Expression::Type type) { - Expression * e = parse_expression(expression); - Expression * e2 = e->simplify(); - assert(e2); - assert(e2->type() == type); - delete e; - delete e2; -} -#endif - template void assert_parsed_expression_evaluates_to(const char * expression, Complex * results, int numberOfRows, int numberOfColumns, Expression::AngleUnit angleUnit) { GlobalContext globalContext; Expression * a = parse_expression(expression); - Evaluation * m = a->evaluate(globalContext, angleUnit); + Expression * m = a->evaluate(globalContext, angleUnit); assert(m); - assert(m->numberOfRows() == numberOfRows); - assert(m->numberOfColumns() == numberOfColumns); + assert(m->numberOfOperands() == numberOfRows*numberOfColumns); + if (m->type() == Expression::Type::Matrix) { + assert(static_cast(m)->numberOfRows() == numberOfRows); + assert(static_cast(m)->numberOfColumns() == numberOfColumns); for (int i = 0; i < m->numberOfOperands(); i++) { - assert(std::fabs(m->complexOperand(i)->a() - results[i].a()) < 0.0001f); - assert(std::fabs(m->complexOperand(i)->b() - results[i].b()) < 0.0001f); + assert(std::fabs(static_cast *>(m->operand(i))->a() - results[i].a()) < 0.0001f); + assert(std::fabs(static_cast *>(m->operand(i))->b() - results[i].b()) < 0.0001f); } + } + assert(std::fabs(static_cast *>(m)->a() - results[0].a()) < 0.0001f); + assert(std::fabs(static_cast *>(m)->b() - results[0].b()) < 0.0001f); delete a; delete m; } diff --git a/poincare/test/helper.h b/poincare/test/helper.h index 39d3376f3..bb4e63350 100644 --- a/poincare/test/helper.h +++ b/poincare/test/helper.h @@ -3,8 +3,8 @@ constexpr Poincare::Expression::AngleUnit Degree = Poincare::Expression::AngleUnit::Degree; constexpr Poincare::Expression::AngleUnit Radian = Poincare::Expression::AngleUnit::Radian; +Poincare::Expression * parse_expression(const char * expression); void assert_parsed_expression_type(const char * expression, Poincare::Expression::Type type); -void assert_parsed_simplified_expression_type(const char * expression, Poincare::Expression::Type type); template void assert_parsed_expression_evaluates_to(const char * expression, Poincare::Complex * results, int numberOfRows, int numberOfColumns = 1, Poincare::Expression::AngleUnit angleUnit = Degree); template diff --git a/poincare/test/identity.cpp b/poincare/test/identity.cpp index e187512d5..1b3906360 100644 --- a/poincare/test/identity.cpp +++ b/poincare/test/identity.cpp @@ -29,8 +29,8 @@ QUIZ_CASE(poincare_identity_simple_term) { } QUIZ_CASE(poincare_identity_commutativity) { - assert(identical_to("1+2", "2+1")); + assert(equivalent_to("1+2", "2+1")); //assert(identical_to("1*2", "2*1")); - assert(!identical_to("1-2", "2-1")); - assert(!identical_to("1/2", "2/1")); + assert(!equivalent_to("1-2", "2-1")); + assert(!equivalent_to("1/2", "2/1")); } diff --git a/poincare/test/integer.cpp b/poincare/test/integer.cpp index dbcca306e..ee69159b4 100644 --- a/poincare/test/integer.cpp +++ b/poincare/test/integer.cpp @@ -5,61 +5,82 @@ using namespace Poincare; QUIZ_CASE(poincare_integer) { - assert(Integer(123) == Integer(123)); - assert(Integer("123") == Integer(123)); - assert(!(Integer("-123") == Integer(123))); - assert(Integer("-123") == Integer(-123)); + assert(Integer(123).isEqualTo(Integer(123))); + assert(Integer("123").isEqualTo(Integer(123))); + assert(!Integer("-123").isEqualTo(Integer(123))); + assert(Integer("-123").isEqualTo(Integer(-123))); //assert(Integer("0123") == Integer(123)); //FIXME: assert(Integer("0x2BABE") == Integer(178878)); //FIXME: assert(Integer("0b1011") == Integer(11)); } QUIZ_CASE(poincare_integer_compare) { - assert(Integer(123) < Integer(456)); - assert(!(Integer(123) < Integer(123))); - assert(!(Integer(-123) < Integer(-123))); - assert(Integer(-100) < Integer(2)); - assert(Integer(-200) < Integer(-100)); - assert(Integer(123) < Integer("123456789123456789")); - assert(Integer("123456789123456788") < Integer("123456789123456789")); - assert(!(Integer("123456789123456789") < Integer("123456789123456788"))); + assert(Integer(123).isLowerThan(Integer(456))); + assert(!Integer(123).isLowerThan(Integer(123))); + assert(!Integer(-123).isLowerThan(Integer(-123))); + assert(Integer(-100).isLowerThan(Integer(2))); + assert(Integer(-200).isLowerThan(Integer(-100))); + assert(Integer(123).isLowerThan(Integer("123456789123456789"))); + assert(Integer(-123).isLowerThan(Integer("123456789123456789"))); + assert(Integer("123456789123456788").isLowerThan(Integer("123456789123456789"))); + assert(Integer("-1234567891234567892109209109").isLowerThan(Integer("123456789123456789"))); + assert(!Integer("123456789123456789").isLowerThan(Integer("123456789123456788"))); } -QUIZ_CASE(poincare_integer_add) { - assert(Integer("0").add(Integer("0")) == Integer("0")); - assert(Integer(123).add(Integer(456)) == Integer(579)); - assert(Integer("123456789123456789").add(Integer(1)) == Integer("123456789123456790")); +QUIZ_CASE(poincare_integer_addition) { + assert(Integer::Addition(Integer("0"), Integer("0")).isEqualTo(Integer(0))); + assert(Integer::Addition(Integer(123), Integer(456)).isEqualTo(Integer(579))); + assert(Integer::Addition(Integer("123456789123456789"), Integer(1)).isEqualTo(Integer("123456789123456790"))); + assert(Integer::Addition(Integer("-123456789123456789"), Integer("123456789123456789")).isEqualTo(Integer("0"))); + assert(Integer::Addition(Integer("234"), Integer(-234)).isEqualTo(Integer(0))); } -QUIZ_CASE(poincare_integer_subtract) { - assert(Integer(123).subtract(Integer(23)) == Integer(100)); - assert(Integer("123456789123456789").subtract(Integer("9999999999")) == Integer("123456779123456790")); - assert(Integer(23).subtract(Integer(100)) == Integer(-77)); +QUIZ_CASE(poincare_integer_subtraction) { + assert(Integer::Subtraction(Integer(123), Integer(23)).isEqualTo(Integer(100))); + assert(Integer::Subtraction(Integer("123456789123456789"), Integer("9999999999")).isEqualTo(Integer("123456779123456790"))); + assert(Integer::Subtraction(Integer(23), Integer(100)).isEqualTo(Integer(-77))); + assert(Integer::Subtraction(Integer(23), Integer(23)).isEqualTo(Integer(0))); + assert(Integer::Subtraction(Integer(-23), Integer(-23)).isEqualTo(Integer(0))); + assert(Integer::Subtraction(Integer("-123456789123456789"), Integer("-123456789123456789")).isEqualTo(Integer(0))); + assert(Integer::Subtraction(Integer("123456789123456789"), Integer("123456789123456789")).isEqualTo(Integer(0))); } -QUIZ_CASE(poincare_integer_multiply) { - assert(Integer(12).multiply_by(Integer(34)) == Integer(408)); - assert(Integer(56).multiply_by(Integer("0")) == Integer("0")); - assert(Integer(-12).multiply_by(Integer(34)) == Integer(-408)); - assert(Integer(-12).multiply_by(Integer(-34)) == Integer(408)); - assert(Integer(999999).multiply_by(Integer(999999)) == Integer("999998000001")); - assert(Integer("9999999999").multiply_by(Integer("9999999999")) == Integer("99999999980000000001")); +QUIZ_CASE(poincare_integer_multiplication) { + assert(Integer::Multiplication(Integer(12), Integer(34)).isEqualTo(Integer(408))); + assert(Integer::Multiplication(Integer(56), Integer(0)).isEqualTo(Integer(0))); + assert(Integer::Multiplication(Integer(-12), Integer(34)).isEqualTo(Integer(-408))); + assert(Integer::Multiplication(Integer(-12), Integer(-34)).isEqualTo(Integer(408))); + assert(Integer::Multiplication(Integer(123456), Integer(654321)).isEqualTo(Integer("80779853376"))); + assert(Integer::Multiplication(Integer("9999999999"), Integer("9999999999")).isEqualTo(Integer("99999999980000000001"))); + assert(Integer::Multiplication(Integer("-23"), Integer("0")).isEqualTo(Integer("0"))); + assert(Integer::Multiplication(Integer("-23456787654567765456"), Integer("0")).isEqualTo(Integer("0"))); } QUIZ_CASE(poincare_integer_divide) { - assert(Integer(8).divide_by(Integer(4)) == Integer(2)); - assert(Integer("3293920983029832").divide_by(Integer("38928")) == Integer("84615726033")); - assert(Integer("3293920983029832").divide_by(Integer("389090928")) == Integer("8465684")); + assert(Integer::Division(Integer(8), Integer(4)).quotient.isEqualTo(Integer(2)) && Integer::Division(Integer(8), Integer(4)).remainder.isEqualTo(Integer(0))); + assert(Integer::Division(Integer("3293920983030066"), Integer(38928)).quotient.isEqualTo(Integer("84615726033")) && Integer::Division(Integer("3293920983030066"), Integer(38928)).remainder.isEqualTo(Integer(17442))); + assert(Integer::Division(Integer("3293920983030066"), Integer("389282362616")).quotient.isEqualTo(Integer(8461)) && Integer::Division(Integer("3293920983030066"), Integer("389282362616")).remainder.isEqualTo(Integer("202912936090"))); + assert(Integer::Division(Integer("-18940566"), Integer("499030")).quotient.isEqualTo(Integer(-38)) && Integer::Division(Integer("-18940566"), Integer("499030")).remainder.isEqualTo(Integer("22574"))); + assert(Integer::Division(Integer("234567909876"), Integer("-234567898")).quotient.isEqualTo(Integer(-1000)) && Integer::Division(Integer("234567909876"), Integer("-234567898")).remainder.isEqualTo(Integer("11876"))); + assert(Integer::Division(Integer("-567"), Integer("-12")).quotient.isEqualTo(Integer(48)) && Integer::Division(Integer("-567"), Integer("-12")).remainder.isEqualTo(Integer("9"))); + assert(Integer::Division(Integer("-576"), Integer("-12")).quotient.isEqualTo(Integer(48)) && Integer::Division(Integer("-576"), Integer("-12")).remainder.isEqualTo(Integer("0"))); + assert(Integer::Division(Integer("576"), Integer("-12")).quotient.isEqualTo(Integer(-48)) && Integer::Division(Integer("576"), Integer("-12")).remainder.isEqualTo(Integer("0"))); + assert(Integer::Division(Integer("-576"), Integer("12")).quotient.isEqualTo(Integer(-48)) && Integer::Division(Integer("-576"), Integer("12")).remainder.isEqualTo(Integer("0"))); + assert(Integer::Division(Integer("12345678910111213141516171819202122232425"), Integer("10")).remainder.isEqualTo(Integer("5"))); + assert(Integer::Division(Integer("12345678910111213141516171819202122232425"), Integer("10")).quotient.isEqualTo(Integer("1234567891011121314151617181920212223242"))); + assert(Integer::Division(Integer("1234567891011121314151617181920212223242"), Integer("10")).quotient.isEqualTo(Integer("123456789101112131415161718192021222324")) && Integer::Division(Integer("1234567891011121314151617181920212223242"), Integer("10")).remainder.isEqualTo(Integer("2"))); + assert(Integer::Division(Integer("123456789101112131415161718192021222324"), Integer("10")).quotient.isEqualTo(Integer("12345678910111213141516171819202122232")) && Integer::Division(Integer("123456789101112131415161718192021222324"), Integer("10")).remainder.isEqualTo(Integer("4"))); + assert(Integer::Division(Integer("12345678910111213141516171819202122232"), Integer("10")).quotient.isEqualTo(Integer("1234567891011121314151617181920212223")) && Integer::Division(Integer("12345678910111213141516171819202122232"), Integer("10")).remainder.isEqualTo(Integer("2"))); + assert(Integer::Division(Integer("1234567891011121314151617181920212223"), Integer("10")).quotient.isEqualTo(Integer("123456789101112131415161718192021222")) && Integer::Division(Integer("1234567891011121314151617181920212223"), Integer("10")).remainder.isEqualTo(Integer("3"))); + assert(Integer::Division(Integer("123456789101112131415161718192021222"), Integer("10")).quotient.isEqualTo(Integer("12345678910111213141516171819202122")) && Integer::Division(Integer("123456789101112131415161718192021222"), Integer("10")).remainder.isEqualTo(Integer("2"))); + assert(Integer::Division(Integer("12345678910111213141516171819202122"), Integer("10")).quotient.isEqualTo(Integer("1234567891011121314151617181920212")) && Integer::Division(Integer("12345678910111213141516171819202122"), Integer("10")).remainder.isEqualTo(Integer("2"))); + assert(Integer::Division(Integer("0"), Integer("-10")).quotient.isEqualTo(Integer("0")) && Integer::Division(Integer("0"), Integer("-10")).remainder.isEqualTo(Integer("0"))); + assert(Integer::Division(Integer("0"), Integer("-123456789098760")).quotient.isEqualTo(Integer("0")) && Integer::Division(Integer("0"), Integer("-123456789098760")).remainder.isEqualTo(Integer("0"))); } template void assert_integer_evals_to(int i, T result) { - GlobalContext globalContext; - Evaluation * m = Integer(i).evaluate(globalContext); - assert(m->complexOperand(0)->a() == result); - assert(m->complexOperand(0)->b() == 0.0f); - assert(m->numberOfOperands() == 1); - delete m; + assert(Integer(i).approximate() == result); } QUIZ_CASE(poincare_integer_evaluate) { diff --git a/poincare/test/simplify_easy.cpp b/poincare/test/simplify_easy.cpp new file mode 100644 index 000000000..3cd135ba1 --- /dev/null +++ b/poincare/test/simplify_easy.cpp @@ -0,0 +1,506 @@ +#include +#include +#include +#include +#include "helper.h" +#if POINCARE_TESTS_PRINT_EXPRESSIONS +#include "../src/expression_debug.h" +#include +using namespace std; +#endif + +using namespace Poincare; + +void assert_parsed_expression_simplify_to(const char * expression, const char * simplifiedExpression, Expression::AngleUnit angleUnit = Expression::AngleUnit::Radian) { + GlobalContext globalContext; + Expression * e = parse_expression(expression); +#if POINCARE_TESTS_PRINT_EXPRESSIONS + cout << "---- Simplify: " << expression << "----" << endl; +#endif + Expression::Simplify(&e, globalContext, angleUnit); +#if POINCARE_TESTS_PRINT_EXPRESSIONS + print_expression(e, 0); +#endif + Expression * f = parse_expression(simplifiedExpression); + Expression::Simplify(&f, globalContext, angleUnit); +#if POINCARE_TESTS_PRINT_EXPRESSIONS + cout << "---- compared to: " << simplifiedExpression << "----" << endl; + print_expression(f, 0); +#endif + assert(e->isIdenticalTo(f)); + delete e; + delete f; +} + +QUIZ_CASE(poincare_simplify_easy) { + //assert_parsed_expression_simplify_to("(((R(6)-R(2))/4)/((R(6)+R(2))/4))+1", "((1/2)*R(6))/((R(6)+R(2))/4)"); + // Addition Matrix +#if MATRIX_EXACT_REDUCING + assert_parsed_expression_simplify_to("1+[[1,2,3][4,5,6]]", "[[2,3,4][5,6,7]]"); + assert_parsed_expression_simplify_to("[[1,2,3][4,5,6]]+1", "[[2,3,4][5,6,7]]"); + assert_parsed_expression_simplify_to("[[1,2][3,4]]+[[1,2,3][4,5,6]]", "undef"); + assert_parsed_expression_simplify_to("[[1,2,3][4,5,6]]+[[1,2,3][4,5,6]]", "[[2,4,6][8,10,12]]"); + assert_parsed_expression_simplify_to("2+[[1,2,3][4,5,6]]+[[1,2,3][4,5,6]]", "[[4,6,8][10,12,14]]"); + assert_parsed_expression_simplify_to("[[1,2,3][4,5,6]]+cos(2)+[[1,2,3][4,5,6]]", "[[2+cos(2),4+cos(2),6+cos(2)][8+cos(2),10+cos(2),12+cos(2)]]"); + assert_parsed_expression_simplify_to("[[1,2,3][4,5,6]]+10+[[1,2,3][4,5,6]]+R(2)", "[[12+R(2),14+R(2),16+R(2)][18+R(2),20+R(2),22+R(2)]]"); + assert_parsed_expression_simplify_to("[[1,2][3,4]]^(-1)+3", "inverse([[1,2][3,4]])+3"); + assert_parsed_expression_simplify_to("[[1,2][3,4]]^(-3)+3", "inverse([[37,54][81,118]])+3"); + assert_parsed_expression_simplify_to("[[1,2][3,4]]^(-3)+[[1,2][3,4]]", "inverse([[37,54][81,118]])+[[1,2][3,4]]"); + assert_parsed_expression_simplify_to("[[1,2][3,4]]^(-3)+[[1,2][3,4]]+4+R(2)", "inverse([[37,54][81,118]])+[[5+R(2),6+R(2)][7+R(2),8+R(2)]]"); + + // Multiplication Matrix + assert_parsed_expression_simplify_to("2*[[1,2,3][4,5,6]]", "[[2,4,6][8,10,12]]"); + assert_parsed_expression_simplify_to("[[1,2,3][4,5,6]]*R(2)", "[[R(2),2R(2),3R(2)][4R(2),5R(2),6R(2)]]"); + assert_parsed_expression_simplify_to("[[1,2][3,4]]*[[1,2,3][4,5,6]]", "[[9, 12, 15][19, 26, 33]]"); + assert_parsed_expression_simplify_to("[[1,2,3][4,5,6]]*[[1,2][2,3][5,6]]", "[[20, 26][44, 59]]"); + assert_parsed_expression_simplify_to("[[1,2,3,4][4,5,6,5]]*[[1,2][2,3][5,6]]", "undef"); + assert_parsed_expression_simplify_to("[[1,2][3,4]]^(-3)*[[1,2][3,4]]", "[[1,2][3,4]]^(-3)*[[1,2][3,4]]"); + assert_parsed_expression_simplify_to("[[1,2][3,4]]^(-3)*[[1,2,3][3,4,5]]*[[1,2][3,2][4,5]]*4", "[[37,54][81,118]]^(-1)*[[76,84][140,156]]"); + assert_parsed_expression_simplify_to("[[1,2][3,4]]^(-3)*[[1,2][3,4]]", "[[1,2][3,4]]^(-3)*[[1,2][3,4]]"); + + // Power Matrix + assert_parsed_expression_simplify_to("[[1,2,3][4,5,6][7,8,9]]^3", "[[468,576,684][1062,1305,1548][1656,2034,2412]]"); + assert_parsed_expression_simplify_to("[[1,2,3][4,5,6]]^(-1)", "undef"); + assert_parsed_expression_simplify_to("[[1,2][3,4]]^(-1)", "[[1,2][3,4]]^(-1)"); // TODO: Implement matrix inverse for dim < 3 + + // Function on matrix + assert_parsed_expression_simplify_to("abs([[1,-2][3,4]])", "[[1,2][3,4]]"); + assert_parsed_expression_simplify_to("acos([[1/R(2),1/2][1,-1]])", "[[P/4,P/3][0,P]]"); + assert_parsed_expression_simplify_to("asin([[1/R(2),1/2][1,-1]])", "[[P/4,P/6][P/2,-P/2]]"); + assert_parsed_expression_simplify_to("atan([[R(3),1][1/R(3),-1]])", "[[P/3,P/4][P/6,-P/4]]"); + assert_parsed_expression_simplify_to("acos([[1/R(2),1/2][1,-1]])", "[[P/4,P/3][0,P]]"); + assert_parsed_expression_simplify_to("binomial([[1,-2][3,4]], 2)", "undef"); + assert_parsed_expression_simplify_to("ceil([[1/R(2),1/2][1,-1.3]])", "[[ceil(R(2)/2),1][1,-1]]"); + assert_parsed_expression_simplify_to("confidence(1/3, 25)", "[[2/15,8/15]]"); + assert_parsed_expression_simplify_to("confidence(45, 25)", "undef"); + assert_parsed_expression_simplify_to("confidence(1/3, -34)", "undef"); + assert_parsed_expression_simplify_to("conj([[1/R(2),1/2][1,-1]])", "[[conj(1/R(2)),1/2][1,-1]]"); + assert_parsed_expression_simplify_to("cos([[P/3,0][P/7,P/2]])", "[[1/2,1][cos(P/7),0]]"); + assert_parsed_expression_simplify_to("diff([[P/3,0][P/7,P/2]],3)", "undef"); + assert_parsed_expression_simplify_to("det([[1,2][3,4]])", "det([[1,2][3,4]])"); // TODO: implement determinant if dim < 3 + assert_parsed_expression_simplify_to("det([[2,2][3,4]])", "det([[2,2][3,4]])"); + assert_parsed_expression_simplify_to("det([[2,2][3,3]])", "det([[2,2][3,3]])"); + assert_parsed_expression_simplify_to("quo([[2,2][3,3]],2)", "undef"); + assert_parsed_expression_simplify_to("rem([[2,2][3,3]],2)", "undef"); + assert_parsed_expression_simplify_to("[[1,2][3,4]]!", "[[1,2][6,24]]"); + assert_parsed_expression_simplify_to("floor([[1/R(2),1/2][1,-1.3]])", "[[floor(R(2)/2),0][1,-2]]"); + assert_parsed_expression_simplify_to("frac([[1/R(2),1/2][1,-1.3]])", "[[frac(R(2)/2),1/2][0,0.7]]"); + assert_parsed_expression_simplify_to("gcd([[1/R(2),1/2][1,-1.3]], [[1]])", "undef"); + assert_parsed_expression_simplify_to("asinh([[1/R(2),1/2][1,-1]])", "[[asinh(1/R(2)),asinh(1/2)][asinh(1),asinh(-1)]]"); + assert_parsed_expression_simplify_to("atanh([[R(3),1][1/R(3),-1]])", "[[atanh(R(3)),atanh(1)][atanh(1/R(3)),atanh(-1)]]"); + assert_parsed_expression_simplify_to("acosh([[1/R(2),1/2][1,-1]])", "[[acosh(1/R(2)),acosh(1/2)][acosh(1),acosh(-1)]]"); + assert_parsed_expression_simplify_to("sinh([[1/R(2),1/2][1,-1]])", "[[sinh(1/R(2)),sinh(1/2)][sinh(1),sinh(-1)]]"); + assert_parsed_expression_simplify_to("tanh([[R(3),1][1/R(3),-1]])", "[[tanh(R(3)),tanh(1)][tanh(1/R(3)),tanh(-1)]]"); + assert_parsed_expression_simplify_to("cosh([[1/R(2),1/2][1,-1]])", "[[cosh(1/R(2)),cosh(1/2)][cosh(1),cosh(-1)]]"); + assert_parsed_expression_simplify_to("im([[1/R(2),1/2][1,-1]])", "[[im(1/R(2)),0][0,0]]"); + assert_parsed_expression_simplify_to("int([[P/3,0][P/7,P/2]],3,2)", "undef"); + assert_parsed_expression_simplify_to("lcm(2, [[1]])", "undef"); + assert_parsed_expression_simplify_to("log([[R(2),1/2][1,3]])", "[[(1/2)*log(2),-log(2)][0,log(3)]]"); + assert_parsed_expression_simplify_to("log([[1/R(2),1/2][1,-3]])", "undef"); + assert_parsed_expression_simplify_to("log([[1/R(2),1/2][1,-3]],3)", "undef"); + assert_parsed_expression_simplify_to("ln([[R(2),1/2][1,3]])", "[[(1/2)*ln(2),-ln(2)][0,ln(3)]]"); + assert_parsed_expression_simplify_to("log([[1/R(2),1/2][1,-3]])", "undef"); + assert_parsed_expression_simplify_to("dim([[1/R(2),1/2,3][2,1,-3]])", "[[2,3]]"); + assert_parsed_expression_simplify_to("inverse([[1/R(2),1/2,3][2,1,-3]])", "undef"); + assert_parsed_expression_simplify_to("inverse([[1,2][3,4]])", "inverse([[1,2][3,4]])"); // TODO: implement matrix inverse if dim < 3 + assert_parsed_expression_simplify_to("trace([[1/R(2),1/2,3][2,1,-3]])", "undef"); + assert_parsed_expression_simplify_to("trace([[R(2),2][4,3+log(3)]])", "R(2)+3+log(3)"); + assert_parsed_expression_simplify_to("trace(R(2)+log(3))", "R(2)+log(3)"); + assert_parsed_expression_simplify_to("transpose([[1/R(2),1/2,3][2,1,-3]])", "[[1/R(2),2][1/2, 1][3,-3]]"); + assert_parsed_expression_simplify_to("transpose(R(4))", "2"); + assert_parsed_expression_simplify_to("root([[R(4)]],2)", "undef"); + assert_parsed_expression_simplify_to("root(4,3)", "4^(1/3)"); + assert_parsed_expression_simplify_to("-[[1/R(2),1/2,3][2,1,-3]]", "[[-1/R(2),-1/2,-3][-2,-1,3]]"); + assert_parsed_expression_simplify_to("permute([[1,-2][3,4]], 2)", "undef"); + assert_parsed_expression_simplify_to("prediction95(1/3, 25)", "[[1/3-49R(2)/375,1/3+49R(2)/375]]"); + assert_parsed_expression_simplify_to("prediction95(45, 25)", "undef"); + assert_parsed_expression_simplify_to("prediction95(1/3, -34)", "undef"); + assert_parsed_expression_simplify_to("product([[1,2][3,4]], 1/3, -34)", "product([[1,2][3,4]], 1/3, -34)"); + assert_parsed_expression_simplify_to("sum([[1,2][3,4]], 1/3, -34)", "sum([[1,2][3,4]], 1/3, -34)"); + assert_parsed_expression_simplify_to("re([[1/R(2),1/2][1,-1]])", "[[re(1/R(2)),1/2][1,-1]]"); + assert_parsed_expression_simplify_to("round([[1/R(2),1/2][1,-1]],2)", "undef"); + assert_parsed_expression_simplify_to("sin([[P/3,0][P/7,P/2]])", "[[R(3)/2,0][sin(P/7),1]]"); + assert_parsed_expression_simplify_to("R([[4,2][P/7,1]])", "[[2,R(2)][R(P/7),1]]"); + assert_parsed_expression_simplify_to("tan([[P/3,0][P/7,P/6]])", "[[R(3),0][tan(P/7),R(3)/3]]"); +#else + assert_parsed_expression_simplify_to("R([[4,2][P/7,1]])", "R([[4,2][P/7,1]])"); +#endif + /* Complex */ + assert_parsed_expression_simplify_to("I", "I"); + assert_parsed_expression_simplify_to("R(-33)", "R(33)*I"); + assert_parsed_expression_simplify_to("I^(3/5)", "X^(IP3/10)"); + + //Functions + assert_parsed_expression_simplify_to("binomial(20,3)", "1140"); + assert_parsed_expression_simplify_to("binomial(20,10)", "184756"); + 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"); + assert_parsed_expression_simplify_to("quo(19,0)", "undef"); + assert_parsed_expression_simplify_to("quo(-19,3)", "-7"); + assert_parsed_expression_simplify_to("rem(19,3)", "1"); + assert_parsed_expression_simplify_to("rem(-19,3)", "2"); + assert_parsed_expression_simplify_to("rem(19,0)", "undef"); + assert_parsed_expression_simplify_to("99!", "933262154439441526816992388562667004907159682643816214685929638952175999932299156089414639761565182862536979208272237582511852109168640000000000000000000000"); + assert_parsed_expression_simplify_to("floor(-1.3)", "-2"); + assert_parsed_expression_simplify_to("frac(-1.3)", "0.7"); + assert_parsed_expression_simplify_to("gcd(123,278)", "1"); + assert_parsed_expression_simplify_to("gcd(11,121)", "11"); + assert_parsed_expression_simplify_to("lcm(123,278)", "34194"); + assert_parsed_expression_simplify_to("lcm(11,121)", "121"); + assert_parsed_expression_simplify_to("root(4,3)", "4^(1/3)"); + 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"); + + assert_parsed_expression_simplify_to("1*tan(2)*tan(5)", "tan(2)*tan(5)"); + assert_parsed_expression_simplify_to("P+(3R(2)-2R(3))/25", "(3R(2)-2R(3)+25P)/25"); + assert_parsed_expression_simplify_to("-1/3", "-1/3"); + assert_parsed_expression_simplify_to("2+13cos(2)-23cos(2)", "2-10cos(2)"); + assert_parsed_expression_simplify_to("1/(R(2)+R(3))", "-(R(2)-R(3))"); + assert_parsed_expression_simplify_to("1/(5+R(3))", "(5-R(3))/22"); + assert_parsed_expression_simplify_to("1/(R(2)+4)", "(4-R(2))/14"); + assert_parsed_expression_simplify_to("1/(2R(2)-4)", "-R(2)/4-1/2"); + assert_parsed_expression_simplify_to("1/(-2R(2)+4)", "R(2)/4+1/2"); + assert_parsed_expression_simplify_to("5!", "120"); + assert_parsed_expression_simplify_to("1/3!", "1/6"); + assert_parsed_expression_simplify_to("(1/3)!", "undef"); + assert_parsed_expression_simplify_to("P!", "undef"); + assert_parsed_expression_simplify_to("X!", "undef"); + assert_parsed_expression_simplify_to("tan(62P/21)", "-tan(P/21)"); + assert_parsed_expression_simplify_to("cos(26P/21)/sin(25P/17)", "cos(5P/21)/sin(8P/17)"); + assert_parsed_expression_simplify_to("cos(62P/21)*P*3/sin(62P/21)", "-3P/tan(P/21)"); + assert_parsed_expression_simplify_to("cos(62P/21)/(P*3sin(62P/21))", "-1/(3Ptan(P/21))"); + assert_parsed_expression_simplify_to("sin(62P/21)*P*3/cos(62P/21)", "-3Ptan(P/21)"); + assert_parsed_expression_simplify_to("sin(62P/21)/(P*3cos(62P/21))", "-tan(P/21)/(3P)"); + assert_parsed_expression_simplify_to("-cos(P/62)ln(3)/(sin(P/62)P)", "-ln(3)/(tan(P/62)P)"); + assert_parsed_expression_simplify_to("-2cos(P/62)ln(3)/(sin(P/62)P)", "-2ln(3)/(tan(P/62)P)"); + assert_parsed_expression_simplify_to("0000.000000", "0"); + assert_parsed_expression_simplify_to(".000000", "0"); + assert_parsed_expression_simplify_to("0000", "0"); + assert_parsed_expression_simplify_to("0.1234567", "1234567/10000000"); + assert_parsed_expression_simplify_to("123.4567", "1234567/10000"); + assert_parsed_expression_simplify_to("0.1234", "1234/10000"); + assert_parsed_expression_simplify_to("0.1234000", "1234/10000"); + assert_parsed_expression_simplify_to("001234000", "1234000"); + assert_parsed_expression_simplify_to("001.234000E3", "1234"); + assert_parsed_expression_simplify_to("001234000E-4", "1234/10"); + assert_parsed_expression_simplify_to("1+1+ln(2)+(5+3*2)/9-4/7+1/98", "ln(2)+2347/882"); + assert_parsed_expression_simplify_to("1+1+ln(2)+(5+3*2)/9-4/7+1/98", "ln(2)+2347/882"); + assert_parsed_expression_simplify_to("1+1+ln(2)+(5+3*2)/9-4/7+1/98", "ln(2)+2347/882"); + assert_parsed_expression_simplify_to("3/4+5/4-12+1/567", "-5669/567"); + assert_parsed_expression_simplify_to("34/78+67^(-1)", "1178/2613"); + assert_parsed_expression_simplify_to("root(4,5)", "4^(1/5)"); + assert_parsed_expression_simplify_to("R(4)", "2"); + assert_parsed_expression_simplify_to("3^4", "81"); + assert_parsed_expression_simplify_to("3^(-4)", "1/81"); + assert_parsed_expression_simplify_to("12348/34564", "3087/8641"); + assert_parsed_expression_simplify_to("1256^(1/3)*x", "2*157^(1/3)*x"); + assert_parsed_expression_simplify_to("1256^(-1/3)", "2^(-1)*157^(-1/3)"); + assert_parsed_expression_simplify_to("32^(-1/5)", "1/2"); + assert_parsed_expression_simplify_to("ln(2+3)", "ln(5)"); + assert_parsed_expression_simplify_to("1-0.3-0.7", "0"); + assert_parsed_expression_simplify_to("(2+3-4)^(x)", "1"); + assert_parsed_expression_simplify_to("1^x", "1"); + assert_parsed_expression_simplify_to("x^1", "x"); + assert_parsed_expression_simplify_to("0^3", "0"); + assert_parsed_expression_simplify_to("0^0", "1"); + assert_parsed_expression_simplify_to("0^(-3)", "undef"); + assert_parsed_expression_simplify_to("0*x+B", "B"); + assert_parsed_expression_simplify_to("0*x*0*32*cos(3)", "0"); + assert_parsed_expression_simplify_to("1+2+0+cos(2)", "3+cos(2)"); + assert_parsed_expression_simplify_to("2+0", "2"); + assert_parsed_expression_simplify_to("3*A*B*C+4*cos(2)-2*A*B*C+A*B*C+ln(3)+4*A*B-5*A*B*C+cos(3)*ln(5)+cos(2)-45*cos(2)", "-3ABC+4AB-40cos(2)+cos(3)ln(5)+ln(3)"); + assert_parsed_expression_simplify_to("2*A+3*cos(2)+3+4*ln(5)+5*A+2*ln(5)+1+0", "7A+3cos(2)+6ln(5)+4"); + assert_parsed_expression_simplify_to("2.3*A+3*cos(2)+3+4*ln(5)+5*A+2*ln(5)+1.2+0.235", "73*A/10+3cos(2)+6ln(5)+4435/1000"); + assert_parsed_expression_simplify_to("2*A*B*C+2.3*A*B+3*A^2+5.2*A*B*C+4*A^2", "36/5*ABC+23/10*AB+7A^2"); + assert_parsed_expression_simplify_to("3*A^4*B^x*B^2*(A^2+2)*2*1.2", "36/5*A^6*B^(x+2)+72/5*A^4*B^(x+2)"); + assert_parsed_expression_simplify_to("A*(B+C)*(D+3)", "ABD+3AB+ACD+3AC"); + assert_parsed_expression_simplify_to("A/B", "A/B"); + assert_parsed_expression_simplify_to("-5P+3P", "-2P"); + assert_parsed_expression_simplify_to("(A*B)^2", "A^2*B^2"); + assert_parsed_expression_simplify_to("(A*B)^2*A+4*A^3", "A^3*B^2+4A^3"); + assert_parsed_expression_simplify_to("(A*3)^2*A+4*A^3", "13A^3"); + assert_parsed_expression_simplify_to("A^2^2*A+4*A^3", "A^5+4A^3"); + assert_parsed_expression_simplify_to("4^0.5", "2"); + assert_parsed_expression_simplify_to("8^0.5", "2R(2)"); + assert_parsed_expression_simplify_to("(12^4*3)^(0.5)", "144*R(3)"); + assert_parsed_expression_simplify_to("(2^A)^B", "2^(AB)"); + assert_parsed_expression_simplify_to("(2*A)^B", "2^B*A^B"); + assert_parsed_expression_simplify_to("(12^4*x)^(0.5)", "144R(x)"); + assert_parsed_expression_simplify_to("45^2", "2025"); + assert_parsed_expression_simplify_to("1-3+A-5+2A-4A", "-7-A"); + assert_parsed_expression_simplify_to("(1/2)*A/B", "A/(2B)"); + assert_parsed_expression_simplify_to("0.5+4*A*B-2.3+2*A*B-2*B*A^C-cos(4)+2*A^C*B+A*B*C*D", "ABCD+6AB-cos(4)-9/5"); + assert_parsed_expression_simplify_to("1+2", "3"); + assert_parsed_expression_simplify_to("123456789123456789+112233445566778899", "235690234690235688"); + assert_parsed_expression_simplify_to("1+2+3+4+5+6", "21"); + assert_parsed_expression_simplify_to("1-2+3-4+5-6", "-3"); + assert_parsed_expression_simplify_to("987654321123456789*998877665544332211", "986545842648570754445552922919330479"); + assert_parsed_expression_simplify_to("2/3", "2/3"); + assert_parsed_expression_simplify_to("9/17+5/4", "121/68"); + assert_parsed_expression_simplify_to("1/2*3/4", "3/8"); + assert_parsed_expression_simplify_to("0*2/3", "0"); + assert_parsed_expression_simplify_to("1+(1/(1+1/(1+1/(1+1))))", "8/5"); + assert_parsed_expression_simplify_to("1+2/(3+4/(5+6/(7+8)))", "155/101"); + assert_parsed_expression_simplify_to("3/4*16/12", "1"); + assert_parsed_expression_simplify_to("3/4*(8+8)/12", "1"); + assert_parsed_expression_simplify_to("916791/794976477", "305597/264992159"); + assert_parsed_expression_simplify_to("321654987123456789/112233445566778899", "3249040273974311/1133671167341201"); + assert_parsed_expression_simplify_to("0.1+0.2", "3/10"); + assert_parsed_expression_simplify_to("2^3", "8"); + assert_parsed_expression_simplify_to("(-1)*(-1)", "1"); + assert_parsed_expression_simplify_to("(-2)^2", "4"); + assert_parsed_expression_simplify_to("(-3)^3", "-27"); + assert_parsed_expression_simplify_to("(1/2)^-1", "2"); + assert_parsed_expression_simplify_to("R(32)", "4*R(2)"); + assert_parsed_expression_simplify_to("R(3^2)", "3"); + assert_parsed_expression_simplify_to("2^(2+P)", "4*2^P"); + assert_parsed_expression_simplify_to("R(15241578780673678515622620750190521)", "123456789123456789"); + assert_parsed_expression_simplify_to("R(P)^2", "P"); + assert_parsed_expression_simplify_to("R(P^2)", "P"); + assert_parsed_expression_simplify_to("R((-P)^2)", "P"); + assert_parsed_expression_simplify_to("R(x*144)", "12*R(x)"); + assert_parsed_expression_simplify_to("R(x*144*P^2)", "12*P*R(x)"); + assert_parsed_expression_simplify_to("R(x*144*P)", "12*R(xP)"); + assert_parsed_expression_simplify_to("abs(P)", "P"); + assert_parsed_expression_simplify_to("abs(-P)", "P"); + assert_parsed_expression_simplify_to("R(2)*R(3)", "R(6)"); + assert_parsed_expression_simplify_to("2*2^P", "2*2^P"); + assert_parsed_expression_simplify_to("A-A", "0"); + assert_parsed_expression_simplify_to("A-A+2cos(2)+B-B-cos(2)", "cos(2)"); + assert_parsed_expression_simplify_to("A^3*B*A^(-3)", "B"); + assert_parsed_expression_simplify_to("A^3*A^(-3)", "1"); + assert_parsed_expression_simplify_to("2^P*(1/2)^P", "1"); + assert_parsed_expression_simplify_to("A^3*A^(-3)", "1"); + assert_parsed_expression_simplify_to("1+A+2+B+3", "6+A+B"); + assert_parsed_expression_simplify_to("(x+1)*(x+2)", "x^2+3x+2"); + + assert_parsed_expression_simplify_to("(x+1)*(x-1)", "-1+x^2"); + assert_parsed_expression_simplify_to("11P/(22P+11P)", "1/3"); + assert_parsed_expression_simplify_to("11/(22P+11P)", "1/(3P)"); + assert_parsed_expression_simplify_to("A^2*BA^(-2)*B^(-2)", "1/B"); + assert_parsed_expression_simplify_to("A^(-1)*B^(-1)", "1/(AB)"); + assert_parsed_expression_simplify_to("-11/(22P+11P)", "-1/(3P)"); + assert_parsed_expression_simplify_to("-A", "-A"); + assert_parsed_expression_simplify_to("1/(x+1)+1/(P+2)", "(P+x+3)/((x+1)(P+2))"); + assert_parsed_expression_simplify_to("1/x^2+1/(x^2*P)", "(P+1)/(x^2*P)"); + assert_parsed_expression_simplify_to("1/x^2+1/(x^3*P)", "(Px+1)/(x^3*P)"); + assert_parsed_expression_simplify_to("4x/x^2+3P/(x^3*P)", "(4*x^2*P+3P)/(x^3*P)"); + assert_parsed_expression_simplify_to("x^(1/2)", "R(x)"); + assert_parsed_expression_simplify_to("x^(-1/2)", "1/R(x)"); + assert_parsed_expression_simplify_to("x^(1/7)", "root(x, 7)"); + assert_parsed_expression_simplify_to("x^(-1/7)", "1/root(x, 7)"); + assert_parsed_expression_simplify_to("log(12925)", "log(47)+log(11)+2*log(5)"); + assert_parsed_expression_simplify_to("ln(12925)", "ln(47)+ln(11)+2*ln(5)"); + assert_parsed_expression_simplify_to("log(1742279/12925, 6)", "log(7, 6)+3log(11, 6)+log(17,6)-log(47,6)-2*log(5,6)"); + assert_parsed_expression_simplify_to("ln(2/3)", "ln(2)-ln(3)"); + assert_parsed_expression_simplify_to("log(1742279/12925, -6)", "undef"); + assert_parsed_expression_simplify_to("(1+R(2))/5", "(1+R(2))/5"); + assert_parsed_expression_simplify_to("(2+R(6))^2", "(2+R(6))^2"); // Check for parenthesis + assert_parsed_expression_simplify_to("cos(0)", "1"); + assert_parsed_expression_simplify_to("cos(P)", "-1"); + assert_parsed_expression_simplify_to("cos(P*4/7)", "-cos(3P/7)"); + assert_parsed_expression_simplify_to("cos(P*35/29)", "-cos(P*6/29)"); + assert_parsed_expression_simplify_to("cos(-P*35/29)", "-cos(P*6/29)"); + assert_parsed_expression_simplify_to("cos(P*340000)", "1"); + assert_parsed_expression_simplify_to("cos(-P*340001)", "-1"); + assert_parsed_expression_simplify_to("cos(-P*R(2))", "cos(P*R(2))"); + assert_parsed_expression_simplify_to("cos(1311P/6)", "0"); + assert_parsed_expression_simplify_to("cos(P/12)", "(R(6)+R(2))/4"); + assert_parsed_expression_simplify_to("cos(-P/12)", "(R(6)+R(2))/4"); + assert_parsed_expression_simplify_to("cos(-P17/8)", "R(R(2)+2)/2"); + assert_parsed_expression_simplify_to("cos(41P/6)", "-R(3)/2"); + assert_parsed_expression_simplify_to("cos(P/4+1000P)", "R(2)/2"); + assert_parsed_expression_simplify_to("cos(-P/3)", "1/2"); + assert_parsed_expression_simplify_to("cos(41P/5)", "(5^(1/2)+1)*4^(-1)"); + assert_parsed_expression_simplify_to("cos(7P/10)", "-R(5/8-R(5)/8)"); + assert_parsed_expression_simplify_to("sin(0)", "0"); + assert_parsed_expression_simplify_to("sin(P)", "0"); + assert_parsed_expression_simplify_to("sin(P*35/29)", "-sin(P*6/29)"); + assert_parsed_expression_simplify_to("sin(-P*35/29)", "sin(P*6/29)"); + assert_parsed_expression_simplify_to("sin(P*340000)", "0"); + assert_parsed_expression_simplify_to("sin(P*340001)", "0"); + assert_parsed_expression_simplify_to("sin(-P*340001)", "0"); + assert_parsed_expression_simplify_to("sin(P/12)", "(R(6)-R(2))/4"); + assert_parsed_expression_simplify_to("sin(-P/12)", "(R(2)-R(6))/4"); + assert_parsed_expression_simplify_to("sin(-P*R(2))", "-sin(P*R(2))"); + assert_parsed_expression_simplify_to("sin(1311P/6)", "1"); + assert_parsed_expression_simplify_to("sin(-P17/8)", "-R(-R(2)+2)/2"); + assert_parsed_expression_simplify_to("sin(41P/6)", "1/2"); + assert_parsed_expression_simplify_to("sin(-3P/10)", "(-1-R(5))/4"); + assert_parsed_expression_simplify_to("sin(P/4+1000P)", "R(2)/2"); + assert_parsed_expression_simplify_to("sin(-P/3)", "-R(3)/2"); + assert_parsed_expression_simplify_to("sin(17P/5)", "-R(5/8+R(5)/8)"); + assert_parsed_expression_simplify_to("sin(P/5)", "R(5/8-R(5)/8)"); + + assert_parsed_expression_simplify_to("tan(0)", "0"); + assert_parsed_expression_simplify_to("tan(P)", "0"); + assert_parsed_expression_simplify_to("tan(P*35/29)", "tan(P*6/29)"); + assert_parsed_expression_simplify_to("tan(-P*35/29)", "-tan(P*6/29)"); + assert_parsed_expression_simplify_to("tan(P*340000)", "0"); + assert_parsed_expression_simplify_to("tan(P*340001)", "0"); + assert_parsed_expression_simplify_to("tan(-P*340001)", "0"); + assert_parsed_expression_simplify_to("tan(P/12)", "2-R(3)"); + assert_parsed_expression_simplify_to("tan(-P/12)", "R(3)-2"); + assert_parsed_expression_simplify_to("tan(-P*R(2))", "-tan(P*R(2))"); + assert_parsed_expression_simplify_to("tan(1311P/6)", "undef"); + assert_parsed_expression_simplify_to("tan(-P17/8)", "1-R(2)"); + assert_parsed_expression_simplify_to("tan(41P/6)", "-1/R(3)"); + assert_parsed_expression_simplify_to("tan(P/4+1000P)", "1"); + assert_parsed_expression_simplify_to("tan(-P/3)", "-R(3)"); + assert_parsed_expression_simplify_to("tan(-P/10)", "-R(1-2/R(5))"); + assert_parsed_expression_simplify_to("sin(x)/cos(x)", "tan(x)"); + assert_parsed_expression_simplify_to("cos(x)/sin(x)", "1/tan(x)"); + assert_parsed_expression_simplify_to("sin(x)*P/cos(x)", "P*tan(x)"); + assert_parsed_expression_simplify_to("sin(x)/(P*cos(x))", "tan(x)/P"); + assert_parsed_expression_simplify_to("56^56", "79164324866862966607842406018063254671922245312646690223362402918484170424104310169552592050323456"); + + assert_parsed_expression_simplify_to("acos(-1/2)", "P*2*3^(-1)"); + assert_parsed_expression_simplify_to("acos(-1.2)", "undef"); + assert_parsed_expression_simplify_to("acos(cos(2/3))", "2/3"); + assert_parsed_expression_simplify_to("acos(cos(3/2))", "3/2"); + assert_parsed_expression_simplify_to("cos(acos(3/2))", "undef"); + assert_parsed_expression_simplify_to("cos(acos(2/3))", "2/3"); + assert_parsed_expression_simplify_to("acos(cos(12))", "acos(cos(12))"); + assert_parsed_expression_simplify_to("acos(cos(4P/7))", "4P/7"); + assert_parsed_expression_simplify_to("acos(-cos(2))", "P-2"); + assert_parsed_expression_simplify_to("asin(-1/2)", "P*(-6)^(-1)"); + assert_parsed_expression_simplify_to("asin(-1.2)", "undef"); + assert_parsed_expression_simplify_to("asin(sin(2/3))", "2/3"); + assert_parsed_expression_simplify_to("sin(asin(2/3))", "2/3"); + assert_parsed_expression_simplify_to("sin(asin(3/2))", "sin(asin(3/2))"); + assert_parsed_expression_simplify_to("asin(sin(3/2))", "3/2"); + assert_parsed_expression_simplify_to("asin(sin(12))", "asin(sin(12))"); + assert_parsed_expression_simplify_to("asin(sin(-P/7))", "-P/7"); + assert_parsed_expression_simplify_to("asin(sin(-R(2)))", "-R(2)"); + assert_parsed_expression_simplify_to("atan(-1)", "P*(-4)^(-1)"); + assert_parsed_expression_simplify_to("atan(-1.2)", "atan(-1.2)"); + assert_parsed_expression_simplify_to("atan(tan(2/3))", "2/3"); + assert_parsed_expression_simplify_to("tan(atan(2/3))", "2/3"); + assert_parsed_expression_simplify_to("tan(atan(5/2))", "5/2"); + assert_parsed_expression_simplify_to("atan(tan(5/2))", "atan(tan(5/2))"); + assert_parsed_expression_simplify_to("atan(tan(5/2))", "atan(tan(5/2))"); + assert_parsed_expression_simplify_to("atan(tan(-P/7))", "-P/7"); + assert_parsed_expression_simplify_to("atan(R(3))", "P/3"); + assert_parsed_expression_simplify_to("atan(tan(-R(2)))", "-R(2)"); + + assert_parsed_expression_simplify_to("cos(0)", "1", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("cos(180)", "-1", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("cos(720/7)", "-cos(540/7)", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("cos(6300/29)", "-cos(1080/29)", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("cos(-6300/29)", "-cos(1080/29)", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("cos(61200000)", "1", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("cos(-61200180)", "-1", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("cos(-180*R(2))", "cos(180*R(2))", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("cos(39330)", "0", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("cos(15)", "(R(6)+R(2))/4", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("cos(-15)", "(R(6)+R(2))/4", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("cos(-765/2)", "R(R(2)+2)/2", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("cos(7380/6)", "-R(3)/2", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("cos(180045)", "R(2)/2", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("cos(-60)", "1/2", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("cos(7380/5)", "(5^(1/2)+1)*4^(-1)", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("cos(112.5)", "-R(2-R(2))/2", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("sin(0)", "0", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("sin(180)", "0", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("sin(6300/29)", "-sin(1080/29)", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("sin(-6300/29)", "sin(1080/29)", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("sin(61200000)", "0", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("sin(61200180)", "0", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("sin(-61200180)", "0", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("sin(15)", "(R(6)-R(2))/4", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("sin(-15)", "(R(2)-R(6))/4", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("sin(-180*R(2))", "-sin(180*R(2))", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("sin(39330)", "1", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("sin(-765/2)", "-R(-R(2)+2)/2", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("sin(1230)", "1/2", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("sin(180045)", "R(2)/2", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("sin(-60)", "-R(3)/2", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("sin(612)", "-R(5/8+R(5)/8)", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("sin(36)", "R(5/8-R(5)/8)", Expression::AngleUnit::Degree); + + assert_parsed_expression_simplify_to("tan(0)", "0", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("tan(180)", "0", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("tan(6300/29)", "tan(1080/29)", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("tan(-6300/29)", "-tan(1080/29)", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("tan(61200000)", "0", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("tan(61200180)", "0", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("tan(-61200180)", "0", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("tan(15)", "2-R(3)", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("tan(-15)", "R(3)-2", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("tan(-180*R(2))", "-tan(180*R(2))", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("tan(39330)", "undef", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("tan(-382.5)", "1-R(2)", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("tan(1230)", "-1/R(3)", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("tan(180045)", "1", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("tan(-60)", "-R(3)", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("acos(-1/2)", "120", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("acos(-1.2)", "undef", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("acos(cos(2/3))", "2/3", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("acos(cos(190))", "acos(cos(190))", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("acos(cos(75))", "75", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("cos(acos(190))", "undef", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("cos(acos(75))", "undef", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("acos(cos(12))", "12", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("acos(cos(720/7))", "720/7", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("asin(-1/2)", "-30", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("asin(-1.2)", "undef", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("asin(sin(75))", "75", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("sin(asin(75))", "undef", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("sin(asin(190))", "sin(asin(190))", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("asin(sin(32))", "32", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("asin(sin(400))", "asin(sin(400))", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("asin(sin(-180/7))", "-180/7", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("atan(-1)", "-45", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("atan(-1.2)", "atan(-1.2)", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("atan(tan(-45))", "-45", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("tan(atan(120))", "120", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("tan(atan(2293))", "2293", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("atan(tan(2293))", "-47", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("atan(tan(1808))", "8", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("atan(tan(-180/7))", "-180/7", Expression::AngleUnit::Degree); + assert_parsed_expression_simplify_to("atan(R(3))", "60", Expression::AngleUnit::Degree); + + assert_parsed_expression_simplify_to("1/(3R(2))", "R(2)/6"); + assert_parsed_expression_simplify_to("1/(R(2)ln(3))", "R(2)/(2ln(3))"); + + + assert_parsed_expression_simplify_to("A+B-A-B", "0"); + assert_parsed_expression_simplify_to("A+B+(-1)*A+(-1)*B", "0"); + assert_parsed_expression_simplify_to("ln(R(2))", "ln(2)/2"); + assert_parsed_expression_simplify_to("R(3/2)", "R(6)/2"); + assert_parsed_expression_simplify_to("tan(3)ln(2)+P", "tan(3)ln(2)+P"); + assert_parsed_expression_simplify_to("ln(X^3)", "3"); + assert_parsed_expression_simplify_to("log(10)", "1"); + assert_parsed_expression_simplify_to("log(R(3),R(3))", "1"); + assert_parsed_expression_simplify_to("X^ln(3)", "3"); + assert_parsed_expression_simplify_to("X^ln(R(3))", "R(3)"); + assert_parsed_expression_simplify_to("P^log(R(3),P)", "R(3)"); + assert_parsed_expression_simplify_to("10^log(P)", "P"); + assert_parsed_expression_simplify_to("log(1/R(2))", "-log(2)/2"); + assert_parsed_expression_simplify_to("log(-I)", "log(-I)"); + assert_parsed_expression_simplify_to("R(-I)", "R(-I)"); + assert_parsed_expression_simplify_to("X^ln(65)", "65"); + assert_parsed_expression_simplify_to("X^ln(PX)", "PX"); + assert_parsed_expression_simplify_to("X^log(PX)", "X^(log(P)+log(X))"); + assert_parsed_expression_simplify_to("R(X^2)", "X"); + assert_parsed_expression_simplify_to("ln(X^(IP/7))", "IP/7"); + assert_parsed_expression_simplify_to("log(10^24)", "24"); + assert_parsed_expression_simplify_to("log((23P)^4,23P)", "4"); + assert_parsed_expression_simplify_to("log(10^(2+P))", "2+P"); + + //assert_parsed_expression_simplify_to("log(cos(9)^ln(6), cos(9))", "ln(2)+ln(3)"); // TODO: for this to work, we must know the sign of cos(9) + //assert_parsed_expression_simplify_to("log(cos(9)^ln(6), 9)", "ln(6)*log(cos(9), 9)"); // TODO: for this to work, we must know the sign of cos(9) + assert_parsed_expression_simplify_to("IIII", "1"); + assert_parsed_expression_simplify_to("Acos(9)IIln(2)", "-Acos(9)ln(2)"); + assert_parsed_expression_simplify_to("(R(2)+R(2)*I)/2(R(2)+R(2)*I)/2(R(2)+R(2)*I)/2", "(R(2)+R(2)*I)/(2(2R(2)+2R(2)*I)^2)"); + assert_parsed_expression_simplify_to("tan(tan(tan(tan(9))))", "tan(tan(tan(tan(9))))"); + assert_parsed_expression_simplify_to("999^999", "999^999"); + assert_parsed_expression_simplify_to("999^(10000/3)", "999^(10000/3)"); + + /* This does not work but should not as it is above k_primorial32 = 1*3*5*7*11*... (product of first 32 primes. */ + //assert_parsed_expression_simplify_to("1881676377434183981909562699940347954480361860897069^(1/3)", "123456789123456789"); + + //assert_parsed_expression_simplify_to("1/sqrt(2)", "sqrt(2)/2"); +} diff --git a/poincare/test/simplify_utils.cpp b/poincare/test/simplify_utils.cpp index d26ffd067..cb9381d46 100644 --- a/poincare/test/simplify_utils.cpp +++ b/poincare/test/simplify_utils.cpp @@ -21,11 +21,10 @@ bool simplifies_to(const char * input_string, const char * expected_string) { print_expression(input); #endif - Expression * simplified = input->simplify(); - assert(simplified != nullptr); + Expression::simplify(&input); #if POINCARE_TESTS_PRINT_EXPRESSIONS cout << "Simplified = " << endl; - print_expression(simplified); + print_expression(input); #endif Expression * expected = Expression::parse(expected_string); @@ -35,10 +34,9 @@ bool simplifies_to(const char * input_string, const char * expected_string) { print_expression(expected); #endif - bool isIdentical = simplified->isIdenticalTo(expected); + bool isIdentical = input->isIdentical(expected); delete expected; - delete simplified; delete input; return isIdentical; @@ -63,7 +61,7 @@ bool identical_to(const char * input_string, const char * expected_string) { print_expression(expected); #endif - bool isIdentical = input->isIdenticalTo(expected); + bool isIdentical = input->isIdentical(expected); delete expected; delete input; @@ -90,25 +88,21 @@ bool equivalent_to(const char * input_string, const char * expected_string) { print_expression(expected); #endif - Expression * simplified_input = input->simplify(); - assert(simplified_input != nullptr); + Expression::simplify(&input); #if POINCARE_TESTS_PRINT_EXPRESSIONS cout << "Simplified Input = " << endl; - print_expression(simplified_input); + print_expression(input); #endif - Expression * simplified_expected = Expression::parse(expected_string); - assert(simplified_expected != nullptr); + Expression::simplify(&expected); #if POINCARE_TESTS_PRINT_EXPRESSIONS cout << "Simplified Expected = " << endl; - print_expression(simplified_expected); + print_expression(expected); #endif - bool isEquivalent = simplified_input->isIdenticalTo(simplified_expected); + bool isEquivalent = input->isIdentical(expected); delete expected; delete input; - delete simplified_expected; - delete simplified_input; return isEquivalent; } diff --git a/poincare/test/simplify_utils.h b/poincare/test/simplify_utils.h index 28dd36297..98805b49e 100644 --- a/poincare/test/simplify_utils.h +++ b/poincare/test/simplify_utils.h @@ -6,6 +6,8 @@ bool simplifies_to(const char * input_string, const char * expected_string); /* Tests that the first expression is identical to the second. */ bool identical_to(const char * input_string, const char * expected_string); + +/* Tests that the first and the second expressions simplify to the same expression. */ bool equivalent_to(const char * input_string, const char * expected_string); #endif // POINCARE_TEST_SIMPLIFY_UTILS_H diff --git a/poincare/test/trigo.cpp b/poincare/test/trigo.cpp index 163c6317a..0ba08252e 100644 --- a/poincare/test/trigo.cpp +++ b/poincare/test/trigo.cpp @@ -7,20 +7,18 @@ using namespace Poincare; QUIZ_CASE(poincare_parse_trigo) { -#if POINCARE_SIMPLIFY - assert_parsed_simplified_expression_type("sin(0)", Expression::Type::Sine); - assert_parsed_simplified_expression_type("cos(0)", Expression::Type::Cosine); - assert_parsed_simplified_expression_type("tan(0)", Expression::Type::Tangent); - assert_parsed_simplified_expression_type("cosh(0)", Expression::Type::HyperbolicCosine); - assert_parsed_simplified_expression_type("sinh(0)", Expression::Type::HyperbolicSine); - assert_parsed_simplified_expression_type("tanh(0)", Expression::Type::HyperbolicTangent); - assert_parsed_simplified_expression_type("acos(0)", Expression::Type::ArcCosine); - assert_parsed_simplified_expression_type("asin(0)", Expression::Type::ArcSine); - assert_parsed_simplified_expression_type("atan(0)", Expression::Type::ArcTangent); - assert_parsed_simplified_expression_type("acosh(0)", Expression::Type::HyperbolicArcCosine); - assert_parsed_simplified_expression_type("asinh(0)", Expression::Type::HyperbolicArcSine); - assert_parsed_simplified_expression_type("atanh(0)", Expression::Type::HyperbolicArcTangent); -#endif + assert_parsed_expression_type("sin(0)", Expression::Type::Sine); + assert_parsed_expression_type("cos(0)", Expression::Type::Cosine); + assert_parsed_expression_type("tan(0)", Expression::Type::Tangent); + assert_parsed_expression_type("cosh(0)", Expression::Type::HyperbolicCosine); + assert_parsed_expression_type("sinh(0)", Expression::Type::HyperbolicSine); + assert_parsed_expression_type("tanh(0)", Expression::Type::HyperbolicTangent); + assert_parsed_expression_type("acos(0)", Expression::Type::ArcCosine); + assert_parsed_expression_type("asin(0)", Expression::Type::ArcSine); + assert_parsed_expression_type("atan(0)", Expression::Type::ArcTangent); + assert_parsed_expression_type("acosh(0)", Expression::Type::HyperbolicArcCosine); + assert_parsed_expression_type("asinh(0)", Expression::Type::HyperbolicArcSine); + assert_parsed_expression_type("atanh(0)", Expression::Type::HyperbolicArcTangent); } QUIZ_CASE(poincare_trigo_evaluate) {