[poincare] Enable to choose the "UnitConversion" mode at simplification

between: only SI, none, default...
This commit is contained in:
Émilie Feral
2020-04-17 15:12:01 +02:00
parent d805c59202
commit 3f34e5e644
10 changed files with 136 additions and 124 deletions

View File

@@ -239,11 +239,11 @@ public:
* account the complex format required in the expression they return.
* (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, ExpressionNode::SymbolicComputation symbolicComputation = ExpressionNode::SymbolicComputation::ReplaceAllDefinedSymbolsWithDefinition);
static Expression ParseAndSimplify(const char * text, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::SymbolicComputation symbolicComputation = ExpressionNode::SymbolicComputation::ReplaceAllDefinedSymbolsWithDefinition, ExpressionNode::UnitConversion unitConversion = ExpressionNode::UnitConversion::Default);
Expression simplify(ExpressionNode::ReductionContext reductionContext);
static void ParseAndSimplifyAndApproximate(const char * text, Expression * simplifiedExpression, Expression * approximateExpression, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::SymbolicComputation symbolicComputation = ExpressionNode::SymbolicComputation::ReplaceAllDefinedSymbolsWithDefinition);
void simplifyAndApproximate(Expression * simplifiedExpression, Expression * approximateExpression, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::SymbolicComputation symbolicComputation = ExpressionNode::SymbolicComputation::ReplaceAllDefinedSymbolsWithDefinition);
static void ParseAndSimplifyAndApproximate(const char * text, Expression * simplifiedExpression, Expression * approximateExpression, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::SymbolicComputation symbolicComputation = ExpressionNode::SymbolicComputation::ReplaceAllDefinedSymbolsWithDefinition, ExpressionNode::UnitConversion unitConversion = ExpressionNode::UnitConversion::Default);
void simplifyAndApproximate(Expression * simplifiedExpression, Expression * approximateExpression, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::SymbolicComputation symbolicComputation = ExpressionNode::SymbolicComputation::ReplaceAllDefinedSymbolsWithDefinition, ExpressionNode::UnitConversion unitConversion = ExpressionNode::UnitConversion::Default);
Expression reduce(ExpressionNode::ReductionContext context);
Expression mapOnMatrixFirstChild(ExpressionNode::ReductionContext reductionContext);

View File

@@ -128,8 +128,13 @@ public:
ReplaceAllSymbolsWithDefinitionsOrUndefined = 0,
ReplaceAllDefinedSymbolsWithDefinition = 1,
ReplaceDefinedFunctionsWithDefinitions = 2,
ReplaceAllSymbolsWithUndefinedAndDoNotReplaceUnits = 3, // Used in UnitConvert::shallowReduce
ReplaceAllSymbolsWithUndefinedAndReplaceUnits = 4 // Used in UnitConvert::shallowReduce
ReplaceAllSymbolsWithUndefined = 3 // Used in UnitConvert::shallowReduce
};
enum class UnitConversion {
None = 0,
Default,
InternationalSystem,
Classic // km/h, days + hours + minute
};
enum class Sign {
Negative = -1,
@@ -139,24 +144,27 @@ public:
class ReductionContext {
public:
ReductionContext(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target, SymbolicComputation symbolicComputation = SymbolicComputation::ReplaceAllDefinedSymbolsWithDefinition) :
ReductionContext(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target, SymbolicComputation symbolicComputation = SymbolicComputation::ReplaceAllDefinedSymbolsWithDefinition, UnitConversion unitConversion = UnitConversion::Default) :
m_context(context),
m_complexFormat(complexFormat),
m_angleUnit(angleUnit),
m_target(target),
m_symbolicComputation(symbolicComputation)
m_symbolicComputation(symbolicComputation),
m_unitConversion(unitConversion)
{}
Context * context() { return m_context; }
Preferences::ComplexFormat complexFormat() const { return m_complexFormat; }
Preferences::AngleUnit angleUnit() const { return m_angleUnit; }
ReductionTarget target() const { return m_target; }
SymbolicComputation symbolicComputation() const { return m_symbolicComputation; }
UnitConversion unitConversion() const { return m_unitConversion; }
private:
Context * m_context;
Preferences::ComplexFormat m_complexFormat;
Preferences::AngleUnit m_angleUnit;
ReductionTarget m_target;
SymbolicComputation m_symbolicComputation;
UnitConversion m_unitConversion;
};
virtual Sign sign(Context * context) const { return Sign::Unknown; }

View File

@@ -571,12 +571,12 @@ int Expression::serialize(char * buffer, int bufferSize, Preferences::PrintFloat
/* Simplification */
Expression Expression::ParseAndSimplify(const char * text, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::SymbolicComputation symbolicComputation) {
Expression Expression::ParseAndSimplify(const char * text, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::SymbolicComputation symbolicComputation, ExpressionNode::UnitConversion unitConversion) {
Expression exp = Parse(text, context, false);
if (exp.isUninitialized()) {
return Undefined::Builder();
}
exp = exp.simplify(ExpressionNode::ReductionContext(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::User, symbolicComputation));
exp = exp.simplify(ExpressionNode::ReductionContext(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::User, symbolicComputation, unitConversion));
/* simplify might have been interrupted, in which case the resulting
* expression is uninitialized, so we need to check that. */
if (exp.isUninitialized()) {
@@ -585,7 +585,7 @@ Expression Expression::ParseAndSimplify(const char * text, Context * context, Pr
return exp;
}
void Expression::ParseAndSimplifyAndApproximate(const char * text, Expression * simplifiedExpression, Expression * approximateExpression, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::SymbolicComputation symbolicComputation) {
void Expression::ParseAndSimplifyAndApproximate(const char * text, Expression * simplifiedExpression, Expression * approximateExpression, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::SymbolicComputation symbolicComputation, ExpressionNode::UnitConversion unitConversion) {
assert(simplifiedExpression);
Expression exp = Parse(text, context, false);
if (exp.isUninitialized()) {
@@ -593,7 +593,7 @@ void Expression::ParseAndSimplifyAndApproximate(const char * text, Expression *
*approximateExpression = Undefined::Builder();
return;
}
exp.simplifyAndApproximate(simplifiedExpression, approximateExpression, context, complexFormat, angleUnit, symbolicComputation);
exp.simplifyAndApproximate(simplifiedExpression, approximateExpression, context, complexFormat, angleUnit, symbolicComputation, unitConversion);
/* simplify might have been interrupted, in which case the resulting
* expression is uninitialized, so we need to check that. */
if (simplifiedExpression->isUninitialized()) {
@@ -675,16 +675,16 @@ void Expression::beautifyAndApproximateScalar(Expression * simplifiedExpression,
}
}
void Expression::simplifyAndApproximate(Expression * simplifiedExpression, Expression * approximateExpression, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::SymbolicComputation symbolicComputation) {
void Expression::simplifyAndApproximate(Expression * simplifiedExpression, Expression * approximateExpression, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::SymbolicComputation symbolicComputation, ExpressionNode::UnitConversion unitConversion) {
assert(simplifiedExpression);
sSimplificationHasBeenInterrupted = false;
// Step 1: we reduce the expression
ExpressionNode::ReductionContext userReductionContext = ExpressionNode::ReductionContext(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::User, symbolicComputation);
ExpressionNode::ReductionContext userReductionContext = ExpressionNode::ReductionContext(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::User, symbolicComputation, unitConversion);
const bool isUnitConvert = type() == ExpressionNode::Type::UnitConvert;
Expression e = clone().reduce(userReductionContext);
if (sSimplificationHasBeenInterrupted) {
sSimplificationHasBeenInterrupted = false;
ExpressionNode::ReductionContext systemReductionContext = ExpressionNode::ReductionContext(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::SystemForApproximation, symbolicComputation);
ExpressionNode::ReductionContext systemReductionContext = ExpressionNode::ReductionContext(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::SystemForApproximation, symbolicComputation, unitConversion);
e = reduce(systemReductionContext);
}
*simplifiedExpression = Expression();

View File

@@ -115,8 +115,7 @@ Expression Function::replaceSymbolWithExpression(const SymbolAbstract & symbol,
}
Expression Function::shallowReduce(ExpressionNode::ReductionContext reductionContext) {
if (reductionContext.symbolicComputation() == ExpressionNode::SymbolicComputation::ReplaceAllSymbolsWithUndefinedAndDoNotReplaceUnits
|| reductionContext.symbolicComputation() == ExpressionNode::SymbolicComputation::ReplaceAllSymbolsWithUndefinedAndReplaceUnits
if (reductionContext.symbolicComputation() == ExpressionNode::SymbolicComputation::ReplaceAllSymbolsWithUndefined
|| childAtIndex(0).isUndefined())
{
return replaceWithUndefinedInPlace();

View File

@@ -355,75 +355,77 @@ Expression Multiplication::shallowBeautify(ExpressionNode::ReductionContext redu
return std::move(o);
}
// Step 2: Handle the units
Expression self = *this;
Expression units;
self = removeUnit(&units);
Expression result;
if (!units.isUninitialized()) {
/* Step 2: Handle the units
*
* Recognize derived units
* - Look up in the table of derived units, the one which itself or its inverse simplifies 'units' the most.
* - If an entry is found, simplify 'units' and add the corresponding unit or its inverse in 'unitsAccu'.
* - Repeat those steps until no more simplification is possible.
*/
Multiplication unitsAccu = Multiplication::Builder();
Unit::Dimension::Vector<Integer> unitsExponents = Unit::Dimension::Vector<Integer>::FromBaseUnits(units);
Unit::Dimension::Vector<Integer>::Metrics unitsMetrics = unitsExponents.metrics();
Unit::Dimension::Vector<Integer> bestRemainderExponents;
Unit::Dimension::Vector<Integer>::Metrics bestRemainderMetrics;
while (unitsMetrics.supportSize > 1) {
const Unit::Dimension * bestDim = nullptr;
int8_t bestUnitExponent = 0;
for (const Unit::Dimension * dim = Unit::DimensionTable + Unit::NumberOfBaseUnits; dim < Unit::DimensionTableUpperBound; dim++) {
const Unit::Dimension::Vector<int8_t> * entryUnitExponents = dim->vector();
int8_t entryUnitNorm = entryUnitExponents->metrics().norm;
if (CanSimplifyUnitProduct(
unitsExponents, unitsMetrics,
entryUnitExponents, entryUnitNorm, 1,
bestUnitExponent, bestRemainderExponents, bestRemainderMetrics
)
||
CanSimplifyUnitProduct(
unitsExponents, unitsMetrics,
entryUnitExponents, entryUnitNorm, -1,
bestUnitExponent, bestRemainderExponents, bestRemainderMetrics
))
{
bestDim = dim;
ExpressionNode::UnitConversion unitConversionMode = reductionContext.unitConversion();
if (unitConversionMode == ExpressionNode::UnitConversion::Default || unitConversionMode == ExpressionNode::UnitConversion::Classic) {
/* Step 2a: Recognize derived units
* - Look up in the table of derived units, the one which itself or its inverse simplifies 'units' the most.
* - If an entry is found, simplify 'units' and add the corresponding unit or its inverse in 'unitsAccu'.
* - Repeat those steps until no more simplification is possible.
*/
Multiplication unitsAccu = Multiplication::Builder();
Unit::Dimension::Vector<Integer> unitsExponents = Unit::Dimension::Vector<Integer>::FromBaseUnits(units);
Unit::Dimension::Vector<Integer>::Metrics unitsMetrics = unitsExponents.metrics();
Unit::Dimension::Vector<Integer> bestRemainderExponents;
Unit::Dimension::Vector<Integer>::Metrics bestRemainderMetrics;
while (unitsMetrics.supportSize > 1) {
const Unit::Dimension * bestDim = nullptr;
int8_t bestUnitExponent = 0;
for (const Unit::Dimension * dim = Unit::DimensionTable + Unit::NumberOfBaseUnits; dim < Unit::DimensionTableUpperBound; dim++) {
const Unit::Dimension::Vector<int8_t> * entryUnitExponents = dim->vector();
int8_t entryUnitNorm = entryUnitExponents->metrics().norm;
if (CanSimplifyUnitProduct(
unitsExponents, unitsMetrics,
entryUnitExponents, entryUnitNorm, 1,
bestUnitExponent, bestRemainderExponents, bestRemainderMetrics
)
||
CanSimplifyUnitProduct(
unitsExponents, unitsMetrics,
entryUnitExponents, entryUnitNorm, -1,
bestUnitExponent, bestRemainderExponents, bestRemainderMetrics
))
{
bestDim = dim;
}
}
if (bestDim == nullptr) {
break;
}
Expression derivedUnit = Unit::Builder(bestDim, bestDim->stdRepresentative(), bestDim->stdRepresentativePrefix());
assert(bestUnitExponent == 1 || bestUnitExponent == -1);
if (bestUnitExponent == -1) {
derivedUnit = Power::Builder(derivedUnit, Rational::Builder(-1));
}
const int position = unitsAccu.numberOfChildren();
unitsAccu.addChildAtIndexInPlace(derivedUnit, position, position);
unitsExponents = bestRemainderExponents;
unitsMetrics = bestRemainderMetrics;
}
if (bestDim == nullptr) {
break;
}
Expression derivedUnit = Unit::Builder(bestDim, bestDim->stdRepresentative(), bestDim->stdRepresentativePrefix());
assert(bestUnitExponent == 1 || bestUnitExponent == -1);
if (bestUnitExponent == -1) {
derivedUnit = Power::Builder(derivedUnit, Rational::Builder(-1));
}
const int position = unitsAccu.numberOfChildren();
unitsAccu.addChildAtIndexInPlace(derivedUnit, position, position);
unitsExponents = bestRemainderExponents;
unitsMetrics = bestRemainderMetrics;
}
if (unitsAccu.numberOfChildren() > 0) {
units = Division::Builder(units, unitsAccu.clone()).deepReduce(reductionContext);
Expression newUnits;
units = units.removeUnit(&newUnits);
Multiplication m = Multiplication::Builder(units);
self.replaceWithInPlace(m);
m.addChildAtIndexInPlace(self, 0, 1);
self = m;
if (newUnits.isUninitialized()) {
units = unitsAccu;
} else {
units = Multiplication::Builder(unitsAccu, newUnits);
static_cast<Multiplication &>(units).mergeSameTypeChildrenInPlace();
if (unitsAccu.numberOfChildren() > 0) {
units = Division::Builder(units, unitsAccu.clone()).deepReduce(reductionContext);
Expression newUnits;
units = units.removeUnit(&newUnits);
Multiplication m = Multiplication::Builder(units);
self.replaceWithInPlace(m);
m.addChildAtIndexInPlace(self, 0, 1);
self = m;
if (newUnits.isUninitialized()) {
units = unitsAccu;
} else {
units = Multiplication::Builder(unitsAccu, newUnits);
static_cast<Multiplication &>(units).mergeSameTypeChildrenInPlace();
}
}
}
/* Turn into 'Float x units'.
/* Step 2b: Turn into 'Float x units'.
* Choose a unit multiple adequate for the numerical value.
* An exhaustive exploration of all possible multiples would have
* exponential complexity with respect to the number of factors. Instead,
@@ -436,41 +438,46 @@ Expression Multiplication::shallowBeautify(ExpressionNode::ReductionContext redu
// If the value is undefined, return "undef" without any unit
result = Undefined::Builder();
} else {
Expression resultWithoutUnit;
if (std::isinf(value)) {
resultWithoutUnit = Infinity::Builder(value < 0.0);
if (unitConversionMode == ExpressionNode::UnitConversion::Classic) {
// TODO create new result 1h 23min 24secondes ?
// Utiliser store ?
} else {
// Find the right unit prefix when the value ≠ 0
if (value != 0.0 && value != 1.0) {
// Identify the first Unit factor and its exponent
Expression firstFactor = units;
int exponent = 1;
if (firstFactor.type() == ExpressionNode::Type::Multiplication) {
firstFactor = firstFactor.childAtIndex(0);
}
if (firstFactor.type() == ExpressionNode::Type::Power) {
Expression exp = firstFactor.childAtIndex(1);
firstFactor = firstFactor.childAtIndex(0);
assert(exp.type() == ExpressionNode::Type::Rational && static_cast<Rational &>(exp).isInteger());
Integer expInt = static_cast<Rational &>(exp).signedIntegerNumerator();
if (expInt.isLowerThan(Integer(Integer::k_maxExtractableInteger))) {
exponent = expInt.extractedInt();
} else {
// The exponent is too large to be extracted, so do not try to use it.
exponent = 0;
Expression resultWithoutUnit;
if (std::isinf(value)) {
resultWithoutUnit = Infinity::Builder(value < 0.0);
} else {
// Find the right unit prefix when the value ≠ 0
if (unitConversionMode == ExpressionNode::UnitConversion::Default && value != 0.0 && value != 1.0) {
// Identify the first Unit factor and its exponent
Expression firstFactor = units;
int exponent = 1;
if (firstFactor.type() == ExpressionNode::Type::Multiplication) {
firstFactor = firstFactor.childAtIndex(0);
}
if (firstFactor.type() == ExpressionNode::Type::Power) {
Expression exp = firstFactor.childAtIndex(1);
firstFactor = firstFactor.childAtIndex(0);
assert(exp.type() == ExpressionNode::Type::Rational && static_cast<Rational &>(exp).isInteger());
Integer expInt = static_cast<Rational &>(exp).signedIntegerNumerator();
if (expInt.isLowerThan(Integer(Integer::k_maxExtractableInteger))) {
exponent = expInt.extractedInt();
} else {
// The exponent is too large to be extracted, so do not try to use it.
exponent = 0;
}
}
assert(firstFactor.type() == ExpressionNode::Type::Unit);
// Choose its multiple and update value accordingly
if (exponent != 0) {
static_cast<Unit&>(firstFactor).chooseBestMultipleForValue(value, exponent, reductionContext);
}
}
assert(firstFactor.type() == ExpressionNode::Type::Unit);
// Choose its multiple and update value accordingly
if (exponent != 0) {
static_cast<Unit&>(firstFactor).chooseBestMultipleForValue(value, exponent, reductionContext);
}
resultWithoutUnit = Float<double>::Builder(value);
}
resultWithoutUnit = Float<double>::Builder(value);
// Build final Expression
result = Multiplication::Builder(resultWithoutUnit, units);
static_cast<Multiplication &>(result).mergeSameTypeChildrenInPlace();
}
// Build final Expression
result = Multiplication::Builder(resultWithoutUnit, units);
static_cast<Multiplication &>(result).mergeSameTypeChildrenInPlace();
}
} else {

View File

@@ -154,9 +154,7 @@ Expression Symbol::shallowReduce(ExpressionNode::ReductionContext reductionConte
if (reductionContext.symbolicComputation() == ExpressionNode::SymbolicComputation::ReplaceDefinedFunctionsWithDefinitions) {
return *this;
}
if (reductionContext.symbolicComputation() == ExpressionNode::SymbolicComputation::ReplaceAllSymbolsWithUndefinedAndDoNotReplaceUnits
|| reductionContext.symbolicComputation() == ExpressionNode::SymbolicComputation::ReplaceAllSymbolsWithUndefinedAndReplaceUnits)
{
if (reductionContext.symbolicComputation() == ExpressionNode::SymbolicComputation::ReplaceAllSymbolsWithUndefined) {
return replaceWithUndefinedInPlace();
}
{

View File

@@ -282,7 +282,7 @@ Unit Unit::Builder(const Dimension * dimension, const Representative * represent
}
Expression Unit::shallowReduce(ExpressionNode::ReductionContext reductionContext) {
if (reductionContext.symbolicComputation() == ExpressionNode::SymbolicComputation::ReplaceAllSymbolsWithUndefinedAndDoNotReplaceUnits) {
if (reductionContext.unitConversion() == ExpressionNode::UnitConversion::None) {
return *this;
}
UnitNode * unitNode = static_cast<UnitNode *>(node());

View File

@@ -35,12 +35,13 @@ Expression UnitConvert::shallowReduce(ExpressionNode::ReductionContext reduction
// Find the unit
{
//
ExpressionNode::ReductionContext reductionContextWithUnits = ExpressionNode::ReductionContext(
reductionContext.context(),
reductionContext.complexFormat(),
reductionContext.angleUnit(),
reductionContext.target(),
ExpressionNode::SymbolicComputation::ReplaceAllSymbolsWithUndefinedAndReplaceUnits);
ExpressionNode::SymbolicComputation::ReplaceAllSymbolsWithUndefined);
Expression unit;
childAtIndex(1).clone().reduce(reductionContextWithUnits).removeUnit(&unit);
if (unit.isUninitialized()) {
@@ -54,7 +55,8 @@ Expression UnitConvert::shallowReduce(ExpressionNode::ReductionContext reduction
reductionContext.complexFormat(),
reductionContext.angleUnit(),
reductionContext.target(),
ExpressionNode::SymbolicComputation::ReplaceAllSymbolsWithUndefinedAndDoNotReplaceUnits);
ExpressionNode::SymbolicComputation::ReplaceAllSymbolsWithUndefined,
ExpressionNode::UnitConversion::None);
Expression finalUnit;
childAtIndex(1).reduce(reductionContextWithoutUnits).removeUnit(&finalUnit);

View File

@@ -368,9 +368,9 @@ QUIZ_CASE(poincare_properties_get_polynomial_coefficients) {
Ion::Storage::sharedStorage()->recordNamed("x.exp").destroy();
}
void assert_reduced_expression_unit(const char * expression, const char * unit, ExpressionNode::SymbolicComputation symbolicComutation) {
void assert_reduced_expression_unit_is(const char * expression, const char * unit) {
Shared::GlobalContext globalContext;
ExpressionNode::ReductionContext redContext(&globalContext, Real, Degree, SystemForApproximation, symbolicComutation);
ExpressionNode::ReductionContext redContext(&globalContext, Real, Degree, SystemForApproximation);
Expression e = parse_expression(expression, &globalContext, false);
e = e.reduce(redContext);
Expression u1;
@@ -383,15 +383,9 @@ void assert_reduced_expression_unit(const char * expression, const char * unit,
}
QUIZ_CASE(poincare_properties_get_unit) {
assert_reduced_expression_unit("_km", "_km", ReplaceAllSymbolsWithUndefinedAndDoNotReplaceUnits);
assert_reduced_expression_unit("_min/_km", "_km^(-1)×_min", ReplaceAllSymbolsWithUndefinedAndDoNotReplaceUnits);
assert_reduced_expression_unit("_km^3", "_km^3", ReplaceAllSymbolsWithUndefinedAndDoNotReplaceUnits);
assert_reduced_expression_unit("1_m+_km", Undefined::Name(), ReplaceAllSymbolsWithUndefinedAndDoNotReplaceUnits);
assert_reduced_expression_unit("_L^2×3×_s", "_L^2×_s", ReplaceAllSymbolsWithUndefinedAndDoNotReplaceUnits);
assert_reduced_expression_unit("_km", "_m", ReplaceAllSymbolsWithDefinitionsOrUndefined);
assert_reduced_expression_unit("_min/_km", "_m^(-1)×_s", ReplaceAllSymbolsWithDefinitionsOrUndefined);
assert_reduced_expression_unit("_km^3", "_m^3", ReplaceAllSymbolsWithDefinitionsOrUndefined);
assert_reduced_expression_unit("1_m+_km", "_m", ReplaceAllSymbolsWithDefinitionsOrUndefined);
assert_reduced_expression_unit("_L^2×3×_s", "_m^6×_s", ReplaceAllSymbolsWithDefinitionsOrUndefined);
assert_reduced_expression_unit_is("_km", "_m");
assert_reduced_expression_unit_is("_min/_km", "_m^(-1)×_s");
assert_reduced_expression_unit_is("_km^3", "_m^3");
assert_reduced_expression_unit_is("1_m+_km", "_m");
assert_reduced_expression_unit_is("_L^2×3×_s", "_m^6×_s");
}

View File

@@ -11,7 +11,11 @@ constexpr Poincare::ExpressionNode::ReductionTarget User = Poincare::ExpressionN
constexpr Poincare::ExpressionNode::SymbolicComputation ReplaceAllDefinedSymbolsWithDefinition = Poincare::ExpressionNode::SymbolicComputation::ReplaceAllDefinedSymbolsWithDefinition;
constexpr Poincare::ExpressionNode::SymbolicComputation ReplaceAllSymbolsWithDefinitionsOrUndefined = Poincare::ExpressionNode::SymbolicComputation::ReplaceAllSymbolsWithDefinitionsOrUndefined;
constexpr Poincare::ExpressionNode::SymbolicComputation ReplaceDefinedFunctionsWithDefinitions = Poincare::ExpressionNode::SymbolicComputation::ReplaceDefinedFunctionsWithDefinitions;
constexpr Poincare::ExpressionNode::SymbolicComputation ReplaceAllSymbolsWithUndefinedAndDoNotReplaceUnits = Poincare::ExpressionNode::SymbolicComputation::ReplaceAllSymbolsWithUndefinedAndDoNotReplaceUnits;
constexpr Poincare::ExpressionNode::SymbolicComputation ReplaceAllSymbolsWithUndefined = Poincare::ExpressionNode::SymbolicComputation::ReplaceAllSymbolsWithUndefined;
constexpr Poincare::ExpressionNode::UnitConversion NoUnitConversion = Poincare::ExpressionNode::UnitConversion::None;
constexpr Poincare::ExpressionNode::UnitConversion DefaultUnitConversion = Poincare::ExpressionNode::UnitConversion::Default;
constexpr Poincare::ExpressionNode::UnitConversion ClassicUnitConversion = Poincare::ExpressionNode::UnitConversion::Classic;
constexpr Poincare::ExpressionNode::UnitConversion InternationalSystemUnitConversion = Poincare::ExpressionNode::UnitConversion::InternationalSystem;
constexpr Poincare::Preferences::AngleUnit Degree = Poincare::Preferences::AngleUnit::Degree;
constexpr Poincare::Preferences::AngleUnit Radian = Poincare::Preferences::AngleUnit::Radian;
constexpr Poincare::Preferences::AngleUnit Gradian = Poincare::Preferences::AngleUnit::Gradian;