mirror of
https://github.com/UpsilonNumworks/Upsilon.git
synced 2026-01-18 16:27:34 +01:00
Merge remote-tracking branch 'upstream/master' into omega-dev
This commit is contained in:
@@ -18,14 +18,14 @@ void Model::tidy() {
|
||||
Poincare::Expression Model::simplifiedExpression(double * modelCoefficients, Poincare::Context * context) {
|
||||
Expression e = expression(modelCoefficients);
|
||||
if (!e.isUninitialized()) {
|
||||
PoincareHelpers::Simplify(&e, context);
|
||||
PoincareHelpers::Simplify(&e, context, ExpressionNode::ReductionTarget::SystemForApproximation);
|
||||
}
|
||||
return e;
|
||||
}
|
||||
|
||||
double Model::levelSet(double * modelCoefficients, double xMin, double step, double xMax, double y, Poincare::Context * context) {
|
||||
Expression yExpression = Number::DecimalNumber(y);
|
||||
PoincareHelpers::Simplify(&yExpression, context);
|
||||
PoincareHelpers::Simplify(&yExpression, context, ExpressionNode::ReductionTarget::SystemForApproximation);
|
||||
Expression modelExpression = simplifiedExpression(modelCoefficients, context);
|
||||
double result = PoincareHelpers::NextIntersection(modelExpression, "x", xMin, step, xMax, context, yExpression).x1();
|
||||
return result;
|
||||
|
||||
@@ -24,6 +24,8 @@ ValuesController::ValuesController(Responder * parentResponder, InputEventHandle
|
||||
StackViewController * stack = ((StackViewController *)valuesController->stackController());
|
||||
IntervalParameterController * controller = valuesController->intervalParameterController();
|
||||
controller->setInterval(valuesController->intervalAtColumn(valuesController->selectedColumn()));
|
||||
/* No need to change Nstart/Nend messages because they are the only messages
|
||||
* used and we set them in ValuesController::ValuesController(...) */
|
||||
stack->push(controller);
|
||||
return true;
|
||||
}, this), k_font)
|
||||
@@ -32,6 +34,7 @@ ValuesController::ValuesController(Responder * parentResponder, InputEventHandle
|
||||
m_sequenceTitleCells[i].setOrientation(Shared::FunctionTitleCell::Orientation::HorizontalIndicator);
|
||||
}
|
||||
setupSelectableTableViewAndCells(inputEventHandlerDelegate);
|
||||
setDefaultStartEndMessages();
|
||||
}
|
||||
|
||||
// TableViewDataSource
|
||||
@@ -64,7 +67,7 @@ I18n::Message ValuesController::emptyMessage() {
|
||||
}
|
||||
|
||||
// ValuesController
|
||||
void ValuesController::setStartEndMessages(Shared::IntervalParameterController * controller, int column) {
|
||||
void ValuesController::setDefaultStartEndMessages() {
|
||||
m_intervalParameterController.setStartEndMessages(I18n::Message::NStart, I18n::Message::NEnd);
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,11 @@ private:
|
||||
constexpr static int k_maxNumberOfDisplayableCells = k_maxNumberOfDisplayableSequences * k_maxNumberOfDisplayableRows;
|
||||
|
||||
// ValuesController
|
||||
void setStartEndMessages(Shared::IntervalParameterController * controller, int column) override;
|
||||
void setStartEndMessages(Shared::IntervalParameterController * controller, int column) override {
|
||||
setDefaultStartEndMessages();
|
||||
}
|
||||
|
||||
void setDefaultStartEndMessages();
|
||||
I18n::Message valuesParameterMessageAtColumn(int columnIndex) const override;
|
||||
int maxNumberOfCells() override { return k_maxNumberOfDisplayableCells; }
|
||||
int maxNumberOfFunctions() override { return k_maxNumberOfDisplayableSequences; }
|
||||
|
||||
@@ -47,7 +47,7 @@ Expression ExpressionModel::expressionReduced(const Storage::Record * record, Po
|
||||
m_expression = Undefined::Builder();
|
||||
} else {
|
||||
m_expression = Expression::ExpressionFromAddress(expressionAddress(record), expressionSize(record));
|
||||
PoincareHelpers::Simplify(&m_expression, context);
|
||||
PoincareHelpers::Simplify(&m_expression, context, ExpressionNode::ReductionTarget::SystemForApproximation);
|
||||
// simplify might return an uninitialized Expression if interrupted
|
||||
if (m_expression.isUninitialized()) {
|
||||
m_expression = Expression::ExpressionFromAddress(expressionAddress(record), expressionSize(record));
|
||||
|
||||
@@ -62,10 +62,10 @@ inline Poincare::Expression ParseAndSimplify(const char * text, Poincare::Contex
|
||||
return Poincare::Expression::ParseAndSimplify(text, context, complexFormat, preferences->angleUnit(), symbolicComputation);
|
||||
}
|
||||
|
||||
inline void Simplify(Poincare::Expression * e, Poincare::Context * context, bool symbolicComputation = true) {
|
||||
inline void Simplify(Poincare::Expression * e, Poincare::Context * context, Poincare::ExpressionNode::ReductionTarget target, bool symbolicComputation = true) {
|
||||
Poincare::Preferences * preferences = Poincare::Preferences::sharedPreferences();
|
||||
Poincare::Preferences::ComplexFormat complexFormat = Poincare::Expression::UpdatedComplexFormatWithExpressionInput(preferences->complexFormat(), *e, context);
|
||||
*e = e->simplify(context, complexFormat, preferences->angleUnit(), symbolicComputation);
|
||||
*e = e->simplify(context, complexFormat, preferences->angleUnit(), target, symbolicComputation);
|
||||
}
|
||||
|
||||
inline void ParseAndSimplifyAndApproximate(const char * text, Poincare::Expression * simplifiedExpression, Poincare::Expression * approximateExpression, Poincare::Context * context, bool symbolicComputation = true) {
|
||||
|
||||
@@ -243,10 +243,23 @@ void ValuesController::didChangeCell(int column, int row) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Find the abscissa column corresponding to column
|
||||
int abscissaColumn = 0;
|
||||
int nbOfColumns = numberOfColumnsForAbscissaColumn(abscissaColumn);
|
||||
while (column >= nbOfColumns) {
|
||||
abscissaColumn = nbOfColumns;
|
||||
nbOfColumns += numberOfColumnsForAbscissaColumn(abscissaColumn);
|
||||
}
|
||||
|
||||
// Update the memoization of rows linked to the changed cell
|
||||
int nbOfMemoizedColumns = numberOfMemoizedColumn();
|
||||
for (int i = column+1; i < column+numberOfColumnsForAbscissaColumn(column); i++) {
|
||||
int nbOfColumnsForAbscissa = numberOfColumnsForAbscissaColumn(abscissaColumn);
|
||||
for (int i = abscissaColumn+1; i < abscissaColumn+nbOfColumnsForAbscissa; i++) {
|
||||
int memoizedI = valuesColumnForAbsoluteColumn(i) - m_firstMemoizedColumn;
|
||||
if (memoizedI < 0 || memoizedI >= nbOfMemoizedColumns) {
|
||||
// The changed column is out of the memoized table
|
||||
continue;
|
||||
}
|
||||
fillMemoizedBuffer(i, row, nbOfMemoizedColumns*memoizedRow+memoizedI);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -276,7 +276,7 @@ EquationStore::Error EquationStore::oneDimensialPolynomialSolve(Expression exact
|
||||
assert(degree == 2);
|
||||
// Compute delta = b*b-4ac
|
||||
Expression delta = Subtraction::Builder(Power::Builder(coefficients[1].clone(), Rational::Builder(2)), Multiplication::Builder(Rational::Builder(4), coefficients[0].clone(), coefficients[2].clone()));
|
||||
delta = delta.simplify(context, updatedComplexFormat(context), Poincare::Preferences::sharedPreferences()->angleUnit());
|
||||
delta = delta.simplify(context, updatedComplexFormat(context), Poincare::Preferences::sharedPreferences()->angleUnit(), ExpressionNode::ReductionTarget::SystemForApproximation);
|
||||
if (delta.isUninitialized()) {
|
||||
delta = Poincare::Undefined::Builder();
|
||||
}
|
||||
|
||||
@@ -128,6 +128,11 @@ QUIZ_CASE(equation_solve) {
|
||||
const char * solutions11[] = {"\u0012\u0012√\u0012π^\u00122\u0013-2π+8√\u00125\u0013+9\u0013-π+1\u0013/\u00124\u0013\u0013", "\u0012\u0012-√\u0012π^\u00122\u0013-2π+8√\u00125\u0013+9\u0013-π+1\u0013/\u00124\u0013\u0013", "π^\u00122\u0013-2π+8√\u00125\u0013+9"}; // (√(π^2-2π+8√(5)+9)-π+1)/4, (-√(π^2-2π+8×√(5)+9)-π+1)/4, π^2-2π+8√(5)+9
|
||||
assert_equation_system_exact_solve_to(equations11, EquationStore::Error::NoError, EquationStore::Type::PolynomialMonovariable, (const char **)variablesx, solutions11, 3);
|
||||
|
||||
// (x-3)^2
|
||||
const char * equations21[] = {"(x-3)^2=0", 0};
|
||||
const char * solutions21[] = {"3", "0"};
|
||||
assert_equation_system_exact_solve_to(equations21, EquationStore::Error::NoError, EquationStore::Type::PolynomialMonovariable, (const char **)variablesx, solutions21, 2);
|
||||
|
||||
// TODO
|
||||
// x^3 - 4x^2 + 6x - 24 = 0
|
||||
//const char * equations10[] = {"2×x^2-4×x+4=3", 0};
|
||||
@@ -205,6 +210,11 @@ QUIZ_CASE(equation_solve_complex_format) {
|
||||
const char * equations4[] = {"x+√(-1)×√(-1)=0", 0};
|
||||
assert_equation_system_exact_solve_to(equations4, EquationStore::Error::EquationUnreal, EquationStore::Type::LinearSystem, (const char **)variablesx, nullptr, 0);
|
||||
|
||||
// root(-8,3)*x+3 = 0 --> 3/2 in R
|
||||
const char * equations5[] = {"root(-8,3)*x+3=0", 0};
|
||||
const char * solutions5[] = {"\u0012\u00123\u0013/\u00122\u0013\u0013"};
|
||||
assert_equation_system_exact_solve_to(equations5, EquationStore::Error::NoError, EquationStore::Type::LinearSystem, (const char **)variablesx, solutions5, 1);
|
||||
|
||||
Poincare::Preferences::sharedPreferences()->setComplexFormat(Poincare::Preferences::ComplexFormat::Cartesian);
|
||||
// x+𝐢 = 0 --> x = -𝐢
|
||||
assert_equation_system_exact_solve_to(equations0, EquationStore::Error::NoError, EquationStore::Type::LinearSystem, (const char **)variablesx, solutions0, 1);
|
||||
@@ -224,6 +234,9 @@ QUIZ_CASE(equation_solve_complex_format) {
|
||||
const char * solutions4[] = {"1"};
|
||||
assert_equation_system_exact_solve_to(equations4, EquationStore::Error::NoError, EquationStore::Type::LinearSystem, (const char **)variablesx, solutions4, 1);
|
||||
|
||||
const char * solutions5Cartesain[] = {"-\u0012\u00123\u0013/\u00124\u0013\u0013+\u0012\u00123√\u00123\u0013\u0013/\u00124\u0013\u0013𝐢"}; //-3/4+(3√3/4)*𝐢
|
||||
assert_equation_system_exact_solve_to(equations5, EquationStore::Error::NoError, EquationStore::Type::LinearSystem, (const char **)variablesx, solutions5Cartesain, 1);
|
||||
|
||||
Poincare::Preferences::sharedPreferences()->setComplexFormat(Poincare::Preferences::ComplexFormat::Polar);
|
||||
// x+𝐢 = 0 --> x = e^(-π/2×i)
|
||||
const char * solutions0Polar[] = {"ℯ^\u0012-\u0012\u0012π\u0013/\u00122\u0013\u0013𝐢\u0013"}; // ℯ^(-(π/2)𝐢)
|
||||
@@ -240,6 +253,9 @@ QUIZ_CASE(equation_solve_complex_format) {
|
||||
const char * solutions3Polar[] = {"ℯ^\u0012-\u0012\u00123π\u0013/\u00124\u0013\u0013𝐢\u0013", "ℯ^\u0012\u0012\u0012π\u0013/\u00124\u0013\u0013𝐢\u0013", "4ℯ^\u0012\u0012\u0012π\u0013/\u00122\u0013\u0013𝐢\u0013"}; // ℯ^(-(3×π/4)𝐢)", "ℯ^((π/4)𝐢)", "4ℯ^((π/2)𝐢)
|
||||
assert_equation_system_exact_solve_to(equations3, EquationStore::Error::NoError, EquationStore::Type::PolynomialMonovariable, (const char **)variablesx, solutions3Polar, 3);
|
||||
|
||||
const char * solutions5Polar[] = {"\u0012\u00123\u0013/\u00122\u0013\u0013ℯ^\u0012\u0012\u00122π\u0013/\u00123\u0013\u0013𝐢\u0013"}; //3/2ℯ^\u0012\u00122π\u0012/3\u0013𝐢"};
|
||||
assert_equation_system_exact_solve_to(equations5, EquationStore::Error::NoError, EquationStore::Type::LinearSystem, (const char **)variablesx, solutions5Polar, 1);
|
||||
|
||||
}
|
||||
|
||||
QUIZ_CASE(equation_and_symbolic_computation) {
|
||||
|
||||
@@ -12,7 +12,10 @@ $(BUILD_DIR)/test.external_flash.write.$(EXE): $(BUILD_DIR)/quiz/src/test_ion_ex
|
||||
@echo " using an USB cable and press at the same time the 6 key and the RESET"
|
||||
@echo " button on the back of your device."
|
||||
$(Q) until $(PYTHON) build/device/dfu.py -l | grep -E "0483:a291|0483:df11" > /dev/null 2>&1; do sleep 2;done
|
||||
$(Q) $(PYTHON) build/device/dfu.py -u $(word 2,$^)
|
||||
$(Q) sleep 2
|
||||
$(eval DFU_SLAVE := $(shell $(PYTHON) build/device/dfu.py -l | grep -E "0483:a291|0483:df11"))
|
||||
$(Q) if [[ "$(DFU_SLAVE)" == *"0483:df11"* ]]; \
|
||||
then \
|
||||
$(PYTHON) build/device/dfu.py -u $(word 2,$^); \
|
||||
sleep 2; \
|
||||
fi
|
||||
$(Q) $(PYTHON) build/device/dfu.py -u $(word 1,$^)
|
||||
|
||||
|
||||
@@ -345,11 +345,14 @@ Storage::Record::ErrorStatus Storage::setBaseNameWithExtensionOfRecord(Record re
|
||||
return notifyFullnessToDelegate();
|
||||
}
|
||||
overrideSizeAtPosition(p, newRecordSize);
|
||||
overrideBaseNameWithExtensionAtPosition(p+sizeof(record_size_t), baseName, extension);
|
||||
char * fullNamePosition = p + sizeof(record_size_t);
|
||||
overrideBaseNameWithExtensionAtPosition(fullNamePosition, baseName, extension);
|
||||
// Recompute the CRC32
|
||||
record = Record(fullNamePosition);
|
||||
notifyChangeToDelegate(record);
|
||||
m_lastRecordRetrieved = record;
|
||||
m_lastRecordRetrievedPointer = p;
|
||||
return Record::ErrorStatus::None;
|
||||
return Record::ErrorStatus::None;
|
||||
}
|
||||
return Record::ErrorStatus::RecordDoesNotExist;
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
|
||||
namespace UTF8Helper {
|
||||
|
||||
static inline int minInt(int x, int y) { return x < y ? x : y; }
|
||||
static inline size_t minSizeT(size_t x, size_t y) { return x < y ? x : y; }
|
||||
|
||||
int CountOccurrences(const char * s, CodePoint c) {
|
||||
@@ -110,7 +109,11 @@ void CopyAndRemoveCodePoint(char * dst, size_t dstSize, const char * src, CodePo
|
||||
// Remove CodePoint c
|
||||
while (codePoint != UCodePointNull && bufferIndex < dstSize) {
|
||||
if (codePoint != c) {
|
||||
int copySize = minInt(nextPointer - currentPointer, dstSize - bufferIndex);
|
||||
int copySize = nextPointer - currentPointer;
|
||||
if (copySize > dstSize - 1 - bufferIndex) {
|
||||
// Copying the current code point to the buffer would overflow the buffer
|
||||
break;
|
||||
}
|
||||
memcpy(dst + bufferIndex, currentPointer, copySize);
|
||||
bufferIndex+= copySize;
|
||||
}
|
||||
@@ -118,7 +121,7 @@ void CopyAndRemoveCodePoint(char * dst, size_t dstSize, const char * src, CodePo
|
||||
codePoint = decoder.nextCodePoint();
|
||||
nextPointer = decoder.stringPosition();
|
||||
}
|
||||
*(dst + minInt(bufferIndex, dstSize - 1)) = 0;
|
||||
*(dst + bufferIndex) = 0;
|
||||
}
|
||||
|
||||
void RemoveCodePoint(char * buffer, CodePoint c, const char * * pointerToUpdate, const char * stoppingPosition) {
|
||||
|
||||
@@ -87,6 +87,21 @@ QUIZ_CASE(ion_utf8_copy_and_remove_code_point) {
|
||||
c = UCodePointLatinLetterSmallCapitalE;
|
||||
result = "124";
|
||||
assert_copy_and_remove_code_point_gives(buffer, bufferSize, s, c, result);
|
||||
|
||||
// The buffer size is to small to hold s
|
||||
s = "1234ᴇ";
|
||||
c = '5';
|
||||
result = "1234"; // "1234ᴇ" size is 7
|
||||
assert_copy_and_remove_code_point_gives(buffer, 6, s, c, result);
|
||||
assert_copy_and_remove_code_point_gives(buffer, 7, s, c, result);
|
||||
result = "1234ᴇ";
|
||||
assert_copy_and_remove_code_point_gives(buffer, 8, s, c, result);
|
||||
|
||||
s = "1234ᴇ";
|
||||
c = '4';
|
||||
result = "123ᴇ";
|
||||
assert_copy_and_remove_code_point_gives(buffer, 7, s, c, result);
|
||||
|
||||
}
|
||||
|
||||
void assert_remove_code_point_gives(char * buffer, CodePoint c, const char * * indexToUpdate, const char * stoppingPosition, const char * indexToUpdateResult, const char * result) {
|
||||
|
||||
@@ -221,11 +221,11 @@ public:
|
||||
* (For instance, in Polar mode, they return an expression of the form
|
||||
* r*e^(i*th) reduced and approximated.) */
|
||||
static Expression ParseAndSimplify(const char * text, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, bool symbolicComputation = true);
|
||||
Expression simplify(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, bool symbolicComputation = true);
|
||||
Expression simplify(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target, bool symbolicComputation = true);
|
||||
|
||||
static void ParseAndSimplifyAndApproximate(const char * text, Expression * simplifiedExpression, Expression * approximateExpression, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, bool symbolicComputation = true);
|
||||
void simplifyAndApproximate(Expression * simplifiedExpression, Expression * approximateExpression, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, bool symbolicComputation = true);
|
||||
Expression reduce(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit);
|
||||
Expression reduce(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target = ExpressionNode::ReductionTarget::SystemForApproximation);
|
||||
|
||||
Expression mapOnMatrixFirstChild(ExpressionNode::ReductionContext reductionContext);
|
||||
static Expression ExpressionWithoutSymbols(Expression expressionWithSymbols, Context * context);
|
||||
@@ -342,7 +342,7 @@ protected:
|
||||
* Warning: this must be called on reduced expressions
|
||||
*/
|
||||
Expression makePositiveAnyNegativeNumeralFactor(ExpressionNode::ReductionContext reductionContext);
|
||||
Expression denominator(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { return node()->denominator(context, complexFormat, angleUnit); }
|
||||
Expression denominator(ExpressionNode::ReductionContext reductionContext) const { return node()->denominator(reductionContext); }
|
||||
Expression shallowReduce(ExpressionNode::ReductionContext reductionContext) { return node()->shallowReduce(reductionContext); }
|
||||
Expression shallowBeautify(ExpressionNode::ReductionContext reductionContext) { return node()->shallowBeautify(reductionContext); }
|
||||
Expression deepBeautify(ExpressionNode::ReductionContext reductionContext);
|
||||
|
||||
@@ -110,7 +110,15 @@ public:
|
||||
|
||||
/* Properties */
|
||||
enum class ReductionTarget {
|
||||
System = 0,
|
||||
/* Minimal reduction: this at least reduces rationals operations as
|
||||
* "1-0.3-0.7 --> 0" */
|
||||
SystemForApproximation = 0,
|
||||
/* Expansion of Newton multinome to be able to identify polynoms */
|
||||
SystemForAnalysis,
|
||||
/* Additional features as:
|
||||
* - factorizing on a common denominator
|
||||
* - turning complex expression into the form a+ib
|
||||
* - identifying tangent in cos/sin polynoms ... */
|
||||
User
|
||||
};
|
||||
enum class Sign {
|
||||
@@ -196,7 +204,7 @@ public:
|
||||
/*!*/ virtual Expression shallowReduce(ReductionContext reductionContext);
|
||||
/*!*/ virtual Expression shallowBeautify(ReductionContext reductionContext);
|
||||
/* Return a clone of the denominator part of the expression */
|
||||
/*!*/ virtual Expression denominator(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const;
|
||||
/*!*/ virtual Expression denominator(ExpressionNode::ReductionContext reductionContext) const;
|
||||
/* LayoutShape is used to check if the multiplication sign can be omitted between two expressions. It depends on the "layout syle" of the on the right of the left expression */
|
||||
enum class LayoutShape {
|
||||
Decimal,
|
||||
|
||||
@@ -47,7 +47,7 @@ private:
|
||||
// Simplification
|
||||
Expression shallowReduce(ReductionContext reductionContext) override;
|
||||
Expression shallowBeautify(ReductionContext reductionContext) override;
|
||||
Expression denominator(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override;
|
||||
Expression denominator(ExpressionNode::ReductionContext reductionContext) const override;
|
||||
|
||||
// Approximation
|
||||
template<typename T> static MatrixComplex<T> computeOnMatrixAndComplex(const MatrixComplex<T> m, const std::complex<T> c, Preferences::ComplexFormat complexFormat) {
|
||||
@@ -82,7 +82,7 @@ public:
|
||||
Expression setSign(ExpressionNode::Sign s, ExpressionNode::ReductionContext reductionContext);
|
||||
Expression shallowReduce(ExpressionNode::ReductionContext reductionContext);
|
||||
Expression shallowBeautify(ExpressionNode::ReductionContext reductionContext);
|
||||
Expression denominator(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const;
|
||||
Expression denominator(ExpressionNode::ReductionContext reductionContext) const;
|
||||
void sortChildrenInPlace(NAryExpressionNode::ExpressionOrder order, Context * context, bool canBeInterrupted) {
|
||||
NAryExpression::sortChildrenInPlace(order, context, false, canBeInterrupted);
|
||||
}
|
||||
@@ -94,8 +94,8 @@ private:
|
||||
void mergeInChildByFactorizingBase(int i, Expression e, ExpressionNode::ReductionContext reductionContext);
|
||||
void factorizeExponent(int i, int j, ExpressionNode::ReductionContext reductionContext);
|
||||
Expression distributeOnOperandAtIndex(int index, ExpressionNode::ReductionContext reductionContext);
|
||||
void addMissingFactors(Expression factor, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit);
|
||||
void factorizeSineAndCosine(int i, int j, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit);
|
||||
void addMissingFactors(Expression factor, ExpressionNode::ReductionContext reductionContext);
|
||||
void factorizeSineAndCosine(int i, int j, ExpressionNode::ReductionContext reductionContext);
|
||||
static bool HaveSameNonNumeralFactors(const Expression & e1, const Expression & e2);
|
||||
static bool TermsHaveIdenticalBase(const Expression & e1, const Expression & e2);
|
||||
static bool TermsHaveIdenticalExponent(const Expression & e1, const Expression & e2);
|
||||
@@ -104,7 +104,7 @@ private:
|
||||
static const Expression CreateExponent(Expression e);
|
||||
/* Warning: mergeNegativePower doesnot always return a multiplication:
|
||||
* *(b^-1,c^-1) -> (bc)^-1 */
|
||||
Expression mergeNegativePower(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit);
|
||||
Expression mergeNegativePower(ExpressionNode::ReductionContext reductionContext);
|
||||
static inline const Expression Base(const Expression e);
|
||||
};
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ private:
|
||||
LayoutShape rightLayoutShape() const override { return LayoutShape::RightOfPower; }
|
||||
int simplificationOrderGreaterType(const ExpressionNode * e, bool ascending, bool canBeInterrupted) const override;
|
||||
int simplificationOrderSameType(const ExpressionNode * e, bool ascending, bool canBeInterrupted) const override;
|
||||
Expression denominator(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override;
|
||||
Expression denominator(ReductionContext reductionContext) const override;
|
||||
// Evaluation
|
||||
template<typename T> static MatrixComplex<T> computeOnComplexAndMatrix(const std::complex<T> c, const MatrixComplex<T> n, Preferences::ComplexFormat complexFormat);
|
||||
template<typename T> static MatrixComplex<T> computeOnMatrixAndComplex(const MatrixComplex<T> m, const std::complex<T> d, Preferences::ComplexFormat complexFormat);
|
||||
@@ -82,7 +82,7 @@ private:
|
||||
constexpr static int k_maxNumberOfTermsInExpandedMultinome = 25;
|
||||
|
||||
// Simplification
|
||||
Expression denominator(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const;
|
||||
Expression denominator(ExpressionNode::ReductionContext reductionContext) const;
|
||||
|
||||
Expression simplifyPowerPower(ExpressionNode::ReductionContext reductionContext);
|
||||
Expression simplifyPowerMultiplication(ExpressionNode::ReductionContext reductionContext);
|
||||
|
||||
@@ -57,7 +57,7 @@ private:
|
||||
Expression shallowBeautify(ReductionContext reductionContext) override;
|
||||
LayoutShape leftLayoutShape() const override { assert(!m_negative); return isInteger() ? LayoutShape::Integer : LayoutShape::Fraction; };
|
||||
Expression setSign(Sign s, ReductionContext reductionContext) override;
|
||||
Expression denominator(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override;
|
||||
Expression denominator(ReductionContext reductionContext) const override;
|
||||
bool m_negative;
|
||||
uint8_t m_numberOfDigitsNumerator;
|
||||
uint8_t m_numberOfDigitsDenominator;
|
||||
|
||||
@@ -354,7 +354,7 @@ Expression Addition::factorizeOnCommonDenominator(ExpressionNode::ReductionConte
|
||||
Multiplication commonDenominator = Multiplication::Builder();
|
||||
for (int i = 0; i < numberOfChildren(); i++) {
|
||||
Expression childI = childAtIndex(i);
|
||||
Expression currentDenominator = childI.denominator(reductionContext.context(), reductionContext.complexFormat(), reductionContext.angleUnit());
|
||||
Expression currentDenominator = childI.denominator(reductionContext);
|
||||
if (!currentDenominator.isUninitialized()) {
|
||||
if (currentDenominator.recursivelyMatches(Expression::IsRandom, reductionContext.context(), true)) {
|
||||
// Remove "random" factors
|
||||
@@ -364,7 +364,7 @@ Expression Addition::factorizeOnCommonDenominator(ExpressionNode::ReductionConte
|
||||
continue;
|
||||
}
|
||||
// Make commonDenominator = LeastCommonMultiple(commonDenominator, denominator);
|
||||
commonDenominator.addMissingFactors(currentDenominator, reductionContext.context(), reductionContext.complexFormat(), reductionContext.angleUnit());
|
||||
commonDenominator.addMissingFactors(currentDenominator, reductionContext);
|
||||
}
|
||||
}
|
||||
if (commonDenominator.numberOfChildren() == 0) {
|
||||
|
||||
@@ -46,7 +46,11 @@ Evaluation<T> EqualNode::templatedApproximate(Context * context, Preferences::Co
|
||||
|
||||
Expression Equal::standardEquation(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const {
|
||||
Expression sub = Subtraction::Builder(childAtIndex(0).clone(), childAtIndex(1).clone());
|
||||
return sub.reduce(context, complexFormat, angleUnit);
|
||||
/* When reducing the equation, we specify the reduction target to be
|
||||
* SystemForAnalysis. This enables to expand Newton multinom to be able to
|
||||
* detect polynom correctly ("(x+2)^2" in this form won't be detected
|
||||
* unless expanded). */
|
||||
return sub.reduce(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::SystemForAnalysis);
|
||||
}
|
||||
|
||||
Expression Equal::shallowReduce() {
|
||||
|
||||
@@ -519,7 +519,7 @@ Expression Expression::ParseAndSimplify(const char * text, Context * context, Pr
|
||||
if (exp.isUninitialized()) {
|
||||
return Undefined::Builder();
|
||||
}
|
||||
exp = exp.simplify(context, complexFormat, angleUnit, symbolicSimplification);
|
||||
exp = exp.simplify(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::User, symbolicSimplification);
|
||||
/* simplify might have been interrupted, in which case the resulting
|
||||
* expression is uninitialized, so we need to check that. */
|
||||
if (exp.isUninitialized()) {
|
||||
@@ -547,9 +547,9 @@ void Expression::ParseAndSimplifyAndApproximate(const char * text, Expression *
|
||||
}
|
||||
}
|
||||
|
||||
Expression Expression::simplify(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, bool symbolicComputation) {
|
||||
Expression Expression::simplify(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target, bool symbolicComputation) {
|
||||
sSimplificationHasBeenInterrupted = false;
|
||||
ExpressionNode::ReductionContext c = ExpressionNode::ReductionContext(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::System, symbolicComputation);
|
||||
ExpressionNode::ReductionContext c = ExpressionNode::ReductionContext(context, complexFormat, angleUnit, target, symbolicComputation);
|
||||
Expression e = deepReduce(c);
|
||||
if (!sSimplificationHasBeenInterrupted) {
|
||||
e = e.deepBeautify(c);
|
||||
@@ -618,7 +618,7 @@ void Expression::simplifyAndApproximate(Expression * simplifiedExpression, Expre
|
||||
Expression e = clone().deepReduce(userReductionContext);
|
||||
if (sSimplificationHasBeenInterrupted) {
|
||||
sSimplificationHasBeenInterrupted = false;
|
||||
ExpressionNode::ReductionContext systemReductionContext = ExpressionNode::ReductionContext(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::System, symbolicComputation);
|
||||
ExpressionNode::ReductionContext systemReductionContext = ExpressionNode::ReductionContext(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::SystemForApproximation, symbolicComputation);
|
||||
e = deepReduce(systemReductionContext);
|
||||
}
|
||||
*simplifiedExpression = Expression();
|
||||
@@ -730,9 +730,9 @@ Expression Expression::angleUnitToRadian(Preferences::AngleUnit angleUnit) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
Expression Expression::reduce(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) {
|
||||
Expression Expression::reduce(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) {
|
||||
sSimplificationHasBeenInterrupted = false;
|
||||
return deepReduce(ExpressionNode::ReductionContext(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::System, true));
|
||||
return deepReduce(ExpressionNode::ReductionContext(context, complexFormat, angleUnit, target, true));
|
||||
}
|
||||
|
||||
Expression Expression::deepReduce(ExpressionNode::ReductionContext reductionContext) {
|
||||
@@ -856,8 +856,13 @@ Expression Expression::CreateComplexExpression(Expression ra, Expression tb, Pre
|
||||
Expression norm;
|
||||
Expression exp;
|
||||
if (!isOneRa || isZeroTb) {
|
||||
assert(!isNegativeRa); // norm cannot be negative
|
||||
norm = ra;
|
||||
/* Norm cannot be negative but can be preceded by a negative sign (for
|
||||
* instance "-log(0.3)") which would lead to isNegativeRa = True. */
|
||||
if (isNegativeRa) {
|
||||
norm = Opposite::Builder(ra);
|
||||
} else {
|
||||
norm = ra;
|
||||
}
|
||||
}
|
||||
if (!isZeroRa && !isZeroTb) {
|
||||
Expression arg;
|
||||
|
||||
@@ -129,7 +129,7 @@ void ExpressionNode::setChildrenInPlace(Expression other) {
|
||||
Expression(this).defaultSetChildrenInPlace(other);
|
||||
}
|
||||
|
||||
Expression ExpressionNode::denominator(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const {
|
||||
Expression ExpressionNode::denominator(ReductionContext reductionContext) const {
|
||||
return Expression();
|
||||
}
|
||||
|
||||
|
||||
@@ -115,7 +115,7 @@ void Matrix::addChildrenAsRowInPlace(TreeHandle t, int i) {
|
||||
|
||||
int Matrix::rank(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, bool inPlace) {
|
||||
Matrix m = inPlace ? *this : clone().convert<Matrix>();
|
||||
ExpressionNode::ReductionContext systemReductionContext = ExpressionNode::ReductionContext(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::System);
|
||||
ExpressionNode::ReductionContext systemReductionContext = ExpressionNode::ReductionContext(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::SystemForApproximation);
|
||||
m = m.rowCanonize(systemReductionContext, nullptr);
|
||||
int rank = m.numberOfRows();
|
||||
int i = rank-1;
|
||||
|
||||
@@ -219,8 +219,8 @@ Expression MultiplicationNode::shallowBeautify(ReductionContext reductionContext
|
||||
return Multiplication(this).shallowBeautify(reductionContext);
|
||||
}
|
||||
|
||||
Expression MultiplicationNode::denominator(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const {
|
||||
return Multiplication(this).denominator(context, complexFormat, angleUnit);
|
||||
Expression MultiplicationNode::denominator(ReductionContext reductionContext) const {
|
||||
return Multiplication(this).denominator(reductionContext);
|
||||
}
|
||||
|
||||
/* Multiplication */
|
||||
@@ -306,7 +306,7 @@ Expression Multiplication::shallowBeautify(ExpressionNode::ReductionContext redu
|
||||
|
||||
/* 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 thisExp = mergeNegativePower(reductionContext.context(), reductionContext.complexFormat(), reductionContext.angleUnit());
|
||||
Expression thisExp = mergeNegativePower(reductionContext);
|
||||
if (thisExp.type() == ExpressionNode::Type::Power) {
|
||||
return thisExp.shallowBeautify(reductionContext);
|
||||
}
|
||||
@@ -339,13 +339,13 @@ Expression Multiplication::shallowBeautify(ExpressionNode::ReductionContext redu
|
||||
return thisExp;
|
||||
}
|
||||
|
||||
Expression Multiplication::denominator(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const {
|
||||
Expression Multiplication::denominator(ExpressionNode::ReductionContext reductionContext) 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.
|
||||
Multiplication thisClone = clone().convert<Multiplication>();
|
||||
Expression e = thisClone.mergeNegativePower(context, complexFormat, angleUnit);
|
||||
Expression e = thisClone.mergeNegativePower(reductionContext);
|
||||
if (e.type() == ExpressionNode::Type::Power) {
|
||||
return e.denominator(context, complexFormat, angleUnit);
|
||||
return e.denominator(reductionContext);
|
||||
} else {
|
||||
assert(e.type() == ExpressionNode::Type::Multiplication);
|
||||
for (int i = 0; i < e.numberOfChildren(); i++) {
|
||||
@@ -511,7 +511,7 @@ Expression Multiplication::privateShallowReduce(ExpressionNode::ReductionContext
|
||||
for (int j = i+1; j < numberOfChildren(); j++) {
|
||||
Expression o2 = childAtIndex(j);
|
||||
if (Base(o2).type() == ExpressionNode::Type::Cosine && TermHasNumeralExponent(o2) && Base(o2).childAtIndex(0).isIdenticalTo(x)) {
|
||||
factorizeSineAndCosine(i, j, reductionContext.context(), reductionContext.complexFormat(), reductionContext.angleUnit());
|
||||
factorizeSineAndCosine(i, j, reductionContext);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -738,10 +738,10 @@ Expression Multiplication::distributeOnOperandAtIndex(int i, ExpressionNode::Red
|
||||
return a.shallowReduce(reductionContext); // Order terms, put under a common denominator if needed
|
||||
}
|
||||
|
||||
void Multiplication::addMissingFactors(Expression factor, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) {
|
||||
void Multiplication::addMissingFactors(Expression factor, ExpressionNode::ReductionContext reductionContext) {
|
||||
if (factor.type() == ExpressionNode::Type::Multiplication) {
|
||||
for (int j = 0; j < factor.numberOfChildren(); j++) {
|
||||
addMissingFactors(factor.childAtIndex(j), context, complexFormat, angleUnit);
|
||||
addMissingFactors(factor.childAtIndex(j), reductionContext);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -762,7 +762,6 @@ void Multiplication::addMissingFactors(Expression factor, Context * context, Pre
|
||||
if (factor.type() != ExpressionNode::Type::Rational) {
|
||||
/* If factor is not a rational, we merge it with the child of identical
|
||||
* base if any. Otherwise, we add it as an new child. */
|
||||
ExpressionNode::ReductionContext reductionContext = ExpressionNode::ReductionContext(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::User);
|
||||
for (int i = 0; i < numberOfChildren(); i++) {
|
||||
if (TermsHaveIdenticalBase(childAtIndex(i), factor)) {
|
||||
Expression sub = Subtraction::Builder(CreateExponent(childAtIndex(i)), CreateExponent(factor)).deepReduce(reductionContext);
|
||||
@@ -783,10 +782,10 @@ void Multiplication::addMissingFactors(Expression factor, Context * context, Pre
|
||||
}
|
||||
}
|
||||
addChildAtIndexInPlace(factor.clone(), 0, numberOfChildren());
|
||||
sortChildrenInPlace([](const ExpressionNode * e1, const ExpressionNode * e2, bool canBeInterrupted) { return ExpressionNode::SimplificationOrder(e1, e2, true, canBeInterrupted); }, context, true);
|
||||
sortChildrenInPlace([](const ExpressionNode * e1, const ExpressionNode * e2, bool canBeInterrupted) { return ExpressionNode::SimplificationOrder(e1, e2, true, canBeInterrupted); }, reductionContext.context(), true);
|
||||
}
|
||||
|
||||
void Multiplication::factorizeSineAndCosine(int i, int j, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) {
|
||||
void Multiplication::factorizeSineAndCosine(int i, int j, ExpressionNode::ReductionContext reductionContext) {
|
||||
/* 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 */
|
||||
@@ -802,7 +801,6 @@ void Multiplication::factorizeSineAndCosine(int i, int j, Context * context, Pre
|
||||
Number absP = p.clone().convert<Number>().setSign(ExpressionNode::Sign::Positive);
|
||||
Number absQ = q.clone().convert<Number>().setSign(ExpressionNode::Sign::Positive);
|
||||
Expression tan = Tangent::Builder(x.clone());
|
||||
ExpressionNode::ReductionContext userReductionContext = ExpressionNode::ReductionContext(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::User);
|
||||
if (Number::NaturalOrder(absP, absQ) < 0) {
|
||||
// Replace sin(x) by tan(x) or sin(x)^p by tan(x)^p
|
||||
if (p.isRationalOne()) {
|
||||
@@ -810,19 +808,19 @@ void Multiplication::factorizeSineAndCosine(int i, int j, Context * context, Pre
|
||||
} else {
|
||||
replaceChildAtIndexInPlace(i, Power::Builder(tan, p));
|
||||
}
|
||||
childAtIndex(i).shallowReduce(userReductionContext);
|
||||
childAtIndex(i).shallowReduce(reductionContext);
|
||||
// Replace cos(x)^q by cos(x)^(p+q)
|
||||
replaceChildAtIndexInPlace(j, Power::Builder(Base(childAtIndex(j)), sumPQ));
|
||||
childAtIndex(j).shallowReduce(userReductionContext);
|
||||
childAtIndex(j).shallowReduce(reductionContext);
|
||||
} else {
|
||||
// Replace cos(x)^q by tan(x)^(-q)
|
||||
Expression newPower = Power::Builder(tan, Number::Multiplication(q, Rational::Builder(-1)));
|
||||
newPower.childAtIndex(1).shallowReduce(userReductionContext);
|
||||
newPower.childAtIndex(1).shallowReduce(reductionContext);
|
||||
replaceChildAtIndexInPlace(j, newPower);
|
||||
newPower.shallowReduce(userReductionContext);
|
||||
newPower.shallowReduce(reductionContext);
|
||||
// Replace sin(x)^p by sin(x)^(p+q)
|
||||
replaceChildAtIndexInPlace(i, Power::Builder(Base(childAtIndex(i)), sumPQ));
|
||||
childAtIndex(i).shallowReduce(userReductionContext);
|
||||
childAtIndex(i).shallowReduce(reductionContext);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -876,7 +874,7 @@ bool Multiplication::TermHasNumeralExponent(const Expression & e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Expression Multiplication::mergeNegativePower(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) {
|
||||
Expression Multiplication::mergeNegativePower(ExpressionNode::ReductionContext reductionContext) {
|
||||
/* mergeNegativePower groups all factors that are power of form a^(-b) together
|
||||
* for instance, a^(-1)*b^(-c)*c = c*(a*b^c)^(-1) */
|
||||
Multiplication m = Multiplication::Builder();
|
||||
@@ -895,7 +893,7 @@ Expression Multiplication::mergeNegativePower(Context * context, Preferences::Co
|
||||
while (i < numberOfChildren()) {
|
||||
if (childAtIndex(i).type() == ExpressionNode::Type::Power) {
|
||||
Expression p = childAtIndex(i);
|
||||
Expression positivePIndex = p.childAtIndex(1).makePositiveAnyNegativeNumeralFactor( ExpressionNode::ReductionContext(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::User));
|
||||
Expression positivePIndex = p.childAtIndex(1).makePositiveAnyNegativeNumeralFactor(reductionContext);
|
||||
if (!positivePIndex.isUninitialized()) {
|
||||
// Remove a^(-b) from the Multiplication
|
||||
removeChildAtIndexInPlace(i);
|
||||
@@ -913,10 +911,10 @@ Expression Multiplication::mergeNegativePower(Context * context, Preferences::Co
|
||||
if (m.numberOfChildren() == 0) {
|
||||
return *this;
|
||||
}
|
||||
m.sortChildrenInPlace([](const ExpressionNode * e1, const ExpressionNode * e2, bool canBeInterrupted) { return ExpressionNode::SimplificationOrder(e1, e2, true, canBeInterrupted); }, context, true);
|
||||
m.sortChildrenInPlace([](const ExpressionNode * e1, const ExpressionNode * e2, bool canBeInterrupted) { return ExpressionNode::SimplificationOrder(e1, e2, true, canBeInterrupted); }, reductionContext.context(), true);
|
||||
Power p = Power::Builder(m.squashUnaryHierarchyInPlace(), Rational::Builder(-1));
|
||||
addChildAtIndexInPlace(p, 0, numberOfChildren());
|
||||
sortChildrenInPlace([](const ExpressionNode * e1, const ExpressionNode * e2, bool canBeInterrupted) { return ExpressionNode::SimplificationOrder(e1, e2, true, canBeInterrupted); }, context, true);
|
||||
sortChildrenInPlace([](const ExpressionNode * e1, const ExpressionNode * e2, bool canBeInterrupted) { return ExpressionNode::SimplificationOrder(e1, e2, true, canBeInterrupted); }, reductionContext.context(), true);
|
||||
return squashUnaryHierarchyInPlace();
|
||||
}
|
||||
|
||||
|
||||
@@ -211,8 +211,8 @@ int PowerNode::simplificationOrderSameType(const ExpressionNode * e, bool ascend
|
||||
return SimplificationOrder(childAtIndex(1), e->childAtIndex(1), ascending, canBeInterrupted);
|
||||
}
|
||||
|
||||
Expression PowerNode::denominator(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const {
|
||||
return Power(this).denominator(context, complexFormat, angleUnit);
|
||||
Expression PowerNode::denominator(ReductionContext reductionContext) const {
|
||||
return Power(this).denominator(reductionContext);
|
||||
}
|
||||
|
||||
// Evaluation
|
||||
@@ -743,8 +743,12 @@ Expression Power::shallowReduce(ExpressionNode::ReductionContext reductionContex
|
||||
}
|
||||
|
||||
/* Step 13: (a0+a1+...am)^n with n integer
|
||||
* -> a^n+?a^(n-1)*b+?a^(n-2)*b^2+...+b^n (Multinome) */
|
||||
* -> a^n+?a^(n-1)*b+?a^(n-2)*b^2+...+b^n (Multinome)
|
||||
* We don't apply this rule when the target is the SystemForApproximation.
|
||||
* Indeed, developing the multinome is likely to increase the numbers of
|
||||
* operations and lead to precision loss. */
|
||||
if (!letPowerAtRoot
|
||||
&& reductionContext.target() != ExpressionNode::ReductionTarget::SystemForApproximation
|
||||
&& indexType == ExpressionNode::Type::Rational
|
||||
&& !static_cast<Rational &>(index).signedIntegerNumerator().isZero()
|
||||
&& static_cast<Rational &>(index).isInteger()
|
||||
@@ -845,7 +849,7 @@ Expression Power::shallowReduce(ExpressionNode::ReductionContext reductionContex
|
||||
|
||||
Expression Power::shallowBeautify(ExpressionNode::ReductionContext reductionContext) {
|
||||
// Step 1: X^-y -> 1/(X->shallowBeautify)^y
|
||||
Expression p = denominator(reductionContext.context(), reductionContext.complexFormat(), reductionContext.angleUnit());
|
||||
Expression p = denominator(reductionContext);
|
||||
// If the denominator is initialized, the index of the power is of form -y
|
||||
if (!p.isUninitialized()) {
|
||||
Division d = Division::Builder(Rational::Builder(1), p);
|
||||
@@ -867,12 +871,12 @@ Expression Power::shallowBeautify(ExpressionNode::ReductionContext reductionCont
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Optional Step 3: if the ReductionTarget is the System, turn a^(p/q) into
|
||||
* (root(a, q))^p
|
||||
/* Optional Step 3: if the ReductionTarget is the SystemForApproximation or
|
||||
* SystemForAnalysis, turn a^(p/q) into (root(a, q))^p
|
||||
* Indeed, root(a, q) can have a real root which is not the principale angle
|
||||
* but that we want to return in real complex format. This special case is
|
||||
* handled in NthRoot approximation but not in Power approximation. */
|
||||
if (reductionContext.target() == ExpressionNode::ReductionTarget::System && childAtIndex(1).type() == ExpressionNode::Type::Rational) {
|
||||
if (reductionContext.target() != ExpressionNode::ReductionTarget::User && childAtIndex(1).type() == ExpressionNode::Type::Rational) {
|
||||
Integer p = childAtIndex(1).convert<Rational>().signedIntegerNumerator();
|
||||
Integer q = childAtIndex(1).convert<Rational>().integerDenominator();
|
||||
Expression nthRoot = q.isOne() ? childAtIndex(0) : NthRoot::Builder(childAtIndex(0), Rational::Builder(q));
|
||||
@@ -887,11 +891,11 @@ Expression Power::shallowBeautify(ExpressionNode::ReductionContext reductionCont
|
||||
// Private
|
||||
|
||||
// Simplification
|
||||
Expression Power::denominator(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const {
|
||||
Expression Power::denominator(ExpressionNode::ReductionContext reductionContext) const {
|
||||
// Clone the power
|
||||
Expression clone = Power::Builder(childAtIndex(0).clone(), childAtIndex(1).clone());
|
||||
// If the power is of form x^(-y), denominator should be x^y
|
||||
Expression positiveIndex = clone.childAtIndex(1).makePositiveAnyNegativeNumeralFactor(ExpressionNode::ReductionContext(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::User));
|
||||
Expression positiveIndex = clone.childAtIndex(1).makePositiveAnyNegativeNumeralFactor(reductionContext);
|
||||
if (!positiveIndex.isUninitialized()) {
|
||||
// if y was -1, clone is now x^1, denominator is then only x
|
||||
// we cannot shallowReduce the clone as it is not attached to its parent yet
|
||||
|
||||
@@ -145,7 +145,7 @@ Expression RationalNode::shallowBeautify(ReductionContext reductionContext) {
|
||||
return Rational(this).shallowBeautify();
|
||||
}
|
||||
|
||||
Expression RationalNode::denominator(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const {
|
||||
Expression RationalNode::denominator(ReductionContext reductionContext) const {
|
||||
return Rational(this).denominator();
|
||||
}
|
||||
|
||||
|
||||
@@ -311,7 +311,7 @@ QUIZ_CASE(poincare_preperties_get_variables) {
|
||||
void assert_reduced_expression_has_polynomial_coefficient(const char * expression, const char * symbolName, const char ** coefficients, Preferences::ComplexFormat complexFormat = Cartesian, Preferences::AngleUnit angleUnit = Radian) {
|
||||
Shared::GlobalContext globalContext;
|
||||
Expression e = parse_expression(expression, false);
|
||||
e = e.reduce(&globalContext, complexFormat, angleUnit);
|
||||
e = e.reduce(&globalContext, complexFormat, angleUnit, SystemForAnalysis);
|
||||
Expression coefficientBuffer[Poincare::Expression::k_maxNumberOfPolynomialCoefficients];
|
||||
int d = e.getPolynomialReducedCoefficients(symbolName, coefficientBuffer, &globalContext, complexFormat, Radian);
|
||||
for (int i = 0; i <= d; i++) {
|
||||
|
||||
@@ -54,11 +54,11 @@ Poincare::Expression parse_expression(const char * expression, bool addParenthes
|
||||
return result;
|
||||
}
|
||||
|
||||
void assert_simplify(const char * expression, Preferences::AngleUnit angleUnit, Preferences::ComplexFormat complexFormat) {
|
||||
void assert_simplify(const char * expression, Preferences::AngleUnit angleUnit, Preferences::ComplexFormat complexFormat, ExpressionNode::ReductionTarget target) {
|
||||
Shared::GlobalContext globalContext;
|
||||
Expression e = parse_expression(expression, false);
|
||||
quiz_assert_print_if_failure(!e.isUninitialized(), expression);
|
||||
e = e.simplify(&globalContext, complexFormat, angleUnit);
|
||||
e = e.simplify(&globalContext, complexFormat, angleUnit, target);
|
||||
quiz_assert_print_if_failure(!(e.isUninitialized()), expression);
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ void assert_parsed_expression_simplify_to(const char * expression, const char *
|
||||
if (target == ExpressionNode::ReductionTarget::User) {
|
||||
copy.simplifyAndApproximate(©, nullptr, context, complexFormat, angleUnit, symbolicComputation);
|
||||
} else {
|
||||
copy = copy.simplify(context, complexFormat, angleUnit, symbolicComputation);
|
||||
copy = copy.simplify(context, complexFormat, angleUnit, target, symbolicComputation);
|
||||
}
|
||||
if (copy.isUninitialized()) {
|
||||
return e;
|
||||
@@ -81,7 +81,7 @@ template<typename T>
|
||||
void assert_expression_approximates_to(const char * expression, const char * approximation, Preferences::AngleUnit angleUnit, Preferences::ComplexFormat complexFormat, int numberOfSignificantDigits) {
|
||||
int numberOfDigits = sizeof(T) == sizeof(double) ? PrintFloat::k_numberOfStoredSignificantDigits : PrintFloat::k_numberOfPrintedSignificantDigits;
|
||||
numberOfDigits = numberOfSignificantDigits > 0 ? numberOfSignificantDigits : numberOfDigits;
|
||||
assert_parsed_expression_process_to(expression, approximation, ExpressionNode::ReductionTarget::System, complexFormat, angleUnit, false, [](Expression e, Context * context, ExpressionNode::ReductionTarget target, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, bool symbolicComputation) {
|
||||
assert_parsed_expression_process_to(expression, approximation, ExpressionNode::ReductionTarget::SystemForApproximation, complexFormat, angleUnit, false, [](Expression e, Context * context, ExpressionNode::ReductionTarget target, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, bool symbolicComputation) {
|
||||
return e.approximate<T>(context, complexFormat, angleUnit);
|
||||
}, numberOfDigits);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,8 @@ const char * MaxIntegerString(); // (2^32)^k_maxNumberOfDigits-1
|
||||
const char * OverflowedIntegerString(); // (2^32)^k_maxNumberOfDigits
|
||||
const char * BigOverflowedIntegerString(); // OverflowedIntegerString with a 2 on first digit
|
||||
|
||||
constexpr Poincare::ExpressionNode::ReductionTarget System = Poincare::ExpressionNode::ReductionTarget::System;
|
||||
constexpr Poincare::ExpressionNode::ReductionTarget SystemForApproximation = Poincare::ExpressionNode::ReductionTarget::SystemForApproximation;
|
||||
constexpr Poincare::ExpressionNode::ReductionTarget SystemForAnalysis = Poincare::ExpressionNode::ReductionTarget::SystemForAnalysis;
|
||||
constexpr Poincare::ExpressionNode::ReductionTarget User = Poincare::ExpressionNode::ReductionTarget::User;
|
||||
constexpr Poincare::Preferences::AngleUnit Degree = Poincare::Preferences::AngleUnit::Degree;
|
||||
constexpr Poincare::Preferences::AngleUnit Radian = Poincare::Preferences::AngleUnit::Radian;
|
||||
@@ -30,7 +31,7 @@ Poincare::Expression parse_expression(const char * expression, bool addParenthes
|
||||
|
||||
// Simplification
|
||||
|
||||
void assert_simplify(const char * expression, Poincare::Preferences::AngleUnit angleUnit = Radian, Poincare::Preferences::ComplexFormat complexFormat = Cartesian);
|
||||
void assert_simplify(const char * expression, Poincare::Preferences::AngleUnit angleUnit = Radian, Poincare::Preferences::ComplexFormat complexFormat = Cartesian, Poincare::ExpressionNode::ReductionTarget target = User);
|
||||
|
||||
void assert_parsed_expression_simplify_to(const char * expression, const char * simplifiedExpression, Poincare::ExpressionNode::ReductionTarget target = User, Poincare::Preferences::AngleUnit angleUnit = Radian, Poincare::Preferences::ComplexFormat complexFormat = Cartesian, bool symbolicComputation = true);
|
||||
|
||||
|
||||
@@ -108,6 +108,9 @@ QUIZ_CASE(assert_print_floats) {
|
||||
assert_float_prints_to(10000000000000000000000000000.0, "1ᴇ28", DecimalMode, 14);
|
||||
assert_float_prints_to(10000000000000000000000000000.0, "10ᴇ27", EngineeringMode, 14);
|
||||
|
||||
// This used to crash on web platform
|
||||
assert_float_prints_to(1000.0, "1000", DecimalMode, 7);
|
||||
|
||||
assert_float_prints_to(1000000.0f, "1ᴇ6", ScientificMode, 7);
|
||||
assert_float_prints_to(1000000.0f, "1000000", DecimalMode, 7);
|
||||
assert_float_prints_to(1000000.0f, "1ᴇ6", EngineeringMode, 7);
|
||||
|
||||
@@ -941,6 +941,7 @@ QUIZ_CASE(poincare_simplification_complex_format) {
|
||||
assert_parsed_expression_simplify_to("[[1,√(-1)]]", "[[1,ℯ^\u0012π/2×𝐢\u0013]]", User, Radian, Polar);
|
||||
assert_parsed_expression_simplify_to("atan(2)", "atan(2)", User, Radian, Polar);
|
||||
assert_parsed_expression_simplify_to("atan(-2)", "atan(2)×ℯ^\u0012π×𝐢\u0013", User, Radian, Polar);
|
||||
assert_parsed_expression_simplify_to("cos(42π)", "-cos(42×π)×ℯ^\x12π×𝐢\x13", User, Degree, Polar);
|
||||
|
||||
// User defined variable
|
||||
assert_parsed_expression_simplify_to("a", "a", User, Radian, Polar);
|
||||
@@ -959,36 +960,54 @@ QUIZ_CASE(poincare_simplification_complex_format) {
|
||||
}
|
||||
|
||||
QUIZ_CASE(poincare_simplification_reduction_target) {
|
||||
assert_parsed_expression_simplify_to("1/π+1/x", "1/x+1/π", System);
|
||||
// Factorize on the same denominator only for ReductionTarget = User
|
||||
assert_parsed_expression_simplify_to("1/π+1/x", "1/x+1/π", SystemForAnalysis);
|
||||
assert_parsed_expression_simplify_to("1/π+1/x", "1/x+1/π", SystemForApproximation);
|
||||
assert_parsed_expression_simplify_to("1/π+1/x", "\u0012x+π\u0013/\u0012π×x\u0013", User);
|
||||
|
||||
assert_parsed_expression_simplify_to("1/(1+𝐢)", "1/\u0012𝐢+1\u0013", System);
|
||||
// Display in the form a+ib only for ReductionTarget = User
|
||||
assert_parsed_expression_simplify_to("1/(1+𝐢)", "1/\u0012𝐢+1\u0013", SystemForAnalysis);
|
||||
assert_parsed_expression_simplify_to("1/(1+𝐢)", "1/\u0012𝐢+1\u0013", SystemForApproximation);
|
||||
assert_parsed_expression_simplify_to("1/(1+𝐢)", "1/2-1/2×𝐢", User);
|
||||
|
||||
assert_parsed_expression_simplify_to("sin(x)/(cos(x)×cos(x))", "sin(x)/cos(x)^2", System);
|
||||
// Replace sin/cos-->tan for ReductionTarget = User
|
||||
assert_parsed_expression_simplify_to("sin(x)/(cos(x)×cos(x))", "sin(x)/cos(x)^2", SystemForAnalysis);
|
||||
assert_parsed_expression_simplify_to("sin(x)/(cos(x)×cos(x))", "sin(x)/cos(x)^2", SystemForApproximation);
|
||||
assert_parsed_expression_simplify_to("sin(x)/(cos(x)×cos(x))", "tan(x)/cos(x)", User);
|
||||
|
||||
assert_parsed_expression_simplify_to("x^0", "x^0", System);
|
||||
// Apply rule x^0 --> 1 for ReductionTarget = User (because this is not always true)
|
||||
assert_parsed_expression_simplify_to("x^0", "x^0", SystemForAnalysis);
|
||||
assert_parsed_expression_simplify_to("x^0", "x^0", SystemForApproximation);
|
||||
assert_parsed_expression_simplify_to("x^0", "1", User);
|
||||
assert_parsed_expression_simplify_to("(1+x)/(1+x)", "(x+1)^0", SystemForApproximation);
|
||||
assert_parsed_expression_simplify_to("(1+x)/(1+x)", "1", User);
|
||||
|
||||
assert_parsed_expression_simplify_to("x^(2/3)", "root(x,3)^2", System);
|
||||
// Apply rule x^(2/3) --> root(x,3)^2 for ReductionTarget = System
|
||||
assert_parsed_expression_simplify_to("x^(2/3)", "root(x,3)^2", SystemForApproximation);
|
||||
assert_parsed_expression_simplify_to("x^(2/3)", "root(x,3)^2", SystemForAnalysis);
|
||||
assert_parsed_expression_simplify_to("x^(2/3)", "x^\u00122/3\u0013", User);
|
||||
assert_parsed_expression_simplify_to("x^(1/3)", "root(x,3)", System);
|
||||
assert_parsed_expression_simplify_to("x^(1/3)", "root(x,3)", System);
|
||||
assert_parsed_expression_simplify_to("x^2", "x^2", System);
|
||||
assert_parsed_expression_simplify_to("x^(1/3)", "root(x,3)", SystemForApproximation);
|
||||
assert_parsed_expression_simplify_to("x^(1/3)", "root(x,3)", SystemForAnalysis);
|
||||
assert_parsed_expression_simplify_to("x^(1/3)", "root(x,3)", User);
|
||||
assert_parsed_expression_simplify_to("x^2", "x^2", SystemForApproximation);
|
||||
assert_parsed_expression_simplify_to("x^2", "x^2", User);
|
||||
|
||||
assert_parsed_expression_simplify_to("1/(√(2)+√(3))", "1/\u0012√(3)+√(2)\u0013", System);
|
||||
// Remove square root at denominator for ReductionTarget = User
|
||||
assert_parsed_expression_simplify_to("1/(√(2)+√(3))", "1/\u0012√(3)+√(2)\u0013", SystemForApproximation);
|
||||
assert_parsed_expression_simplify_to("1/(√(2)+√(3))", "√(3)-√(2)", User);
|
||||
|
||||
assert_parsed_expression_simplify_to("sign(abs(x))", "sign(abs(x))", System);
|
||||
// Always reduce sign for ReductionTarget = User
|
||||
assert_parsed_expression_simplify_to("sign(abs(x))", "sign(abs(x))", SystemForApproximation);
|
||||
assert_parsed_expression_simplify_to("sign(abs(x))", "1", User);
|
||||
|
||||
assert_parsed_expression_simplify_to("atan(1/x)", "atan(1/x)", System);
|
||||
// Apply rule atan(1/x)-> (π×sign(x)-2×atan(x))/2 for ReductionTarget = User (as it is not always true)
|
||||
assert_parsed_expression_simplify_to("atan(1/x)", "atan(1/x)", SystemForApproximation);
|
||||
assert_parsed_expression_simplify_to("atan(1/x)", "\u0012π×sign(x)-2×atan(x)\u0013/2", User);
|
||||
|
||||
assert_parsed_expression_simplify_to("(1+x)/(1+x)", "(x+1)^0", System);
|
||||
assert_parsed_expression_simplify_to("(1+x)/(1+x)", "1", User);
|
||||
// Expand multinome when ReductionTarget is not SystemForApproximation as it increases precision loss
|
||||
assert_parsed_expression_simplify_to("(2+x)^2", "(x+2)^2", SystemForApproximation);
|
||||
assert_parsed_expression_simplify_to("(2+x)^2", "x^2+4×x+4", SystemForAnalysis);
|
||||
assert_parsed_expression_simplify_to("(2+x)^2", "x^2+4×x+4", User);
|
||||
}
|
||||
|
||||
QUIZ_CASE(poincare_simplification_mix) {
|
||||
|
||||
Reference in New Issue
Block a user