Files
Upsilon/apps/solver/equation_store.cpp
Arthur Camouseigt 913c81a0d3 [solver] Modified the way to solve some equations
The solutions of equations that need numerical approximations to be
solved are now computed base on the undeveloped equation (instead of
fully the expended one used to identify polynomials)

This allow (x-10)^7=0 to yield x=10 as result (9.95 before)
Change-Id: Ia8acbe57a9cfebf0b5016e9c896d21c8ddac7a64
2020-11-04 15:32:59 +01:00

458 lines
24 KiB
C++
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "equation_store.h"
#include "../constant.h"
#include "../shared/poincare_helpers.h"
#include "../exam_mode_configuration.h"
#include "../global_preferences.h"
#include <limits.h>
#include <poincare/constant.h>
#include <poincare/symbol.h>
#include <poincare/matrix.h>
#include <poincare/rational.h>
#include <poincare/opposite.h>
#include <poincare/addition.h>
#include <poincare/subtraction.h>
#include <poincare/multiplication.h>
#include <poincare/division.h>
#include <poincare/square_root.h>
#include <poincare/power.h>
#include <poincare/undefined.h>
using namespace Poincare;
using namespace Shared;
namespace Solver {
EquationStore::EquationStore() :
ExpressionModelStore(),
m_type(Type::LinearSystem),
m_numberOfSolutions(0),
m_exactSolutionExactLayouts{},
m_exactSolutionApproximateLayouts{},
m_numberOfUserVariables(0)
{
}
Ion::Storage::Record::ErrorStatus EquationStore::addEmptyModel() {
char name[3] = {'e', '?', 0}; // name is going to be e0 or e1 or ... e5
int currentNumber = 0;
while (currentNumber < k_maxNumberOfEquations) {
name[1] = '0'+currentNumber;
if (Ion::Storage::sharedStorage()->recordBaseNamedWithExtension(name, Ion::Storage::eqExtension).isNull()) {
break;
}
currentNumber++;
}
assert(currentNumber < k_maxNumberOfEquations);
return Ion::Storage::sharedStorage()->createRecordWithExtension(name, Ion::Storage::eqExtension, nullptr, 0);
}
Shared::ExpressionModelHandle * EquationStore::setMemoizedModelAtIndex(int cacheIndex, Ion::Storage::Record record) const {
assert(cacheIndex >= 0 && cacheIndex < maxNumberOfMemoizedModels());
m_equations[cacheIndex] = Equation(record);
return &m_equations[cacheIndex];
}
ExpressionModelHandle * EquationStore::memoizedModelAtIndex(int cacheIndex) const {
assert(cacheIndex >= 0 && cacheIndex < maxNumberOfMemoizedModels());
return &m_equations[cacheIndex];
}
void EquationStore::tidy() {
ExpressionModelStore::tidy();
tidySolution();
}
Poincare::Layout EquationStore::exactSolutionLayoutAtIndex(int i, bool exactLayout) {
assert(m_type != Type::Monovariable && i >= 0 && (i < m_numberOfSolutions));
if (exactLayout) {
return m_exactSolutionExactLayouts[i];
} else {
return m_exactSolutionApproximateLayouts[i];
}
}
double EquationStore::intervalBound(int index) const {
assert(m_type == Type::Monovariable && index >= 0 && index < 2);
return m_intervalApproximateSolutions[index];
}
void EquationStore::setIntervalBound(int index, double value) {
assert(m_type == Type::Monovariable && index >= 0 && index < 2);
m_intervalApproximateSolutions[index] = value;
if (m_intervalApproximateSolutions[0] > m_intervalApproximateSolutions[1]) {
if (index == 0) {
m_intervalApproximateSolutions[1] = m_intervalApproximateSolutions[0]+1;
} else {
m_intervalApproximateSolutions[0] = m_intervalApproximateSolutions[1]-1;
}
}
}
void EquationStore::approximateSolve(Poincare::Context * context, bool shouldReplaceFunctionsButNotSymbols) {
m_hasMoreThanMaxNumberOfApproximateSolution = false;
Expression undevelopedExpression = modelForRecord(definedRecordAtIndex(0))->standardForm(context, shouldReplaceFunctionsButNotSymbols, ExpressionNode::ReductionTarget::SystemForApproximation);
m_userVariablesUsed = !shouldReplaceFunctionsButNotSymbols;
assert(m_variables[0][0] != 0 && m_variables[1][0] == 0);
assert(m_type == Type::Monovariable);
m_numberOfSolutions = 0;
double start = m_intervalApproximateSolutions[0];
double step = (m_intervalApproximateSolutions[1]-m_intervalApproximateSolutions[0])*k_precision;
double root;
for (int i = 0; i <= k_maxNumberOfApproximateSolutions; i++) {
root = PoincareHelpers::NextRoot(undevelopedExpression, m_variables[0], start, step, m_intervalApproximateSolutions[1], context);
if (i == k_maxNumberOfApproximateSolutions) {
m_hasMoreThanMaxNumberOfApproximateSolution = !isnan(root);
break;
}
m_approximateSolutions[i] = root;
if (std::isnan(m_approximateSolutions[i])) {
break;
} else {
start = m_approximateSolutions[i];
m_numberOfSolutions++;
}
}
}
EquationStore::Error EquationStore::exactSolve(Poincare::Context * context, bool * replaceFunctionsButNotSymbols) {
assert(replaceFunctionsButNotSymbols != nullptr);
*replaceFunctionsButNotSymbols = false;
Error e = privateExactSolve(context, false);
if (m_numberOfUserVariables > 0 && (e != Error::NoError || numberOfSolutions() == 0)) {
*replaceFunctionsButNotSymbols = true;
e = privateExactSolve(context, true);
}
return e;
}
/* Equations are solved according to the following procedure :
* 1) We develop the equations using the reduction target "SystemForAnalysis".
* This expands structures like Newton multinoms and allows us to detect
* polynoms afterwards. ("(x+2)^2" in this form is not detected but is if
* expanded).
* 2) We look for classic forms of equations for which we have algorithms
* that output the exact answer. If one is recognized in the input equation,
* the exact answer is given to the user.
* 3) If no classic form has been found in the developped form, we need to use
* numerical approximation. Therefore, to prevent precision losses, we work
* with the undevelopped form of the equation. Therefore we set reductionTarget
* to SystemForApproximation. Solutions are then numericaly approximated
* between the bounds provided by the user. */
EquationStore::Error EquationStore::privateExactSolve(Poincare::Context * context, bool replaceFunctionsButNotSymbols) {
tidySolution();
m_userVariablesUsed = !replaceFunctionsButNotSymbols;
// Step 1. Get unknown and user-defined variables
m_variables[0][0] = 0;
int numberOfVariables = 0;
// TODO we look twice for variables but not the same, is there a way to not do the same work twice?
m_userVariables[0][0] = 0;
m_numberOfUserVariables = 0;
Expression simplifiedExpressions[k_maxNumberOfEquations];
for (int i = 0; i < numberOfDefinedModels(); i++) {
Shared::ExpiringPointer<Equation> eq = modelForRecord(definedRecordAtIndex(i));
/* Start by looking for user variables, so that if we escape afterwards, we
* know if it might be due to a user variable. */
if (m_numberOfUserVariables < Expression::k_maxNumberOfVariables) {
const Expression eWithSymbols = eq->standardForm(context, true, ExpressionNode::ReductionTarget::SystemForAnalysis);
/* if replaceFunctionsButNotSymbols is true we can memoize the expressions
* for the rest of the function. Otherwise, we will memoize them at the
* next call to standardForm*/
if (replaceFunctionsButNotSymbols == true) {
simplifiedExpressions[i] = eWithSymbols;
}
int varCount = eWithSymbols.getVariables(context, [](const char * symbol, Poincare::Context * context) { return context->expressionTypeForIdentifier(symbol, strlen(symbol)) == Poincare::Context::SymbolAbstractType::Symbol; }, (char *)m_userVariables, Poincare::SymbolAbstract::k_maxNameSize, m_numberOfUserVariables);
m_numberOfUserVariables = varCount < 0 ? Expression::k_maxNumberOfVariables : varCount;
}
if (simplifiedExpressions[i].isUninitialized()) {
// The expression was not memoized before.
simplifiedExpressions[i] = eq->standardForm(context, replaceFunctionsButNotSymbols, ExpressionNode::ReductionTarget::SystemForAnalysis);
}
const Expression e = simplifiedExpressions[i];
if (e.isUninitialized() || e.type() == ExpressionNode::Type::Undefined || e.recursivelyMatches(Expression::IsMatrix, context, replaceFunctionsButNotSymbols ? ExpressionNode::SymbolicComputation::ReplaceDefinedFunctionsWithDefinitions : ExpressionNode::SymbolicComputation::ReplaceAllDefinedSymbolsWithDefinition)) {
return Error::EquationUndefined;
}
if (e.type() == ExpressionNode::Type::Unreal) {
return Error::EquationUnreal;
}
numberOfVariables = e.getVariables(context, [](const char * symbol, Poincare::Context * context) { return true; }, (char *)m_variables, Poincare::SymbolAbstract::k_maxNameSize, numberOfVariables);
if (numberOfVariables == -1) {
return Error::TooManyVariables;
}
/* The equation has been parsed so there should be no
* Error::VariableNameTooLong*/
assert(numberOfVariables >= 0);
}
// Step 2. Linear System?
/* Create matrix coefficients and vector constants as:
* coefficients * (x y z ...) = constants */
Expression coefficients[k_maxNumberOfEquations][Expression::k_maxNumberOfVariables];
Expression constants[k_maxNumberOfEquations];
bool isLinear = true; // Invalid the linear system if one equation is non-linear
Preferences * preferences = Preferences::sharedPreferences();
for (int i = 0; i < numberOfDefinedModels(); i++) {
isLinear = isLinear && simplifiedExpressions[i].getLinearCoefficients((char *)m_variables, Poincare::SymbolAbstract::k_maxNameSize, coefficients[i], &constants[i], context, updatedComplexFormat(context), preferences->angleUnit(), GlobalPreferences::sharedGlobalPreferences()->unitFormat(), replaceFunctionsButNotSymbols ? ExpressionNode::SymbolicComputation::ReplaceDefinedFunctionsWithDefinitions : ExpressionNode::SymbolicComputation::ReplaceAllDefinedSymbolsWithDefinition);
if (!isLinear) {
// TODO: should we clean pool allocated memory if the system is not linear
#if 0
for (int j = 0; j < i; j++) {
for (int k = 0; k < numberOfVariables; k++) {
coefficients[j][k] = Expression();
}
constants[j] = Expression();
}
#endif
if (numberOfDefinedModels() > 1 || numberOfVariables > 1) {
return Error::NonLinearSystem;
} else {
break;
}
}
}
// Initialize result
Expression exactSolutions[k_maxNumberOfExactSolutions];
Expression exactSolutionsApproximations[k_maxNumberOfExactSolutions];
EquationStore::Error error;
if (isLinear) {
m_type = Type::LinearSystem;
error = resolveLinearSystem(exactSolutions, exactSolutionsApproximations, coefficients, constants, context);
} else {
// Step 3. Polynomial & Monovariable?
assert(numberOfVariables == 1 && numberOfDefinedModels() == 1);
Expression polynomialCoefficients[Expression::k_maxNumberOfPolynomialCoefficients];
int degree = simplifiedExpressions[0]
.getPolynomialReducedCoefficients(
m_variables[0],
polynomialCoefficients,
context,
updatedComplexFormat(context),
preferences->angleUnit(),
GlobalPreferences::sharedGlobalPreferences()->unitFormat(),
replaceFunctionsButNotSymbols ?
ExpressionNode::SymbolicComputation::ReplaceDefinedFunctionsWithDefinitions :
ExpressionNode::SymbolicComputation::ReplaceAllDefinedSymbolsWithDefinition);
if (degree == 2) {
// Polynomial degree <= 2
m_type = Type::PolynomialMonovariable;
error = oneDimensialPolynomialSolve(exactSolutions, exactSolutionsApproximations, polynomialCoefficients, degree, context);
} else {
// Step 4. Monovariable non-polynomial or polynomial with degree > 2
m_type = Type::Monovariable;
m_intervalApproximateSolutions[0] = -10;
m_intervalApproximateSolutions[1] = 10;
return Error::RequireApproximateSolution;
}
}
// Create the results' layouts
// Some exam mode configuration requires to display only approximate solutions
bool forbidExactSolutions = ExamModeConfiguration::exactExpressionsAreForbidden(GlobalPreferences::sharedGlobalPreferences()->examMode());
int solutionIndex = 0;
int initialNumberOfSolutions = m_numberOfSolutions <= k_maxNumberOfExactSolutions ? m_numberOfSolutions : -1;
// We iterate through the solutions and the potential delta
for (int i = 0; i < initialNumberOfSolutions; i++) {
if (!exactSolutions[i].isUninitialized()) {
assert(!exactSolutionsApproximations[i].isUninitialized());
if (exactSolutionsApproximations[i].type() == ExpressionNode::Type::Unreal) {
// Discard unreal solutions.
m_numberOfSolutions--;
continue;
}
m_exactSolutionExactLayouts[solutionIndex] = PoincareHelpers::CreateLayout(exactSolutions[i]);
m_exactSolutionApproximateLayouts[solutionIndex] = PoincareHelpers::CreateLayout(exactSolutionsApproximations[i]);
// Check for identity between exact and approximate layouts
char exactBuffer[::Constant::MaxSerializedExpressionSize];
char approximateBuffer[::Constant::MaxSerializedExpressionSize];
m_exactSolutionExactLayouts[solutionIndex].serializeForParsing(exactBuffer, ::Constant::MaxSerializedExpressionSize);
m_exactSolutionApproximateLayouts[solutionIndex].serializeForParsing(approximateBuffer, ::Constant::MaxSerializedExpressionSize);
/* Cheat: declare exact and approximate solutions to be identical in when
* 'forbidExactSolutions' is true to display only the approximate
* solutions. */
m_exactSolutionIdentity[solutionIndex] = forbidExactSolutions || strcmp(exactBuffer, approximateBuffer) == 0;
if (!m_exactSolutionIdentity[solutionIndex]) {
m_exactSolutionEquality[solutionIndex] = Expression::ParsedExpressionsAreEqual(exactBuffer, approximateBuffer, context, updatedComplexFormat(context), preferences->angleUnit(), GlobalPreferences::sharedGlobalPreferences()->unitFormat());
}
solutionIndex++;
}
}
return error;
}
EquationStore::Error EquationStore::resolveLinearSystem(Expression exactSolutions[k_maxNumberOfExactSolutions], Expression exactSolutionsApproximations[k_maxNumberOfExactSolutions], Expression coefficients[k_maxNumberOfEquations][Expression::k_maxNumberOfVariables], Expression constants[k_maxNumberOfEquations], Context * context) {
Preferences::AngleUnit angleUnit = Preferences::sharedPreferences()->angleUnit();
Preferences::UnitFormat unitFormat = GlobalPreferences::sharedGlobalPreferences()->unitFormat();
// n unknown variables
int n = 0;
while (n < Expression::k_maxNumberOfVariables && m_variables[n][0] != 0) {
n++;
}
int m = numberOfDefinedModels(); // m equations
/* Create the matrix (A | b) for the equation Ax=b */
Matrix Ab = Matrix::Builder();
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
Ab.addChildAtIndexInPlace(coefficients[i][j], Ab.numberOfChildren(), Ab.numberOfChildren());
}
Ab.addChildAtIndexInPlace(constants[i], Ab.numberOfChildren(), Ab.numberOfChildren());
}
Ab.setDimensions(m, n+1);
// Compute the rank of (A | b)
int rankAb = Ab.rank(context, updatedComplexFormat(context), angleUnit, unitFormat, true);
// Initialize the number of solutions
m_numberOfSolutions = INT_MAX;
/* If the matrix has one null row except the last column, the system is
* inconsistent (equivalent to 0 = x with x non-null */
for (int j = m-1; j >= 0; j--) {
bool rowWithNullCoefficients = true;
for (int i = 0; i < n; i++) {
if (!Ab.matrixChild(j, i).isRationalZero()) {
rowWithNullCoefficients = false;
break;
}
}
if (rowWithNullCoefficients && !Ab.matrixChild(j, n).isRationalZero()) {
m_numberOfSolutions = 0;
}
}
if (m_numberOfSolutions > 0) {
// if rank(A | b) < n, the system has an infinite number of solutions
if (rankAb == n && n > 0) {
// Otherwise, the system has n solutions which correspond to the last column
m_numberOfSolutions = n;
for (int i = 0; i < m_numberOfSolutions; i++) {
exactSolutions[i] = Ab.matrixChild(i,n);
exactSolutions[i].simplifyAndApproximate(&exactSolutions[i], &exactSolutionsApproximations[i], context, updatedComplexFormat(context), angleUnit, unitFormat);
}
}
}
return Error::NoError;
}
EquationStore::Error EquationStore::oneDimensialPolynomialSolve(Expression exactSolutions[k_maxNumberOfExactSolutions], Expression exactSolutionsApproximations[k_maxNumberOfExactSolutions], Expression coefficients[Expression::k_maxNumberOfPolynomialCoefficients], int degree, Context * context) {
/* Equation ax^2+bx+c = 0 */
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(ExpressionNode::ReductionContext(context, updatedComplexFormat(context), Poincare::Preferences::sharedPreferences()->angleUnit(), GlobalPreferences::sharedGlobalPreferences()->unitFormat(), ExpressionNode::ReductionTarget::SystemForApproximation));
if (delta.isUninitialized()) {
delta = Poincare::Undefined::Builder();
}
if (delta.isRationalZero()) {
// if delta = 0, x0=x1= -b/(2a)
exactSolutions[0] = Division::Builder(Opposite::Builder(coefficients[1]), Multiplication::Builder(Rational::Builder(2), coefficients[2]));
m_numberOfSolutions = 2;
} else {
// x0 = (-b-sqrt(delta))/(2a)
exactSolutions[0] = Division::Builder(Subtraction::Builder(Opposite::Builder(coefficients[1].clone()), SquareRoot::Builder(delta.clone())), Multiplication::Builder(Rational::Builder(2), coefficients[2].clone()));
// x1 = (-b+sqrt(delta))/(2a)
exactSolutions[1] = Division::Builder(Addition::Builder(Opposite::Builder(coefficients[1]), SquareRoot::Builder(delta.clone())), Multiplication::Builder(Rational::Builder(2), coefficients[2]));
m_numberOfSolutions = 3;
}
exactSolutions[m_numberOfSolutions-1] = delta;
for (int i = 0; i < m_numberOfSolutions; i++) {
exactSolutions[i].simplifyAndApproximate(&exactSolutions[i], &exactSolutionsApproximations[i], context, updatedComplexFormat(context), Poincare::Preferences::sharedPreferences()->angleUnit(), GlobalPreferences::sharedGlobalPreferences()->unitFormat());
}
return Error::NoError;
#if 0
if (degree == 3) {
Expression * a = coefficients[3];
Expression * b = coefficients[2];
Expression * c = coefficients[1];
Expression * d = coefficients[0];
// Delta = b^2*c^2+18abcd-27a^2*d^2-4ac^3-4db^3
Expression * mult0Operands[2] = {new Power::Builder(b->clone(), new Rational::Builder(2), false), new Power::Builder(c->clone(), new Rational::Builder(2), false)};
Expression * mult1Operands[5] = {new Rational::Builder(18), a->clone(), b->clone(), c->clone(), d->clone()};
Expression * mult2Operands[3] = {new Rational::Builder(-27), new Power::Builder(a->clone(), new Rational::Builder(2), false), new Power::Builder(d->clone(), new Rational::Builder(2), false)};
Expression * mult3Operands[3] = {new Rational::Builder(-4), a->clone(), new Power::Builder(c->clone(), new Rational::Builder(3), false)};
Expression * mult4Operands[3] = {new Rational::Builder(-4), d->clone(), new Power::Builder(b->clone(), new Rational::Builder(3), false)};
Expression * add0Operands[5] = {new Multiplication::Builder(mult0Operands, 2, false), new Multiplication::Builder(mult1Operands, 5, false), new Multiplication::Builder(mult2Operands, 3, false), new Multiplication::Builder(mult3Operands, 3, false), new Multiplication::Builder(mult4Operands, 3, false)};
Expression * delta = new Addition(add0Operands, 5, false);
PoincareHelpers::Simplify(&delta, *context);
// Delta0 = b^2-3ac
Expression * mult5Operands[3] = {new Rational::Builder(3), a->clone(), c->clone()};
Expression * delta0 = new Subtraction::Builder(new Power::Builder(b->clone(), new Rational::Builder(2), false), new Multiplication::Builder(mult5Operands, 3, false), false);
Reduce(&delta0, *context);
if (delta->isRationalZero()) {
if (delta0->isRationalZero()) {
// delta0 = 0 && delta = 0 --> x0 = -b/(3a)
delete delta0;
m_exactSolutions[0] = new Opposite::Builder(new Division::Builder(b, new Multiplication::Builder(new Rational::Builder(3), a, false), false), false);
m_numberOfSolutions = 1;
delete c;
delete d;
} else {
// delta = 0 --> x0 = (9ad-bc)/(2delta0)
// --> x1 = (4abc-9a^2d-b^3)/(a*delta0)
Expression * mult6Operands[3] = {new Rational::Builder(9), a, d};
m_exactSolutions[0] = new Division::Builder(new Subtraction::Builder(new Multiplication::Builder(mult6Operands, 3, false), new Multiplication::Builder(b, c, false), false), new Multiplication::Builder(new Rational::Builder(2), delta0, false), false);
Expression * mult7Operands[4] = {new Rational::Builder(4), a->clone(), b->clone(), c->clone()};
Expression * mult8Operands[3] = {new Rational::Builder(-9), new Power::Builder(a->clone(), new Rational::Builder(2), false), d->clone()};
Expression * add1Operands[3] = {new Multiplication::Builder(mult7Operands, 4, false), new Multiplication::Builder(mult8Operands,3, false), new Opposite::Builder(new Power::Builder(b->clone(), new Rational::Builder(3), false), false)};
m_exactSolutions[1] = new Division::Builder(new Addition(add1Operands, 3, false), new Multiplication::Builder(a->clone(), delta0, false), false);
m_numberOfSolutions = 2;
}
} else {
// delta1 = 2b^3-9abc+27a^2*d
Expression * mult9Operands[4] = {new Rational::Builder(-9), a, b, c};
Expression * mult10Operands[3] = {new Rational::Builder(27), new Power::Builder(a->clone(), new Rational::Builder(2), false), d};
Expression * add2Operands[3] = {new Multiplication::Builder(new Rational::Builder(2), new Power::Builder(b->clone(), new Rational::Builder(3), false), false), new Multiplication::Builder(mult9Operands, 4, false), new Multiplication::Builder(mult10Operands, 3, false)};
Expression * delta1 = new Addition(add2Operands, 3, false);
// C = Root((delta1+sqrt(-27a^2*delta))/2, 3)
Expression * mult11Operands[3] = {new Rational::Builder(-27), new Power::Builder(a->clone(), new Rational::Builder(2), false), (*delta)->clone()};
Expression * c = new Power::Builder(new Division::Builder(new Addition(delta1, new SquareRoot(new Multiplication::Builder(mult11Operands, 3, false), false), false), new Rational::Builder(2), false), new Rational::Builder(1,3), false);
Expression * unary3roots[2] = {new Addition(new Rational::Builder(-1,2), new Division::Builder(new Multiplication::Builder(new SquareRoot(new Rational::Builder(3), false), new Constant::Builder(UCodePointMathematicalBoldSmallI), false), new Rational::Builder(2), false), false), new Subtraction::Builder(new Rational::Builder(-1,2), new Division::Builder(new Multiplication::Builder(new SquareRoot(new Rational::Builder(3), false), new Constant::Builder(UCodePointMathematicalBoldSmallI), false), new Rational::Builder(2), false), false)};
// x_k = -1/(3a)*(b+C*z+delta0/(zC)) with z = unary cube root
for (int k = 0; k < 3; k++) {
Expression * ccopy = c;
Expression * delta0copy = delta0;
if (k < 2) {
ccopy = new Multiplication::Builder(c->clone(), unary3roots[k], false);
delta0copy = delta0->clone();
}
Expression * add3Operands[3] = {b->clone(), ccopy, new Division::Builder(delta0copy, ccopy->clone(), false)};
m_exactSolutions[k] = new Multiplication::Builder(new Division::Builder(new Rational::Builder(-1), new Multiplication::Builder(new Rational::Builder(3), a->clone(), false), false), new Addition(add3Operands, 3, false), false);
}
m_numberOfSolutions = 3;
}
m_exactSolutions[m_numberOfSolutions] = delta;
}
#endif
}
void EquationStore::tidySolution() {
for (int i = 0; i < k_maxNumberOfExactSolutions; i++) {
m_exactSolutionExactLayouts[i] = Layout();
m_exactSolutionApproximateLayouts[i] = Layout();
}
}
Preferences::ComplexFormat EquationStore::updatedComplexFormat(Context * context) {
Preferences::ComplexFormat complexFormat = Preferences::sharedPreferences()->complexFormat();
if (complexFormat == Preferences::ComplexFormat::Real && isExplictlyComplex(context)) {
return Preferences::ComplexFormat::Cartesian;
}
return complexFormat;
}
bool EquationStore::isExplictlyComplex(Context * context) {
for (int i = 0; i < numberOfDefinedModels(); i++) {
if (modelForRecord(definedRecordAtIndex(i))->containsIComplex(context)) {
return true;
}
}
return false;
}
}