[apps/calculation] Add additional outputs for matrix

Change-Id: Ia4b285eb0f28eaed838d32a1fdfb785d13664f65
This commit is contained in:
Hugo Saint-Vignes
2020-06-29 15:28:19 +02:00
committed by Émilie Feral
parent 007c38652f
commit f00c135b69
20 changed files with 253 additions and 112 deletions

View File

@@ -17,6 +17,7 @@ app_calculation_src = $(addprefix apps/calculation/,\
additional_outputs/integer_list_controller.cpp \
additional_outputs/scrollable_three_expressions_cell.cpp \
additional_outputs/list_controller.cpp \
additional_outputs/matrix_list_controller.cpp \
additional_outputs/rational_list_controller.cpp \
additional_outputs/trigonometry_graph_cell.cpp \
additional_outputs/trigonometry_list_controller.cpp \

View File

@@ -11,7 +11,7 @@ ExpressionsListController::ExpressionsListController(EditExpressionController *
ListController(editExpressionController),
m_cells{}
{
for (int i = 0; i < k_maxNumberOfCells; i++) {
for (int i = 0; i < k_maxNumberOfRows; i++) {
m_cells[i].setParentResponder(m_listController.selectableTableView());
}
}
@@ -21,15 +21,17 @@ void ExpressionsListController::didEnterResponderChain(Responder * previousFirst
}
int ExpressionsListController::reusableCellCount(int type) {
return k_maxNumberOfCells;
return k_maxNumberOfRows;
}
void ExpressionsListController::viewDidDisappear() {
ListController::viewDidDisappear();
// Reset cell memoization to avoid taking extra space in the pool
for (int i = 0; i < k_maxNumberOfCells; i++) {
// Reset layout and cell memoization to avoid taking extra space in the pool
for (int i = 0; i < k_maxNumberOfRows; i++) {
m_cells[i].setLayout(Layout());
m_layouts[i] = Layout();
}
m_expression = Expression();
}
HighlightCell * ExpressionsListController::reusableCell(int index, int type) {
@@ -43,24 +45,34 @@ KDCoordinate ExpressionsListController::rowHeight(int j) {
}
void ExpressionsListController::willDisplayCellForIndex(HighlightCell * cell, int index) {
/* Note : To further optimize memoization space in the pool, layout
* serialization could be memoized instead, and layout would be recomputed
* here, when setting cell's layout. */
ExpressionTableCellWithPointer * myCell = static_cast<ExpressionTableCellWithPointer *>(cell);
myCell->setLayout(layoutAtIndex(index));
myCell->setAccessoryMessage(messageAtIndex(index));
myCell->reloadScroll();
}
int ExpressionsListController::numberOfRows() const {
int nbOfRows = 0;
for (size_t i = 0; i < k_maxNumberOfRows; i++) {
if (!m_layouts[i].isUninitialized()) {
nbOfRows++;
}
}
return nbOfRows;
}
void ExpressionsListController::setExpression(Poincare::Expression e) {
// Reinitialize memoization
for (int i = 0; i < k_maxNumberOfCells; i++) {
for (int i = 0; i < k_maxNumberOfRows; i++) {
m_layouts[i] = Layout();
}
m_expression = e;
}
Poincare::Layout ExpressionsListController::layoutAtIndex(int index) {
if (m_layouts[index].isUninitialized()) {
computeLayoutAtIndex(index);
}
assert(!m_layouts[index].isUninitialized());
return m_layouts[index];
}

View File

@@ -22,22 +22,22 @@ public:
KDCoordinate rowHeight(int j) override;
int typeAtLocation(int i, int j) override { return 0; }
void willDisplayCellForIndex(HighlightCell * cell, int index) override;
int numberOfRows() const override;
// IllustratedListController
void setExpression(Poincare::Expression e) override;
protected:
constexpr static int k_maxNumberOfCells = 4;
constexpr static int k_maxNumberOfRows = 5;
int textAtIndex(char * buffer, size_t bufferSize, int index) override;
Poincare::Expression m_expression;
// Memoization of layouts
mutable Poincare::Layout m_layouts[k_maxNumberOfCells];
mutable Poincare::Layout m_layouts[k_maxNumberOfRows];
private:
Poincare::Layout layoutAtIndex(int index);
virtual void computeLayoutAtIndex(int index) = 0;
virtual I18n::Message messageAtIndex(int index) = 0;
// Cells
ExpressionTableCellWithPointer m_cells[k_maxNumberOfCells];
ExpressionTableCellWithPointer m_cells[k_maxNumberOfRows];
};
}

View File

@@ -11,10 +11,6 @@ using namespace Shared;
namespace Calculation {
int IntegerListController::numberOfRows() const {
return 3 + factorExpressionIsComputable();
}
Integer::Base baseAtIndex(int index) {
switch (index) {
case 0:
@@ -27,12 +23,20 @@ Integer::Base baseAtIndex(int index) {
}
}
void IntegerListController::computeLayoutAtIndex(int index) {
assert(m_expression.type() == ExpressionNode::Type::BasedInteger);
// For index = k_indexOfFactorExpression, the layout is assumed to be alreday memoized because it is needed to compute the numberOfRows
assert(index < k_indexOfFactorExpression);
Integer i = static_cast<BasedInteger &>(m_expression).integer();
m_layouts[index] = i.createLayout(baseAtIndex(index));
void IntegerListController::setExpression(Poincare::Expression e) {
ExpressionsListController::setExpression(e);
static_assert(k_maxNumberOfRows >= k_indexOfFactorExpression + 1, "k_maxNumberOfRows must be greater than k_indexOfFactorExpression");
assert(!m_expression.isUninitialized() && m_expression.type() == ExpressionNode::Type::BasedInteger);
Integer integer = static_cast<BasedInteger &>(m_expression).integer();
for (int index = 0; index < k_indexOfFactorExpression; ++index) {
m_layouts[index] = integer.createLayout(baseAtIndex(index));
}
// Computing factorExpression
Expression factor = Factor::Builder(m_expression.clone());
PoincareHelpers::Simplify(&factor, App::app()->localContext(), ExpressionNode::ReductionTarget::User);
if (!factor.isUndefined()) {
m_layouts[k_indexOfFactorExpression] = PoincareHelpers::CreateLayout(factor);
}
}
I18n::Message IntegerListController::messageAtIndex(int index) {
@@ -48,20 +52,4 @@ I18n::Message IntegerListController::messageAtIndex(int index) {
}
}
bool IntegerListController::factorExpressionIsComputable() const {
if (!m_layouts[k_indexOfFactorExpression].isUninitialized()) {
// The factor expression is already memoized
return !m_layouts[k_indexOfFactorExpression].isEmpty();
}
Poincare::Context * context = App::app()->localContext();
Expression factor = Factor::Builder(m_expression.clone());
PoincareHelpers::Simplify(&factor, context, ExpressionNode::ReductionTarget::User);
if (!factor.isUndefined()) {
m_layouts[k_indexOfFactorExpression] = PoincareHelpers::CreateLayout(factor);
return true;
}
m_layouts[k_indexOfFactorExpression] = EmptyLayout::Builder();
return false;
}
}

View File

@@ -10,13 +10,11 @@ public:
IntegerListController(EditExpressionController * editExpressionController) :
ExpressionsListController(editExpressionController) {}
//ListViewDataSource
int numberOfRows() const override;
void setExpression(Poincare::Expression e) override;
private:
static constexpr int k_indexOfFactorExpression = 3;
void computeLayoutAtIndex(int index) override;
I18n::Message messageAtIndex(int index) override;
bool factorExpressionIsComputable() const;
};
}

View File

@@ -0,0 +1,103 @@
#include "matrix_list_controller.h"
#include "../app.h"
#include "../../shared/poincare_helpers.h"
#include <poincare_nodes.h>
#include <poincare/matrix.h>
#include <string.h>
using namespace Poincare;
using namespace Shared;
namespace Calculation {
void MatrixListController::setExpression(Poincare::Expression e) {
ExpressionsListController::setExpression(e);
assert(!m_expression.isUninitialized());
static_assert(k_maxNumberOfRows >= k_maxNumberOfOutputRows, "k_maxNumberOfRows must be greater than k_maxNumberOfOutputRows");
Poincare::Preferences * preferences = Poincare::Preferences::sharedPreferences();
Poincare::Preferences::ComplexFormat currentComplexFormat = preferences->complexFormat();
if (currentComplexFormat == Poincare::Preferences::ComplexFormat::Real) {
/* Temporary change complex format to avoid all additional expressions to be
* "unreal" (with [i] for instance). As additional results are computed from
* the output, which is built taking ComplexFormat into account, there are
* no risks of displaying additional results on an unreal output. */
preferences->setComplexFormat(Poincare::Preferences::ComplexFormat::Cartesian);
}
Context * context = App::app()->localContext();
ExpressionNode::ReductionContext reductionContext(
context,
preferences->complexFormat(),
preferences->angleUnit(),
ExpressionNode::ReductionTarget::SystemForApproximation,
ExpressionNode::SymbolicComputation::ReplaceAllSymbolsWithDefinitionsOrUndefined);
// The expression must be reduced to call methods such as determinant or trace
assert(m_expression.type() == ExpressionNode::Type::Matrix);
bool mIsSquared = (static_cast<Matrix &>(m_expression).numberOfRows() == static_cast<Matrix &>(m_expression).numberOfColumns());
size_t index = 0;
size_t messageIndex = 0;
// 1. Matrix determinant if square matrix
if (mIsSquared) {
/* Determinant is reduced so that a null determinant can be detected.
* However, some exceptions remain such as cos(x)^2+sin(x)^2-1 which will
* not be reduced to a rational, but will be null in theory. */
Expression determinant = Determinant::Builder(m_expression.clone()).reduce(reductionContext);
m_indexMessageMap[index] = messageIndex++;
m_layouts[index++] = getLayoutFromExpression(determinant, context, preferences);
// 2. Matrix inverse if invertible matrix
// A squared matrix is invertible if and only if determinant is non null
if (!determinant.isUndefined() && !determinant.isRationalZero()) {
m_indexMessageMap[index] = messageIndex++;
m_layouts[index++] = getLayoutFromExpression(MatrixInverse::Builder(m_expression.clone()), context, preferences);
}
}
// 3. Matrix row echelon form
messageIndex = 2;
Expression rowEchelonForm = MatrixRef::Builder(m_expression.clone());
m_indexMessageMap[index] = messageIndex++;
m_layouts[index++] = getLayoutFromExpression(rowEchelonForm, context, preferences);
/* 4. Matrix reduced row echelon form
* it can be computed from row echelon form to save computation time.*/
m_indexMessageMap[index] = messageIndex++;
m_layouts[index++] = getLayoutFromExpression(MatrixRref::Builder(rowEchelonForm), context, preferences);
// 5. Matrix trace if square matrix
if (mIsSquared) {
m_indexMessageMap[index] = messageIndex++;
m_layouts[index++] = getLayoutFromExpression(MatrixTrace::Builder(m_expression.clone()), context, preferences);
}
// Reset complex format as before
preferences->setComplexFormat(currentComplexFormat);
}
Poincare::Layout MatrixListController::getLayoutFromExpression(Expression e, Context * context, Poincare::Preferences * preferences) {
assert(!e.isUninitialized());
// Simplify or approximate expression
Expression approximateExpression;
Expression simplifiedExpression;
e.simplifyAndApproximate(&simplifiedExpression, &approximateExpression, context,
preferences->complexFormat(), preferences->angleUnit(),
ExpressionNode::SymbolicComputation::ReplaceAllSymbolsWithDefinitionsOrUndefined);
// simplify might have been interrupted, in which case we use approximate
if (simplifiedExpression.isUninitialized()) {
assert(!approximateExpression.isUninitialized());
return Shared::PoincareHelpers::CreateLayout(approximateExpression);
}
return Shared::PoincareHelpers::CreateLayout(simplifiedExpression);
}
I18n::Message MatrixListController::messageAtIndex(int index) {
// Message index is mapped in setExpression because it depends on the Matrix.
assert(index < k_maxNumberOfOutputRows && index >=0);
I18n::Message messages[k_maxNumberOfOutputRows] = {
I18n::Message::Determinant,
I18n::Message::Inverse,
I18n::Message::RowEchelonForm,
I18n::Message::ReducedRowEchelonForm,
I18n::Message::Trace};
return messages[m_indexMessageMap[index]];
}
}

View File

@@ -0,0 +1,27 @@
#ifndef CALCULATION_ADDITIONAL_OUTPUTS_MATRIX_LIST_CONTROLLER_H
#define CALCULATION_ADDITIONAL_OUTPUTS_MATRIX_LIST_CONTROLLER_H
#include "expressions_list_controller.h"
namespace Calculation {
class MatrixListController : public ExpressionsListController {
public:
MatrixListController(EditExpressionController * editExpressionController) :
ExpressionsListController(editExpressionController) {}
void setExpression(Poincare::Expression e) override;
private:
I18n::Message messageAtIndex(int index) override;
Poincare::Layout getLayoutFromExpression(Poincare::Expression e, Poincare::Context * context, Poincare::Preferences * preferences);
// Map from cell index to message index
constexpr static int k_maxNumberOfOutputRows = 5;
int m_indexMessageMap[k_maxNumberOfOutputRows];
};
}
#endif

View File

@@ -9,34 +9,31 @@ using namespace Shared;
namespace Calculation {
int RationalListController::numberOfRows() const {
return 2;
}
Integer extractInteger(const Expression e) {
assert(e.type() == ExpressionNode::Type::BasedInteger);
return static_cast<const BasedInteger &>(e).integer();
}
void RationalListController::computeLayoutAtIndex(int index) {
void RationalListController::setExpression(Poincare::Expression e) {
ExpressionsListController::setExpression(e);
assert(!m_expression.isUninitialized());
static_assert(k_maxNumberOfRows >= 2, "k_maxNumberOfRows must be greater than 2");
bool negative = false;
Expression div = m_expression;
if (m_expression.type() == ExpressionNode::Type::Opposite) {
negative = true;
div = m_expression.childAtIndex(0);
}
assert(div.type() == ExpressionNode::Type::Division);
Integer numerator = extractInteger(div.childAtIndex(0));
numerator.setNegative(negative);
Integer denominator = extractInteger(div.childAtIndex(1));
Expression e;
if (index == 0) {
e = Integer::CreateMixedFraction(numerator, denominator);
} else {
assert(index == 1);
e = Integer::CreateEuclideanDivision(numerator, denominator);
}
m_layouts[index] = PoincareHelpers::CreateLayout(e);
int index = 0;
m_layouts[index++] = PoincareHelpers::CreateLayout(Integer::CreateMixedFraction(numerator, denominator));
m_layouts[index++] = PoincareHelpers::CreateLayout(Integer::CreateEuclideanDivision(numerator, denominator));
}
I18n::Message RationalListController::messageAtIndex(int index) {

View File

@@ -10,10 +10,9 @@ public:
RationalListController(EditExpressionController * editExpressionController) :
ExpressionsListController(editExpressionController) {}
//ListViewDataSource
int numberOfRows() const override;
void setExpression(Poincare::Expression e) override;
private:
void computeLayoutAtIndex(int index) override;
I18n::Message messageAtIndex(int index) override;
int textAtIndex(char * buffer, size_t bufferSize, int index) override;
};

View File

@@ -15,12 +15,15 @@ namespace Calculation {
void UnitListController::setExpression(Poincare::Expression e) {
ExpressionsListController::setExpression(e);
assert(!m_expression.isUninitialized());
// Reinitialize m_memoizedExpressions
for (size_t i = 0; i < k_maxNumberOfCells; i++) {
m_memoizedExpressions[i] = Expression();
static_assert(k_maxNumberOfRows >= 3, "k_maxNumberOfRows must be greater than 3");
Poincare::Expression expressions[k_maxNumberOfRows];
// Initialize expressions
for (size_t i = 0; i < k_maxNumberOfRows; i++) {
expressions[i] = Expression();
}
size_t numberOfMemoizedExpressions = 0;
size_t numberOfExpressions = 0;
// 1. First rows: miscellaneous classic units for some dimensions
Expression copy = m_expression.clone();
Expression units;
@@ -32,7 +35,7 @@ void UnitListController::setExpression(Poincare::Expression e) {
if (Unit::IsSISpeed(units)) {
// 1.a. Turn speed into km/h
m_memoizedExpressions[numberOfMemoizedExpressions++] = UnitConvert::Builder(
expressions[numberOfExpressions++] = UnitConvert::Builder(
m_expression.clone(),
Multiplication::Builder(
Unit::Kilometer(),
@@ -45,7 +48,7 @@ void UnitListController::setExpression(Poincare::Expression e) {
requireSimplification = true; // Simplify the conversion
} else if (Unit::IsSIVolume(units)) {
// 1.b. Turn volume into L
m_memoizedExpressions[numberOfMemoizedExpressions++] = UnitConvert::Builder(
expressions[numberOfExpressions++] = UnitConvert::Builder(
m_expression.clone(),
Unit::Liter()
);
@@ -53,14 +56,14 @@ void UnitListController::setExpression(Poincare::Expression e) {
canChangeUnitPrefix = true; // Pick best prefix (mL)
} else if (Unit::IsSIEnergy(units)) {
// 1.c. Turn energy into Wh
m_memoizedExpressions[numberOfMemoizedExpressions++] = UnitConvert::Builder(
expressions[numberOfExpressions++] = UnitConvert::Builder(
m_expression.clone(),
Multiplication::Builder(
Unit::Watt(),
Unit::Hour()
)
);
m_memoizedExpressions[numberOfMemoizedExpressions++] = UnitConvert::Builder(
expressions[numberOfExpressions++] = UnitConvert::Builder(
m_expression.clone(),
Unit::ElectronVolt()
);
@@ -69,21 +72,21 @@ void UnitListController::setExpression(Poincare::Expression e) {
} else if (Unit::IsSITime(units)) {
// Turn time into ? year + ? month + ? day + ? h + ? min + ? s
double value = Shared::PoincareHelpers::ApproximateToScalar<double>(copy, App::app()->localContext());
m_memoizedExpressions[numberOfMemoizedExpressions++] = Unit::BuildTimeSplit(value, App::app()->localContext(), Preferences::sharedPreferences()->complexFormat(), Preferences::sharedPreferences()->angleUnit());
expressions[numberOfExpressions++] = Unit::BuildTimeSplit(value, App::app()->localContext(), Preferences::sharedPreferences()->complexFormat(), Preferences::sharedPreferences()->angleUnit());
}
// 1.d. Simplify and tune prefix of all computed expressions
size_t currentExpressionIndex = 0;
while (currentExpressionIndex < numberOfMemoizedExpressions) {
assert(!m_memoizedExpressions[currentExpressionIndex].isUninitialized());
while (currentExpressionIndex < numberOfExpressions) {
assert(!expressions[currentExpressionIndex].isUninitialized());
if (requireSimplification) {
Shared::PoincareHelpers::Simplify(&m_memoizedExpressions[currentExpressionIndex], App::app()->localContext(), ExpressionNode::ReductionTarget::User);
Shared::PoincareHelpers::Simplify(&expressions[currentExpressionIndex], App::app()->localContext(), ExpressionNode::ReductionTarget::User);
}
if (canChangeUnitPrefix) {
Expression newUnits;
// Reduce to be able to removeUnit
PoincareHelpers::Reduce(&m_memoizedExpressions[currentExpressionIndex], App::app()->localContext(), ExpressionNode::ReductionTarget::User);
m_memoizedExpressions[currentExpressionIndex] = m_memoizedExpressions[currentExpressionIndex].removeUnit(&newUnits);
double value = Shared::PoincareHelpers::ApproximateToScalar<double>(m_memoizedExpressions[currentExpressionIndex], App::app()->localContext());
PoincareHelpers::Reduce(&expressions[currentExpressionIndex], App::app()->localContext(), ExpressionNode::ReductionTarget::User);
expressions[currentExpressionIndex] = expressions[currentExpressionIndex].removeUnit(&newUnits);
double value = Shared::PoincareHelpers::ApproximateToScalar<double>(expressions[currentExpressionIndex], App::app()->localContext());
ExpressionNode::ReductionContext reductionContext(
App::app()->localContext(),
Preferences::sharedPreferences()->complexFormat(),
@@ -91,36 +94,36 @@ void UnitListController::setExpression(Poincare::Expression e) {
ExpressionNode::ReductionTarget::User,
ExpressionNode::SymbolicComputation::ReplaceAllSymbolsWithDefinitionsOrUndefined);
Unit::ChooseBestPrefixForValue(&newUnits, &value, reductionContext);
m_memoizedExpressions[currentExpressionIndex] = Multiplication::Builder(Number::FloatNumber(value), newUnits);
expressions[currentExpressionIndex] = Multiplication::Builder(Number::FloatNumber(value), newUnits);
}
currentExpressionIndex++;
}
// 2. IS units only
assert(numberOfMemoizedExpressions < k_maxNumberOfCells - 1);
m_memoizedExpressions[numberOfMemoizedExpressions] = m_expression.clone();
Shared::PoincareHelpers::Simplify(&m_memoizedExpressions[numberOfMemoizedExpressions], App::app()->localContext(), ExpressionNode::ReductionTarget::User, Poincare::ExpressionNode::SymbolicComputation::ReplaceAllDefinedSymbolsWithDefinition, Poincare::ExpressionNode::UnitConversion::InternationalSystem);
numberOfMemoizedExpressions++;
assert(numberOfExpressions < k_maxNumberOfRows - 1);
expressions[numberOfExpressions] = m_expression.clone();
Shared::PoincareHelpers::Simplify(&expressions[numberOfExpressions], App::app()->localContext(), ExpressionNode::ReductionTarget::User, Poincare::ExpressionNode::SymbolicComputation::ReplaceAllDefinedSymbolsWithDefinition, Poincare::ExpressionNode::UnitConversion::InternationalSystem);
numberOfExpressions++;
// 3. Get rid of duplicates
Expression reduceExpression = m_expression.clone();
// Make m_expression compareable to m_memoizedExpressions (turn BasedInteger into Rational for instance)
// Make m_expression comparable to expressions (turn BasedInteger into Rational for instance)
Shared::PoincareHelpers::Simplify(&reduceExpression, App::app()->localContext(), ExpressionNode::ReductionTarget::User, Poincare::ExpressionNode::SymbolicComputation::ReplaceAllDefinedSymbolsWithDefinition, Poincare::ExpressionNode::UnitConversion::None);
currentExpressionIndex = 1;
while (currentExpressionIndex < numberOfMemoizedExpressions) {
while (currentExpressionIndex < numberOfExpressions) {
bool duplicateFound = false;
for (size_t i = 0; i < currentExpressionIndex + 1; i++) {
// Compare the currentExpression to all previous memoized expressions and to m_expression
Expression comparedExpression = i == currentExpressionIndex ? reduceExpression : m_memoizedExpressions[i];
// Compare the currentExpression to all previous expressions and to m_expression
Expression comparedExpression = i == currentExpressionIndex ? reduceExpression : expressions[i];
assert(!comparedExpression.isUninitialized());
if (comparedExpression.isIdenticalTo(m_memoizedExpressions[currentExpressionIndex])) {
numberOfMemoizedExpressions--;
if (comparedExpression.isIdenticalTo(expressions[currentExpressionIndex])) {
numberOfExpressions--;
// Shift next expressions
for (size_t j = currentExpressionIndex; j < numberOfMemoizedExpressions; j++) {
m_memoizedExpressions[j] = m_memoizedExpressions[j+1];
for (size_t j = currentExpressionIndex; j < numberOfExpressions; j++) {
expressions[j] = expressions[j+1];
}
// Remove last expression
m_memoizedExpressions[numberOfMemoizedExpressions] = Expression();
expressions[numberOfExpressions] = Expression();
// The current expression has been discarded, no need to increment the current index
duplicateFound = true;
break;
@@ -131,21 +134,12 @@ void UnitListController::setExpression(Poincare::Expression e) {
currentExpressionIndex++;
}
}
}
int UnitListController::numberOfRows() const {
int nbOfRows = 0;
for (size_t i = 0; i < k_maxNumberOfCells; i++) {
if (!m_memoizedExpressions[i].isUninitialized()) {
nbOfRows++;
// Memoize layouts
for (size_t i = 0; i < k_maxNumberOfRows; i++) {
if (!expressions[i].isUninitialized()) {
m_layouts[i] = Shared::PoincareHelpers::CreateLayout(expressions[i]);
}
}
return nbOfRows;
}
void UnitListController::computeLayoutAtIndex(int index) {
assert(!m_memoizedExpressions[index].isUninitialized());
m_layouts[index] = Shared::PoincareHelpers::CreateLayout(m_memoizedExpressions[index]);
}
I18n::Message UnitListController::messageAtIndex(int index) {

View File

@@ -12,13 +12,8 @@ public:
void setExpression(Poincare::Expression e) override;
//ListViewDataSource
int numberOfRows() const override;
private:
void computeLayoutAtIndex(int index) override;
I18n::Message messageAtIndex(int index) override;
// Memoization of expressions
mutable Poincare::Expression m_memoizedExpressions[k_maxNumberOfCells];
};
}

View File

@@ -291,6 +291,9 @@ Calculation::AdditionalInformationType Calculation::additionalInformationType(Co
if (o.hasDefinedComplexApproximation(context, complexFormat, preferences->angleUnit())) {
return AdditionalInformationType::Complex;
}
if (o.type() == ExpressionNode::Type::Matrix) {
return AdditionalInformationType::Matrix;
}
return AdditionalInformationType::None;
}

View File

@@ -41,6 +41,7 @@ public:
Rational,
Trigonometry,
Unit,
Matrix,
Complex
};
static bool DisplaysExact(DisplayOutput d) { return d != DisplayOutput::ApproximateOnly; }

View File

@@ -17,7 +17,8 @@ HistoryController::HistoryController(EditExpressionController * editExpressionCo
m_integerController(editExpressionController),
m_rationalController(editExpressionController),
m_trigonometryController(editExpressionController),
m_unitController(editExpressionController)
m_unitController(editExpressionController),
m_matrixController(editExpressionController)
{
for (int i = 0; i < k_maxNumberOfDisplayedRows; i++) {
m_calculationHistory[i].setParentResponder(&m_selectableTableView);
@@ -110,6 +111,8 @@ bool HistoryController::handleEvent(Ion::Events::Event event) {
vc = &m_rationalController;
} else if (additionalInfoType == Calculation::AdditionalInformationType::Unit) {
vc = &m_unitController;
} else if (additionalInfoType == Calculation::AdditionalInformationType::Matrix) {
vc = &m_matrixController;
}
if (vc) {
vc->setExpression(e);

View File

@@ -10,6 +10,7 @@
#include "additional_outputs/rational_list_controller.h"
#include "additional_outputs/trigonometry_list_controller.h"
#include "additional_outputs/unit_list_controller.h"
#include "additional_outputs/matrix_list_controller.h"
namespace Calculation {
@@ -48,6 +49,7 @@ private:
RationalListController m_rationalController;
TrigonometryListController m_trigonometryController;
UnitListController m_unitController;
MatrixListController m_matrixController;
};
}

View File

@@ -17,7 +17,7 @@ public:
size_t size() const override;
#if POINCARE_TREE_LOG
void logNodeName(std::ostream & stream) const override {
stream << "Based Integer";
stream << "BasedInteger";
}
virtual void logAttributes(std::ostream & stream) const override;
#endif
@@ -38,6 +38,7 @@ public:
template<typename T> T templatedApproximate() const;
private:
int simplificationOrderSameType(const ExpressionNode * e, bool ascending, bool canBeInterrupted, bool ignoreParentheses) const override;
Expression shallowReduce(ReductionContext reductionContext) override;
LayoutShape leftLayoutShape() const override { return m_base == Integer::Base::Decimal ? LayoutShape::Integer : LayoutShape::BinaryHexadecimal; }
Integer::Base m_base;

View File

@@ -75,6 +75,7 @@ public:
/* Operation on matrix */
int rank(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, bool inPlace = false);
Expression createTrace();
// Inverse the array in-place. Array has to be given in the form array[row_index][column_index]
template<typename T> static int ArrayInverse(T * array, int numberOfRows, int numberOfColumns);
static Matrix CreateIdentity(int dim);

View File

@@ -66,6 +66,15 @@ template<typename T> T BasedIntegerNode::templatedApproximate() const {
// Comparison
int BasedIntegerNode::simplificationOrderSameType(const ExpressionNode * e, bool ascending, bool canBeInterrupted, bool ignoreParentheses) const {
if (!ascending) {
return e->simplificationOrderSameType(this, true, canBeInterrupted, ignoreParentheses);
}
assert(e->type() == ExpressionNode::Type::BasedInteger);
const BasedIntegerNode * other = static_cast<const BasedIntegerNode *>(e);
return Integer::NaturalOrder(integer(), other->integer());
}
Expression BasedIntegerNode::shallowReduce(ReductionContext reductionContext) {
return BasedInteger(this).shallowReduce();
}

View File

@@ -146,6 +146,16 @@ int Matrix::rank(Context * context, Preferences::ComplexFormat complexFormat, Pr
return rank;
}
Expression Matrix::createTrace() {
assert(numberOfRows() == numberOfColumns());
int n = numberOfRows();
Addition a = Addition::Builder();
for (int i = 0; i < n; i++) {
a.addChildAtIndexInPlace(matrixChild(i,i).clone(), i, i);
}
return std::move(a);
}
template<typename T>
int Matrix::ArrayInverse(T * array, int numberOfRows, int numberOfColumns) {
if (numberOfRows != numberOfColumns) {
@@ -378,6 +388,7 @@ Expression Matrix::createInverse(ExpressionNode::ReductionContext reductionConte
}
Expression Matrix::determinant(ExpressionNode::ReductionContext reductionContext, bool * couldComputeDeterminant, bool inPlace) {
// Determinant must be called on a reduced matrix only.
*couldComputeDeterminant = true;
Matrix m = inPlace ? *this : clone().convert<Matrix>();
int dim = m.numberOfRows();

View File

@@ -48,11 +48,7 @@ Expression MatrixTrace::shallowReduce(ExpressionNode::ReductionContext reduction
if (matrixChild0.numberOfRows() != matrixChild0.numberOfColumns()) {
return replaceWithUndefinedInPlace();
}
int n = matrixChild0.numberOfRows();
Addition a = Addition::Builder();
for (int i = 0; i < n; i++) {
a.addChildAtIndexInPlace(matrixChild0.matrixChild(i,i), i, i); // No need to clone
}
Expression a = matrixChild0.createTrace();
replaceWithInPlace(a);
return a.shallowReduce(reductionContext);
}