diff --git a/poincare/Makefile b/poincare/Makefile index 718600d50..c7bcf21b4 100644 --- a/poincare/Makefile +++ b/poincare/Makefile @@ -143,6 +143,7 @@ poincare_src += $(addprefix poincare/src/,\ trigonometry.cpp \ trigonometry_cheat_table.cpp \ undefined.cpp \ + unit.cpp \ unreal.cpp \ variable_context.cpp \ ) diff --git a/poincare/include/poincare/expression.h b/poincare/include/poincare/expression.h index 15792a564..2acf65668 100644 --- a/poincare/include/poincare/expression.h +++ b/poincare/include/poincare/expression.h @@ -99,6 +99,7 @@ class Expression : public TreeHandle { friend class Tangent; friend class Trigonometry; friend class TrigonometryCheatTable; + friend class Unit; friend class AdditionNode; friend class DerivativeNode; diff --git a/poincare/include/poincare/expression_node.h b/poincare/include/poincare/expression_node.h index d55e010ac..5ea4a93c3 100644 --- a/poincare/include/poincare/expression_node.h +++ b/poincare/include/poincare/expression_node.h @@ -103,6 +103,7 @@ public: MatrixTranspose, PredictionInterval, Matrix, + Unit, EmptyExpression }; diff --git a/poincare/include/poincare/unit.h b/poincare/include/poincare/unit.h new file mode 100644 index 000000000..a4e6cc1f1 --- /dev/null +++ b/poincare/include/poincare/unit.h @@ -0,0 +1,448 @@ +#ifndef POINCARE_UNIT_H +#define POINCARE_UNIT_H + +#include + +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. + * + * A standard unit is a derived unit, when defined from base units + * or otherwise a base unit (if no definition is provided). + * + * Each representative has + * - a root symbol + * - a definition + * - a list of allowed prefixes + * Given a Dimension, a representative in that Dimension and a Prefix + * allowed for that representative, one may get a symbol and an Expression. + */ + + class Prefix { + public: + constexpr Prefix(const char * symbol, int8_t exponent) : + m_symbol(symbol), + m_exponent(exponent) + {} + const char * symbol() const { return m_symbol; } + const int8_t exponent() const { return m_exponent; } + size_t serialize(char * buffer, size_t length) const; + private: + const char * m_symbol; + int8_t m_exponent; + }; + + class Representative { + public: + constexpr Representative(const char * rootSymbol, const char * definition, const Prefix * allowedPrefixes, size_t numberOfAllowedPrefixes) : + m_rootSymbol(rootSymbol), + m_definition(definition), + m_allowedPrefixes(allowedPrefixes), + m_allowedPrefixesUpperBound(allowedPrefixes + numberOfAllowedPrefixes) + { + } + const char * rootSymbol() const { return m_rootSymbol; } + const char * definition() const { return m_definition; } + const Prefix * allowedPrefixes() const { return m_allowedPrefixes; } + const Prefix * allowedPrefixesUpperBound() const { return m_allowedPrefixesUpperBound; } + bool canParse(const char * symbol, size_t length, + const Prefix * * prefix) const; + size_t serialize(char * buffer, size_t length, const Prefix * prefix) const; + private: + const char * m_rootSymbol; + const char * m_definition; + const Prefix * m_allowedPrefixes; + const Prefix * m_allowedPrefixesUpperBound; + }; + + class Dimension { + public: + constexpr Dimension(const Representative * representatives, size_t numberOfRepresentatives, const Prefix * stdRepresentativePrefix) : + m_representatives(representatives), + m_representativesUpperBound(representatives + numberOfRepresentatives), + m_stdRepresentativePrefix(stdRepresentativePrefix) + { + } + 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; + private: + const Representative * m_representatives; + const Representative * m_representativesUpperBound; + const Prefix * m_stdRepresentativePrefix; + }; + + UnitNode(const Dimension * dimension, const Representative * representative, const Prefix * prefix) : + ExpressionNode(), + m_dimension(dimension), + m_representative(representative), + m_prefix(prefix) + {} + + // TreeNode + size_t size() const override { return sizeof(UnitNode); } + int numberOfChildren() const override { return 0; } +#if POINCARE_TREE_LOG + virtual void logNodeName(std::ostream & stream) const override { + stream << "Unit"; + } + virtual void logAttributes(std::ostream & stream) const override { + stream << " symbol=\"" << m_prefix->symbol() << m_representative->rootSymbol() << "\""; + } +#endif + + // Expression Properties + Type type() const override { return Type::Unit; } + Sign sign(Context * context) const override; + + /* Layout */ + Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; + int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; + + /* Approximation */ + Evaluation approximate(SinglePrecision p, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, complexFormat, angleUnit); } + Evaluation approximate(DoublePrecision p, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, complexFormat, angleUnit); } + + // Comparison + int simplificationOrderSameType(const ExpressionNode * e, bool ascending, bool canBeInterrupted) const override; + + // Simplification + Expression shallowReduce(ReductionContext reductionContext) override; + 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 setPrefix(const Prefix * prefix) { m_prefix = prefix; } + +private: + const Dimension * m_dimension; + 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 { +public: + typedef UnitNode::Prefix Prefix; + typedef UnitNode::Representative Representative; + typedef UnitNode::Dimension Dimension; + static constexpr const Prefix + PicoPrefix = Prefix("p", -12), + NanoPrefix = Prefix("n", -9), + MicroPrefix = Prefix("u", -6), // FIXME μ + 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 + }, + NegativeLongScalePrefixes[] = { + PicoPrefix, + NanoPrefix, + MicroPrefix, + MilliPrefix, + EmptyPrefix, + }, + PositiveLongScalePrefixes[] = { + EmptyPrefix, + KiloPrefix, + MegaPrefix, + GigaPrefix, + TeraPrefix, + }, + AllPrefixes[] = { + PicoPrefix, + NanoPrefix, + MicroPrefix, + MilliPrefix, + CentiPrefix, + DeciPrefix, + EmptyPrefix, + DecaPrefix, + HectoPrefix, + KiloPrefix, + MegaPrefix, + GigaPrefix, + TeraPrefix, + }; + static constexpr size_t + NoPrefixCount = sizeof(NoPrefix)/sizeof(Prefix), + NegativeLongScalePrefixesCount = sizeof(NegativeLongScalePrefixes)/sizeof(Prefix), + PositiveLongScalePrefixesCount = sizeof(PositiveLongScalePrefixes)/sizeof(Prefix), + AllPrefixesCount = sizeof(AllPrefixes)/sizeof(Prefix); + static constexpr const Representative + TimeRepresentatives[] = { + Representative("s", nullptr, + NegativeLongScalePrefixes, NegativeLongScalePrefixesCount), + Representative("min", "60*s", + NoPrefix, NoPrefixCount), + Representative("h", "60*60*s", + NoPrefix, NoPrefixCount), + Representative("day", "24*60*60*s", + NoPrefix, NoPrefixCount), + Representative("week", "7*24*60*60*s", + NoPrefix, NoPrefixCount), + Representative("month", "30*7*24*60*60*s", + NoPrefix, NoPrefixCount), + Representative("year", "365.25*24*60*60*s", + NoPrefix, NoPrefixCount), + }, + DistanceRepresentatives[] = { + Representative("m", nullptr, + AllPrefixes, AllPrefixesCount), + Representative("ang", "10^-10*m", + NoPrefix, NoPrefixCount), //FIXME Codepoint + Representative("au", "149587870700*m", + NoPrefix, NoPrefixCount), + Representative("ly", "299792458*m/s*year", + NoPrefix, NoPrefixCount), + Representative("pc", "180*60*60/π*au", + NoPrefix, NoPrefixCount), + }, + MassRepresentatives[] = { + Representative("g", nullptr, + AllPrefixes, AllPrefixesCount), + Representative("t", "1000kg", + PositiveLongScalePrefixes, PositiveLongScalePrefixesCount), + Representative("Da", "(6.02214076*10^23*1000)^-1*kg", + NoPrefix, NoPrefixCount), + }, + CurrentRepresentatives[] = { + Representative("A", nullptr, + NegativeLongScalePrefixes, NegativeLongScalePrefixesCount), + }, + TemperatureRepresentatives[] = { + Representative("K", nullptr, + NoPrefix, NoPrefixCount), + }, + AmountOfSubstanceRepresentatives[] = { + Representative("mol", nullptr, + NegativeLongScalePrefixes, NegativeLongScalePrefixesCount), + }, + LuminousIntensityRepresentatives[] = { + Representative("cd", nullptr, + NoPrefix, NoPrefixCount), + }, + FrequencyRepresentatives[] = { + Representative("Hz", "s^-1", + PositiveLongScalePrefixes, PositiveLongScalePrefixesCount), + }, + ForceRepresentatives[] = { + Representative("N", "kg*m*s^-2", + AllPrefixes, AllPrefixesCount), + }, + PressureRepresentatives[] = { + Representative("Pa", "kg*m^-1*s^-2", + NoPrefix, NoPrefixCount), + Representative("bar", "1000hPa", + NoPrefix, NoPrefixCount), + Representative("atm", "101325Pa", + NoPrefix, NoPrefixCount), + }, + EnergyRepresentatives[] = { + Representative("J", "kg*m^2*s^-2", + AllPrefixes, AllPrefixesCount), + Representative("eV", "1.602176634*10^−19*J", + AllPrefixes, AllPrefixesCount), + }, + PowerRepresentatives[] = { + Representative("W", "kg*m^2*s^-3", + AllPrefixes, AllPrefixesCount), + }, + ElectricChargeRepresentatives[] = { + Representative("C", "A*s", + AllPrefixes, AllPrefixesCount), + }, + ElectricPotentialRepresentatives[] = { + Representative("V", "kg*m^2*s^-3*A^-1", + AllPrefixes, AllPrefixesCount), + }, + ElectricCapacitanceRepresentatives[] = { + Representative("F", "A^2*s^4*kg^-1*m^-2", + AllPrefixes, AllPrefixesCount), + }, + ElectricResistanceRepresentatives[] = { + Representative("Ohm", "kg*m^2*s^-3*A^-2", + AllPrefixes, AllPrefixesCount), //FIXME Omega CodePoint? + }, + ElectricConductanceRepresentatives[] = { + Representative("S", "A^2*s^3*kg^-1*m^-2", + AllPrefixes, AllPrefixesCount), + }, + MagneticFluxRepresentatives[] = { + Representative("Wb", "kg*m^2*s^-2*A^-1", + NoPrefix, NoPrefixCount), + }, + MagneticFieldRepresentatives[] = { + Representative("T", "kg*s^-2*A^-1", + NoPrefix, NoPrefixCount), + }, + InductanceRepresentatives[] = { + Representative("H", "kg*m^2*s^-2*A^-2", + NoPrefix, NoPrefixCount), + }, + CatalyticActivityRepresentatives[] = { + Representative("kat", "mol*s^-1", + NoPrefix, NoPrefixCount), + }, + SurfaceRepresentatives[] = { + Representative("ha", "10^4*m^2", + NoPrefix, NoPrefixCount), + }, + VolumeRepresentatives[] = { + Representative("L", "10^-3*m^3", + NoPrefix, NoPrefixCount), + }; + static constexpr const Dimension DimensionTable[] = { + /* The current table is sorted from most to least simple units. + * The order determines the behavior of simplification. + */ + Dimension( + TimeRepresentatives, + sizeof(TimeRepresentatives)/sizeof(Representative), + &EmptyPrefix + ), + Dimension( + DistanceRepresentatives, + sizeof(DistanceRepresentatives)/sizeof(Representative), + &EmptyPrefix + ), + Dimension( + MassRepresentatives, + sizeof(MassRepresentatives)/sizeof(Representative), + &KiloPrefix + ), + Dimension( + CurrentRepresentatives, + sizeof(CurrentRepresentatives)/sizeof(Representative), + &EmptyPrefix + ), + Dimension( + TemperatureRepresentatives, + sizeof(TemperatureRepresentatives)/sizeof(Representative), + &EmptyPrefix + ), + Dimension( + AmountOfSubstanceRepresentatives, + sizeof(AmountOfSubstanceRepresentatives)/sizeof(Representative), + &EmptyPrefix + ), + Dimension( + LuminousIntensityRepresentatives, + sizeof(LuminousIntensityRepresentatives)/sizeof(Representative), + &EmptyPrefix + ), + Dimension( + FrequencyRepresentatives, + sizeof(FrequencyRepresentatives)/sizeof(Representative), + &EmptyPrefix + ), + Dimension( + ForceRepresentatives, + sizeof(ForceRepresentatives)/sizeof(Representative), + &EmptyPrefix + ), + Dimension( + PressureRepresentatives, + sizeof(PressureRepresentatives)/sizeof(Representative), + &EmptyPrefix + ), + Dimension( + EnergyRepresentatives, + sizeof(EnergyRepresentatives)/sizeof(Representative), + &EmptyPrefix + ), + Dimension( + PowerRepresentatives, + sizeof(PowerRepresentatives)/sizeof(Representative), + &EmptyPrefix + ), + Dimension( + ElectricChargeRepresentatives, + sizeof(ElectricChargeRepresentatives)/sizeof(Representative), + &EmptyPrefix + ), + Dimension( + ElectricPotentialRepresentatives, + sizeof(ElectricPotentialRepresentatives)/sizeof(Representative), + &EmptyPrefix + ), + Dimension( + ElectricCapacitanceRepresentatives, + sizeof(ElectricCapacitanceRepresentatives)/sizeof(Representative), + &EmptyPrefix + ), + Dimension( + ElectricResistanceRepresentatives, + sizeof(ElectricResistanceRepresentatives)/sizeof(Representative), + &EmptyPrefix + ), + Dimension( + ElectricConductanceRepresentatives, + sizeof(ElectricConductanceRepresentatives)/sizeof(Representative), + &EmptyPrefix + ), + Dimension( + MagneticFluxRepresentatives, + sizeof(MagneticFluxRepresentatives)/sizeof(Representative), + &EmptyPrefix + ), + Dimension( + MagneticFieldRepresentatives, + sizeof(MagneticFieldRepresentatives)/sizeof(Representative), + &EmptyPrefix + ), + Dimension( + InductanceRepresentatives, + sizeof(InductanceRepresentatives)/sizeof(Representative), + &EmptyPrefix + ), + Dimension( + CatalyticActivityRepresentatives, + sizeof(CatalyticActivityRepresentatives)/sizeof(Representative), + &EmptyPrefix + ), + Dimension( + SurfaceRepresentatives, + sizeof(SurfaceRepresentatives)/sizeof(Representative), + &EmptyPrefix + ), + Dimension( + VolumeRepresentatives, + sizeof(VolumeRepresentatives)/sizeof(Representative), + &EmptyPrefix + ), + }; + 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); + + Unit(const UnitNode * node) : Expression(node) {} + static Unit Builder(const Dimension * dimension, const Representative * representative, const Prefix * prefix); + + // Simplification + Expression shallowReduce(ExpressionNode::ReductionContext reductionContext); + Expression shallowBeautify(ExpressionNode::ReductionContext reductionContext); +}; + +} + +#endif diff --git a/poincare/include/poincare_nodes.h b/poincare/include/poincare_nodes.h index 5c84c5ec9..c0b5a00f7 100644 --- a/poincare/include/poincare_nodes.h +++ b/poincare/include/poincare_nodes.h @@ -83,6 +83,7 @@ #include #include #include +#include #include #include diff --git a/poincare/src/unit.cpp b/poincare/src/unit.cpp new file mode 100644 index 000000000..97a715817 --- /dev/null +++ b/poincare/src/unit.cpp @@ -0,0 +1,240 @@ +#include +#include +#include +#include +#include +#include +#include + +namespace Poincare { + +static inline int minInt(int x, int y) { return x < y ? x : y; } + +size_t UnitNode::Prefix::serialize(char * buffer, size_t length) const { + return minInt(strlcpy(buffer, m_symbol, length), length - 1); +} + +bool UnitNode::Representative::canParse(const char * symbol, size_t length, + const Prefix * * prefix) const +{ + const Prefix * pre = m_allowedPrefixes; + while (pre < m_allowedPrefixesUpperBound) { + const char * prefixSymbol = pre->symbol(); + if (strncmp(symbol, prefixSymbol, length) == 0 && + prefixSymbol[length] == 0) + { + *prefix = pre; + return true; + } + pre++; + } + return false; +} + +size_t UnitNode::Representative::serialize(char * buffer, size_t length, const Prefix * prefix) const { + size_t prefixLength = prefix->serialize(buffer, length); + buffer += prefixLength; + length -= prefixLength; + return prefixLength + minInt(strlcpy(buffer, m_rootSymbol, length), length - 1); +} + +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(); + 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; + return true; + } + rep++; + } + return false; +} + +ExpressionNode::Sign UnitNode::sign(Context * context) const { + return Sign::Positive; +} + +int UnitNode::simplificationOrderSameType(const ExpressionNode * e, bool ascending, bool canBeInterrupted) const { + if (!ascending) { + return e->simplificationOrderSameType(this, true, canBeInterrupted); + } + assert(type() == e->type()); + const UnitNode * eNode = static_cast(e); + const ptrdiff_t dimdiff = eNode->dimension() - m_dimension; + if (dimdiff != 0) { + return dimdiff; + } + const ptrdiff_t repdiff = eNode->representative() - m_representative; + if (repdiff != 0) { + return repdiff; + } + const ptrdiff_t prediff = eNode->prefix() - m_prefix; + return prediff; +} + +Layout UnitNode::createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const { + static constexpr size_t bufferSize = 10; + char buffer[bufferSize]; + int length = serialize(buffer, bufferSize, floatDisplayMode, numberOfSignificantDigits); + assert(length < bufferSize); + return LayoutHelper::String(buffer, length); +} + +int UnitNode::serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const { + return m_representative->serialize(buffer, bufferSize, m_prefix); +} + +template +Evaluation UnitNode::templatedApproximate(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { + return Complex::Undefined(); +} + +Expression UnitNode::shallowReduce(ReductionContext reductionContext) { + return Unit(this).shallowReduce(reductionContext); +} + +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 + Unit::NoPrefix[], + Unit::NegativeLongScalePrefixes[], + Unit::PositiveLongScalePrefixes[], + Unit::AllPrefixes[]; +constexpr size_t + Unit::NoPrefixCount, + Unit::NegativeLongScalePrefixesCount, + Unit::PositiveLongScalePrefixesCount, + Unit::AllPrefixesCount; +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[]; +constexpr const Unit::Dimension Unit::DimensionTable[]; +constexpr const Unit::Dimension * Unit::DimensionTableUpperBound; + +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; + 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); +} + +Expression Unit::shallowReduce(ExpressionNode::ReductionContext reductionContext) { + UnitNode * unitNode = static_cast(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 = *this; + if (rep->definition() != nullptr) { + result = Expression::Parse(rep->definition(), nullptr).deepReduce(reductionContext); + } + if (prefixMultiplier != 0) { + Expression multiplier = Power::Builder(Rational::Builder(10), Rational::Builder(prefixMultiplier)); + if (result.type() != ExpressionNode::Type::Multiplication) { + result = Multiplication::Builder(multiplier, result.clone()); + } else { + static_cast(result).addChildAtIndexInPlace( + multiplier, + 0, + result.numberOfChildren()); + } + } + replaceWithInPlace(result); + return result; +} + +Expression Unit::shallowBeautify(ExpressionNode::ReductionContext reductionContext) { + Expression ancestor = parent(); + // Check that the exponent, if any, of a Unit is an integer + if (!ancestor.isUninitialized() && ancestor.type() == ExpressionNode::Type::Power) { + Expression exponent = ancestor.childAtIndex(1); + if (!(exponent.type() == ExpressionNode::Type::Rational && static_cast(exponent).isInteger())) { + goto UnitCheckUnsuccessful; + } + ancestor = ancestor.parent(); + } + /* Check homogeneity: at this point, ancestor must be + * - either uninitialized + * - or a Multiplication whose parent is uninitialized. + */ + if (!ancestor.isUninitialized() && ancestor.type() == ExpressionNode::Type::Multiplication) { + ancestor = ancestor.parent(); + } + if (ancestor.isUninitialized()) { + return *this; + } + UnitCheckUnsuccessful: + /* If the latter checks are not successfully passed, then the function + * returns replaceWithUndefinedInPlace. + * TODO Something else should be returned in order to report a more + * specific error. For instance: inhomogeneous expression. + */ + return replaceWithUndefinedInPlace(); +} + +template Evaluation UnitNode::templatedApproximate(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; +template Evaluation UnitNode::templatedApproximate(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; + +}