From 52dcd8e749f8fd22aa424b9e9153d9a0561372a4 Mon Sep 17 00:00:00 2001 From: Gabriel Ozouf Date: Fri, 31 Jul 2020 16:00:07 +0200 Subject: [PATCH] [poincare/unit] Restructure Unit 1. Information about a unit's dimension now uses inheritance. _m is an instance of DistanceAlias, which is derived from Alias. A UnitNode now keeps a pointer to an Alias and one to a Prefix. All aliases are still defined as constexpr. This cleans up a lot of the code used namely for computing the additional outputs in Calculation. 2. Instead of being defined with a string, each unit is described by its ratio with the base SI unit (ex: _L is 0.001 instead of "0.001_m^3"). This greatly speeds up the calculations using units, as the algorithm to find the best unit used to parse the definition. Change-Id: I4d6ed6ad4cb967026a3f01a335aec270066e2b9f --- .../unit_list_controller.cpp | 71 +- apps/calculation/calculation.cpp | 31 +- poincare/include/poincare/unit.h | 1373 +++++++---------- poincare/src/multiplication.cpp | 29 +- poincare/src/parsing/parser.cpp | 10 +- poincare/src/unit.cpp | 1107 +++++++------ poincare/test/expression.cpp | 10 +- poincare/test/expression_properties.cpp | 87 +- poincare/test/parsing.cpp | 19 +- poincare/test/simplification.cpp | 99 +- 10 files changed, 1341 insertions(+), 1495 deletions(-) diff --git a/apps/calculation/additional_outputs/unit_list_controller.cpp b/apps/calculation/additional_outputs/unit_list_controller.cpp index 2ae84d917..af0cc851f 100644 --- a/apps/calculation/additional_outputs/unit_list_controller.cpp +++ b/apps/calculation/additional_outputs/unit_list_controller.cpp @@ -12,27 +12,6 @@ using namespace Shared; namespace Calculation { -void storeAndSimplify(Expression e, Expression * dest, bool requireSimplification, bool canChangeUnitPrefix, ExpressionNode::ReductionContext reductionContext) { - assert(!e.isUninitialized()); - *dest = e; - if (requireSimplification) { - *dest = dest->simplify(reductionContext); - } - if (canChangeUnitPrefix) { - Expression newUnits; - /* If the expression has already been simplified, we do not want to reduce - * it further, as this would units introduced by a UnitConvert node back to - * SI units. */ - if (!requireSimplification) { - // Reduce to be able to removeUnit - *dest = dest->reduce(reductionContext); - } - *dest = dest->removeUnit(&newUnits); - double value = Shared::PoincareHelpers::ApproximateToScalar(*dest, App::app()->localContext()); - *dest = Multiplication::Builder(Number::FloatNumber(value), newUnits); - } -} - void UnitListController::setExpression(Poincare::Expression e) { ExpressionsListController::setExpression(e); assert(!m_expression.isUninitialized()); @@ -44,7 +23,6 @@ void UnitListController::setExpression(Poincare::Expression e) { expressions[i] = Expression(); } - size_t numberOfExpressions = 0; /* 1. First rows: miscellaneous classic units for some dimensions, in both * metric and imperial units. */ Expression copy = m_expression.clone(); @@ -52,52 +30,15 @@ void UnitListController::setExpression(Poincare::Expression e) { // Reduce to be able to recognize units PoincareHelpers::Reduce(©, App::app()->localContext(), ExpressionNode::ReductionTarget::User); copy = copy.removeUnit(&units); - Preferences::UnitFormat chosenFormat = GlobalPreferences::sharedGlobalPreferences()->unitFormat(); - Preferences::UnitFormat otherFormat = (chosenFormat == Preferences::UnitFormat::Metric) ? Preferences::UnitFormat::Imperial : Preferences::UnitFormat::Metric; - ExpressionNode::ReductionContext chosenFormatContext( + double value = Shared::PoincareHelpers::ApproximateToScalar(copy, App::app()->localContext()); + ExpressionNode::ReductionContext reductionContext( App::app()->localContext(), Preferences::sharedPreferences()->complexFormat(), Preferences::sharedPreferences()->angleUnit(), - chosenFormat, + GlobalPreferences::sharedGlobalPreferences()->unitFormat(), ExpressionNode::ReductionTarget::User, ExpressionNode::SymbolicComputation::ReplaceAllSymbolsWithDefinitionsOrUndefined); - ExpressionNode::ReductionContext otherFormatContext( - App::app()->localContext(), - Preferences::sharedPreferences()->complexFormat(), - Preferences::sharedPreferences()->angleUnit(), - otherFormat, - ExpressionNode::ReductionTarget::User, - ExpressionNode::SymbolicComputation::ReplaceAllSymbolsWithDefinitionsOrUndefined); - - if (Unit::IsSISpeed(units)) { - // 1.a. Turn speed into km/h or mi/h - storeAndSimplify(Unit::StandardSpeedConversion(m_expression.clone(), chosenFormatContext), &expressions[numberOfExpressions++], true, false, chosenFormatContext); - storeAndSimplify(Unit::StandardSpeedConversion(m_expression.clone(), otherFormatContext), &expressions[numberOfExpressions++], true, false, otherFormatContext); - } else if (Unit::IsSIVolume(units)) { - // 1.b. Turn volume into L or _gal + _cp + _floz - storeAndSimplify(Unit::StandardVolumeConversion(m_expression.clone(), chosenFormatContext), &expressions[numberOfExpressions++], chosenFormat == Preferences::UnitFormat::Metric, chosenFormat == Preferences::UnitFormat::Metric, chosenFormatContext); - storeAndSimplify(Unit::StandardVolumeConversion(m_expression.clone(), otherFormatContext), &expressions[numberOfExpressions++], otherFormat == Preferences::UnitFormat::Metric, otherFormat == Preferences::UnitFormat::Metric, otherFormatContext); - } else if (Unit::IsSIEnergy(units)) { - // 1.c. Turn energy into Wh - storeAndSimplify(UnitConvert::Builder(m_expression.clone(), Multiplication::Builder(Unit::Watt(), Unit::Hour())), &expressions[numberOfExpressions++], true, true, chosenFormatContext); - storeAndSimplify(UnitConvert::Builder(m_expression.clone(), Unit::ElectronVolt()), &expressions[numberOfExpressions++], true, true, chosenFormatContext); - } else if (Unit::IsSITime(units)) { - // 1.d. Turn time into ? year + ? month + ? day + ? h + ? min + ? s - double value = Shared::PoincareHelpers::ApproximateToScalar(copy, App::app()->localContext()); - expressions[numberOfExpressions++] = Unit::BuildTimeSplit(value, App::app()->localContext()); - } else if (Unit::IsSIDistance(units)) { - // 1.e. Turn distance into _?m or _mi + _yd + _ft + _in - storeAndSimplify(Unit::StandardDistanceConversion(m_expression.clone(), chosenFormatContext), &expressions[numberOfExpressions++], chosenFormat == Preferences::UnitFormat::Metric, chosenFormat == Preferences::UnitFormat::Metric, chosenFormatContext); - storeAndSimplify(Unit::StandardDistanceConversion(m_expression.clone(), otherFormatContext), &expressions[numberOfExpressions++], otherFormat == Preferences::UnitFormat::Metric, otherFormat == Preferences::UnitFormat::Metric, otherFormatContext); - } else if (Unit::IsSISurface(units)) { - // 1.f. Turn surface into hectares or acres - storeAndSimplify(Unit::StandardSurfaceConversion(m_expression.clone(), chosenFormatContext), &expressions[numberOfExpressions++], true, false, chosenFormatContext); - storeAndSimplify(Unit::StandardSurfaceConversion(m_expression.clone(), otherFormatContext), &expressions[numberOfExpressions++], true, false, otherFormatContext); - } else if (Unit::IsSIMass(units)) { - // 1.g. Turn mass into _?g and _lb + _oz - storeAndSimplify(Unit::StandardMassConversion(m_expression.clone(), chosenFormatContext), &expressions[numberOfExpressions++], chosenFormat == Preferences::UnitFormat::Metric, chosenFormat == Preferences::UnitFormat::Metric, chosenFormatContext); - storeAndSimplify(Unit::StandardMassConversion(m_expression.clone(), otherFormatContext), &expressions[numberOfExpressions++], otherFormat == Preferences::UnitFormat::Metric, otherFormat == Preferences::UnitFormat::Metric, otherFormatContext); - } + int numberOfExpressions = Unit::SetAdditionalExpressions(units, value, expressions, k_maxNumberOfRows, reductionContext); // 2. SI units only assert(numberOfExpressions < k_maxNumberOfRows - 1); @@ -112,14 +53,14 @@ void UnitListController::setExpression(Poincare::Expression e) { int currentExpressionIndex = 1; while (currentExpressionIndex < numberOfExpressions) { bool duplicateFound = false; - for (size_t i = 0; i < currentExpressionIndex + 1; i++) { + for (int i = 0; i < currentExpressionIndex + 1; i++) { // Compare the currentExpression to all previous expressions and to m_expression Expression comparedExpression = i == currentExpressionIndex ? reduceExpression : expressions[i]; assert(!comparedExpression.isUninitialized()); if (comparedExpression.isIdenticalTo(expressions[currentExpressionIndex])) { numberOfExpressions--; // Shift next expressions - for (size_t j = currentExpressionIndex; j < numberOfExpressions; j++) { + for (int j = currentExpressionIndex; j < numberOfExpressions; j++) { expressions[j] = expressions[j+1]; } // Remove last expression diff --git a/apps/calculation/calculation.cpp b/apps/calculation/calculation.cpp index f0c62dddb..682a27d66 100644 --- a/apps/calculation/calculation.cpp +++ b/apps/calculation/calculation.cpp @@ -254,32 +254,13 @@ Calculation::AdditionalInformationType Calculation::additionalInformationType(Co } if (o.hasUnit()) { Expression unit; - PoincareHelpers::Reduce(&o, - App::app()->localContext(), - ExpressionNode::ReductionTarget::User, - ExpressionNode::SymbolicComputation::ReplaceAllSymbolsWithDefinitionsOrUndefined, - ExpressionNode::UnitConversion::None); + /* FIXME : When this method is accessed via leaving the additional outputs, + * ie via a press on BACK, the reduction is interrupted, and removeUnit + * goes badly.*/ + PoincareHelpers::Reduce(&o, App::app()->localContext(), ExpressionNode::ReductionTarget::User, ExpressionNode::SymbolicComputation::ReplaceAllSymbolsWithDefinitionsOrUndefined); o = o.removeUnit(&unit); - // There might be no unit in the end, if the reduction was interrupted. - if (!unit.isUninitialized()) { - if (Unit::IsSI(unit)) { - if (Unit::IsSISpeed(unit) || Unit::IsSIVolume(unit) || Unit::IsSIEnergy(unit)) { - /* All these units will provide misc. classic representatives in - * addition to the SI unit in additional information. */ - return AdditionalInformationType::Unit; - } - if (Unit::IsSITime(unit)) { - /* If the number of seconds is above 60s, we can write it in the form - * of an addition: 23_min + 12_s for instance. */ - double value = Shared::PoincareHelpers::ApproximateToScalar(o, App::app()->localContext()); - if (value > Unit::SecondsPerMinute) { - return AdditionalInformationType::Unit; - } - } - return AdditionalInformationType::None; - } - return AdditionalInformationType::Unit; - } + double value = PoincareHelpers::ApproximateToScalar(o, App::app()->localContext()); + return (Unit::ShouldDisplayAdditionalOutputs(value, unit)) ? AdditionalInformationType::Unit : AdditionalInformationType::None; } if (o.isBasedIntegerCappedBy(k_maximalIntegerWithAdditionalInformation)) { return AdditionalInformationType::Integer; diff --git a/poincare/include/poincare/unit.h b/poincare/include/poincare/unit.h index d16ad23c2..9802de816 100644 --- a/poincare/include/poincare/unit.h +++ b/poincare/include/poincare/unit.h @@ -5,22 +5,21 @@ namespace Poincare { -class UnitNode final : public ExpressionNode { -public: - /* The units having the same physical dimension are grouped together. * Each such group has a standard representative with a standard prefix. * * Each representative has * - a root symbol - * - a definition - * - a list of allowed output prefixes - * Given a Dimension, a representative in that Dimension and a Prefix - * allowed for that representative, one may get a symbol and an Expression. + * - a definition, as the conversion ratio with the SI unit of the same + * dimensions + * - informations on how the representative should be prefixed. + * + * Given an representative and a Prefix allowed for that representative, one may get + * a symbol and an Expression. * * FIXME ? - * The UnitNode class holds as members pointers to a Dimension, a - * Representative, a Prefix. Those nested classes may not be forward + * The UnitNode class holds as members pointers to a Representative and a + * Prefix. Those nested classes may not be forward * declared and must be defined in UnitNode and then aliased in Unit so as * to be used outside. That technical limitation could have been avoided if * UnitNode were itself a nested class of Unit, say Unit::Node. More @@ -29,115 +28,401 @@ public: * and scopes. */ - // There are 7 base units from which all other units are derived. - static constexpr size_t NumberOfBaseUnits = 7; +class Unit; + +class UnitNode final : public ExpressionNode { +public: + static constexpr int k_numberOfBaseUnits = 7; class Prefix { + friend class Unit; public: - constexpr Prefix(const char * symbol, int8_t exponent) : - m_symbol(symbol), - m_exponent(exponent) - {} + static constexpr int k_numberOfPrefixes = 13; + static const Prefix * Prefixes(); + static const Prefix * EmptyPrefix(); const char * symbol() const { return m_symbol; } int8_t exponent() const { return m_exponent; } int serialize(char * buffer, int bufferSize) const; private: + constexpr Prefix(const char * symbol, int8_t exponent) : + m_symbol(symbol), + m_exponent(exponent) + {} + const char * m_symbol; int8_t m_exponent; }; + template + struct Vector { + // 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 < k_numberOfBaseUnits); + const T coefficients[k_numberOfBaseUnits] = {time, distance, mass, current, temperature, amountOfSubstance, luminuousIntensity}; + return coefficients[i]; + } + void setCoefficientAtIndex(size_t i, T c) { + assert(i < k_numberOfBaseUnits); + T * coefficientsAddresses[k_numberOfBaseUnits] = {&time, &distance, &mass, ¤t, &temperature, &amountOfSubstance, &luminuousIntensity}; + *(coefficientsAddresses[i]) = c; + } + bool operator==(const Vector &rhs) const { return time == rhs.time && distance == rhs.distance && mass == rhs.mass && current == rhs.current && temperature == rhs.temperature && amountOfSubstance == rhs.amountOfSubstance && luminuousIntensity == rhs.luminuousIntensity; } + void addAllCoefficients(const Vector other, int factor); + Expression toBaseUnits() const; + T time; + T distance; + T mass; + T current; + T temperature; + T amountOfSubstance; + T luminuousIntensity; + }; + class Representative { + friend class Unit; public: - /* The following class is meant to be an attribute determining whether a - * unit symbol is prefixable at all. If Yes, any prefix is accepted as - * input, whereas the allowed output prefixes are prescribed manually. */ enum class Prefixable { - No, - Yes, - PositiveOnly - }; - enum class OutputSystem { None, - Imperial, - Metric, - All + PositiveLongScale, + NegativeLongScale, + Positive, + Negative, + NegativeAndKilo, + LongScale, + All, }; - template - constexpr Representative(const char * rootSymbol, const char * definition, const Prefixable prefixable, const Prefix * const (&outputPrefixes)[N], const OutputSystem outputSystem = OutputSystem::All) : + static constexpr int k_numberOfDimensions = 24; + static const Representative * const * DefaultRepresentatives(); + static const Representative * RepresentativeForDimension(Vector vector); + constexpr Representative(const char * rootSymbol, double ratio, Prefixable inputPrefixable, Prefixable outputPrefixable) : m_rootSymbol(rootSymbol), - m_definition(definition), - m_prefixable(prefixable), - m_outputPrefixes(outputPrefixes), - m_outputPrefixesLength(N), - m_outputSystem(outputSystem) - { - } + m_ratio(ratio), + m_inputPrefixable(inputPrefixable), + m_outputPrefixable(outputPrefixable) + {} + virtual const Vector dimensionVector() const { return Vector{.time = 0, .distance = 0, .mass = 0, .current = 0, .temperature = 0, .amountOfSubstance = 0, .luminuousIntensity = 0}; }; + virtual int numberOfRepresentatives() const { return 0; }; + /* representativesOfSameDimension returns a pointer to the array containing + * all representatives for this's dimension. */ + virtual const Representative * representativesOfSameDimension() const { return nullptr; }; + virtual const Prefix * basePrefix() const { return Prefix::EmptyPrefix(); } + virtual bool isBaseUnit() const { return false; } + virtual const Representative * standardRepresentative(double value, double exponent, ExpressionNode::ReductionContext reductionContext, const Prefix * * prefix) const { return DefaultFindBestRepresentative(value, exponent, representativesOfSameDimension(), numberOfRepresentatives(), prefix); } + virtual bool hasAdditionalExpressions(double value) const { return true; } + virtual int setAdditionalExpressions(double value, Expression * dest, int availableLength, ExpressionNode::ReductionContext reductionContext) const { return 0; } const char * rootSymbol() const { return m_rootSymbol; } - const char * definition() const { return m_definition; } - bool isPrefixable() const { return m_prefixable != Prefixable::No; } - const Prefix * const * outputPrefixes() const { return m_outputPrefixes; } - size_t outputPrefixesLength() const { return m_outputPrefixesLength; } - bool canParse(const char * symbol, size_t length, - const Prefix * * prefix) const; + double ratio() const { return m_ratio; } + bool isInputPrefixable() const { return m_inputPrefixable != Prefixable::None; } + bool isOutputPrefixable() const { return m_outputPrefixable != Prefixable::None; } + bool canPrefix(const Prefix * prefix, bool input) const; int serialize(char * buffer, int bufferSize, const Prefix * prefix) const; - const Prefix * bestPrefixForValue(double & value, const float exponent) const; - bool canOutputInSystem(Preferences::UnitFormat system) const; - private: + bool canParseWithEquivalents(const char * symbol, size_t length, const Representative * * representative, const Prefix * * prefix) const; + bool canParse(const char * symbol, size_t length, const Prefix * * prefix) const; + Expression toBaseUnits() const; + const Prefix * findBestPrefix(double value, double exponent) const; + protected: + static const Representative * DefaultFindBestRepresentative(double value, double exponent, const Representative * representatives, int length, const Prefix * * prefix); const char * m_rootSymbol; - const char * m_definition; - const Prefixable m_prefixable; - const Prefix * const * m_outputPrefixes; - const size_t m_outputPrefixesLength; - const OutputSystem m_outputSystem; + /* m_ratio is the factor used to convert a unit made of the representative + * and its base prefix into base SI units. + * ex : m_ratio for Liter is 1e-3 (as 1_L = 1e-3_m). + * m_ratio for gram is 1 (as k is its best prefix and _kg is SI) */ + const double m_ratio; + const Prefixable m_inputPrefixable; + const Prefixable m_outputPrefixable; }; - class Dimension { + class TimeRepresentative : public Representative { + friend class Unit; public: - template - struct Vector { - // 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); - return *(reinterpret_cast(this) + i); - } - void setCoefficientAtIndex(size_t i, T c) { - assert(i < NumberOfBaseUnits); - *(reinterpret_cast(this) + i) = c; - } - T time; - T distance; - T mass; - T current; - T temperature; - T amountOfSubstance; - T luminuousIntensity; - }; - template - constexpr Dimension(Vector vector, const Representative (&representatives)[N], const Prefix * stdRepresentativePrefix) : - m_vector(vector), - m_representatives(representatives), - m_representativesUpperBound(representatives + N), - m_stdRepresentativePrefix(stdRepresentativePrefix) - { - } - const Vector * vector() const { return &m_vector; } - const Representative * stdRepresentative() const { return m_representatives; } - const Representative * representativesUpperBound() const { return m_representativesUpperBound; } - const Prefix * stdRepresentativePrefix() const { return m_stdRepresentativePrefix; } - bool canParse(const char * symbol, size_t length, - const Representative * * representative, const Prefix * * prefix) const; + constexpr static TimeRepresentative Default() { return TimeRepresentative(nullptr, 0., Prefixable::None, Prefixable::None); } + const Vector dimensionVector() const override { return Vector{.time = 1, .distance = 0, .mass = 0, .current = 0, .temperature = 0, .amountOfSubstance = 0, .luminuousIntensity = 0}; } + int numberOfRepresentatives() const override { return 7; } + const Representative * representativesOfSameDimension() const override; + bool isBaseUnit() const override { return this == representativesOfSameDimension(); } + bool hasAdditionalExpressions(double value) const override { return m_ratio * value >= representativesOfSameDimension()[1].ratio(); } + int setAdditionalExpressions(double value, Expression * dest, int availableLength, ExpressionNode::ReductionContext reductionContext) const override; private: - Vector m_vector; - const Representative * m_representatives; - const Representative * m_representativesUpperBound; - const Prefix * m_stdRepresentativePrefix; + using Representative::Representative; }; - UnitNode(const Dimension * dimension, const Representative * representative, const Prefix * prefix) : + class DistanceRepresentative : public Representative { + friend class Unit; + public: + constexpr static DistanceRepresentative Default() { return DistanceRepresentative(nullptr, 0., Prefixable::None, Prefixable::None); } + const Vector dimensionVector() const override { return Vector{.time = 0, .distance = 1, .mass = 0, .current = 0, .temperature = 0, .amountOfSubstance = 0, .luminuousIntensity = 0}; } + int numberOfRepresentatives() const override { return 8; } + const Representative * representativesOfSameDimension() const override; + bool isBaseUnit() const override { return this == representativesOfSameDimension(); } + const Representative * standardRepresentative(double value, double exponent, ExpressionNode::ReductionContext reductionContext, const Prefix * * prefix) const override; + int setAdditionalExpressions(double value, Expression * dest, int availableLength, ExpressionNode::ReductionContext reductionContext) const override; + private: + using Representative::Representative; + }; + + class MassRepresentative : public Representative { + friend class Unit; + public: + constexpr static MassRepresentative Default() { return MassRepresentative(nullptr, 0., Prefixable::None, Prefixable::None); } + const Vector dimensionVector() const override { return Vector{.time = 0, .distance = 0, .mass = 1, .current = 0, .temperature = 0, .amountOfSubstance = 0, .luminuousIntensity = 0}; } + int numberOfRepresentatives() const override { return 7; } + const Representative * representativesOfSameDimension() const override; + const Prefix * basePrefix() const override; + bool isBaseUnit() const override { return this == representativesOfSameDimension(); } + const Representative * standardRepresentative(double value, double exponent, ExpressionNode::ReductionContext reductionContext, const Prefix * * prefix) const override; + int setAdditionalExpressions(double value, Expression * dest, int availableLength, ExpressionNode::ReductionContext reductionContext) const override; + private: + using Representative::Representative; + }; + + class CurrentRepresentative : public Representative { + friend class Unit; + public: + constexpr static CurrentRepresentative Default() { return CurrentRepresentative(nullptr, 0., Prefixable::None, Prefixable::None); } + const Vector dimensionVector() const override { return Vector{.time = 0, .distance = 0, .mass = 0, .current = 1, .temperature = 0, .amountOfSubstance = 0, .luminuousIntensity = 0}; } + int numberOfRepresentatives() const override { return 1; } + const Representative * representativesOfSameDimension() const override; + bool isBaseUnit() const override { return this == representativesOfSameDimension(); } + bool hasAdditionalExpressions(double value) const override { return false; } + private: + using Representative::Representative; + }; + + class TemperatureRepresentative : public Representative { + friend class Unit; + public: + constexpr static TemperatureRepresentative Default() { return TemperatureRepresentative(nullptr, 0., Prefixable::None, Prefixable::None); } + const Vector dimensionVector() const override { return Vector{.time = 0, .distance = 0, .mass = 0, .current = 0, .temperature = 1, .amountOfSubstance = 0, .luminuousIntensity = 0}; } + int numberOfRepresentatives() const override { return 1; } + const Representative * representativesOfSameDimension() const override; + bool isBaseUnit() const override { return this == representativesOfSameDimension(); } + bool hasAdditionalExpressions(double value) const override { return false; } + private: + using Representative::Representative; + }; + + class AmountOfSubstanceRepresentative : public Representative { + friend class Unit; + public: + constexpr static AmountOfSubstanceRepresentative Default() { return AmountOfSubstanceRepresentative(nullptr, 0., Prefixable::None, Prefixable::None); } + const Vector dimensionVector() const override { return Vector{.time = 0, .distance = 0, .mass = 0, .current = 0, .temperature = 0, .amountOfSubstance = 1, .luminuousIntensity = 0}; } + int numberOfRepresentatives() const override { return 1; } + const Representative * representativesOfSameDimension() const override; + bool isBaseUnit() const override { return this == representativesOfSameDimension(); } + bool hasAdditionalExpressions(double value) const override { return false; } + private: + using Representative::Representative; + }; + + class LuminousIntensityRepresentative : public Representative { + friend class Unit; + public: + constexpr static LuminousIntensityRepresentative Default() { return LuminousIntensityRepresentative(nullptr, 0., Prefixable::None, Prefixable::None); } + const Vector dimensionVector() const override { return Vector{.time = 0, .distance = 0, .mass = 0, .current = 0, .temperature = 0, .amountOfSubstance = 0, .luminuousIntensity = 1}; } + int numberOfRepresentatives() const override { return 1; } + const Representative * representativesOfSameDimension() const override; + bool isBaseUnit() const override { return this == representativesOfSameDimension(); } + bool hasAdditionalExpressions(double value) const override { return false; } + private: + using Representative::Representative; + }; + + class FrequencyRepresentative : public Representative { + friend class Unit; + public: + constexpr static FrequencyRepresentative Default() { return FrequencyRepresentative(nullptr, 0., Prefixable::None, Prefixable::None); } + const Vector dimensionVector() const override { return Vector{.time = -1, .distance = 0, .mass = 0, .current = 0, .temperature = 0, .amountOfSubstance = 0, .luminuousIntensity = 0}; } + int numberOfRepresentatives() const override { return 1; } + const Representative * representativesOfSameDimension() const override; + bool hasAdditionalExpressions(double value) const override { return false; } + private: + using Representative::Representative; + }; + + class ForceRepresentative : public Representative { + friend class Unit; + public: + constexpr static ForceRepresentative Default() { return ForceRepresentative(nullptr, 0., Prefixable::None, Prefixable::None); } + const Vector dimensionVector() const override { return Vector{.time = -2, .distance = 1, .mass = 1, .current = 0, .temperature = 0, .amountOfSubstance = 0, .luminuousIntensity = 0}; } + int numberOfRepresentatives() const override { return 1; } + const Representative * representativesOfSameDimension() const override; + private: + using Representative::Representative; + }; + + class PressureRepresentative : public Representative { + friend class Unit; + public: + constexpr static PressureRepresentative Default() { return PressureRepresentative(nullptr, 0., Prefixable::None, Prefixable::None); } + const Vector dimensionVector() const override { return Vector{.time = -2, .distance = -1, .mass = 1, .current = 0, .temperature = 0, .amountOfSubstance = 0, .luminuousIntensity = 0}; } + int numberOfRepresentatives() const override { return 3; } + const Representative * representativesOfSameDimension() const override; + private: + using Representative::Representative; + }; + + class EnergyRepresentative : public Representative { + friend class Unit; + public: + constexpr static EnergyRepresentative Default() { return EnergyRepresentative(nullptr, 0., Prefixable::None, Prefixable::None); } + const Vector dimensionVector() const override { return Vector{.time = -2, .distance = 2, .mass = 1, .current = 0, .temperature = 0, .amountOfSubstance = 0, .luminuousIntensity = 0}; } + int numberOfRepresentatives() const override { return 2; } + const Representative * representativesOfSameDimension() const override; + int setAdditionalExpressions(double value, Expression * dest, int availableLength, ExpressionNode::ReductionContext reductionContext) const override; + private: + using Representative::Representative; + }; + + class PowerRepresentative : public Representative { + friend class Unit; + public: + constexpr static PowerRepresentative Default() { return PowerRepresentative(nullptr, 0., Prefixable::None, Prefixable::None); } + const Vector dimensionVector() const override { return Vector{.time = -3, .distance = 2, .mass = 1, .current = 0, .temperature = 0, .amountOfSubstance = 0, .luminuousIntensity = 0}; } + int numberOfRepresentatives() const override { return 1; } + const Representative * representativesOfSameDimension() const override; + private: + using Representative::Representative; + }; + + class ElectricChargeRepresentative : public Representative { + friend class Unit; + public: + using Representative::Representative; + constexpr static ElectricChargeRepresentative Default() { return ElectricChargeRepresentative(nullptr, 0., Prefixable::None, Prefixable::None); } + const Vector dimensionVector() const override { return Vector{.time = 1, .distance = 0, .mass = 0, .current = 1, .temperature = 0, .amountOfSubstance = 0, .luminuousIntensity = 0}; } + int numberOfRepresentatives() const override { return 1; } + const Representative * representativesOfSameDimension() const override; + }; + + class ElectricPotentialRepresentative : public Representative { + friend class Unit; + public: + constexpr static ElectricPotentialRepresentative Default() { return ElectricPotentialRepresentative(nullptr, 0., Prefixable::None, Prefixable::None); } + const Vector dimensionVector() const override { return Vector{.time = -3, .distance = 2, .mass = 1, .current = -1, .temperature = 0, .amountOfSubstance = 0, .luminuousIntensity = 0}; } + int numberOfRepresentatives() const override { return 1; } + const Representative * representativesOfSameDimension() const override; + private: + using Representative::Representative; + }; + + class ElectricCapacitanceRepresentative : public Representative { + friend class Unit; + public: + constexpr static ElectricCapacitanceRepresentative Default() { return ElectricCapacitanceRepresentative(nullptr, 0., Prefixable::None, Prefixable::None); } + const Vector dimensionVector() const override { return Vector{.time = 4, .distance = -2, .mass = -1, .current = 2, .temperature = 0, .amountOfSubstance = 0, .luminuousIntensity = 0}; } + int numberOfRepresentatives() const override { return 1; } + const Representative * representativesOfSameDimension() const override; + private: + using Representative::Representative; + }; + + class ElectricResistanceRepresentative : public Representative { + friend class Unit; + public: + constexpr static ElectricResistanceRepresentative Default() { return ElectricResistanceRepresentative(nullptr, 0., Prefixable::None, Prefixable::None); } + const Vector dimensionVector() const override { return Vector{.time = -3, .distance = 2, .mass = 1, .current = -2, .temperature = 0, .amountOfSubstance = 0, .luminuousIntensity = 0}; } + int numberOfRepresentatives() const override { return 1; } + const Representative * representativesOfSameDimension() const override; + private: + using Representative::Representative; + }; + + class ElectricConductanceRepresentative : public Representative { + friend class Unit; + public: + constexpr static ElectricConductanceRepresentative Default() { return ElectricConductanceRepresentative(nullptr, 1., Prefixable::None, Prefixable::None); } + const Vector dimensionVector() const override { return Vector{.time = 3, .distance = -2, .mass = -1, .current = 2, .temperature = 0, .amountOfSubstance = 0, .luminuousIntensity = 0}; } + int numberOfRepresentatives() const override { return 1; } + const Representative * representativesOfSameDimension() const override; + private: + using Representative::Representative; + }; + + class MagneticFluxRepresentative : public Representative { + friend class Unit; + public: + constexpr static MagneticFluxRepresentative Default() { return MagneticFluxRepresentative(nullptr, 0., Prefixable::None, Prefixable::None); } + const Vector dimensionVector() const override { return Vector{.time = -2, .distance = 2, .mass = 1, .current = -1, .temperature = 0, .amountOfSubstance = 0, .luminuousIntensity = 0}; } + int numberOfRepresentatives() const override { return 1; } + const Representative * representativesOfSameDimension() const override; + private: + using Representative::Representative; + }; + + class MagneticFieldRepresentative : public Representative { + friend class Unit; + public: + constexpr static MagneticFieldRepresentative Default() { return MagneticFieldRepresentative(nullptr, 0., Prefixable::None, Prefixable::None); } + const Vector dimensionVector() const override { return Vector{.time = -2, .distance = 0, .mass = 1, .current = -1, .temperature = 0, .amountOfSubstance = 0, .luminuousIntensity = 0}; } + int numberOfRepresentatives() const override { return 1; } + const Representative * representativesOfSameDimension() const override; + private: + using Representative::Representative; + }; + + class InductanceRepresentative : public Representative { + friend class Unit; + public: + constexpr static InductanceRepresentative Default() { return InductanceRepresentative(nullptr, 0., Prefixable::None, Prefixable::None); } + const Vector dimensionVector() const override { return Vector{.time = -2, .distance = 2, .mass = 1, .current = -2, .temperature = 0, .amountOfSubstance = 0, .luminuousIntensity = 0}; } + int numberOfRepresentatives() const override { return 1; } + const Representative * representativesOfSameDimension() const override; + private: + using Representative::Representative; + }; + + class CatalyticActivityRepresentative : public Representative { + friend class Unit; + public: + constexpr static CatalyticActivityRepresentative Default() { return CatalyticActivityRepresentative(nullptr, 0., Prefixable::None, Prefixable::None); } + const Vector dimensionVector() const override { return Vector{.time = -1, .distance = 0, .mass = 0, .current = 0, .temperature = 0, .amountOfSubstance = 1, .luminuousIntensity = 0}; } + int numberOfRepresentatives() const override { return 1; } + const Representative * representativesOfSameDimension() const override; + private: + using Representative::Representative; + }; + + class SurfaceRepresentative : public Representative { + friend class Unit; + public: + constexpr static SurfaceRepresentative Default() { return SurfaceRepresentative(nullptr, 0., Prefixable::None, Prefixable::None); } + const Vector dimensionVector() const override { return Vector{.time = 0, .distance = 2, .mass = 0, .current = 0, .temperature = 0, .amountOfSubstance = 0, .luminuousIntensity = 0}; } + int numberOfRepresentatives() const override { return 2; } + const Representative * representativesOfSameDimension() const override; + const Representative * standardRepresentative(double value, double exponent, ExpressionNode::ReductionContext reductionContext, const Prefix * * prefix) const override; + int setAdditionalExpressions(double value, Expression * dest, int availableLength, ExpressionNode::ReductionContext reductionContext) const override; + private: + using Representative::Representative; + }; + + class VolumeRepresentative : public Representative { + friend class Unit; + public: + constexpr static VolumeRepresentative Default() { return VolumeRepresentative(nullptr, 0., Prefixable::None, Prefixable::None); } + const Vector dimensionVector() const override { return Vector{.time = 0, .distance = 3, .mass = 0, .current = 0, .temperature = 0, .amountOfSubstance = 0, .luminuousIntensity = 0}; } + int numberOfRepresentatives() const override { return 8; } + const Representative * representativesOfSameDimension() const override; + const Representative * standardRepresentative(double value, double exponent, ExpressionNode::ReductionContext reductionContext, const Prefix * * prefix) const override; + int setAdditionalExpressions(double value, Expression * dest, int availableLength, ExpressionNode::ReductionContext reductionContext) const override; + private: + using Representative::Representative; + }; + + class SpeedRepresentative : public Representative { + friend class Unit; + public: + constexpr static SpeedRepresentative Default() { return SpeedRepresentative(nullptr, 0., Prefixable::None, Prefixable::None); } + const VectordimensionVector() const override { return Vector{.time = -1, .distance = 1, .mass = 0, .current = 0, .temperature = 0, .amountOfSubstance = 0, .luminuousIntensity = 0}; } + const Representative * standardRepresentative(double value, double exponent, ExpressionNode::ReductionContext reductionContext, const Prefix * * prefix) const override { return nullptr; } + int setAdditionalExpressions(double value, Expression * dest, int availableLength, ExpressionNode::ReductionContext reductionContext) const override; + private: + using Representative::Representative; + }; + + UnitNode(const Representative * representative, const Prefix * prefix) : ExpressionNode(), - m_dimension(dimension), m_representative(representative), m_prefix(prefix) {} @@ -176,758 +461,208 @@ public: Expression shallowBeautify(ReductionContext reductionContext) override; LayoutShape leftLayoutShape() const override { return LayoutShape::OneLetter; } // TODO - const Dimension * dimension() const { return m_dimension; } const Representative * representative() const { return m_representative; } const Prefix * prefix() const { return m_prefix; } void setRepresentative(const Representative * representative) { m_representative = representative; } void setPrefix(const Prefix * prefix) { m_prefix = prefix; } private: - const Dimension * m_dimension; + template Evaluation templatedApproximate(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; + const Representative * m_representative; const Prefix * m_prefix; - - template Evaluation templatedApproximate(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; }; -class Unit final : public Expression { +/* FIXME : This can be replaced by std::string_view when moving to C++17 */ +constexpr bool strings_equal(const char * s1, const char * s2) { return *s1 == *s2 && (*s1 == '\0' || strings_equal(s1 + 1, s2 + 1)); } + +class Unit : public Expression { friend class UnitNode; public: + /* Prefixes and Representativees defined below must be defined only once and + * all units must be constructed from their pointers. This way we can easily + * check if two Unit objects are equal by comparing pointers. This saves us + * from overloading the == operator on Prefix and Representative and saves + * time at execution. As such, their constructor are private and can only be + * accessed by their friend class Unit. */ typedef UnitNode::Prefix Prefix; - typedef UnitNode::Representative Representative; - typedef UnitNode::Dimension Dimension; - /* TODO: Prefix, Representative and Dimension defined below must be defined - * only once and all units must be constructed from their pointers. This way - * we can easily check if two Unit objects are equal by comparing pointers. - * This saves us from overloading the == operator on Prefix, Representative - * and Dimension and saves time at execution. We should assert at compilation - * that only one occurence of each is built by maybe privatizing constructors - * on these classes? */ - static constexpr const Prefix - PicoPrefix = Prefix("p", -12), - NanoPrefix = Prefix("n", -9), - MicroPrefix = Prefix("μ", -6), - MilliPrefix = Prefix("m", -3), - CentiPrefix = Prefix("c", -2), - DeciPrefix = Prefix("d", -1), - EmptyPrefix = Prefix("", 0), - DecaPrefix = Prefix("da", 1), - HectoPrefix = Prefix("h", 2), - KiloPrefix = Prefix("k", 3), - MegaPrefix = Prefix("M", 6), - GigaPrefix = Prefix("G", 9), - TeraPrefix = Prefix("T", 12); - static constexpr const Prefix * NoPrefix[] = { - &EmptyPrefix - }; - static constexpr const Prefix * NegativeLongScalePrefixes[] = { - &PicoPrefix, - &NanoPrefix, - &MicroPrefix, - &MilliPrefix, - &EmptyPrefix, - }; - static constexpr const Prefix * PositiveLongScalePrefixes[] = { - &EmptyPrefix, - &KiloPrefix, - &MegaPrefix, - &GigaPrefix, - &TeraPrefix, - }; - static constexpr const Prefix * LongScalePrefixes[] = { - &PicoPrefix, - &NanoPrefix, - &MicroPrefix, - &MilliPrefix, - &EmptyPrefix, - &KiloPrefix, - &MegaPrefix, - &GigaPrefix, - &TeraPrefix, - }; - static constexpr const Prefix * NegativePrefixes[] = { - &PicoPrefix, - &NanoPrefix, - &MicroPrefix, - &MilliPrefix, - &CentiPrefix, - &DeciPrefix, - &EmptyPrefix, - }; - static constexpr const Prefix * AllPrefixes[] = { - &PicoPrefix, - &NanoPrefix, - &MicroPrefix, - &MilliPrefix, - &CentiPrefix, - &DeciPrefix, - &EmptyPrefix, - &DecaPrefix, - &HectoPrefix, - &KiloPrefix, - &MegaPrefix, - &GigaPrefix, - &TeraPrefix, - }; - static constexpr size_t NumberOfBaseUnits = UnitNode::NumberOfBaseUnits; - static constexpr const Representative - TimeRepresentatives[] = { - Representative("s", nullptr, - Representative::Prefixable::Yes, - NegativeLongScalePrefixes), - Representative("min", "60*_s", - Representative::Prefixable::No, - NoPrefix), - Representative("h", "60*60*_s", - Representative::Prefixable::No, - NoPrefix), - Representative("day", "24*60*60*_s", - Representative::Prefixable::No, - NoPrefix), - Representative("week", "7*24*60*60*_s", - Representative::Prefixable::No, - NoPrefix), - Representative("month", "365.25/12*24*60*60*_s", - Representative::Prefixable::No, - NoPrefix), - Representative("year", "365.25*24*60*60*_s", - Representative::Prefixable::No, - NoPrefix), - }, - DistanceRepresentatives[] = { - Representative("m", nullptr, - Representative::Prefixable::Yes, - LongScalePrefixes, - Representative::OutputSystem::Metric), - Representative("au", "149597870700*_m", - Representative::Prefixable::No, - NoPrefix), - Representative("ly", "299792458*_m/_s*_year", - Representative::Prefixable::No, - NoPrefix), - Representative("pc", "180*60*60/π*_au", - Representative::Prefixable::No, - NoPrefix), - Representative("in", "0.0254*_m", - Representative::Prefixable::No, - NoPrefix, - Representative::OutputSystem::Imperial), - Representative("ft", "12*0.0254*_m", - Representative::Prefixable::No, - NoPrefix, - Representative::OutputSystem::Imperial), - Representative("yd", "3*12*0.0254*_m", - Representative::Prefixable::No, - NoPrefix, - Representative::OutputSystem::None), - Representative("mi", "1760*3*12*0.0254*_m", - Representative::Prefixable::No, - NoPrefix, - Representative::OutputSystem::Imperial), - }, - MassRepresentatives[] = { - Representative("kg", nullptr, - Representative::Prefixable::No, - NoPrefix, - Representative::OutputSystem::Metric), - Representative("g", "0.001_kg", - Representative::Prefixable::Yes, - NegativeLongScalePrefixes, - Representative::OutputSystem::Metric), - Representative("t", "1000_kg", - Representative::Prefixable::PositiveOnly, - NoPrefix, - Representative::OutputSystem::Metric), - Representative("Da", "(6.02214076*10^23*1000)^-1*_kg", - Representative::Prefixable::Yes, - NoPrefix), - Representative("oz", "0.028349523125*_kg", - Representative::Prefixable::No, - NoPrefix, - Representative::OutputSystem::Imperial), - Representative("lb", "16*0.028349523125*_kg", - Representative::Prefixable::No, - NoPrefix, - Representative::OutputSystem::Imperial), - Representative("shtn", "2000*16*0.028349523125*_kg", - Representative::Prefixable::No, - NoPrefix, - Representative::OutputSystem::Imperial), - Representative("lgtn", "2240*16*0.028349523125*_kg", - Representative::Prefixable::No, - NoPrefix, - Representative::OutputSystem::None), - }, - CurrentRepresentatives[] = { - Representative("A", nullptr, - Representative::Prefixable::Yes, - NegativeLongScalePrefixes), - }, - TemperatureRepresentatives[] = { - Representative("K", nullptr, - Representative::Prefixable::No, - NoPrefix), - }, - AmountOfSubstanceRepresentatives[] = { - Representative("mol", nullptr, - Representative::Prefixable::Yes, - NegativeLongScalePrefixes), - }, - LuminousIntensityRepresentatives[] = { - Representative("cd", nullptr, - Representative::Prefixable::No, - NoPrefix), - }, - FrequencyRepresentatives[] = { - Representative("Hz", "_s^-1", - Representative::Prefixable::Yes, - PositiveLongScalePrefixes), - }, - ForceRepresentatives[] = { - Representative("N", "_kg*_m*_s^-2", - Representative::Prefixable::Yes, - LongScalePrefixes), - }, - PressureRepresentatives[] = { - Representative("Pa", "_kg*_m^-1*_s^-2", - Representative::Prefixable::Yes, - LongScalePrefixes), - Representative("bar", "1000_hPa", - Representative::Prefixable::Yes, - NoPrefix), - Representative("atm", "101325_Pa", - Representative::Prefixable::Yes, - NoPrefix), - }, - EnergyRepresentatives[] = { - Representative("J", "_kg*_m^2*_s^-2", - Representative::Prefixable::Yes, - LongScalePrefixes), - Representative("eV", "1.602176634ᴇ-19*_J", - Representative::Prefixable::Yes, - LongScalePrefixes), - }, - PowerRepresentatives[] = { - Representative("W", "_kg*_m^2*_s^-3", - Representative::Prefixable::Yes, - LongScalePrefixes), - }, - ElectricChargeRepresentatives[] = { - Representative("C", "_A*_s", - Representative::Prefixable::Yes, - LongScalePrefixes), - }, - ElectricPotentialRepresentatives[] = { - Representative("V", "_kg*_m^2*_s^-3*_A^-1", - Representative::Prefixable::Yes, - LongScalePrefixes), - }, - ElectricCapacitanceRepresentatives[] = { - Representative("F", "_A^2*_s^4*_kg^-1*_m^-2", - Representative::Prefixable::Yes, - LongScalePrefixes), - }, - ElectricResistanceRepresentatives[] = { - Representative("Ω", "_kg*_m^2*_s^-3*_A^-2", - Representative::Prefixable::Yes, - LongScalePrefixes), - }, - ElectricConductanceRepresentatives[] = { - Representative("S", "_A^2*_s^3*_kg^-1*_m^-2", - Representative::Prefixable::Yes, - LongScalePrefixes), - }, - MagneticFluxRepresentatives[] = { - Representative("Wb", "_kg*_m^2*_s^-2*_A^-1", - Representative::Prefixable::Yes, - NoPrefix), - }, - MagneticFieldRepresentatives[] = { - Representative("T", "_kg*_s^-2*_A^-1", - Representative::Prefixable::Yes, - NoPrefix), - }, - InductanceRepresentatives[] = { - Representative("H", "_kg*_m^2*_s^-2*_A^-2", - Representative::Prefixable::Yes, - LongScalePrefixes), - }, - CatalyticActivityRepresentatives[] = { - Representative("kat", "_mol*_s^-1", - Representative::Prefixable::Yes, - NoPrefix), - }, - SurfaceRepresentatives[] = { - Representative("ha", "10^4*_m^2", - Representative::Prefixable::No, - NoPrefix, - Representative::OutputSystem::Metric), - Representative("acre", "4046.8564224*_m^2", - Representative::Prefixable::No, - NoPrefix, - Representative::OutputSystem::Imperial), - }, - VolumeRepresentatives[] = { - Representative("L", "10^-3*_m^3", - Representative::Prefixable::Yes, - NegativePrefixes, - Representative::OutputSystem::Metric), - Representative("tsp", "0.00492892159375*_L", - Representative::Prefixable::No, - NoPrefix, - Representative::OutputSystem::None), - Representative("Tbsp", "3*0.00492892159375*_L", - Representative::Prefixable::No, - NoPrefix, - Representative::OutputSystem::None), - Representative("floz", "0.0295735295625*_L", - Representative::Prefixable::No, - NoPrefix, - Representative::OutputSystem::Imperial), - Representative("cp", "8*0.0295735295625*_L", - Representative::Prefixable::No, - NoPrefix, - Representative::OutputSystem::Imperial), - Representative("pt", "16*0.0295735295625*_L", - Representative::Prefixable::No, - NoPrefix, - Representative::OutputSystem::None), - Representative("qt", "32*0.0295735295625*_L", - Representative::Prefixable::No, - NoPrefix, - Representative::OutputSystem::None), - Representative("gal", "128*0.0295735295625*_L", - Representative::Prefixable::No, - NoPrefix, - Representative::OutputSystem::Imperial), - }; - // TODO: find a better way to define these pointers - static_assert(sizeof(TimeRepresentatives)/sizeof(Representative) == 7, "The Unit::SecondRepresentative, Unit::HourRepresentative and so on might require to be fixed if the TimeRepresentatives table was changed."); - static const Representative constexpr * SecondRepresentative = &TimeRepresentatives[0]; - static const Representative constexpr * MinuteRepresentative = &TimeRepresentatives[1]; - static const Representative constexpr * HourRepresentative = &TimeRepresentatives[2]; - static const Representative constexpr * DayRepresentative = &TimeRepresentatives[3]; - static const Representative constexpr * MonthRepresentative = &TimeRepresentatives[5]; - static const Representative constexpr * YearRepresentative = &TimeRepresentatives[6]; - static const Representative constexpr * MeterRepresentative = &DistanceRepresentatives[0]; - static_assert(sizeof(DistanceRepresentatives)/sizeof(Representative) == 8, "The Unit::MileRepresentative et al. might require to be fixed if the DistanceRepresentatives table was changed."); - static const Representative constexpr * InchRepresentative = &DistanceRepresentatives[4]; - static const Representative constexpr * FootRepresentative = &DistanceRepresentatives[5]; - static const Representative constexpr * YardRepresentative = &DistanceRepresentatives[6]; - static const Representative constexpr * MileRepresentative = &DistanceRepresentatives[7]; - static const Representative constexpr * KilogramRepresentative = &MassRepresentatives[0]; - static const Representative constexpr * GramRepresentative = &MassRepresentatives[1]; - static_assert(sizeof(MassRepresentatives)/sizeof(Representative) == 8, "The Unit::OunceRepresentative et al. might require to be fixed if the MassRepresentatives table was changed."); - static const Representative constexpr * OunceRepresentative = &MassRepresentatives[4]; - static const Representative constexpr * PoundRepresentative = &MassRepresentatives[5]; - static const Representative constexpr * ShortTonRepresentative = &MassRepresentatives[6]; - static const Representative constexpr * LiterRepresentative = &VolumeRepresentatives[0]; - static_assert(sizeof(VolumeRepresentatives)/sizeof(Representative) == 8, "The Unit::FluidOunceRepresentative et al. might require to be fixed if the VolumeRepresentatives table was changed."); - static const Representative constexpr * FluidOunceRepresentative = &VolumeRepresentatives[3]; - static const Representative constexpr * CupRepresentative = &VolumeRepresentatives[4]; - static const Representative constexpr * GallonRepresentative = &VolumeRepresentatives[7]; - static const Representative constexpr * WattRepresentative = &PowerRepresentatives[0]; - static_assert(sizeof(EnergyRepresentatives)/sizeof(Representative) == 2, "The Unit::ElectronVoltRepresentative might require to be fixed if the EnergyRepresentatives table was changed."); - static const Representative constexpr * ElectronVoltRepresentative = &EnergyRepresentatives[1]; - static_assert(sizeof(SurfaceRepresentatives)/sizeof(Representative) == 2, "The Unit::HectareRepresentative et al. might require to be fixed if the VolumeRepresentatives table was changed."); - static const Representative constexpr * HectareRepresentative = &SurfaceRepresentatives[0]; - static const Representative constexpr * AcreRepresentative = &SurfaceRepresentatives[1]; - - static constexpr const Dimension DimensionTable[] = { - /* The current table is sorted from most to least simple units. - * The order determines the behavior of simplification. - */ - Dimension( - Dimension::Vector { - .time = 1, - .distance = 0, - .mass = 0, - .current = 0, - .temperature = 0, - .amountOfSubstance = 0, - .luminuousIntensity = 0, - }, - TimeRepresentatives, - &EmptyPrefix - ), - Dimension( - Dimension::Vector { - .time = 0, - .distance = 1, - .mass = 0, - .current = 0, - .temperature = 0, - .amountOfSubstance = 0, - .luminuousIntensity = 0, - }, - DistanceRepresentatives, - &EmptyPrefix - ), - Dimension( - Dimension::Vector { - .time = 0, - .distance = 0, - .mass = 1, - .current = 0, - .temperature = 0, - .amountOfSubstance = 0, - .luminuousIntensity = 0, - }, - MassRepresentatives, - &EmptyPrefix - ), - Dimension( - Dimension::Vector { - .time = 0, - .distance = 0, - .mass = 0, - .current = 1, - .temperature = 0, - .amountOfSubstance = 0, - .luminuousIntensity = 0, - }, - CurrentRepresentatives, - &EmptyPrefix - ), - Dimension( - Dimension::Vector { - .time = 0, - .distance = 0, - .mass = 0, - .current = 0, - .temperature = 1, - .amountOfSubstance = 0, - .luminuousIntensity = 0, - }, - TemperatureRepresentatives, - &EmptyPrefix - ), - Dimension( - Dimension::Vector { - .time = 0, - .distance = 0, - .mass = 0, - .current = 0, - .temperature = 0, - .amountOfSubstance = 1, - .luminuousIntensity = 0, - }, - AmountOfSubstanceRepresentatives, - &EmptyPrefix - ), - Dimension( - Dimension::Vector { - .time = 0, - .distance = 0, - .mass = 0, - .current = 0, - .temperature = 0, - .amountOfSubstance = 0, - .luminuousIntensity = 1, - }, - LuminousIntensityRepresentatives, - &EmptyPrefix - ), - Dimension( - Dimension::Vector { - .time =-1, - .distance = 0, - .mass = 0, - .current = 0, - .temperature = 0, - .amountOfSubstance = 0, - .luminuousIntensity = 0, - }, - FrequencyRepresentatives, - &EmptyPrefix - ), - Dimension( - Dimension::Vector { - .time =-2, - .distance = 1, - .mass = 1, - .current = 0, - .temperature = 0, - .amountOfSubstance = 0, - .luminuousIntensity = 0, - }, - ForceRepresentatives, - &EmptyPrefix - ), - Dimension( - Dimension::Vector { - .time =-2, - .distance =-1, - .mass = 1, - .current = 0, - .temperature = 0, - .amountOfSubstance = 0, - .luminuousIntensity = 0, - }, - PressureRepresentatives, - &EmptyPrefix - ), - Dimension( - Dimension::Vector { - .time =-2, - .distance = 2, - .mass = 1, - .current = 0, - .temperature = 0, - .amountOfSubstance = 0, - .luminuousIntensity = 0, - }, - EnergyRepresentatives, - &EmptyPrefix - ), - Dimension( - Dimension::Vector { - .time =-3, - .distance = 2, - .mass = 1, - .current = 0, - .temperature = 0, - .amountOfSubstance = 0, - .luminuousIntensity = 0, - }, - PowerRepresentatives, - &EmptyPrefix - ), - Dimension( - Dimension::Vector { - .time = 1, - .distance = 0, - .mass = 0, - .current = 1, - .temperature = 0, - .amountOfSubstance = 0, - .luminuousIntensity = 0, - }, - ElectricChargeRepresentatives, - &EmptyPrefix - ), - Dimension( - Dimension::Vector { - .time =-3, - .distance = 2, - .mass = 1, - .current =-1, - .temperature = 0, - .amountOfSubstance = 0, - .luminuousIntensity = 0, - }, - ElectricPotentialRepresentatives, - &EmptyPrefix - ), - Dimension( - Dimension::Vector { - .time = 4, - .distance =-2, - .mass =-1, - .current = 2, - .temperature = 0, - .amountOfSubstance = 0, - .luminuousIntensity = 0, - }, - ElectricCapacitanceRepresentatives, - &EmptyPrefix - ), - Dimension( - Dimension::Vector { - .time =-3, - .distance = 2, - .mass = 1, - .current =-2, - .temperature = 0, - .amountOfSubstance = 0, - .luminuousIntensity = 0, - }, - ElectricResistanceRepresentatives, - &EmptyPrefix - ), - Dimension( - Dimension::Vector { - .time = 3, - .distance =-2, - .mass =-1, - .current = 2, - .temperature = 0, - .amountOfSubstance = 0, - .luminuousIntensity = 0, - }, - ElectricConductanceRepresentatives, - &EmptyPrefix - ), - Dimension( - Dimension::Vector { - .time =-2, - .distance = 2, - .mass = 1, - .current =-1, - .temperature = 0, - .amountOfSubstance = 0, - .luminuousIntensity = 0, - }, - MagneticFluxRepresentatives, - &EmptyPrefix - ), - Dimension( - Dimension::Vector { - .time =-2, - .distance = 0, - .mass = 1, - .current =-1, - .temperature = 0, - .amountOfSubstance = 0, - .luminuousIntensity = 0, - }, - MagneticFieldRepresentatives, - &EmptyPrefix - ), - Dimension( - Dimension::Vector { - .time =-2, - .distance = 2, - .mass = 1, - .current =-2, - .temperature = 0, - .amountOfSubstance = 0, - .luminuousIntensity = 0, - }, - InductanceRepresentatives, - &EmptyPrefix - ), - Dimension( - Dimension::Vector { - .time =-1, - .distance = 0, - .mass = 0, - .current = 0, - .temperature = 0, - .amountOfSubstance = 1, - .luminuousIntensity = 0, - }, - CatalyticActivityRepresentatives, - &EmptyPrefix - ), - Dimension( - Dimension::Vector { - .time = 0, - .distance = 2, - .mass = 0, - .current = 0, - .temperature = 0, - .amountOfSubstance = 0, - .luminuousIntensity = 0, - }, - SurfaceRepresentatives, - &EmptyPrefix - ), - Dimension( - Dimension::Vector { - .time = 0, - .distance = 3, - .mass = 0, - .current = 0, - .temperature = 0, - .amountOfSubstance = 0, - .luminuousIntensity = 0, - }, - VolumeRepresentatives, - &EmptyPrefix - ), + static constexpr const Prefix k_prefixes[Prefix::k_numberOfPrefixes] = { + Prefix("p", -12), + Prefix("n", -9), + Prefix("μ", -6), + Prefix("m", -3), + Prefix("c", -2), + Prefix("d", -1), + Prefix("", 0), + Prefix("da", 1), + Prefix("h", 2), + Prefix("k", 3), + Prefix("M", 6), + Prefix("G", 9), + Prefix("T", 12), }; - // TODO: find a better way to find defines these pointers - static_assert(sizeof(DimensionTable)/sizeof(Dimension) == 23, "The Unit::TimeDimension, Unit::DistanceDimension and so on might require to be fixed if the Dimension table was changed."); - static const Dimension constexpr * TimeDimension = &DimensionTable[0] ; - static const Dimension constexpr * DistanceDimension = &DimensionTable[1]; - static const Dimension constexpr * MassDimension = &DimensionTable[2]; - static const Dimension constexpr * EnergyDimension = &DimensionTable[10]; - static const Dimension constexpr * PowerDimension = &DimensionTable[11]; - static const Dimension constexpr * SurfaceDimension = &DimensionTable[sizeof(DimensionTable)/sizeof(Dimension)-2]; - static const Dimension constexpr * VolumeDimension = &DimensionTable[sizeof(DimensionTable)/sizeof(Dimension)-1]; + typedef UnitNode::Representative Representative; + typedef Representative::Prefixable Prefixable; + typedef UnitNode::TimeRepresentative TimeRepresentative; + static constexpr const TimeRepresentative k_timeRepresentatives[] = { + TimeRepresentative("s", 1., Prefixable::All, Prefixable::NegativeLongScale), + TimeRepresentative("min", 60., Prefixable::None, Prefixable::None), + TimeRepresentative("h", 3600., Prefixable::None, Prefixable::None), + TimeRepresentative("day", 24.*3600., Prefixable::None, Prefixable::None), + TimeRepresentative("week", 7.*24.*3600., Prefixable::None, Prefixable::None), + TimeRepresentative("month", 365.25/12.*24.*3600., Prefixable::None, Prefixable::None), + TimeRepresentative("year", 365.25*24.*3600., Prefixable::None, Prefixable::None), + }; + typedef UnitNode::DistanceRepresentative DistanceRepresentative; + static constexpr const DistanceRepresentative k_distanceRepresentatives[] = { + DistanceRepresentative("m", 1., Prefixable::All, Prefixable::NegativeAndKilo), + DistanceRepresentative("au", 149597870700., Prefixable::None, Prefixable::None), + DistanceRepresentative("ly", 299792458.*365.25*24.*3600., Prefixable::None, Prefixable::None), + DistanceRepresentative("pc", 180.*3600./M_PI*149587870700., Prefixable::None, Prefixable::None), + DistanceRepresentative("in", 0.0254, Prefixable::None, Prefixable::None), + DistanceRepresentative("ft", 12*0.0254, Prefixable::None, Prefixable::None), + DistanceRepresentative("yd", 3*12*0.0254, Prefixable::None, Prefixable::None), + DistanceRepresentative("mi", 1760*3*12*0.0254, Prefixable::None, Prefixable::None), + }; + typedef UnitNode::MassRepresentative MassRepresentative; + static constexpr const MassRepresentative k_massRepresentatives[] = { + MassRepresentative("g", 1., Prefixable::All, Prefixable::NegativeAndKilo), + MassRepresentative("t", 1e3, Prefixable::PositiveLongScale, Prefixable::PositiveLongScale), + MassRepresentative("Da", 1/(6.02214076e26), Prefixable::All, Prefixable::All), + MassRepresentative("oz", 0.028349523125, Prefixable::None, Prefixable::None), + MassRepresentative("lb", 16*0.028349523125, Prefixable::None, Prefixable::None), + MassRepresentative("shtn", 2000*16*0.028349523125, Prefixable::None, Prefixable::None), + MassRepresentative("lgtn", 2240*16*0.028349523125, Prefixable::None, Prefixable::None), + }; + typedef UnitNode::CurrentRepresentative CurrentRepresentative; + static constexpr const CurrentRepresentative k_currentRepresentatives[] = { CurrentRepresentative("A", 1., Prefixable::All, Prefixable::LongScale) }; + typedef UnitNode::TemperatureRepresentative TemperatureRepresentative; + static constexpr const TemperatureRepresentative k_temperatureRepresentatives[] = { TemperatureRepresentative("K", 1., Prefixable::All, Prefixable::LongScale) }; + typedef UnitNode::AmountOfSubstanceRepresentative AmountOfSubstanceRepresentative; + static constexpr const AmountOfSubstanceRepresentative k_amountOfSubstanceRepresentatives[] = { AmountOfSubstanceRepresentative("mol", 1., Prefixable::All, Prefixable::LongScale) }; + typedef UnitNode::LuminousIntensityRepresentative LuminousIntensityRepresentative; + static constexpr const LuminousIntensityRepresentative k_luminousIntensityRepresentatives[] = { LuminousIntensityRepresentative("cd", 1., Prefixable::All, Prefixable::LongScale) }; + typedef UnitNode::FrequencyRepresentative FrequencyRepresentative; + static constexpr const FrequencyRepresentative k_frequencyRepresentatives[] = { FrequencyRepresentative("Hz", 1., Prefixable::All, Prefixable::LongScale) }; + typedef UnitNode::ForceRepresentative ForceRepresentative; + static constexpr const ForceRepresentative k_forceRepresentatives[] = { ForceRepresentative("N", 1., Prefixable::All, Prefixable::LongScale) }; + typedef UnitNode::PressureRepresentative PressureRepresentative; + static constexpr const PressureRepresentative k_pressureRepresentatives[] = { + PressureRepresentative("Pa", 1., Prefixable::All, Prefixable::LongScale), + PressureRepresentative("bar", 100000, Prefixable::All, Prefixable::LongScale), + PressureRepresentative("atm", 101325, Prefixable::None, Prefixable::None), + }; + typedef UnitNode::EnergyRepresentative EnergyRepresentative; + static constexpr const EnergyRepresentative k_energyRepresentatives[] = { + EnergyRepresentative("J", 1., Prefixable::All, Prefixable::LongScale), + EnergyRepresentative("eV", 1.602176634e-19, Prefixable::All, Prefixable::LongScale), + }; + typedef UnitNode::PowerRepresentative PowerRepresentative; + static constexpr const PowerRepresentative k_powerRepresentatives[] = { PowerRepresentative("W", 1., Prefixable::All, Prefixable::LongScale) }; + typedef UnitNode::ElectricChargeRepresentative ElectricChargeRepresentative; + static constexpr const ElectricChargeRepresentative k_electricChargeRepresentatives[] = { ElectricChargeRepresentative("C", 1., Prefixable::All, Prefixable::LongScale) }; + typedef UnitNode::ElectricPotentialRepresentative ElectricPotentialRepresentative; + static constexpr const ElectricPotentialRepresentative k_electricPotentialRepresentatives[] = { ElectricPotentialRepresentative("V", 1., Prefixable::All, Prefixable::LongScale) }; + typedef UnitNode::ElectricCapacitanceRepresentative ElectricCapacitanceRepresentative; + static constexpr const ElectricCapacitanceRepresentative k_electricCapacitanceRepresentatives[] = { ElectricCapacitanceRepresentative("F", 1., Prefixable::All, Prefixable::LongScale) }; + typedef UnitNode::ElectricResistanceRepresentative ElectricResistanceRepresentative; + static constexpr const ElectricResistanceRepresentative k_electricResistanceRepresentatives[] = { ElectricResistanceRepresentative("Ω", 1., Prefixable::All, Prefixable::LongScale) }; + typedef UnitNode::ElectricConductanceRepresentative ElectricConductanceRepresentative; + static constexpr const ElectricConductanceRepresentative k_electricConductanceRepresentatives[] = { ElectricConductanceRepresentative("S", 1., Prefixable::All, Prefixable::LongScale) }; + typedef UnitNode::MagneticFluxRepresentative MagneticFluxRepresentative; + static constexpr const MagneticFluxRepresentative k_magneticFluxRepresentatives[] = { MagneticFluxRepresentative("Wb", 1., Prefixable::All, Prefixable::LongScale) }; + typedef UnitNode::MagneticFieldRepresentative MagneticFieldRepresentative; + static constexpr const MagneticFieldRepresentative k_magneticFieldRepresentatives[] = { MagneticFieldRepresentative("T", 1., Prefixable::All, Prefixable::LongScale) }; + typedef UnitNode::InductanceRepresentative InductanceRepresentative; + static constexpr const InductanceRepresentative k_inductanceRepresentatives[] = { InductanceRepresentative("H", 1., Prefixable::All, Prefixable::LongScale) }; + typedef UnitNode::CatalyticActivityRepresentative CatalyticActivityRepresentative; + static constexpr const CatalyticActivityRepresentative k_catalyticActivityRepresentatives[] = { CatalyticActivityRepresentative("kat", 1., Prefixable::All, Prefixable::LongScale) }; + typedef UnitNode::SurfaceRepresentative SurfaceRepresentative; + static constexpr const SurfaceRepresentative k_surfaceRepresentatives[] = { + SurfaceRepresentative("ha", 10000., Prefixable::None, Prefixable::None), + SurfaceRepresentative("acre", 4046.8564224, Prefixable::None, Prefixable::None), + }; + typedef UnitNode::VolumeRepresentative VolumeRepresentative; + static constexpr const VolumeRepresentative k_volumeRepresentatives[] = { + VolumeRepresentative("L", 0.001, Prefixable::All, Prefixable::Negative), + VolumeRepresentative("tsp", 0.00000492892159375, Prefixable::None, Prefixable::None), + VolumeRepresentative("Tbsp", 3*0.00000492892159375, Prefixable::None, Prefixable::None), + VolumeRepresentative("floz", 0.0000295735295625, Prefixable::None, Prefixable::None), + VolumeRepresentative("cp", 8*0.0000295735295625, Prefixable::None, Prefixable::None), + VolumeRepresentative("pt", 16*0.0000295735295625, Prefixable::None, Prefixable::None), + VolumeRepresentative("qt", 32*0.0000295735295625, Prefixable::None, Prefixable::None), + VolumeRepresentative("gal", 128*0.0000295735295625, Prefixable::None, Prefixable::None), + }; + /* FIXME : Some ratio are too precise too be well approximated by double. + * Maybe switch to a rationnal representation with two int. */ - static constexpr const Unit::Dimension * DimensionTableUpperBound = - DimensionTable + sizeof(DimensionTable)/sizeof(Dimension); - static bool CanParse(const char * symbol, size_t length, - const Dimension * * dimension, const Representative * * representative, const Prefix * * prefix); + /* Define access points to some prefixes and representatives. */ + static constexpr int k_emptyPrefixIndex = 6; + static_assert(k_prefixes[k_emptyPrefixIndex].m_exponent == 0, "Index for the Empty Prefix is incorrect."); + static constexpr int k_kiloPrefixIndex = 9; + static_assert(k_prefixes[k_kiloPrefixIndex].m_exponent == 3, "Index for the Kilo Prefix is incorrect."); + static constexpr int k_secondRepresentativeIndex = 0; + static_assert(strings_equal(k_timeRepresentatives[k_secondRepresentativeIndex].m_rootSymbol, "s"), "Index for the Second Representative is incorrect."); + static constexpr int k_minuteRepresentativeIndex = 1; + static_assert(strings_equal(k_timeRepresentatives[k_minuteRepresentativeIndex].m_rootSymbol, "min"), "Index for the Minute Representative is incorrect."); + static constexpr int k_hourRepresentativeIndex = 2; + static_assert(strings_equal(k_timeRepresentatives[k_hourRepresentativeIndex].m_rootSymbol, "h"), "Index for the Hour Representative is incorrect."); + static constexpr int k_dayRepresentativeIndex = 3; + static_assert(strings_equal(k_timeRepresentatives[k_dayRepresentativeIndex].m_rootSymbol, "day"), "Index for the Day Representative is incorrect."); + static constexpr int k_monthRepresentativeIndex = 5; + static_assert(strings_equal(k_timeRepresentatives[k_monthRepresentativeIndex].m_rootSymbol, "month"), "Index for the Month Representative is incorrect."); + static constexpr int k_yearRepresentativeIndex = 6; + static_assert(strings_equal(k_timeRepresentatives[k_yearRepresentativeIndex].m_rootSymbol, "year"), "Index for the Year Representative is incorrect."); + static constexpr int k_meterRepresentativeIndex = 0; + static_assert(strings_equal(k_distanceRepresentatives[k_meterRepresentativeIndex].m_rootSymbol, "m"), "Index for the Meter Representative is incorrect."); + static constexpr int k_inchRepresentativeIndex = 4; + static_assert(strings_equal(k_distanceRepresentatives[k_inchRepresentativeIndex].m_rootSymbol, "in"), "Index for the Inch Representative is incorrect."); + static constexpr int k_footRepresentativeIndex = 5; + static_assert(strings_equal(k_distanceRepresentatives[k_footRepresentativeIndex].m_rootSymbol, "ft"), "Index for the Foot Representative is incorrect."); + static constexpr int k_yardRepresentativeIndex = 6; + static_assert(strings_equal(k_distanceRepresentatives[k_yardRepresentativeIndex].m_rootSymbol, "yd"), "Index for the Yard Representative is incorrect."); + static constexpr int k_mileRepresentativeIndex = 7; + static_assert(strings_equal(k_distanceRepresentatives[k_mileRepresentativeIndex].m_rootSymbol, "mi"), "Index for the Mile Representative is incorrect."); + static constexpr int k_ounceRepresentativeIndex = 3; + static_assert(strings_equal(k_massRepresentatives[k_ounceRepresentativeIndex].m_rootSymbol, "oz"), "Index for the Ounce Representative is incorrect."); + static constexpr int k_poundRepresentativeIndex = 4; + static_assert(strings_equal(k_massRepresentatives[k_poundRepresentativeIndex].m_rootSymbol, "lb"), "Index for the Pound Representative is incorrect."); + static constexpr int k_shortTonRepresentativeIndex = 5; + static_assert(strings_equal(k_massRepresentatives[k_shortTonRepresentativeIndex].m_rootSymbol, "shtn"), "Index for the Short Ton Representative is incorrect."); + static constexpr int k_electronVoltRepresentativeIndex = 1; + static_assert(strings_equal(k_energyRepresentatives[k_electronVoltRepresentativeIndex].m_rootSymbol, "eV"), "Index for the Electron Volt Representative is incorrect."); + static constexpr int k_wattRepresentativeIndex = 0; + static_assert(strings_equal(k_powerRepresentatives[k_wattRepresentativeIndex].m_rootSymbol, "W"), "Index for the Watt Representative is incorrect."); + static constexpr int k_hectareRepresentativeIndex = 0; + static_assert(strings_equal(k_surfaceRepresentatives[k_hectareRepresentativeIndex].m_rootSymbol, "ha"), "Index for the Hectare Representative is incorrect."); + static constexpr int k_acreRepresentativeIndex = 1; + static_assert(strings_equal(k_surfaceRepresentatives[k_acreRepresentativeIndex].m_rootSymbol, "acre"), "Index for the Acre Representative is incorrect."); + static constexpr int k_literRepresentativeIndex = 0; + static_assert(strings_equal(k_volumeRepresentatives[k_literRepresentativeIndex].m_rootSymbol, "L"), "Index for the Liter Representative is incorrect."); + static constexpr int k_fluidOunceRepresentativeIndex = 3; + static_assert(strings_equal(k_volumeRepresentatives[k_fluidOunceRepresentativeIndex].m_rootSymbol, "floz"), "Index for the Fluid Ounce Representative is incorrect."); + static constexpr int k_cupRepresentativeIndex = 4; + static_assert(strings_equal(k_volumeRepresentatives[k_cupRepresentativeIndex].m_rootSymbol, "cp"), "Index for the Cup Representative is incorrect."); + static constexpr int k_gallonRepresentativeIndex = 7; + static_assert(strings_equal(k_volumeRepresentatives[k_gallonRepresentativeIndex].m_rootSymbol, "gal"), "Index for the Gallon Representative is incorrect."); Unit(const UnitNode * node) : Expression(node) {} - static Unit Builder(const Dimension * dimension, const Representative * representative, const Prefix * prefix); - static Unit Meter() { return Builder(DistanceDimension, MeterRepresentative, &EmptyPrefix); } - static Unit Kilometer() { return Builder(DistanceDimension, MeterRepresentative, &KiloPrefix); } - static Unit Inch() { return Builder(DistanceDimension, InchRepresentative, &EmptyPrefix); } - static Unit Foot() { return Builder(DistanceDimension, FootRepresentative, &EmptyPrefix); } - static Unit Yard() { return Builder(DistanceDimension, YardRepresentative, &EmptyPrefix); } - static Unit Mile() { return Builder(DistanceDimension, MileRepresentative, &EmptyPrefix); } - static Unit Second() { return Builder(TimeDimension, SecondRepresentative, &EmptyPrefix); } - static Unit Minute() { return Builder(TimeDimension, MinuteRepresentative, &EmptyPrefix); } - static Unit Hour() { return Builder(TimeDimension, HourRepresentative, &EmptyPrefix); } - static Unit Day() { return Builder(TimeDimension, DayRepresentative, &EmptyPrefix); } - static Unit Month() { return Builder(TimeDimension, MonthRepresentative, &EmptyPrefix); } - static Unit Year() { return Builder(TimeDimension, YearRepresentative, &EmptyPrefix); } - static Unit Liter() { return Builder(VolumeDimension, LiterRepresentative, &EmptyPrefix); } - static Unit ElectronVolt() { return Builder(EnergyDimension, ElectronVoltRepresentative, &EmptyPrefix); } - static Unit Watt() { return Builder(PowerDimension, WattRepresentative, &EmptyPrefix); } - static Unit Gram() { return Builder(MassDimension, GramRepresentative, &EmptyPrefix); } - static Unit Ounce() { return Builder(MassDimension, OunceRepresentative, &EmptyPrefix); } - static Unit Pound() { return Builder(MassDimension, PoundRepresentative, &EmptyPrefix); } - static Unit ShortTon() { return Builder(MassDimension, ShortTonRepresentative, &EmptyPrefix); } - static Unit FluidOunce() { return Builder(VolumeDimension, FluidOunceRepresentative, &EmptyPrefix); } - static Unit Cup() { return Builder(VolumeDimension, CupRepresentative, &EmptyPrefix); } - static Unit Gallon() { return Builder(VolumeDimension, GallonRepresentative, &EmptyPrefix); } - static Unit Hectare() { return Builder(SurfaceDimension, HectareRepresentative, &EmptyPrefix); } - static Unit Acre() { return Builder(SurfaceDimension, AcreRepresentative, &EmptyPrefix); } - static Expression BuildSplit(double baseValue, const Unit * units, const double * conversionFactors, int numberOfUnits, Context * context); - static Expression BuildTimeSplit(double seconds, Context * context); - static Expression BuildImperialDistanceSplit(double inches, Context * context); - static Expression BuildImperialMassSplit(double ounces, Context * context); - static Expression BuildImperialVolumeSplit(double fluidOunces, Context * context); - static double ConvertedValueInUnit(Expression e, Unit unit, ExpressionNode::ReductionContext reductionContext); - - static bool IsSI(Expression & e); - static bool IsSISpeed(Expression & e); - static bool IsSIVolume(Expression & e); - static bool IsSIEnergy(Expression & e); - static bool IsSITime(Expression & e); - static bool IsSIDistance(Expression & e); - static bool IsSIMass(Expression & e); - static bool IsSISurface(Expression & e); - bool isMeter() const; - bool isSecond() const; - bool isKilogram() const; - - static Expression StandardSpeedConversion(Expression e, ExpressionNode::ReductionContext reductionContext); - static Expression StandardDistanceConversion(Expression e, ExpressionNode::ReductionContext reductionContext); - static Expression StandardVolumeConversion(Expression e, ExpressionNode::ReductionContext reductionContext); - static Expression StandardMassConversion(Expression e, ExpressionNode::ReductionContext reductionContext); - static Expression StandardSurfaceConversion(Expression e, ExpressionNode::ReductionContext reductionContext); + static Unit Builder(const Representative * representative, const Prefix * prefix); + static bool CanParse(const char * symbol, size_t length, const Representative * * representative, const Prefix * * prefix); + static void ChooseBestRepresentativeAndPrefixForValue(Expression units, double * value, ExpressionNode::ReductionContext reductionContext); + static bool ShouldDisplayAdditionalOutputs(double value, Expression unit); + static int SetAdditionalExpressions(Expression units, double value, Expression * dest, int availableLength, ExpressionNode::ReductionContext reductionContext); + static Expression BuildSplit(double value, const Unit * units, int length, ExpressionNode::ReductionContext reductionContext); // Simplification Expression shallowReduce(ExpressionNode::ReductionContext reductionContext); Expression shallowBeautify(ExpressionNode::ReductionContext reductionContext); - static void ChooseBestRepresentativeAndPrefixForValue(Expression * units, double * value, ExpressionNode::ReductionContext reductionContext) { return ChooseBestMultipleForValue(units, value, true, reductionContext); } - static void ChooseBestPrefixForValue(Expression * units, double * value, ExpressionNode::ReductionContext reductionContext) { return ChooseBestMultipleForValue(units, value, false, reductionContext); } - // This could be computed from the time representatives but we save time by using constexpr double - static constexpr double SecondsPerMinute = 60.0; + bool isBaseUnit() const { return node()->representative()->isBaseUnit() && node()->prefix() == node()->representative()->basePrefix(); } + void chooseBestRepresentativeAndPrefix(double * value, double exponent, ExpressionNode::ReductionContext reductionContext, bool optimizePrefix); + private: - static constexpr double MinutesPerHour = 60.0; - static constexpr double HoursPerDay = 24.0; - static constexpr double DaysPerYear = 365.25; - static constexpr double MonthPerYear = 12.0; - static constexpr double DaysPerMonth = DaysPerYear/MonthPerYear; - static constexpr double InchesPerFoot = 12.; - static constexpr double FeetPerYard = 3.; - static constexpr double YardsPerMile = 1760.; - static constexpr double OuncesPerPound = 16.; - static constexpr double PoundsPerShortTon = 2000.; - static constexpr double FluidOuncesPerCup = 8.; - static constexpr double CupsPerGallon = 16.; UnitNode * node() const { return static_cast(Expression::node()); } - bool isSI() const; - static void ChooseBestMultipleForValue(Expression * units, double * value, bool tuneRepresentative, ExpressionNode::ReductionContext reductionContext); - void chooseBestMultipleForValue(double * value, const float exponent, bool tuneRepresentative, ExpressionNode::ReductionContext reductionContext); Expression removeUnit(Expression * unit); }; diff --git a/poincare/src/multiplication.cpp b/poincare/src/multiplication.cpp index bf518c2ce..aadd50c0c 100644 --- a/poincare/src/multiplication.cpp +++ b/poincare/src/multiplication.cpp @@ -322,14 +322,14 @@ Expression Multiplication::shallowReduce(ExpressionNode::ReductionContext reduct } static bool CanSimplifyUnitProduct( - 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) { + const UnitNode::Vector &unitsExponents, size_t &unitsSupportSize, + const UnitNode::Vector * entryUnitExponents, int entryUnitExponent, + int8_t &bestUnitExponent, UnitNode::Vector &bestRemainderExponents, size_t &bestRemainderSupportSize) { /* This function tries to simplify a Unit product (given as 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; + UnitNode::Vector simplifiedExponents; #if 0 /* In the current algorithm, simplification is attempted using derived units @@ -369,7 +369,7 @@ static bool CanSimplifyUnitProduct( n -= step; #endif - for (size_t i = 0; i < Unit::NumberOfBaseUnits; i++) { + for (size_t i = 0; i < UnitNode::k_numberOfBaseUnits; i++) { #if 0 // Undo last step as it did not reduce the norm simplifiedExponents.setCoefficientAtIndex(i, simplifiedExponents.coefficientAtIndex(i) + entryUnitExponent * step * entryUnitExponents->coefficientAtIndex(i)); @@ -444,26 +444,27 @@ 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); + UnitNode::Vector unitsExponents = UnitNode::Vector::FromBaseUnits(units); size_t unitsSupportSize = unitsExponents.supportSize(); - Unit::Dimension::Vector bestRemainderExponents; + UnitNode::Vector bestRemainderExponents; size_t bestRemainderSupportSize; while (unitsSupportSize > 1) { - const Unit::Dimension * bestDim = nullptr; + const UnitNode::Representative * 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(); + for (int i = UnitNode::k_numberOfBaseUnits; i < UnitNode::Representative::k_numberOfDimensions - 1; i++) { + const UnitNode::Representative * dim = UnitNode::Representative::DefaultRepresentatives()[i]; + const UnitNode::Vector entryUnitExponents = dim->dimensionVector(); // A simplification is tried by either multiplying or dividing if (CanSimplifyUnitProduct( unitsExponents, unitsSupportSize, - entryUnitExponents, 1, + &entryUnitExponents, 1, bestUnitExponent, bestRemainderExponents, bestRemainderSupportSize ) || CanSimplifyUnitProduct( unitsExponents, unitsSupportSize, - entryUnitExponents, -1, + &entryUnitExponents, -1, bestUnitExponent, bestRemainderExponents, bestRemainderSupportSize )) { @@ -477,7 +478,7 @@ Expression Multiplication::shallowBeautify(ExpressionNode::ReductionContext redu break; } // Build and add the best derived unit - Expression derivedUnit = Unit::Builder(bestDim, bestDim->stdRepresentative(), bestDim->stdRepresentativePrefix()); + Expression derivedUnit = Unit::Builder(bestDim->representativesOfSameDimension(), bestDim->basePrefix()); #if 0 if (bestUnitExponent != 1) { @@ -533,7 +534,7 @@ Expression Multiplication::shallowBeautify(ExpressionNode::ReductionContext redu } else { if (unitConversionMode == ExpressionNode::UnitConversion::Default) { // Find the right unit prefix - Unit::ChooseBestRepresentativeAndPrefixForValue(&units, &value, reductionContext); + Unit::ChooseBestRepresentativeAndPrefixForValue(units, &value, reductionContext); } // Build final Expression result = Multiplication::Builder(Number::FloatNumber(value), units); diff --git a/poincare/src/parsing/parser.cpp b/poincare/src/parsing/parser.cpp index 782e41757..d20b3960b 100644 --- a/poincare/src/parsing/parser.cpp +++ b/poincare/src/parsing/parser.cpp @@ -347,13 +347,11 @@ void Parser::parseConstant(Expression & leftHandSide, Token::Type stoppingType) void Parser::parseUnit(Expression & leftHandSide, Token::Type stoppingType) { assert(leftHandSide.isUninitialized()); - const Unit::Dimension * unitDimension = nullptr; const Unit::Representative * unitRepresentative = nullptr; - const Unit::Prefix * unitPrefix = nullptr; leftHandSide = Constant::Builder(m_currentToken.codePoint()); - if (Unit::CanParse(m_currentToken.text(), m_currentToken.length(), - &unitDimension, &unitRepresentative, &unitPrefix)) - { - leftHandSide = Unit::Builder(unitDimension, unitRepresentative, unitPrefix); + const Unit::Prefix * unitPrefix = nullptr; + leftHandSide = Constant::Builder(m_currentToken.codePoint()); + if (Unit::CanParse(m_currentToken.text(), m_currentToken.length(), &unitRepresentative, &unitPrefix)) { + leftHandSide = Unit::Builder(unitRepresentative, unitPrefix); } else { m_status = Status::Error; // Unit does not exist return; diff --git a/poincare/src/unit.cpp b/poincare/src/unit.cpp index 12fc5b77f..4312288e8 100644 --- a/poincare/src/unit.cpp +++ b/poincare/src/unit.cpp @@ -1,120 +1,83 @@ #include #include -#include #include -#include -#include #include #include #include #include -#include #include #include -#include -#include #include -#include #include namespace Poincare { -int UnitNode::Prefix::serialize(char * buffer, int bufferSize) const { - assert(bufferSize >= 0); - return std::min(strlcpy(buffer, m_symbol, bufferSize), bufferSize - 1); +constexpr const UnitNode::Prefix Unit::k_prefixes[]; +constexpr const UnitNode::TimeRepresentative Unit::k_timeRepresentatives[]; +constexpr const UnitNode::DistanceRepresentative Unit::k_distanceRepresentatives[]; +constexpr const UnitNode::MassRepresentative Unit::k_massRepresentatives[]; +constexpr const UnitNode::CurrentRepresentative Unit::k_currentRepresentatives[]; +constexpr const UnitNode::TemperatureRepresentative Unit::k_temperatureRepresentatives[]; +constexpr const UnitNode::AmountOfSubstanceRepresentative Unit::k_amountOfSubstanceRepresentatives[]; +constexpr const UnitNode::LuminousIntensityRepresentative Unit::k_luminousIntensityRepresentatives[]; +constexpr const UnitNode::FrequencyRepresentative Unit::k_frequencyRepresentatives[]; +constexpr const UnitNode::ForceRepresentative Unit::k_forceRepresentatives[]; +constexpr const UnitNode::PressureRepresentative Unit::k_pressureRepresentatives[]; +constexpr const UnitNode::EnergyRepresentative Unit::k_energyRepresentatives[]; +constexpr const UnitNode::PowerRepresentative Unit::k_powerRepresentatives[]; +constexpr const UnitNode::ElectricChargeRepresentative Unit::k_electricChargeRepresentatives[]; +constexpr const UnitNode::ElectricPotentialRepresentative Unit::k_electricPotentialRepresentatives[]; +constexpr const UnitNode::ElectricCapacitanceRepresentative Unit::k_electricCapacitanceRepresentatives[]; +constexpr const UnitNode::ElectricResistanceRepresentative Unit::k_electricResistanceRepresentatives[]; +constexpr const UnitNode::ElectricConductanceRepresentative Unit::k_electricConductanceRepresentatives[]; +constexpr const UnitNode::MagneticFluxRepresentative Unit::k_magneticFluxRepresentatives[]; +constexpr const UnitNode::MagneticFieldRepresentative Unit::k_magneticFieldRepresentatives[]; +constexpr const UnitNode::InductanceRepresentative Unit::k_inductanceRepresentatives[]; +constexpr const UnitNode::CatalyticActivityRepresentative Unit::k_catalyticActivityRepresentatives[]; +constexpr const UnitNode::SurfaceRepresentative Unit::k_surfaceRepresentatives[]; +constexpr const UnitNode::VolumeRepresentative Unit::k_volumeRepresentatives[]; + +constexpr const int + Unit::k_emptyPrefixIndex, + Unit::k_kiloPrefixIndex, + Unit::k_secondRepresentativeIndex, + Unit::k_minuteRepresentativeIndex, + Unit::k_hourRepresentativeIndex, + Unit::k_dayRepresentativeIndex, + Unit::k_monthRepresentativeIndex, + Unit::k_yearRepresentativeIndex, + Unit::k_meterRepresentativeIndex, + Unit::k_inchRepresentativeIndex, + Unit::k_footRepresentativeIndex, + Unit::k_yardRepresentativeIndex, + Unit::k_mileRepresentativeIndex, + Unit::k_ounceRepresentativeIndex, + Unit::k_poundRepresentativeIndex, + Unit::k_shortTonRepresentativeIndex, + Unit::k_electronVoltRepresentativeIndex, + Unit::k_wattRepresentativeIndex, + Unit::k_hectareRepresentativeIndex, + Unit::k_acreRepresentativeIndex, + Unit::k_literRepresentativeIndex, + Unit::k_fluidOunceRepresentativeIndex, + Unit::k_cupRepresentativeIndex, + Unit::k_gallonRepresentativeIndex; + +// UnitNode::Prefix +const UnitNode::Prefix * UnitNode::Prefix::Prefixes() { + return Unit::k_prefixes; } -bool UnitNode::Representative::canParse(const char * symbol, size_t length, - const Prefix * * prefix) const -{ - if (!isPrefixable()) { - *prefix = &Unit::EmptyPrefix; - return length == 0; - } - const Prefix * const * prefixesList = (m_prefixable == Prefixable::PositiveOnly) ? Unit::PositiveLongScalePrefixes : Unit::AllPrefixes; - size_t numberOfPrefixes = ((m_prefixable == Prefixable::PositiveOnly) ? sizeof(Unit::PositiveLongScalePrefixes) : sizeof(Unit::AllPrefixes))/sizeof(Unit::Prefix *); - for (size_t i = 0; i < numberOfPrefixes; i++) { - const Prefix * pre = prefixesList[i]; - const char * prefixSymbol = pre->symbol(); - if (strncmp(symbol, prefixSymbol, length) == 0 && - prefixSymbol[length] == 0) - { - *prefix = pre; - return true; - } - pre++; - } - return false; -} - -int UnitNode::Representative::serialize(char * buffer, int bufferSize, const Prefix * prefix) const { - int length = 0; - length += prefix->serialize(buffer, bufferSize); - assert(length == 0 || isPrefixable()); - assert(length < bufferSize); - buffer += length; - bufferSize -= length; - assert(bufferSize >= 0); - length += std::min(strlcpy(buffer, m_rootSymbol, bufferSize), bufferSize - 1); - return length; -} - -static bool compareMagnitudeOrders(float order, float otherOrder) { - /* Precision can be lost (with a year conversion for instance), so the order - * value is rounded */ - if (std::fabs(order) < Expression::Epsilon()) { - order = 0.0f; - } - if (std::fabs(otherOrder) < Expression::Epsilon()) { - otherOrder = 0.0f; - } - if (std::fabs(std::fabs(order) - std::fabs(otherOrder)) < 3.0f && order * otherOrder < 0.0f) { - /* If the two values are close, and their sign are opposed, the positive - * order is preferred */ - return (order >= 0.0f); - } - // Otherwise, the closest order to 0 is preferred - return (std::fabs(order) < std::fabs(otherOrder)); -} - -const UnitNode::Prefix * UnitNode::Representative::bestPrefixForValue(double & value, const float exponent) const { - if (!isPrefixable()) { - return &Unit::EmptyPrefix; - } - float bestOrder; - const Prefix * bestPre = nullptr; - /* Find the 'Prefix' with the most adequate 'exponent' for the order of - * magnitude of 'value'. - */ - const float orderOfMagnitude = std::log10(std::fabs(value)); - for (size_t i = 0; i < m_outputPrefixesLength; i++) { - const Prefix * pre = m_outputPrefixes[i]; - float order = orderOfMagnitude - pre->exponent() * exponent; - if (bestPre == nullptr || compareMagnitudeOrders(order, bestOrder)) { - bestOrder = order; - bestPre = pre; - } - } - value *= std::pow(10.0, -bestPre->exponent() * exponent); - return bestPre; -} - -bool UnitNode::Representative::canOutputInSystem(Preferences::UnitFormat system) const { - if (m_outputSystem == OutputSystem::None) { - return false; - } - if (m_outputSystem == OutputSystem::All) { - return true; - } - return (system == Preferences::UnitFormat::Metric) == (m_outputSystem == OutputSystem::Metric); +const UnitNode::Prefix * UnitNode::Prefix::EmptyPrefix() { + return Prefixes() + Unit::k_emptyPrefixIndex; } +// UnitNode::Vector template<> -size_t UnitNode::Dimension::Vector::supportSize() const { +size_t UnitNode::Vector::supportSize() const { size_t supportSize = 0; - for (const int * i = reinterpret_cast(this); i < reinterpret_cast(this) + NumberOfBaseUnits; i++) { - int coefficient = *i; - if (coefficient == 0) { + for (int i = 0; i < k_numberOfBaseUnits; i++) { + if (coefficientAtIndex(i) == 0) { continue; } supportSize++; @@ -123,7 +86,14 @@ size_t UnitNode::Dimension::Vector::supportSize() const { } template<> -Unit::Dimension::Vector UnitNode::Dimension::Vector::FromBaseUnits(const Expression baseUnits) { +void UnitNode::Vector::addAllCoefficients(const Vector other, int factor) { + for (int i = 0; i < UnitNode::k_numberOfBaseUnits; i++) { + setCoefficientAtIndex(i, coefficientAtIndex(i) + other.coefficientAtIndex(i) * factor); + } +} + +template<> +UnitNode::Vector UnitNode::Vector::FromBaseUnits(const Expression baseUnits) { /* Returns the vector of Base units with integer exponents. If rational, the * closest integer will be used. */ Vector vector = { @@ -169,9 +139,7 @@ Unit::Dimension::Vector UnitNode::Dimension::Vector::FromBaseUnits(con } // Fill the vector with the unit's exponent assert(factor.type() == ExpressionNode::Type::Unit); - const ptrdiff_t indexInTable = static_cast(factor.node())->dimension() - Unit::DimensionTable; - assert(0 <= indexInTable && indexInTable < NumberOfBaseUnits); - vector.setCoefficientAtIndex(indexInTable, exponent); + vector.addAllCoefficients(static_cast(factor).node()->representative()->dimensionVector(), exponent); if (++factorIndex >= numberOfFactors) { break; } @@ -180,26 +148,424 @@ Unit::Dimension::Vector UnitNode::Dimension::Vector::FromBaseUnits(con return vector; } -bool UnitNode::Dimension::canParse(const char * symbol, size_t length, - const Representative * * representative, const Prefix * * prefix) const -{ - const Representative * rep = m_representatives; - while (rep < m_representativesUpperBound) { - const char * rootSymbol = rep->rootSymbol(); +template<> +Expression UnitNode::Vector::toBaseUnits() const { + Expression result = Multiplication::Builder(); + int numberOfChildren = 0; + for (int i = 0; i < k_numberOfBaseUnits; i++) { + // We require the base units to be the first seven in DefaultRepresentatives + const Representative * representative = Representative::DefaultRepresentatives()[i]; + assert(representative); + const Prefix * prefix = representative->basePrefix(); + int exponent = coefficientAtIndex(i); + Expression e; + if (exponent == 0) { + continue; + } + if (exponent == 1) { + e = Unit::Builder(representative, prefix); + } else { + e = Power::Builder(Unit::Builder(representative, prefix), Rational::Builder(exponent)); + } + static_cast(result).addChildAtIndexInPlace(e, numberOfChildren, numberOfChildren); + numberOfChildren++; + } + assert(numberOfChildren > 0); + result = static_cast(result).squashUnaryHierarchyInPlace(); + return result; +} + +// UnitNode::Representative +const UnitNode::Representative * const * UnitNode::Representative::DefaultRepresentatives() { + static constexpr SpeedRepresentative defaultSpeedRepresentative = SpeedRepresentative::Default(); + static constexpr const Representative * defaultRepresentatives[k_numberOfDimensions] = { + Unit::k_timeRepresentatives, + Unit::k_distanceRepresentatives, + Unit::k_massRepresentatives, + Unit::k_currentRepresentatives, + Unit::k_temperatureRepresentatives, + Unit::k_amountOfSubstanceRepresentatives, + Unit::k_luminousIntensityRepresentatives, + Unit::k_frequencyRepresentatives, + Unit::k_forceRepresentatives, + Unit::k_pressureRepresentatives, + Unit::k_energyRepresentatives, + Unit::k_powerRepresentatives, + Unit::k_electricChargeRepresentatives, + Unit::k_electricPotentialRepresentatives, + Unit::k_electricCapacitanceRepresentatives, + Unit::k_electricResistanceRepresentatives, + Unit::k_electricConductanceRepresentatives, + Unit::k_magneticFluxRepresentatives, + Unit::k_magneticFieldRepresentatives, + Unit::k_inductanceRepresentatives, + Unit::k_catalyticActivityRepresentatives, + Unit::k_surfaceRepresentatives, + Unit::k_volumeRepresentatives, + &defaultSpeedRepresentative, + }; + return defaultRepresentatives; +} + +const UnitNode::Representative * UnitNode::Representative::RepresentativeForDimension(UnitNode::Vector vector) { + for (int i = 0; i < k_numberOfDimensions; i++) { + const Representative * representative = Representative::DefaultRepresentatives()[i]; + if (vector == representative->dimensionVector()) { + return representative; + } + } + return nullptr; +} + +static bool compareMagnitudeOrders(float order, float otherOrder) { + /* Precision can be lost (with a year conversion for instance), so the order + * value is rounded */ + if (std::fabs(order) < Expression::Epsilon()) { + order = 0.0f; + } + if (std::fabs(otherOrder) < Expression::Epsilon()) { + otherOrder = 0.0f; + } + if (std::fabs(std::fabs(order) - std::fabs(otherOrder)) <= 3.0f + Expression::Epsilon() && order * otherOrder < 0.0f) { + /* If the two values are close, and their sign are opposed, the positive + * order is preferred */ + return (order >= 0.0f); + } + // Otherwise, the closest order to 0 is preferred + return (std::fabs(order) < std::fabs(otherOrder)); +} + +int UnitNode::Prefix::serialize(char * buffer, int bufferSize) const { + assert(bufferSize >= 0); + return std::min(strlcpy(buffer, m_symbol, bufferSize), bufferSize - 1); +} + +const UnitNode::Representative * UnitNode::Representative::DefaultFindBestRepresentative(double value, double exponent, const UnitNode::Representative * representatives, int length, const Prefix * * prefix) { + assert(length >= 1); + const Representative * res = representatives; + double acc = value / std::pow(res->ratio(), exponent); + if (*prefix) { + *prefix = res->findBestPrefix(acc, exponent); + } + if (length == 1) { + return res; + } + const Prefix * pre = Prefix::EmptyPrefix(); + const Representative * iter = res + 1; + while (iter < representatives + length) { + double temp = value / std::pow(iter->ratio(), exponent); + if (*prefix) { + pre = iter->findBestPrefix(temp, exponent); + } + if (compareMagnitudeOrders(std::log10(temp) - pre->exponent() * exponent, std::log10(acc) - ((!*prefix) ? 0 : (*prefix)->exponent() * exponent))) { + acc = temp; + res = iter; + *prefix = pre; + } + iter++; + } + if (!*prefix) { + *prefix = res->basePrefix(); + } + return res; +} + +int UnitNode::Representative::serialize(char * buffer, int bufferSize, const Prefix * prefix) const { + int length = 0; + length += prefix->serialize(buffer, bufferSize); + assert(length == 0 || isInputPrefixable()); + assert(length < bufferSize); + buffer += length; + bufferSize -= length; + assert(bufferSize >= 0); + length += std::min(strlcpy(buffer, m_rootSymbol, bufferSize), bufferSize - 1); + return length; +} + +bool UnitNode::Representative::canParseWithEquivalents(const char * symbol, size_t length, const Representative * * representative, const Prefix * * prefix) const { + const Representative * candidate = representativesOfSameDimension(); + if (!candidate) { + return false; + } + for (int i = 0; i < numberOfRepresentatives(); i++) { + const char * rootSymbol = (candidate + i)->rootSymbol(); size_t rootSymbolLength = strlen(rootSymbol); int potentialPrefixLength = length - rootSymbolLength; - if (potentialPrefixLength >= 0 && - strncmp(rootSymbol, symbol + potentialPrefixLength, rootSymbolLength) == 0 && - rep->canParse(symbol, potentialPrefixLength, prefix)) - { - *representative = rep; + if (potentialPrefixLength >= 0 + && strncmp(rootSymbol, symbol + potentialPrefixLength, rootSymbolLength) == 0 + && candidate[i].canParse(symbol, potentialPrefixLength, prefix)) { + *representative = (candidate + i); return true; } - rep++; } return false; } +bool UnitNode::Representative::canParse(const char * symbol, size_t length, const Prefix * * prefix) const { + if (!isInputPrefixable()) { + *prefix = Prefix::EmptyPrefix(); + return length == 0; + } + for (size_t i = 0; i < Prefix::k_numberOfPrefixes; i++) { + const Prefix * pre = Prefix::Prefixes() + i; + const char * prefixSymbol = pre->symbol(); + if (prefixSymbol[length] == 0 + && canPrefix(pre, true) + && strncmp(symbol, prefixSymbol, length) == 0) + { + *prefix = pre; + return true; + } + } + return false; +} + +Expression UnitNode::Representative::toBaseUnits() const { + Expression result; + if (isBaseUnit()) { + result = Unit::Builder(this, basePrefix()); + } else { + result = dimensionVector().toBaseUnits(); + } + return Multiplication::Builder(Float::Builder(m_ratio * std::pow(10., - basePrefix()->exponent())), result); +} + +bool UnitNode::Representative::canPrefix(const UnitNode::Prefix * prefix, bool input) const { + Prefixable prefixable = (input) ? m_inputPrefixable : m_outputPrefixable; + if (prefix->exponent() == 0) { + return true; + } + if (prefixable == Prefixable::None) { + return false; + } + if (prefixable == Prefixable::All) { + return true; + } + if (prefixable == Prefixable::LongScale) { + return prefix->exponent() % 3 == 0; + } + if (prefixable == Prefixable::NegativeAndKilo) { + return prefix->exponent() < 0 || prefix->exponent() == 3; + } + if (prefixable == Prefixable::NegativeLongScale) { + return prefix->exponent() < 0 && prefix->exponent() % 3 == 0; + } + if (prefixable == Prefixable::PositiveLongScale) { + return prefix->exponent() > 0 && prefix->exponent() % 3 == 0; + } + if (prefixable == Prefixable::Negative) { + return prefix->exponent() < 0; + } + if (prefixable == Prefixable::Positive) { + return prefix->exponent() > 0; + } + assert(false); + return false; +} + +const UnitNode::Prefix * UnitNode::Representative::findBestPrefix(double value, double exponent) const { + if (!isOutputPrefixable()) { + return Prefix::EmptyPrefix(); + } + if (value < Expression::Epsilon()) { + return basePrefix(); + } + const Prefix * res = basePrefix(); + const float magnitude = std::log10(std::fabs(value)); + float bestOrder = magnitude; + for (int i = 0; i < Prefix::k_numberOfPrefixes; i++) { + if (!canPrefix(Prefix::Prefixes() + i, false)) { + continue; + } + float order = magnitude - (Prefix::Prefixes()[i].exponent() - basePrefix()->exponent()) * exponent; + if (compareMagnitudeOrders(order, bestOrder)) { + bestOrder = order; + res = Prefix::Prefixes() + i; + } + } + return res; +} + +// UnitNode::___Representative +const UnitNode::Representative * UnitNode::TimeRepresentative::representativesOfSameDimension() const { return Unit::k_timeRepresentatives; } +const UnitNode::Representative * UnitNode::DistanceRepresentative::representativesOfSameDimension() const { return Unit::k_distanceRepresentatives; } +const UnitNode::Representative * UnitNode::MassRepresentative::representativesOfSameDimension() const { return Unit::k_massRepresentatives; } +const UnitNode::Representative * UnitNode::CurrentRepresentative::representativesOfSameDimension() const { return Unit::k_currentRepresentatives; } +const UnitNode::Representative * UnitNode::TemperatureRepresentative::representativesOfSameDimension() const { return Unit::k_temperatureRepresentatives; } +const UnitNode::Representative * UnitNode::AmountOfSubstanceRepresentative::representativesOfSameDimension() const { return Unit::k_amountOfSubstanceRepresentatives; } +const UnitNode::Representative * UnitNode::LuminousIntensityRepresentative::representativesOfSameDimension() const { return Unit::k_luminousIntensityRepresentatives; } +const UnitNode::Representative * UnitNode::FrequencyRepresentative::representativesOfSameDimension() const { return Unit::k_frequencyRepresentatives; } +const UnitNode::Representative * UnitNode::ForceRepresentative::representativesOfSameDimension() const { return Unit::k_forceRepresentatives; } +const UnitNode::Representative * UnitNode::PressureRepresentative::representativesOfSameDimension() const { return Unit::k_pressureRepresentatives; } +const UnitNode::Representative * UnitNode::EnergyRepresentative::representativesOfSameDimension() const { return Unit::k_energyRepresentatives; } +const UnitNode::Representative * UnitNode::PowerRepresentative::representativesOfSameDimension() const { return Unit::k_powerRepresentatives; } +const UnitNode::Representative * UnitNode::ElectricChargeRepresentative::representativesOfSameDimension() const { return Unit::k_electricChargeRepresentatives; } +const UnitNode::Representative * UnitNode::ElectricPotentialRepresentative::representativesOfSameDimension() const { return Unit::k_electricPotentialRepresentatives; } +const UnitNode::Representative * UnitNode::ElectricCapacitanceRepresentative::representativesOfSameDimension() const { return Unit::k_electricCapacitanceRepresentatives; } +const UnitNode::Representative * UnitNode::ElectricResistanceRepresentative::representativesOfSameDimension() const { return Unit::k_electricResistanceRepresentatives; } +const UnitNode::Representative * UnitNode::ElectricConductanceRepresentative::representativesOfSameDimension() const { return Unit::k_electricConductanceRepresentatives; } +const UnitNode::Representative * UnitNode::MagneticFluxRepresentative::representativesOfSameDimension() const { return Unit::k_magneticFluxRepresentatives; } +const UnitNode::Representative * UnitNode::MagneticFieldRepresentative::representativesOfSameDimension() const { return Unit::k_magneticFieldRepresentatives; } +const UnitNode::Representative * UnitNode::InductanceRepresentative::representativesOfSameDimension() const { return Unit::k_inductanceRepresentatives; } +const UnitNode::Representative * UnitNode::CatalyticActivityRepresentative::representativesOfSameDimension() const { return Unit::k_catalyticActivityRepresentatives; } +const UnitNode::Representative * UnitNode::SurfaceRepresentative::representativesOfSameDimension() const { return Unit::k_surfaceRepresentatives; } +const UnitNode::Representative * UnitNode::VolumeRepresentative::representativesOfSameDimension() const { return Unit::k_volumeRepresentatives; } + +int UnitNode::TimeRepresentative::setAdditionalExpressions(double value, Expression * dest, int availableLength, ExpressionNode::ReductionContext reductionContext) const { + assert(availableLength >= 1); + /* Use all representatives but week */ + const Unit splitUnits[] = { + Unit::Builder(representativesOfSameDimension() + Unit::k_secondRepresentativeIndex, Prefix::EmptyPrefix()), + Unit::Builder(representativesOfSameDimension() + Unit::k_minuteRepresentativeIndex, Prefix::EmptyPrefix()), + Unit::Builder(representativesOfSameDimension() + Unit::k_hourRepresentativeIndex, Prefix::EmptyPrefix()), + Unit::Builder(representativesOfSameDimension() + Unit::k_dayRepresentativeIndex, Prefix::EmptyPrefix()), + Unit::Builder(representativesOfSameDimension() + Unit::k_monthRepresentativeIndex, Prefix::EmptyPrefix()), + Unit::Builder(representativesOfSameDimension() + Unit::k_yearRepresentativeIndex, Prefix::EmptyPrefix()), + }; + dest[0] = Unit::BuildSplit(value, splitUnits, numberOfRepresentatives() - 1, reductionContext); + return 1; +} + +const UnitNode::Representative * UnitNode::DistanceRepresentative::standardRepresentative(double value, double exponent, ExpressionNode::ReductionContext reductionContext, const Prefix * * prefix) const { + return (reductionContext.unitFormat() == Preferences::UnitFormat::Metric) ? + /* Exclude imperial units from the search. */ + DefaultFindBestRepresentative(value, exponent, representativesOfSameDimension(), Unit::k_inchRepresentativeIndex, prefix) : + /* Exclude m form the search. */ + DefaultFindBestRepresentative(value, exponent, representativesOfSameDimension() + 1, numberOfRepresentatives() - 1, prefix); +} + +int UnitNode::DistanceRepresentative::setAdditionalExpressions(double value, Expression * dest, int availableLength, ExpressionNode::ReductionContext reductionContext) const { + assert(availableLength >= 1); + const Unit splitUnits[] = { + Unit::Builder(representativesOfSameDimension() + Unit::k_inchRepresentativeIndex, Prefix::EmptyPrefix()), + Unit::Builder(representativesOfSameDimension() + Unit::k_footRepresentativeIndex, Prefix::EmptyPrefix()), + Unit::Builder(representativesOfSameDimension() + Unit::k_yardRepresentativeIndex, Prefix::EmptyPrefix()), + Unit::Builder(representativesOfSameDimension() + Unit::k_mileRepresentativeIndex, Prefix::EmptyPrefix()), + }; + dest[0] = Unit::BuildSplit(value, splitUnits, 4, reductionContext); + return 1; +} + +const UnitNode::Prefix * UnitNode::MassRepresentative::basePrefix() const { + return isBaseUnit() ? Prefix::Prefixes() + Unit::k_kiloPrefixIndex : Prefix::EmptyPrefix(); +} + +const UnitNode::Representative * UnitNode::MassRepresentative::standardRepresentative(double value, double exponent, ExpressionNode::ReductionContext reductionContext, const Prefix * * prefix) const { + return (reductionContext.unitFormat() == Preferences::UnitFormat::Metric) ? + /* Only search in g. */ + DefaultFindBestRepresentative(value, exponent, representativesOfSameDimension(), 1, prefix) : + /* Only search in imperial units without the long ton. */ + DefaultFindBestRepresentative(value, exponent, representativesOfSameDimension() + Unit::k_ounceRepresentativeIndex, Unit::k_shortTonRepresentativeIndex - Unit::k_ounceRepresentativeIndex + 1, prefix); +} + +int UnitNode::MassRepresentative::setAdditionalExpressions(double value, Expression * dest, int availableLength, ExpressionNode::ReductionContext reductionContext) const { + assert(availableLength >= 1); + const Unit splitUnits[] = { + Unit::Builder(representativesOfSameDimension() + Unit::k_ounceRepresentativeIndex, Prefix::EmptyPrefix()), + Unit::Builder(representativesOfSameDimension() + Unit::k_poundRepresentativeIndex, Prefix::EmptyPrefix()), + Unit::Builder(representativesOfSameDimension() + Unit::k_shortTonRepresentativeIndex, Prefix::EmptyPrefix()), + }; + dest[0] = Unit::BuildSplit(value, splitUnits, 3, reductionContext); + return 1; +} + +int UnitNode::EnergyRepresentative::setAdditionalExpressions(double value, Expression * dest, int availableLength, ExpressionNode::ReductionContext reductionContext) const { + assert(availableLength >= 2); + /* 1. Convert into Wh + * As value is expressed in SI units (ie _kg_m^2_s^-2), the ratio is that of + * hours to seconds. */ + const Representative * hour = TimeRepresentative::Default().representativesOfSameDimension() + Unit::k_hourRepresentativeIndex; + const Representative * watt = PowerRepresentative::Default().representativesOfSameDimension() + Unit::k_wattRepresentativeIndex; + double adjustedValue = value / hour->ratio() / watt->ratio(); + const Prefix * wattPrefix = watt->findBestPrefix(adjustedValue, 1.); + dest[0] = Multiplication::Builder( + Float::Builder(adjustedValue * std::pow(10., -wattPrefix->exponent())), + Multiplication::Builder( + Unit::Builder(watt, wattPrefix), + Unit::Builder(hour, Prefix::EmptyPrefix()))); + /* 2. Convert into eV */ + const Representative * eV = representativesOfSameDimension() + Unit::k_electronVoltRepresentativeIndex; + adjustedValue = value / eV->ratio(); + const Prefix * eVPrefix = eV->findBestPrefix(adjustedValue, 1.); + dest[1] = Multiplication::Builder( + Float::Builder(adjustedValue * std::pow(10., -eVPrefix->exponent())), + Unit::Builder(eV, eVPrefix)); + return 2; +} + +const UnitNode::Representative * UnitNode::SurfaceRepresentative::standardRepresentative(double value, double exponent, ExpressionNode::ReductionContext reductionContext, const Prefix * * prefix) const { + *prefix = Prefix::EmptyPrefix(); + return representativesOfSameDimension() + (reductionContext.unitFormat() == Preferences::UnitFormat::Metric ? Unit::k_hectareRepresentativeIndex : Unit::k_acreRepresentativeIndex); +} + +int UnitNode::SurfaceRepresentative::setAdditionalExpressions(double value, Expression * dest, int availableLength, ExpressionNode::ReductionContext reductionContext) const { + assert(availableLength >= 2); + int k = (reductionContext.unitFormat() == Preferences::UnitFormat::Metric) ? 0 : 1; + Expression * destMetric = dest + k; + Expression * destImperial = dest + (1 - k); + // 1. Convert to hectares + const Representative * hectare = representativesOfSameDimension() + Unit::k_hectareRepresentativeIndex; + *destMetric = Multiplication::Builder(Float::Builder(value / hectare->ratio()), Unit::Builder(hectare, Prefix::EmptyPrefix())); + // 2. Convert to acres + const Representative * acre = representativesOfSameDimension() + Unit::k_acreRepresentativeIndex; + *destImperial = Multiplication::Builder(Float::Builder(value / acre->ratio()), Unit::Builder(acre, Prefix::EmptyPrefix())); + return 2; +} + +const UnitNode::Representative * UnitNode::VolumeRepresentative::standardRepresentative(double value, double exponent, ExpressionNode::ReductionContext reductionContext, const Prefix * * prefix) const { + if (reductionContext.unitFormat() == Preferences::UnitFormat::Metric) { + *prefix = representativesOfSameDimension()->findBestPrefix(value, exponent); + return representativesOfSameDimension(); + } + return DefaultFindBestRepresentative(value, exponent, representativesOfSameDimension() + 1, numberOfRepresentatives() - 1, prefix); +} + +int UnitNode::VolumeRepresentative::setAdditionalExpressions(double value, Expression * dest, int availableLength, ExpressionNode::ReductionContext reductionContext) const { + assert(availableLength >= 2); + int k = (reductionContext.unitFormat() == Preferences::UnitFormat::Metric) ? 0 : 1; + Expression * destMetric = dest + k; + Expression * destImperial = dest + (1 - k); + // 1. Convert to liters + const Representative * liter = representativesOfSameDimension() + Unit::k_literRepresentativeIndex; + double adjustedValue = value / liter->ratio(); + const Prefix * literPrefix = liter->findBestPrefix(adjustedValue, 1.); + *destMetric = Multiplication::Builder( + Float::Builder(adjustedValue * pow(10., -literPrefix->exponent())), + Unit::Builder(liter, literPrefix)); + // 2. Convert to imperial volumes + const Unit splitUnits[] = { + Unit::Builder(representativesOfSameDimension() + Unit::k_fluidOunceRepresentativeIndex, Prefix::EmptyPrefix()), + Unit::Builder(representativesOfSameDimension() + Unit::k_cupRepresentativeIndex, Prefix::EmptyPrefix()), + Unit::Builder(representativesOfSameDimension() + Unit::k_gallonRepresentativeIndex, Prefix::EmptyPrefix()), + }; + *destImperial = Unit::BuildSplit(value, splitUnits, numberOfRepresentatives() - 5, reductionContext); + return 2; +} + +int UnitNode::SpeedRepresentative::setAdditionalExpressions(double value, Expression * dest, int availableLength, ExpressionNode::ReductionContext reductionContext) const { + assert(availableLength >= 2); + int k = (reductionContext.unitFormat() == Preferences::UnitFormat::Metric) ? 0 : 1; + Expression * destMetric = dest + k; + Expression * destImperial = dest + (1 - k); + // 1. Convert to km/h + const Representative * meter = DistanceRepresentative::Default().representativesOfSameDimension() + Unit::k_meterRepresentativeIndex; + const Representative * hour = TimeRepresentative::Default().representativesOfSameDimension() + Unit::k_hourRepresentativeIndex; + *destMetric = Multiplication::Builder( + Float::Builder(value / 1000. * hour->ratio()), + Multiplication::Builder( + Unit::Builder(meter, Prefix::Prefixes() + Unit::k_kiloPrefixIndex), + Power::Builder(Unit::Builder(hour, Prefix::EmptyPrefix()), Rational::Builder(-1)))); + // 2. Convert to mph + const Representative * mile = DistanceRepresentative::Default().representativesOfSameDimension() + Unit::k_mileRepresentativeIndex; + *destImperial = Multiplication::Builder( + Float::Builder(value / mile->ratio() * hour->ratio()), + Multiplication::Builder( + Unit::Builder(mile, Prefix::EmptyPrefix()), + Power::Builder(Unit::Builder(hour, Prefix::EmptyPrefix()), Rational::Builder(-1)))); + return 2; +} + +// UnitNode ExpressionNode::Sign UnitNode::sign(Context * context) const { return Sign::Positive; } @@ -208,29 +574,6 @@ Expression UnitNode::removeUnit(Expression * unit) { return Unit(this).removeUnit(unit); } -int UnitNode::simplificationOrderSameType(const ExpressionNode * e, bool ascending, bool canBeInterrupted, bool ignoreParentheses) const { - if (!ascending) { - return e->simplificationOrderSameType(this, true, canBeInterrupted, ignoreParentheses); - } - assert(type() == e->type()); - const UnitNode * eNode = static_cast(e); - // This works because dimensions are ordered in a table - const ptrdiff_t dimdiff = eNode->dimension() - m_dimension; - if (dimdiff != 0) { - return dimdiff; - } - // This works because representatives are ordered in a table - const ptrdiff_t repdiff = eNode->representative() - m_representative; - if (repdiff != 0) { - /* We order representatives in the reverse order as how they're stored in - * the representatives table. This enables to sort addition of time as: - * year + month + days + hours + minutes + seconds. */ - return -repdiff; - } - const ptrdiff_t prediff = eNode->prefix()->exponent() - m_prefix->exponent(); - return prediff; -} - Layout UnitNode::createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const { /* TODO: compute the bufferSize more precisely... So far the longest unit is * "month" of size 6 but later, we might add unicode to represent ohm or µ @@ -250,9 +593,25 @@ int UnitNode::serialize(char * buffer, int bufferSize, Preferences::PrintFloatMo return underscoreLength + m_representative->serialize(buffer, bufferSize, m_prefix); } -template -Evaluation UnitNode::templatedApproximate(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { - return Complex::Undefined(); +int UnitNode::simplificationOrderSameType(const ExpressionNode * e, bool ascending, bool canBeInterrupted, bool ignoreParentheses) const { + if (!ascending) { + return e->simplificationOrderSameType(this, true, canBeInterrupted, ignoreParentheses); + } + assert(type() == e->type()); + const UnitNode * eNode = static_cast(e); + Vector v = representative()->dimensionVector(); + Vector w = eNode->representative()->dimensionVector(); + for (int i = 0; i < k_numberOfBaseUnits; i++) { + if (v.coefficientAtIndex(i) != w.coefficientAtIndex(i)) { + return v.coefficientAtIndex(i) - w.coefficientAtIndex(i); + } + } + const ptrdiff_t representativeDiff = m_representative - eNode->representative(); + if (representativeDiff != 0) { + return representativeDiff; + } + const ptrdiff_t prediff = eNode->prefix()->exponent() - m_prefix->exponent(); + return prediff; } Expression UnitNode::shallowReduce(ReductionContext reductionContext) { @@ -263,100 +622,124 @@ Expression UnitNode::shallowBeautify(ReductionContext reductionContext) { return Unit(this).shallowBeautify(reductionContext); } -constexpr const Unit::Prefix - Unit::PicoPrefix, - Unit::NanoPrefix, - Unit::MicroPrefix, - Unit::MilliPrefix, - Unit::CentiPrefix, - Unit::DeciPrefix, - Unit::EmptyPrefix, - Unit::DecaPrefix, - Unit::HectoPrefix, - Unit::KiloPrefix, - Unit::MegaPrefix, - Unit::GigaPrefix, - Unit::TeraPrefix; -constexpr const Unit::Prefix * const Unit::NoPrefix[]; -constexpr const Unit::Prefix * const Unit::NegativeLongScalePrefixes[]; -constexpr const Unit::Prefix * const Unit::PositiveLongScalePrefixes[]; -constexpr const Unit::Prefix * const Unit::LongScalePrefixes[]; -constexpr const Unit::Prefix * const Unit::NegativePrefixes[]; -constexpr const Unit::Prefix * const Unit::AllPrefixes[]; -constexpr const Unit::Representative - Unit::TimeRepresentatives[], - Unit::DistanceRepresentatives[], - Unit::MassRepresentatives[], - Unit::CurrentRepresentatives[], - Unit::TemperatureRepresentatives[], - Unit::AmountOfSubstanceRepresentatives[], - Unit::LuminousIntensityRepresentatives[], - Unit::FrequencyRepresentatives[], - Unit::ForceRepresentatives[], - Unit::PressureRepresentatives[], - Unit::EnergyRepresentatives[], - Unit::PowerRepresentatives[], - Unit::ElectricChargeRepresentatives[], - Unit::ElectricPotentialRepresentatives[], - Unit::ElectricCapacitanceRepresentatives[], - Unit::ElectricResistanceRepresentatives[], - Unit::ElectricConductanceRepresentatives[], - Unit::MagneticFluxRepresentatives[], - Unit::MagneticFieldRepresentatives[], - Unit::InductanceRepresentatives[], - 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; +template +Evaluation UnitNode::templatedApproximate(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { + return Complex::Undefined(); +} -bool Unit::CanParse(const char * symbol, size_t length, - const Dimension * * dimension, const Representative * * representative, const Prefix * * prefix) -{ - for (const Dimension * dim = DimensionTable; dim < DimensionTableUpperBound; dim++) { - if (dim->canParse(symbol, length, representative, prefix)) { - *dimension = dim; +// Unit +Unit Unit::Builder(const Unit::Representative * representative, const Prefix * prefix) { + void * bufferNode = TreePool::sharedPool()->alloc(sizeof(UnitNode)); + UnitNode * node = new (bufferNode) UnitNode(representative, prefix); + TreeHandle h = TreeHandle::BuildWithGhostChildren(node); + return static_cast(h); +} + +bool Unit::CanParse(const char * symbol, size_t length, const Unit::Representative * * representative, const Unit::Prefix * * prefix) { + for (int i = 0; i < Representative::k_numberOfDimensions; i++) { + if (Representative::DefaultRepresentatives()[i]->canParseWithEquivalents(symbol, length, representative, prefix)) { return true; } } return false; } -Unit Unit::Builder(const Dimension * dimension, const Representative * representative, const Prefix * prefix) { - void * bufferNode = TreePool::sharedPool()->alloc(sizeof(UnitNode)); - UnitNode * node = new (bufferNode) UnitNode(dimension, representative, prefix); - TreeHandle h = TreeHandle::BuildWithGhostChildren(node); - return static_cast(h); +static void chooseBestRepresentativeAndPrefixForValueOnSingleUnit(Expression unit, double * value, ExpressionNode::ReductionContext reductionContext, bool optimizePrefix) { + double exponent = 1.f; + Expression factor = unit; + if (factor.type() == ExpressionNode::Type::Power) { + Expression childExponent = factor.childAtIndex(1); + assert(factor.childAtIndex(0).type() == ExpressionNode::Type::Unit); + assert(factor.childAtIndex(1).type() == ExpressionNode::Type::Rational); + exponent = static_cast(childExponent).approximateToScalar(reductionContext.context(), reductionContext.complexFormat(), reductionContext.angleUnit()); + factor = factor.childAtIndex(0); + } + assert(factor.type() == ExpressionNode::Type::Unit); + static_cast(factor).chooseBestRepresentativeAndPrefix(value, exponent, reductionContext, optimizePrefix); +} + +void Unit::ChooseBestRepresentativeAndPrefixForValue(Expression units, double * value, ExpressionNode::ReductionContext reductionContext) { + int numberOfFactors; + Expression factor; + if (units.type() == ExpressionNode::Type::Multiplication) { + numberOfFactors = units.numberOfChildren(); + factor = units.childAtIndex(0); + } else { + numberOfFactors = 1; + factor = units; + } + chooseBestRepresentativeAndPrefixForValueOnSingleUnit(factor, value, reductionContext, true); + for (int i = 1; i < numberOfFactors; i++) { + chooseBestRepresentativeAndPrefixForValueOnSingleUnit(units.childAtIndex(i), value, reductionContext, false); + } +} + +bool Unit::ShouldDisplayAdditionalOutputs(double value, Expression unit) { + UnitNode::Vector vector = UnitNode::Vector::FromBaseUnits(unit); + const Representative * representative = Representative::RepresentativeForDimension(vector); + return representative != nullptr && representative->hasAdditionalExpressions(value); +} + +int Unit::SetAdditionalExpressions(Expression units, double value, Expression * dest, int availableLength, ExpressionNode::ReductionContext reductionContext) { + const Representative * representative = UnitNode::Representative::RepresentativeForDimension(UnitNode::Vector::FromBaseUnits(units)); + assert(representative); + return representative->setAdditionalExpressions(value, dest, availableLength, reductionContext); +} + +Expression Unit::BuildSplit(double value, const Unit * units, int length, ExpressionNode::ReductionContext reductionContext) { + assert(!std::isnan(value)); + assert(units); + assert(length > 0); + + double baseRatio = units->node()->representative()->ratio(); + double basedValue = value / baseRatio; + if (std::isinf(value) || std::fabs(value) < Expression::Epsilon()) { + return Multiplication::Builder(Number::FloatNumber(value), units[0]); + } + double err = std::pow(10.0, Poincare::PrintFloat::k_numberOfStoredSignificantDigits - 1 - std::ceil(log10(std::fabs(basedValue)))); + double remain = std::round(basedValue*err)/err; + + Addition res = Addition::Builder(); + for (int i = length - 1; i >= 0; i--) { + assert(units[i].node()->prefix() == Prefix::EmptyPrefix()); + double factor = std::round(units[i].node()->representative()->ratio() / baseRatio); + double share = remain / factor; + if (i > 0) { + share = (share > 0.0) ? std::floor(share) : std::ceil(share); + } + remain -= share * factor; + if (std::abs(share) > Expression::Epsilon()) { + res.addChildAtIndexInPlace(Multiplication::Builder(Float::Builder(share), units[i]), res.numberOfChildren(), res.numberOfChildren()); + } + } + ExpressionNode::ReductionContext keepUnitsContext( + reductionContext.context(), + reductionContext.complexFormat(), + reductionContext.angleUnit(), + reductionContext.unitFormat(), + ExpressionNode::ReductionTarget::User, + ExpressionNode::SymbolicComputation::ReplaceAllDefinedSymbolsWithDefinition, + ExpressionNode::UnitConversion::None); + return res.squashUnaryHierarchyInPlace().shallowBeautify(keepUnitsContext); } Expression Unit::shallowReduce(ExpressionNode::ReductionContext reductionContext) { - if (reductionContext.unitConversion() == ExpressionNode::UnitConversion::None) { + if (reductionContext.unitConversion() == ExpressionNode::UnitConversion::None + || isBaseUnit()) { + /* We escape early if we are one of the seven base units. + * Nb : For masses, k is considered the base prefix, so kg will be escaped + * here but not g */ return *this; } UnitNode * unitNode = node(); - const Dimension * dim = unitNode->dimension(); - const Representative * rep = unitNode->representative(); - const Prefix * pre = unitNode->prefix(); - int8_t prefixMultiplier = pre->exponent(); - if (rep == dim->stdRepresentative()) { - const Prefix * stdPre = dim->stdRepresentativePrefix(); - unitNode->setPrefix(stdPre); - prefixMultiplier -= stdPre->exponent(); - } - Expression result; - if (rep->definition() == nullptr) { - result = clone(); - } else { - result = Expression::Parse(rep->definition(), nullptr, false).deepReduce(reductionContext); - } - if (prefixMultiplier != 0) { - Expression multiplier = Power::Builder(Rational::Builder(10), Rational::Builder(prefixMultiplier)).shallowReduce(reductionContext); - result = Multiplication::Builder(multiplier, result).shallowReduce(reductionContext); + const Representative * representative = unitNode->representative(); + const Prefix * prefix = unitNode->prefix(); + + Expression result = representative->toBaseUnits().deepReduce(reductionContext); + if (prefix != Prefix::EmptyPrefix()) { + Expression prefixFactor = Power::Builder(Rational::Builder(10), Rational::Builder(prefix->exponent())); + prefixFactor = prefixFactor.shallowReduce(reductionContext); + result = Multiplication::Builder(prefixFactor, result).shallowReduce(reductionContext); } replaceWithInPlace(result); return result; @@ -373,63 +756,6 @@ Expression Unit::shallowBeautify(ExpressionNode::ReductionContext reductionConte return *this; } -void Unit::ChooseBestMultipleForValue(Expression * units, double * value, bool tuneRepresentative, ExpressionNode::ReductionContext reductionContext) { - // Identify the first Unit factor and its exponent - Expression firstFactor = *units; - float exponent = 1.0f; - 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); - exponent = static_cast(exp).node()->templatedApproximate(); - } - assert(firstFactor.type() == ExpressionNode::Type::Unit); - // Choose its multiple and update value accordingly - if (exponent != 0.0f) { - static_cast(firstFactor).chooseBestMultipleForValue(value, exponent, tuneRepresentative, reductionContext); - } -} - -void Unit::chooseBestMultipleForValue(double * value, const float exponent, bool tuneRepresentative, ExpressionNode::ReductionContext reductionContext) { - assert(!std::isnan(*value) && exponent != 0.0f); - if (*value == 0.0 || std::isinf(*value)) { - return; - } - UnitNode * unitNode = node(); - const Dimension * dim = unitNode->dimension(); - /* Find in the Dimension 'dim' which unit (Prefix and optionally - * Representative) make the value closer to 1. - */ - const Representative * bestRep = unitNode->representative(); - const Prefix * bestPre = unitNode->prefix(); - double bestVal = (tuneRepresentative) ? DBL_MAX : *value; - - // Test all representatives if tuneRepresentative is on. Otherwise, force current representative - const Representative * startRep = tuneRepresentative ? dim->stdRepresentative() : bestRep; - const Representative * endRep = tuneRepresentative ? dim->representativesUpperBound() : bestRep + 1; - for (const Representative * rep = startRep; rep < endRep; rep++) { - if (!rep->canOutputInSystem(reductionContext.unitFormat())) { - continue; - } - // evaluate quotient - double val = *value * std::pow(Division::Builder(clone(), Unit::Builder(dim, rep, &EmptyPrefix)).deepReduce(reductionContext).approximateToScalar(reductionContext.context(), reductionContext.complexFormat(), reductionContext.angleUnit()), exponent); - // Get the best prefix and update val accordingly - const Prefix * pre = rep->bestPrefixForValue(val, exponent); - if (compareMagnitudeOrders(std::log10(std::fabs(val)), std::log10(std::fabs(bestVal)))) { - /* At this point, val is closer to one than bestVal is.*/ - bestRep = rep; - bestPre = pre; - bestVal = val; - } - } - unitNode->setRepresentative(bestRep); - unitNode->setPrefix(bestPre); - *value = bestVal; -} - Expression Unit::removeUnit(Expression * unit) { *unit = *this; Expression one = Rational::Builder(1); @@ -437,220 +763,25 @@ Expression Unit::removeUnit(Expression * unit) { return one; } -bool Unit::isSecond() const { - // TODO: comparing pointers suffices because all time dimension are built from the same pointers. This should be asserted some way at compile-time? - return node()->dimension() == TimeDimension && node()->representative() == SecondRepresentative && node()->prefix() == &EmptyPrefix; -} - -bool Unit::isMeter() const { - // See comment on isSecond - return node()->dimension() == DistanceDimension && node()->representative() == MeterRepresentative && node()->prefix() == &EmptyPrefix; -} - - -bool Unit::isKilogram() const { - // See comment on isSecond - return node()->dimension() == MassDimension && node()->representative() == KilogramRepresentative && node()->prefix() == &EmptyPrefix; -} - -bool Unit::isSI() const { - UnitNode * unitNode = node(); - const Dimension * dim = unitNode->dimension(); - const Representative * rep = unitNode->representative(); - return rep == dim->stdRepresentative() && - rep->definition() == nullptr && - unitNode->prefix() == dim->stdRepresentativePrefix(); -} - -bool Unit::IsSI(Expression & e) { - if (e.type() == ExpressionNode::Type::Multiplication) { - for (int i = 0; i < e.numberOfChildren(); i++) { - Expression child = e.childAtIndex(i); - assert(child.type() == ExpressionNode::Type::Power || child.type() == ExpressionNode::Type::Unit); - if (!IsSI(child)) { - return false; - } - } - return true; +void Unit::chooseBestRepresentativeAndPrefix(double * value, double exponent, ExpressionNode::ReductionContext reductionContext, bool optimizePrefix) { + assert(exponent != 0.f); + if (std::isinf(*value) || *value == 0.0) { + node()->setRepresentative(node()->representative()->representativesOfSameDimension()); + node()->setPrefix(node()->representative()->basePrefix()); + return; } - if (e.type() == ExpressionNode::Type::Power) { - // Rational exponents are accepted in IS system - assert(e.childAtIndex(1).type() == ExpressionNode::Type::Rational); - Expression child = e.childAtIndex(0); - assert(child.type() == ExpressionNode::Type::Unit); - return IsSI(child); + // Convert value to base units + double baseValue = *value * std::pow(node()->representative()->ratio() * std::pow(10., node()->prefix()->exponent() - node()->representative()->basePrefix()->exponent()), exponent); + const Prefix * bestPrefix = (optimizePrefix) ? Prefix::EmptyPrefix() : nullptr; + const Representative * bestRepresentative = node()->representative()->standardRepresentative(baseValue, exponent, reductionContext, &bestPrefix); + if (bestRepresentative != node()->representative()) { + *value = *value * std::pow(node()->representative()->ratio() / bestRepresentative->ratio() * std::pow(10., bestRepresentative->basePrefix()->exponent() - node()->representative()->basePrefix()->exponent()), exponent); + node()->setRepresentative(bestRepresentative); } - assert(e.type() == ExpressionNode::Type::Unit); - return static_cast(e).isSI(); -} - -bool Unit::IsSISpeed(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(); -} - -bool Unit::IsSIVolume(Expression & e) { - // Form m^3 - return e.type() == ExpressionNode::Type::Power && - e.childAtIndex(0).type() == ExpressionNode::Type::Unit && e.childAtIndex(0).convert().isMeter() && - e.childAtIndex(1).type() == ExpressionNode::Type::Rational && e.childAtIndex(1).convert().isThree(); -} - -bool Unit::IsSIEnergy(Expression & e) { - // Form _kg*_m^2*_s^-2 - return e.type() == ExpressionNode::Type::Multiplication && e.numberOfChildren() == 3 && - e.childAtIndex(0).type() == ExpressionNode::Type::Unit && e.childAtIndex(0).convert().isKilogram() && - e.childAtIndex(1).type() == ExpressionNode::Type::Power && - e.childAtIndex(1).childAtIndex(0).type() == ExpressionNode::Type::Unit && e.childAtIndex(1).childAtIndex(0).convert().isMeter() && - e.childAtIndex(1).childAtIndex(1).type() == ExpressionNode::Type::Rational && e.childAtIndex(1).childAtIndex(1).convert().isTwo() && - e.childAtIndex(2).type() == ExpressionNode::Type::Power && - e.childAtIndex(2).childAtIndex(0).type() == ExpressionNode::Type::Unit && e.childAtIndex(2).childAtIndex(0).convert().isSecond() && - e.childAtIndex(2).childAtIndex(1).type() == ExpressionNode::Type::Rational && e.childAtIndex(2).childAtIndex(1).convert().isMinusTwo(); -} - -bool Unit::IsSITime(Expression & e) { - return e.type() == ExpressionNode::Type::Unit && static_cast(e).isSecond(); -} - -bool Unit::IsSIDistance(Expression & e) { - return e.type() == ExpressionNode::Type::Unit && static_cast(e).isMeter(); -} - -bool Unit::IsSIMass(Expression & e) { - return e.type() == ExpressionNode::Type::Unit && static_cast(e).isKilogram(); -} - -bool Unit::IsSISurface(Expression & e) { - return e.type() == ExpressionNode::Type::Power && - e.childAtIndex(0).type() == ExpressionNode::Type::Unit && e.childAtIndex(0).convert().isMeter() && - e.childAtIndex(1).type() == ExpressionNode::Type::Rational && e.childAtIndex(1).convert().isTwo(); -} - -double Unit::ConvertedValueInUnit(Expression e, Unit unit, ExpressionNode::ReductionContext reductionContext) { - Expression conversion = UnitConvert::Builder(e.clone(), unit); - Expression newUnit; - conversion = conversion.simplify(reductionContext); - conversion = conversion.removeUnit(&newUnit); - return conversion.approximateToScalar(reductionContext.context(), Preferences::ComplexFormat::Real, Preferences::sharedPreferences()->angleUnit()); -} - -Expression Unit::BuildSplit(double baseValue, Unit const * units, double const * conversionFactors, const int numberOfUnits, Context * context) { - assert(!std::isnan(baseValue)); - if (std::isinf(baseValue) || std::fabs(baseValue) < Expression::Epsilon()) { - return Multiplication::Builder(Number::FloatNumber(baseValue), units[numberOfUnits-1]); + if (optimizePrefix && bestPrefix != node()->prefix()) { + *value = *value * std::pow(10., exponent * (node()->prefix()->exponent() - bestPrefix->exponent())); + node()->setPrefix(bestPrefix); } - - /* Round the base value to 13 significant digits - * (= k_numberOfStoredSignificantDigits - 1). - * Indeed, the user input has been converted to the most adequate unit - * which might have led to approximating the value to 14 significants - * digits. The value was then computed from this approximation. - * We thus round it to avoid displaying small numbers that are - * artifacts of the previous approximations. */ - double err = std::pow(10.0, Poincare::PrintFloat::k_numberOfStoredSignificantDigits - 1 - std::ceil(log10(std::fabs(baseValue)))); - double remain = std::round(baseValue*err)/err; - - double valuesPerUnit[numberOfUnits]; - Addition a = Addition::Builder(); - for (int i = 0; i < numberOfUnits; i++) { - valuesPerUnit[i] = remain/conversionFactors[i]; - // Keep only the floor of the values except for the last unit (seconds) - if (i < numberOfUnits - 1) { - valuesPerUnit[i] = valuesPerUnit[i] >= 0.0 ? std::floor(valuesPerUnit[i]) : std::ceil(valuesPerUnit[i]); - } - remain -= valuesPerUnit[i] * conversionFactors[i]; - if (std::fabs(valuesPerUnit[i]) > Expression::Epsilon()) { - Multiplication m = Multiplication::Builder(Float::Builder(valuesPerUnit[i]), units[i]); - a.addChildAtIndexInPlace(m, a.numberOfChildren(), a.numberOfChildren()); - } - } - - ExpressionNode::ReductionContext reductionContext(context, Preferences::ComplexFormat::Real, Preferences::AngleUnit::Degree, Preferences::UnitFormat::Imperial, ExpressionNode::ReductionTarget::User, ExpressionNode::SymbolicComputation::ReplaceAllDefinedSymbolsWithDefinition, ExpressionNode::UnitConversion::None); - // Beautify the addition into an subtraction if necessary - return a.squashUnaryHierarchyInPlace().shallowBeautify(reductionContext); -} - -Expression Unit::BuildTimeSplit(double seconds, Context * context) { - constexpr static int numberOfTimeUnits = 6; - Unit units[numberOfTimeUnits] = {Unit::Year(), Unit::Month(), Unit::Day(), Unit::Hour(), Unit::Minute(), Unit::Second()}; - constexpr static double timeFactors[numberOfTimeUnits] = {MonthPerYear*DaysPerMonth*HoursPerDay*MinutesPerHour*SecondsPerMinute, DaysPerMonth*HoursPerDay*MinutesPerHour*SecondsPerMinute, HoursPerDay*MinutesPerHour*SecondsPerMinute, MinutesPerHour*SecondsPerMinute, SecondsPerMinute, 1.0}; - return BuildSplit(seconds, units, timeFactors, numberOfTimeUnits, context); -} - -Expression Unit::BuildImperialDistanceSplit(double inches, Context * context) { - constexpr static int numberOfUnits = 4; - Unit units[numberOfUnits] = {Unit::Mile(), Unit::Yard(), Unit::Foot(), Unit::Inch()}; - constexpr static double factors[numberOfUnits] = {InchesPerFoot*FeetPerYard*YardsPerMile, InchesPerFoot*FeetPerYard, InchesPerFoot, 1.}; - return BuildSplit(inches, units, factors, numberOfUnits, context); -} - -Expression Unit::BuildImperialMassSplit(double ounces, Context * context) { - constexpr static int numberOfUnits = 3; - Unit units[numberOfUnits] = {Unit::ShortTon(), Unit::Pound(), Unit::Ounce()}; - constexpr static double factors[numberOfUnits] = {OuncesPerPound*PoundsPerShortTon, OuncesPerPound, 1.}; - return BuildSplit(ounces, units, factors, numberOfUnits, context); -} - -Expression Unit::BuildImperialVolumeSplit(double fluidOunces, Context * context) { - constexpr static int numberOfUnits = 3; - Unit units[numberOfUnits] = {Unit::Gallon(), Unit::Cup(), Unit::FluidOunce()}; - constexpr static double factors[numberOfUnits] = {FluidOuncesPerCup*CupsPerGallon, FluidOuncesPerCup, 1.}; - return BuildSplit(fluidOunces, units, factors, numberOfUnits, context); -} - -Expression Unit::StandardSpeedConversion(Expression e, ExpressionNode::ReductionContext reductionContext) { - Preferences::UnitFormat format = reductionContext.unitFormat(); - return UnitConvert::Builder(e.clone(), Multiplication::Builder( - format == Preferences::UnitFormat::Metric ? Unit::Kilometer() : Unit::Mile(), - Power::Builder( - Unit::Hour(), - Rational::Builder(-1) - ) - ) - ); -} - -Expression Unit::StandardDistanceConversion(Expression e, ExpressionNode::ReductionContext reductionContext) { - Preferences::UnitFormat format = reductionContext.unitFormat(); - if (format == Preferences::UnitFormat::Metric) { - return UnitConvert::Builder(e.clone(), Unit::Meter()); - } - assert(format == Preferences::UnitFormat::Imperial); - double rawValue = ConvertedValueInUnit(e, Unit::Inch(), reductionContext); - return BuildImperialDistanceSplit(rawValue, reductionContext.context()); -} - -Expression Unit::StandardVolumeConversion(Expression e, ExpressionNode::ReductionContext reductionContext) { - Preferences::UnitFormat format = reductionContext.unitFormat(); - if (format == Preferences::UnitFormat::Metric) { - return UnitConvert::Builder(e.clone(), Unit::Liter()); - } - assert(format == Preferences::UnitFormat::Imperial); - double rawValue = ConvertedValueInUnit(e, Unit::FluidOunce(), reductionContext); - return BuildImperialVolumeSplit(rawValue, reductionContext.context()); -} - -Expression Unit::StandardMassConversion(Expression e, ExpressionNode::ReductionContext reductionContext) { - Preferences::UnitFormat format = reductionContext.unitFormat(); - if (format == Preferences::UnitFormat::Metric) { - return UnitConvert::Builder(e.clone(), Unit::Gram()); - } - assert(format == Preferences::UnitFormat::Imperial); - double rawValue = ConvertedValueInUnit(e, Unit::Ounce(), reductionContext); - return BuildImperialMassSplit(rawValue, reductionContext.context()); -} - -Expression Unit::StandardSurfaceConversion(Expression e, ExpressionNode::ReductionContext reductionContext) { - Preferences::UnitFormat format = reductionContext.unitFormat(); - if (format == Preferences::UnitFormat::Metric) { - return UnitConvert::Builder(e.clone(), Unit::Hectare()); - } - assert(format == Preferences::UnitFormat::Imperial); - return UnitConvert::Builder(e.clone(), Unit::Acre()); } template Evaluation UnitNode::templatedApproximate(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; diff --git a/poincare/test/expression.cpp b/poincare/test/expression.cpp index bd1ae80fc..c6508847d 100644 --- a/poincare/test/expression.cpp +++ b/poincare/test/expression.cpp @@ -75,18 +75,18 @@ QUIZ_CASE(poincare_expression_rational_constructor) { } QUIZ_CASE(poincare_expression_unit_constructor) { - Unit u = Unit::Second(); + Unit u = Unit::Builder(Unit::k_timeRepresentatives, Unit::Prefix::EmptyPrefix()); assert_expression_serialize_to(u, "_s"); - u = Unit::Hour(); + u = Unit::Builder(Unit::k_timeRepresentatives + 2, Unit::Prefix::EmptyPrefix()); assert_expression_serialize_to(u, "_h"); - u = Unit::Kilometer(); + u = Unit::Builder(Unit::k_distanceRepresentatives, Unit::k_prefixes + 9); assert_expression_serialize_to(u, "_km"); - u = Unit::Liter(); + u = Unit::Builder(Unit::k_volumeRepresentatives, Unit::Prefix::EmptyPrefix()); assert_expression_serialize_to(u, "_L"); - u = Unit::Watt(); + u = Unit::Builder(Unit::k_powerRepresentatives, Unit::Prefix::EmptyPrefix()); assert_expression_serialize_to(u, "_W"); } diff --git a/poincare/test/expression_properties.cpp b/poincare/test/expression_properties.cpp index 97af3fde3..adbd01e0d 100644 --- a/poincare/test/expression_properties.cpp +++ b/poincare/test/expression_properties.cpp @@ -394,51 +394,62 @@ QUIZ_CASE(poincare_properties_remove_unit) { assert_reduced_expression_unit_is("_L^2×3×_s", "_m^6×_s"); } -void assert_seconds_split_to(double totalSeconds, const char * splittedTime, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) { - Expression time = Unit::BuildTimeSplit(totalSeconds, context); - constexpr static int bufferSize = 100; - char buffer[bufferSize]; - time.serialize(buffer, bufferSize, DecimalMode); - quiz_assert_print_if_failure(strcmp(buffer, splittedTime) == 0, splittedTime); -} - -Expression extract_unit(const char * expression) { +void assert_additional_results_compute_to(const char * expression, const char * * results, int length) { Shared::GlobalContext globalContext; - ExpressionNode::ReductionContext reductionContext = ExpressionNode::ReductionContext(&globalContext, Cartesian, Degree, Metric, User, ReplaceAllSymbolsWithUndefined, NoUnitConversion); + constexpr int maxNumberOfResults = 5; + assert(length <= maxNumberOfResults); + Expression additional[maxNumberOfResults]; + ExpressionNode::ReductionContext reductionContext = ExpressionNode::ReductionContext(&globalContext, Cartesian, Degree, Metric, User, ReplaceAllSymbolsWithUndefined, DefaultUnitConversion); Expression e = parse_expression(expression, &globalContext, false).reduce(reductionContext); - Expression unit; - e.removeUnit(&unit); - return unit; + Expression units; + e = e.removeUnit(&units); + double value = e.approximateToScalar(&globalContext, Cartesian, Degree); + + if (!Unit::ShouldDisplayAdditionalOutputs(value, units)) { + quiz_assert(length == 0); + return; + } + const int numberOfResults = Unit::SetAdditionalExpressions(units, value, additional, maxNumberOfResults, reductionContext); + + quiz_assert(numberOfResults == length); + for (int i = 0; i < length; i++) { + assert_expression_serialize_to(additional[i], results[i], Preferences::PrintFloatMode::Decimal); + } } -QUIZ_CASE(poincare_expression_unit_helper) { - // 1. Time - Expression s = extract_unit("_s"); - quiz_assert(s.type() == ExpressionNode::Type::Unit && static_cast(s).isSecond()); - quiz_assert(!static_cast(s).isMeter()); +QUIZ_CASE(poincare_expression_additional_results) { + // Time + assert_additional_results_compute_to("3×_s", nullptr, 0); + const char * array1[1] = {"1×_min+1×_s"}; + assert_additional_results_compute_to("61×_s", array1, 1); + const char * array2[1] = {"1×_day+10×_h+17×_min+36×_s"}; + assert_additional_results_compute_to("123456×_s", array2, 1); + const char * array3[1] = {"7×_day"}; + assert_additional_results_compute_to("1×_week", array3, 1); - Shared::GlobalContext globalContext; - assert_seconds_split_to(1234567890, "39×_year+1×_month+13×_day+19×_h+1×_min+30×_s", &globalContext, Cartesian, Degree); - assert_seconds_split_to(-122, "-2×_min-2×_s", &globalContext, Cartesian, Degree); + // Distance + const char * array4[1] = {"19×_mi+853×_yd+1×_ft+7×_in"}; + assert_additional_results_compute_to("1234567×_in", array4, 1); + const char * array5[1] = {"1×_yd+7.700787×_in"}; + assert_additional_results_compute_to("1.11×_m", array5, 1); - // 2. Speed - Expression meterPerSecond = extract_unit("_m×_s^-1"); - quiz_assert(Unit::IsSISpeed(meterPerSecond)); + // Masses + const char * array6[1] = {"1×_shtn+240×_lb"}; + assert_additional_results_compute_to("1×_lgtn", array6, 1); + const char * array7[1] = {"2×_lb+3.273962×_oz"}; + assert_additional_results_compute_to("1×_kg", array7, 1); - // 3. Volume - Expression meter3 = extract_unit("_m^3"); - quiz_assert(Unit::IsSIVolume(meter3)); + // Energy + const char * array8[2] = {"1×_kW×_h", "2.246943ᴇ13×_TeV"}; + assert_additional_results_compute_to("3.6×_MN_m", array8, 2); - // 4. Energy - Expression kilogramMeter2PerSecond2 = extract_unit("_kg×_m^2×_s^-2"); - quiz_assert(Unit::IsSIEnergy(kilogramMeter2PerSecond2)); - Expression kilogramMeter3PerSecond2 = extract_unit("_kg×_m^3×_s^-2"); - quiz_assert(!Unit::IsSIEnergy(kilogramMeter3PerSecond2)); + // Volume + const char * array9[2] = {"1000×_L", "264×_gal+2×_cp+6.022702×_floz"}; + assert_additional_results_compute_to("1×_m^3", array9, 2); + const char * array10[2] = {"182.5426×_L", "48×_gal+3×_cp+4.5×_floz"}; + assert_additional_results_compute_to("12345×_Tbsp", array10, 2); - // 5. International System - quiz_assert(Unit::IsSI(kilogramMeter2PerSecond2)); - quiz_assert(Unit::IsSI(meter3)); - quiz_assert(Unit::IsSI(meterPerSecond)); - Expression joule = extract_unit("_J"); - quiz_assert(!Unit::IsSI(joule)); + // Speed + const char * array11[2] = {"3.6×_km×_h^\x12-1\x13", "2.236936×_mi×_h^\x12-1\x13"}; + assert_additional_results_compute_to("1×_m/_s", array11, 2); } diff --git a/poincare/test/parsing.cpp b/poincare/test/parsing.cpp index a0437eaf0..49bd40c2f 100644 --- a/poincare/test/parsing.cpp +++ b/poincare/test/parsing.cpp @@ -286,20 +286,19 @@ QUIZ_CASE(poincare_parsing_matrices) { QUIZ_CASE(poincare_parsing_units) { // Units - for (const Unit::Dimension * dim = Unit::DimensionTable; dim < Unit::DimensionTableUpperBound; dim++) { - for (const Unit::Representative * rep = dim->stdRepresentative(); rep < dim->representativesUpperBound(); rep++) { + for (int i = 0; i < Unit::Representative::k_numberOfDimensions; i++) { + const Unit::Representative * dim = Unit::Representative::DefaultRepresentatives()[i]; + for (int j = 0; j < dim->numberOfRepresentatives(); j++) { + const Unit::Representative * rep = dim->representativesOfSameDimension() + j; static constexpr size_t bufferSize = 10; char buffer[bufferSize]; - Unit::Builder(dim, rep, &Unit::EmptyPrefix).serialize(buffer, bufferSize, Preferences::PrintFloatMode::Decimal, Preferences::VeryShortNumberOfSignificantDigits); + Unit::Builder(rep, Unit::Prefix::EmptyPrefix()).serialize(buffer, bufferSize, Preferences::PrintFloatMode::Decimal, Preferences::VeryShortNumberOfSignificantDigits); Expression unit = parse_expression(buffer, nullptr, false); quiz_assert_print_if_failure(unit.type() == ExpressionNode::Type::Unit, "Should be parsed as a Unit"); - if (rep->isPrefixable()) { - /* ton is only prefixable by positive prefixes */ - bool isTon = strcmp("t", rep->rootSymbol()) == 0; - size_t numberOfPrefixes = ((isTon) ? sizeof(Unit::PositiveLongScalePrefixes) : sizeof(Unit::AllPrefixes))/sizeof(Unit::Prefix *); - for (size_t i = 0; i < numberOfPrefixes; i++) { - const Unit::Prefix * pre = (isTon) ? Unit::PositiveLongScalePrefixes[i] : Unit::AllPrefixes[i]; - Unit::Builder(dim, rep, pre).serialize(buffer, bufferSize, Preferences::PrintFloatMode::Decimal, Preferences::VeryShortNumberOfSignificantDigits); + if (rep->isInputPrefixable()) { + for (size_t i = 0; i < Unit::Prefix::k_numberOfPrefixes; i++) { + const Unit::Prefix * pre = Unit::Prefix::Prefixes(); + Unit::Builder(rep, pre).serialize(buffer, bufferSize, Preferences::PrintFloatMode::Decimal, Preferences::VeryShortNumberOfSignificantDigits); Expression unit = parse_expression(buffer, nullptr, false); quiz_assert_print_if_failure(unit.type() == ExpressionNode::Type::Unit, "Should be parsed as a Unit"); } diff --git a/poincare/test/simplification.cpp b/poincare/test/simplification.cpp index 702fc6665..f4cf7a7d0 100644 --- a/poincare/test/simplification.cpp +++ b/poincare/test/simplification.cpp @@ -196,6 +196,26 @@ QUIZ_CASE(poincare_simplification_multiplication) { assert_parsed_expression_simplify_to("0*[[1,0][0,1]]^500", "0×[[1,0][0,1]]^500"); } +void assert_parsed_unit_simplify_to_with_prefixes(const Unit::Representative * representative) { + int numberOfPrefixes; + const Unit::Prefix * prefixes; + static constexpr size_t bufferSize = 12; + char buffer[bufferSize] = "1×"; + if (representative->isOutputPrefixable()) { + numberOfPrefixes = Unit::Prefix::k_numberOfPrefixes; + prefixes = Unit::k_prefixes; + } else { + numberOfPrefixes = 1; + prefixes = Unit::Prefix::EmptyPrefix(); + } + for (int i = 0; i < numberOfPrefixes; i++) { + if (representative->canPrefix(prefixes + i, true) && representative->canPrefix(prefixes + i, false)) { + Unit::Builder(representative, prefixes + i).serialize(buffer+strlen("1×"), bufferSize-strlen("1×"), Preferences::PrintFloatMode::Decimal, Preferences::VeryShortNumberOfSignificantDigits); + assert_parsed_expression_simplify_to(buffer, buffer); + } + } +} + QUIZ_CASE(poincare_simplification_units) { /* SI base units */ assert_parsed_expression_simplify_to("_s", "1×_s"); @@ -258,10 +278,9 @@ QUIZ_CASE(poincare_simplification_units) { assert_parsed_expression_simplify_to("_mol×_s^-1", "1×_kat"); /* Displayed order of magnitude */ - assert_parsed_expression_simplify_to("1_t", "1×_t"); assert_parsed_expression_simplify_to("100_kg", "100×_kg"); assert_parsed_expression_simplify_to("1_min", "1×_min"); - assert_parsed_expression_simplify_to("0.1_m", "100×_mm"); + assert_parsed_expression_simplify_to("0.1_m", "1×_dm"); assert_parsed_expression_simplify_to("180_MΩ", "180×_MΩ"); assert_parsed_expression_simplify_to("180_MH", "180×_MH"); @@ -269,31 +288,60 @@ QUIZ_CASE(poincare_simplification_units) { * Some symbols are however excluded: * - At present, some units will not appear as simplification output: * t, Hz, S, ha, L. These exceptions are tested below. */ - for (const Unit::Dimension * dim = Unit::DimensionTable; dim < Unit::DimensionTableUpperBound; dim++) { - for (const Unit::Representative * rep = dim->stdRepresentative(); rep < dim->representativesUpperBound(); rep++) { - if (strcmp(rep->rootSymbol(), "Hz") == 0 || strcmp(rep->rootSymbol(), "S") == 0 || strcmp(rep->rootSymbol(), "ha") == 0 || strcmp(rep->rootSymbol(), "L") == 0 || strcmp(rep->rootSymbol(), "yd") || strcmp(rep->rootSymbol(), "tsp") || strcmp(rep->rootSymbol(), "Tbsp") || strcmp(rep->rootSymbol(), "pt") || strcmp(rep->rootSymbol(), "qt") || strcmp(rep->rootSymbol(), "lgtn")) { - continue; - } - static constexpr size_t bufferSize = 12; - char buffer[bufferSize] = "1×"; - Unit::Builder(dim, rep, &Unit::EmptyPrefix).serialize(buffer+strlen("1×"), bufferSize-strlen("1×"), Preferences::PrintFloatMode::Decimal, Preferences::VeryShortNumberOfSignificantDigits); - assert_parsed_expression_simplify_to(buffer, buffer, User, Radian, (rep->canOutputInSystem(Metric) ? Metric : Imperial)); - if (rep->isPrefixable()) { - for (size_t i = 0; i < rep->outputPrefixesLength(); i++) { - const Unit::Prefix * pre = rep->outputPrefixes()[i]; - Unit::Builder(dim, rep, pre).serialize(buffer+strlen("1×"), bufferSize-strlen("1×"), Preferences::PrintFloatMode::Decimal, Preferences::VeryShortNumberOfSignificantDigits); - assert_parsed_expression_simplify_to(buffer, buffer); - } - } - } - } + assert_parsed_expression_simplify_to("_s", "1×_s"); + assert_parsed_expression_simplify_to("_min", "1×_min"); + assert_parsed_expression_simplify_to("_h", "1×_h"); + assert_parsed_expression_simplify_to("_day", "1×_day"); + assert_parsed_expression_simplify_to("_week", "1×_week"); + assert_parsed_expression_simplify_to("_month", "1×_month"); + assert_parsed_expression_simplify_to("_year", "1×_year"); + assert_parsed_unit_simplify_to_with_prefixes(Unit::k_distanceRepresentatives); + assert_parsed_expression_simplify_to("_au", "1×_au"); + assert_parsed_expression_simplify_to("_ly", "1×_ly"); + assert_parsed_expression_simplify_to("_pc", "1×_pc"); + assert_parsed_expression_simplify_to("_in", "1×_in", User, Radian, Imperial); + assert_parsed_expression_simplify_to("_ft", "1×_ft", User, Radian, Imperial); + assert_parsed_expression_simplify_to("_yd", "1×_yd", User, Radian, Imperial); + assert_parsed_expression_simplify_to("_mi", "1×_mi", User, Radian, Imperial); + assert_parsed_unit_simplify_to_with_prefixes(Unit::k_massRepresentatives); + assert_parsed_expression_simplify_to("_oz", "1×_oz", User, Radian, Imperial); + assert_parsed_expression_simplify_to("_lb", "1×_lb", User, Radian, Imperial); + assert_parsed_expression_simplify_to("_shtn", "1×_shtn", User, Radian, Imperial); + assert_parsed_unit_simplify_to_with_prefixes(Unit::k_currentRepresentatives); + assert_parsed_unit_simplify_to_with_prefixes(Unit::k_temperatureRepresentatives); + assert_parsed_unit_simplify_to_with_prefixes(Unit::k_amountOfSubstanceRepresentatives); + assert_parsed_unit_simplify_to_with_prefixes(Unit::k_luminousIntensityRepresentatives); + assert_parsed_unit_simplify_to_with_prefixes(Unit::k_forceRepresentatives); + assert_parsed_unit_simplify_to_with_prefixes(Unit::k_pressureRepresentatives); + assert_parsed_unit_simplify_to_with_prefixes(Unit::k_pressureRepresentatives + 1); + assert_parsed_expression_simplify_to("_atm", "1×_atm"); + assert_parsed_unit_simplify_to_with_prefixes(Unit::k_energyRepresentatives); + assert_parsed_unit_simplify_to_with_prefixes(Unit::k_energyRepresentatives + 1); + assert_parsed_unit_simplify_to_with_prefixes(Unit::k_powerRepresentatives); + assert_parsed_unit_simplify_to_with_prefixes(Unit::k_electricChargeRepresentatives); + assert_parsed_unit_simplify_to_with_prefixes(Unit::k_electricPotentialRepresentatives); + assert_parsed_unit_simplify_to_with_prefixes(Unit::k_electricCapacitanceRepresentatives); + assert_parsed_unit_simplify_to_with_prefixes(Unit::k_electricResistanceRepresentatives); + assert_parsed_unit_simplify_to_with_prefixes(Unit::k_magneticFieldRepresentatives); + assert_parsed_unit_simplify_to_with_prefixes(Unit::k_magneticFluxRepresentatives); + assert_parsed_unit_simplify_to_with_prefixes(Unit::k_inductanceRepresentatives); + assert_parsed_unit_simplify_to_with_prefixes(Unit::k_catalyticActivityRepresentatives); - /* Units that do not appear as output yet */ + /* Units that do not appear as output */ + assert_parsed_expression_simplify_to("_t", "1000×_kg"); assert_parsed_expression_simplify_to("_Hz", "1×_s^\u0012-1\u0013"); assert_parsed_expression_simplify_to("_S", "1×_Ω^\u0012-1\u0013"); - assert_parsed_expression_simplify_to("_L", "0.001×_m^3"); + assert_parsed_expression_simplify_to("_L", "1×_dm^3"); assert_parsed_expression_simplify_to("_ha", "10000×_m^2"); + /* Imperial units */ + assert_parsed_expression_simplify_to("_lgtn", "1016.0469088×_kg"); + assert_parsed_expression_simplify_to("_lgtn", "1.12×_shtn", User, Radian, Imperial); + assert_parsed_expression_simplify_to("_in", "2.54×_cm"); + assert_parsed_expression_simplify_to("_in", "1×_in", User, Radian, Imperial); + assert_parsed_expression_simplify_to("_ft", "1×_ft", User, Radian, Imperial); + assert_parsed_expression_simplify_to("_yd", "1×_yd", User, Radian, Imperial); + /* Unit sum/subtract */ assert_parsed_expression_simplify_to("_m+_m", "2×_m"); assert_parsed_expression_simplify_to("_m-_m", "0×_m"); @@ -339,11 +387,12 @@ QUIZ_CASE(poincare_simplification_units) { assert_parsed_expression_simplify_to("_A^2×_s^4×_kg^(-1)×_m^(-3)", "1×_F×_m^\u0012-1\u0013"); // Vacuum magnetic permeability 𝝴0 assert_parsed_expression_simplify_to("_kg×_s^(-3)×_K^(-4)", "1×_K^\u0012-4\u0013×_kg×_s^\u0012-3\u0013"); // Stefan–Boltzmann constant _W×_m^-2×_K^-4 - /* Keep units for 0, infinity float results, Remove unit for undefined + /* Keep SI units for 0, infinity float results, Remove unit for undefined * expression */ assert_parsed_expression_simplify_to("0×_s", "0×_s"); + assert_parsed_expression_simplify_to("0×_tsp", "0×_m^3"); assert_parsed_expression_simplify_to("inf×_s", "inf×_s"); - assert_parsed_expression_simplify_to("-inf×_s", "-inf×_s"); + assert_parsed_expression_simplify_to("-inf×_oz", "-inf×_kg"); assert_parsed_expression_simplify_to("2_s+3_s-5_s", "0×_s"); assert_parsed_expression_simplify_to("normcdf(0,20,3)×_s", "13.083978345207×_ps"); assert_parsed_expression_simplify_to("log(0)×_s", "-inf×_s"); @@ -1426,7 +1475,7 @@ QUIZ_CASE(poincare_simplification_user_function_with_convert) { e = Store::Builder( UnitConvert::Builder( Rational::Builder(0), - Unit::Second()), + Unit::Builder(&Unit::k_timeRepresentatives[Unit::k_secondRepresentativeIndex], Unit::Prefix::EmptyPrefix())), Function::Builder( "f", 1, Symbol::Builder('x')));