diff --git a/poincare/include/poincare/unit.h b/poincare/include/poincare/unit.h index 2d05ad88a..252de05d9 100644 --- a/poincare/include/poincare/unit.h +++ b/poincare/include/poincare/unit.h @@ -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(Expression::node()); } Expression removeUnit(Expression * unit); diff --git a/poincare/src/multiplication.cpp b/poincare/src/multiplication.cpp index 11cdca1af..8741c1e00 100644 --- a/poincare/src/multiplication.cpp +++ b/poincare/src/multiplication.cpp @@ -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(exp).isInteger()); + Integer expInt = static_cast(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(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(exp).isInteger()); - Integer expInt = static_cast(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(firstFactor).chooseBestMultipleForValue(value, exponent, reductionContext); - } - } resultWithoutUnit = Float::Builder(value); } - // Build final Expression result = Multiplication::Builder(resultWithoutUnit, units); static_cast(result).mergeSameTypeChildrenInPlace(); } } - } else { // Step 3: Create a Division if relevant Expression numer, denom; diff --git a/poincare/src/unit.cpp b/poincare/src/unit.cpp index 91a4b3ef2..da08ba2c7 100644 --- a/poincare/src/unit.cpp +++ b/poincare/src/unit.cpp @@ -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().isMeter() && + e.childAtIndex(1).type() == ExpressionNode::Type::Power && + e.childAtIndex(1).childAtIndex(1).type() == ExpressionNode::Type::Rational && e.childAtIndex(1).childAtIndex(1).convert().isMinusOne() && + e.childAtIndex(1).childAtIndex(0).type() == ExpressionNode::Type::Unit && e.childAtIndex(1).childAtIndex(0).convert().isSecond(); +} + template Evaluation UnitNode::templatedApproximate(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; template Evaluation UnitNode::templatedApproximate(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; diff --git a/poincare/test/simplification.cpp b/poincare/test/simplification.cpp index 40971b7a6..3c3554e8c 100644 --- a/poincare/test/simplification.cpp +++ b/poincare/test/simplification.cpp @@ -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) {