diff --git a/poincare/include/poincare/unit.h b/poincare/include/poincare/unit.h index feb6f6d9a..060327879 100644 --- a/poincare/include/poincare/unit.h +++ b/poincare/include/poincare/unit.h @@ -85,13 +85,8 @@ public: public: template struct Vector { - /* SupportSize is defined as the number of distinct base units. - * Norm is defined as the sum of all unit exponents absolute values. */ - struct Metrics { - size_t supportSize; - T norm; - }; - Metrics metrics() const; + // SupportSize is defined as the number of distinct base units. + size_t supportSize() const; static Vector FromBaseUnits(const Expression baseUnits); const T coefficientAtIndex(size_t i) const { assert(i < NumberOfBaseUnits); diff --git a/poincare/src/multiplication.cpp b/poincare/src/multiplication.cpp index bcd32d7d4..3a3b5f926 100644 --- a/poincare/src/multiplication.cpp +++ b/poincare/src/multiplication.cpp @@ -322,15 +322,14 @@ Expression Multiplication::shallowReduce(ExpressionNode::ReductionContext reduct } static bool CanSimplifyUnitProduct( - const Unit::Dimension::Vector &unitsExponents, Unit::Dimension::Vector::Metrics &unitsMetrics, - const Unit::Dimension::Vector *entryUnitExponents, int8_t entryUnitNorm, int8_t entryUnitExponent, - int8_t & bestUnitExponent, Unit::Dimension::Vector &bestRemainderExponents, Unit::Dimension::Vector::Metrics & bestRemainderMetrics) { + const Unit::Dimension::Vector &unitsExponents, size_t &unitsSupportSize, + const Unit::Dimension::Vector *entryUnitExponents, int8_t entryUnitExponent, + int8_t &bestUnitExponent, Unit::Dimension::Vector &bestRemainderExponents, size_t &bestRemainderSupportSize) { /* This function tries to simplify a Unit product (given as the - * 'unitsExponents' Integer array), by applying a given operation. If the + * 'unitsExponents' int array), by applying a given operation. If the * result of the operation is simpler, 'bestUnit' and * 'bestRemainder' are updated accordingly. */ - Unit::Dimension::Vector simplifiedExponents; - Integer (*operationOnExponents)(const Integer &, const Integer &) = entryUnitExponent == -1 ? Integer::Addition : Integer::Subtraction; + Unit::Dimension::Vector simplifiedExponents; #if 0 /* In the current algorithm, simplification is attempted using derived units @@ -344,14 +343,15 @@ static bool CanSimplifyUnitProduct( * An optimization might be possible using algorithms minimizing the sum of * absolute difference of array elements */ int n = 0; - Integer best_norm; - Integer norm_temp = unitsMetrics.norm; + int best_norm; + // TODO define a norm function summing all base units exponents + int norm_temp = unitsExponents.norm(); /* To extend this algorithm to square root simplifications, rational exponents * can be handled, and a 1/2 step can be used (but it should be asserted that * no square root simplification is performed if all exponents are integers.*/ int step = 1; for (size_t i = 0; i < Unit::NumberOfBaseUnits; i++) { - // Setting simplifiedExponents to unitsExponents + // Set simplifiedExponents to unitsExponents simplifiedExponents.setCoefficientAtIndex(i, unitsExponents.coefficientAtIndex(i)); } do { @@ -359,42 +359,31 @@ static bool CanSimplifyUnitProduct( n+= step; for (size_t i = 0; i < Unit::NumberOfBaseUnits; i++) { // Simplify unitsExponents with base units from derived unit - simplifiedExponents.setCoefficientAtIndex(i, operationOnExponents(simplifiedExponents.coefficientAtIndex(i), step * entryUnitExponents->coefficientAtIndex(i))); + simplifiedExponents.setCoefficientAtIndex(i, simplifiedExponents.coefficientAtIndex(i) - entryUnitExponent * step * entryUnitExponents->coefficientAtIndex(i)); } - Unit::Dimension::Vector::Metrics simplifiedMetrics = simplifiedExponents.metrics(); - norm_temp = Integer::Addition(n, simplifiedMetrics.norm); - - } while (norm_temp.isLowerThan(best_norm)); + int simplifiedNorm = simplifiedExponents.norm(); + // Temp norm is derived norm (n) + simplified norm + norm_temp = n + simplifiedNorm; + } while (norm_temp < best_norm); // Undo last step as it did not reduce the norm n -= step; - - Integer derivedMetricNorm = n * step * entryUnitNorm; - #else - Integer derivedMetricNorm = entryUnitNorm; #endif for (size_t i = 0; i < Unit::NumberOfBaseUnits; i++) { - // Simplify unitsExponents with base units from derived unit #if 0 - simplifiedExponents.setCoefficientAtIndex(i, operationOnExponents(simplifiedExponents.coefficientAtIndex(i), -step * entryUnitExponents->coefficientAtIndex(i))); + // Undo last step as it did not reduce the norm + simplifiedExponents.setCoefficientAtIndex(i, simplifiedExponents.coefficientAtIndex(i) + entryUnitExponent * step * entryUnitExponents->coefficientAtIndex(i)); #else - simplifiedExponents.setCoefficientAtIndex(i, operationOnExponents(unitsExponents.coefficientAtIndex(i), entryUnitExponents->coefficientAtIndex(i))); + // Simplify unitsExponents with base units from derived unit + simplifiedExponents.setCoefficientAtIndex(i, unitsExponents.coefficientAtIndex(i) - entryUnitExponent * entryUnitExponents->coefficientAtIndex(i)); #endif } - Unit::Dimension::Vector::Metrics simplifiedMetrics = simplifiedExponents.metrics(); - // Compute metrics to evaluate the simplification - - bool isSimpler = (1 + simplifiedMetrics.supportSize < unitsMetrics.supportSize); - + size_t simplifiedSupportSize = simplifiedExponents.supportSize(); /* Note: A metric is considered simpler if the support size (number of * symbols) is reduced. A norm taking coefficients into account is possible. * One could use the sum of all coefficients to favor _C_s from _A_s^2. * However, replacing _m_s^-2 with _N_kg^-1 should be avoided. */ - // TODO : Remove Metrics vectors entirely - Unit::Dimension::Vector::Metrics candidateMetrics = { - .supportSize = 1 + simplifiedMetrics.supportSize, - .norm = Integer::Addition(derivedMetricNorm, simplifiedMetrics.norm) - }; + bool isSimpler = (1 + simplifiedSupportSize < unitsSupportSize); if (isSimpler) { #if 0 @@ -403,10 +392,10 @@ static bool CanSimplifyUnitProduct( bestUnitExponent = entryUnitExponent; #endif bestRemainderExponents = simplifiedExponents; - bestRemainderMetrics = simplifiedMetrics; - /* unitsMetrics (support size and norm) is updated and will be taken into + bestRemainderSupportSize = simplifiedSupportSize; + /* unitsSupportSize is updated and will be taken into * account in next iterations of CanSimplifyUnitProduct. */ - unitsMetrics = candidateMetrics; + unitsSupportSize = 1 + simplifiedSupportSize; } return isSimpler; } @@ -455,32 +444,31 @@ Expression Multiplication::shallowBeautify(ExpressionNode::ReductionContext redu * representation of units with base units and integer exponents. * It cause no problem because once the best derived units are found, * units is divided then multiplied by them. */ - Unit::Dimension::Vector unitsExponents = Unit::Dimension::Vector::FromBaseUnits(units); - Unit::Dimension::Vector::Metrics unitsMetrics = unitsExponents.metrics(); - Unit::Dimension::Vector bestRemainderExponents; - Unit::Dimension::Vector::Metrics bestRemainderMetrics; - while (unitsMetrics.supportSize > 1) { + Unit::Dimension::Vector unitsExponents = Unit::Dimension::Vector::FromBaseUnits(units); + size_t unitsSupportSize = unitsExponents.supportSize(); + Unit::Dimension::Vector bestRemainderExponents; + size_t bestRemainderSupportSize; + while (unitsSupportSize > 1) { const Unit::Dimension * bestDim = nullptr; int8_t bestUnitExponent = 0; // Look up in the table of derived units. for (const Unit::Dimension * dim = Unit::DimensionTable + Unit::NumberOfBaseUnits; dim < Unit::DimensionTableUpperBound; dim++) { const Unit::Dimension::Vector * entryUnitExponents = dim->vector(); - int8_t entryUnitNorm = entryUnitExponents->metrics().norm; // A simplification is tried by either multiplying or dividing if (CanSimplifyUnitProduct( - unitsExponents, unitsMetrics, - entryUnitExponents, entryUnitNorm, 1, - bestUnitExponent, bestRemainderExponents, bestRemainderMetrics + unitsExponents, unitsSupportSize, + entryUnitExponents, 1, + bestUnitExponent, bestRemainderExponents, bestRemainderSupportSize ) || CanSimplifyUnitProduct( - unitsExponents, unitsMetrics, - entryUnitExponents, entryUnitNorm, -1, - bestUnitExponent, bestRemainderExponents, bestRemainderMetrics + unitsExponents, unitsSupportSize, + entryUnitExponents, -1, + bestUnitExponent, bestRemainderExponents, bestRemainderSupportSize )) { - /* If successful, unitsMetrics, bestUnitExponent, - * bestRemainderExponents and bestRemainderMetrics have been updated*/ + /* If successful, unitsSupportSize, bestUnitExponent, + * bestRemainderExponents and bestRemainderSupportSize have been updated*/ bestDim = dim; } } @@ -506,7 +494,7 @@ Expression Multiplication::shallowBeautify(ExpressionNode::ReductionContext redu unitsAccu.addChildAtIndexInPlace(derivedUnit, position, position); // Update remainder units and their exponents for next simplifications unitsExponents = bestRemainderExponents; - unitsMetrics = bestRemainderMetrics; + unitsSupportSize = bestRemainderSupportSize; } // Apply simplifications if (unitsAccu.numberOfChildren() > 0) { diff --git a/poincare/src/unit.cpp b/poincare/src/unit.cpp index f9d01741d..5400ea491 100644 --- a/poincare/src/unit.cpp +++ b/poincare/src/unit.cpp @@ -97,41 +97,31 @@ const UnitNode::Prefix * UnitNode::Representative::bestPrefixForValue(double & v } template<> -Unit::Dimension::Vector::Metrics UnitNode::Dimension::Vector::metrics() const { +size_t UnitNode::Dimension::Vector::supportSize() const { size_t supportSize = 0; - Integer norm(0); - for (const Integer * i = reinterpret_cast(this); i < reinterpret_cast(this) + NumberOfBaseUnits; i++) { - Integer coefficient = *i; - if (coefficient.isZero()) { - continue; - } - supportSize++; - coefficient.setNegative(false); - norm = Integer::Addition(norm, coefficient); - } - return {.supportSize = supportSize, .norm = norm}; -} - -template<> -Unit::Dimension::Vector::Metrics UnitNode::Dimension::Vector::metrics() const { - size_t supportSize = 0; - int8_t norm = 0; - for (const int8_t * i = reinterpret_cast(this); i < reinterpret_cast(this) + NumberOfBaseUnits; i++) { - int8_t coefficient = *i; + for (const int * i = reinterpret_cast(this); i < reinterpret_cast(this) + NumberOfBaseUnits; i++) { + int coefficient = *i; if (coefficient == 0) { continue; } supportSize++; - norm += coefficient > 0 ? coefficient : -coefficient; } - return {.supportSize = supportSize, .norm = norm}; + return supportSize; } template<> -Unit::Dimension::Vector UnitNode::Dimension::Vector::FromBaseUnits(const Expression baseUnits) { +Unit::Dimension::Vector UnitNode::Dimension::Vector::FromBaseUnits(const Expression baseUnits) { /* Returns the vector of Base units with integer exponents. If rational, the * closest integer will be used. */ - Vector vector; + Vector vector = { + .time = 0, + .distance = 0, + .mass = 0, + .current = 0, + .temperature = 0, + .amountOfSubstance = 0, + .luminuousIntensity = 0, + }; int numberOfFactors; int factorIndex = 0; Expression factor; @@ -144,21 +134,22 @@ Unit::Dimension::Vector UnitNode::Dimension::Vector::FromBaseU } do { // Get the unit's exponent - Integer exponent(1); + int exponent = 1; if (factor.type() == ExpressionNode::Type::Power) { Expression exp = factor.childAtIndex(1); assert(exp.type() == ExpressionNode::Type::Rational); // Using the closest integer to the exponent. float exponent_float = static_cast(exp).node()->templatedApproximate(); - if (std::abs(exponent_float) < INT_MAX / 2) { + /* We limit to INT_MAX / 3 because an exponent might get bigger with + * simplification. As a worst case scenario, (_s²_m²_kg/_A²)^n should be + * simplified to (_s^5_S)^n. If 2*n is under INT_MAX, 5*n might not. */ + if (std::abs(exponent_float) < INT_MAX / 3) { // Exponent can be safely casted as int exponent = (int)std::round(exponent_float); - assert(std::abs(exponent_float - exponent.approximate()) <= 0.5); + assert(std::abs(exponent_float - (float)exponent) <= 0.5); } else { - /* Base units vector will ignore this coefficient, that could have been - * casted as int8_t in CanSimplifyUnitProduct, leading to homogeneous, - * but badly formatted units. Any way, the missing exponent won't affect - * CanSimplifyUnitProduct as homogeneity is conserved. */ + /* Base units vector will ignore this coefficient, to avoid exponent + * overflow. In any way, shallowBeautify will conserve homogeneity. */ exponent = 0; } factor = factor.childAtIndex(0);