[poincare] Partially implement simplification for UnitConversion::Classic

This commit is contained in:
Émilie Feral
2020-04-21 17:59:21 +02:00
parent ddd3904ea9
commit e2dc79c2d5
4 changed files with 78 additions and 34 deletions

View File

@@ -414,6 +414,9 @@ public:
Representative::Prefixable::Yes,
NegativePrefixes),
};
static const Representative constexpr * SecondRepresentative = &TimeRepresentatives[0];
static const Representative constexpr * HourRepresentative = &TimeRepresentatives[2];
static const Representative constexpr * MeterRepresentative = &DistanceRepresentatives[0];
static constexpr const Dimension DimensionTable[] = {
/* The current table is sorted from most to least simple units.
* The order determines the behavior of simplification.
@@ -718,6 +721,9 @@ public:
&EmptyPrefix
),
};
static const Dimension constexpr * TimeDimension = &DimensionTable[0] ;
static const Dimension constexpr * DistanceDimension = &DimensionTable[1];
static constexpr const Unit::Dimension * DimensionTableUpperBound =
DimensionTable + sizeof(DimensionTable)/sizeof(Dimension);
static bool CanParse(const char * symbol, size_t length,
@@ -725,11 +731,18 @@ public:
Unit(const UnitNode * node) : Expression(node) {}
static Unit Builder(const Dimension * dimension, const Representative * representative, const Prefix * prefix);
static Unit Kilometer() { return Builder(DistanceDimension, MeterRepresentative, &KiloPrefix); }
static Unit Hour() { return Builder(TimeDimension, HourRepresentative, &EmptyPrefix); }
static bool IsISSpeed(Expression & e);
bool isMeter() const;
bool isSecond() const;
// Simplification
Expression shallowReduce(ExpressionNode::ReductionContext reductionContext);
void chooseBestMultipleForValue(double & value, const int exponent, ExpressionNode::ReductionContext reductionContext);
static constexpr double MeterPerSecondToKilometerPerHourFactor = 60.0*60.0/1000.0;
private:
UnitNode * node() const { return static_cast<UnitNode *>(Expression::node()); }
Expression removeUnit(Expression * unit);

View File

@@ -355,12 +355,13 @@ Expression Multiplication::shallowBeautify(ExpressionNode::ReductionContext redu
return std::move(o);
}
Expression result;
// Step 2: Handle the units
Expression self = *this;
Expression units;
self = removeUnit(&units);
Expression result;
if (!units.isUninitialized()) {
ExpressionNode::UnitConversion unitConversionMode = reductionContext.unitConversion();
if (unitConversionMode == ExpressionNode::UnitConversion::Default || unitConversionMode == ExpressionNode::UnitConversion::Classic) {
@@ -438,48 +439,56 @@ Expression Multiplication::shallowBeautify(ExpressionNode::ReductionContext redu
// If the value is undefined, return "undef" without any unit
result = Undefined::Builder();
} else {
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 (unitConversionMode == ExpressionNode::UnitConversion::Default && value != 0.0 && value != 1.0 && !std::isinf(value)) {
// 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);
}
} else if (unitConversionMode == ExpressionNode::UnitConversion::Classic) {
if (Unit::IsISSpeed(units)) {
value *= Unit::MeterPerSecondToKilometerPerHourFactor;
units = Multiplication::Builder(
Unit::Kilometer(),
Power::Builder(
Unit::Hour(),
Rational::Builder(-1)
)
);
}
// TODO: what to do if no classic conversion?
}
if (result.isUninitialized()) {
// Build final Expression
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);
}
}
resultWithoutUnit = Float<double>::Builder(value);
}
// Build final Expression
result = Multiplication::Builder(resultWithoutUnit, units);
static_cast<Multiplication &>(result).mergeSameTypeChildrenInPlace();
}
}
} else {
// Step 3: Create a Division if relevant
Expression numer, denom;

View File

@@ -259,7 +259,12 @@ constexpr const Unit::Representative
Unit::CatalyticActivityRepresentatives[],
Unit::SurfaceRepresentatives[],
Unit::VolumeRepresentatives[];
const Unit::Representative constexpr * Unit::SecondRepresentative;
const Unit::Representative constexpr * Unit::HourRepresentative;
const Unit::Representative constexpr * Unit::MeterRepresentative;
constexpr const Unit::Dimension Unit::DimensionTable[];
const Unit::Dimension constexpr * Unit::TimeDimension;
const Unit::Dimension constexpr * Unit::DistanceDimension;
constexpr const Unit::Dimension * Unit::DimensionTableUpperBound;
bool Unit::CanParse(const char * symbol, size_t length,
@@ -348,6 +353,23 @@ Expression Unit::removeUnit(Expression * unit) {
return one;
}
bool Unit::isSecond() const {
return node()->dimension() == TimeDimension && node()->representative() == SecondRepresentative && node()->prefix() == &EmptyPrefix;
}
bool Unit::isMeter() const {
return node()->dimension() == DistanceDimension && node()->representative() == MeterRepresentative && node()->prefix() == &EmptyPrefix;
}
bool Unit::IsISSpeed(Expression & e) {
// Form m*s^-1
return e.type() == ExpressionNode::Type::Multiplication && e.numberOfChildren() == 2 &&
e.childAtIndex(0).type() == ExpressionNode::Type::Unit && e.childAtIndex(0).convert<Unit>().isMeter() &&
e.childAtIndex(1).type() == ExpressionNode::Type::Power &&
e.childAtIndex(1).childAtIndex(1).type() == ExpressionNode::Type::Rational && e.childAtIndex(1).childAtIndex(1).convert<const Rational>().isMinusOne() &&
e.childAtIndex(1).childAtIndex(0).type() == ExpressionNode::Type::Unit && e.childAtIndex(1).childAtIndex(0).convert<Unit>().isSecond();
}
template Evaluation<float> UnitNode::templatedApproximate<float>(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const;
template Evaluation<double> UnitNode::templatedApproximate<double>(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const;

View File

@@ -1284,7 +1284,7 @@ QUIZ_CASE(poincare_simplification_unit_conversion) {
assert_parsed_expression_simplify_to("1000000_cm", "10×_km", User, Degree, Cartesian, ReplaceAllDefinedSymbolsWithDefinition, DefaultUnitConversion);
assert_parsed_expression_simplify_to("1000000_cm", "1000000×_cm", User, Degree, Cartesian, ReplaceAllDefinedSymbolsWithDefinition, NoUnitConversion);
assert_parsed_expression_simplify_to("1000000_cm", "10000×_m", User, Degree, Cartesian, ReplaceAllDefinedSymbolsWithDefinition, InternationalSystemUnitConversion);
//assert_parsed_expression_simplify_to("10_m/h", "0.01km/h", SystemForApproximation, Degree, Cartesian, ReplaceAllDefinedSymbolsWithDefinition, ClassicUnitConversion);
assert_parsed_expression_simplify_to("10_m/_h", "0.01×_km×_h^\x12-1\x13", User, Degree, Cartesian, ReplaceAllDefinedSymbolsWithDefinition, ClassicUnitConversion);
}
QUIZ_CASE(poincare_simplification_user_function) {