From 9f40e45b20eee35a2b85ae50a8aa79ca5c1abc83 Mon Sep 17 00:00:00 2001 From: Gabriel Ozouf Date: Tue, 21 Jul 2020 11:45:50 +0200 Subject: [PATCH] [poincare/unit] Split for several dimensions A split (such as _h+_min+_s) can now be generated for distances, volumes and masses using imperial units. Change-Id: Ib3ad63614979eddd02fbe0e99f16cf09dcf7c1fc --- poincare/include/poincare/unit.h | 44 ++++++++++++++++++++++++++++++-- poincare/src/unit.cpp | 32 +++++++++++++++++++---- 2 files changed, 69 insertions(+), 7 deletions(-) diff --git a/poincare/include/poincare/unit.h b/poincare/include/poincare/unit.h index 82a45fb39..14cfe8377 100644 --- a/poincare/include/poincare/unit.h +++ b/poincare/include/poincare/unit.h @@ -505,11 +505,28 @@ public: 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) == 7, "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 * 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. @@ -821,6 +838,7 @@ public: 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]; static constexpr const Unit::Dimension * DimensionTableUpperBound = @@ -830,7 +848,12 @@ public: 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); } @@ -840,8 +863,19 @@ public: 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 Expression BuildSplit(double baseValue, const Unit * units, const double * conversionFactors, int numberOfUnits, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit); - static Expression BuildTimeSplit(double seconds, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit); + 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 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 bool IsSI(Expression & e); static bool IsSISpeed(Expression & e); @@ -866,6 +900,12 @@ private: 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 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); diff --git a/poincare/src/unit.cpp b/poincare/src/unit.cpp index f8e897616..ddb3c48cb 100644 --- a/poincare/src/unit.cpp +++ b/poincare/src/unit.cpp @@ -4,10 +4,11 @@ #include #include #include +#include #include #include #include -#include +#include #include #include #include @@ -514,7 +515,7 @@ bool Unit::IsSITime(Expression & e) { return e.type() == ExpressionNode::Type::Unit && static_cast(e).isSecond(); } -Expression Unit::BuildSplit(double baseValue, Unit const * units, double const * conversionFactors, const int numberOfUnits, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit 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]); @@ -545,16 +546,37 @@ Expression Unit::BuildSplit(double baseValue, Unit const * units, double const * } } - ExpressionNode::ReductionContext reductionContext(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::User, ExpressionNode::SymbolicComputation::ReplaceAllDefinedSymbolsWithDefinition, ExpressionNode::UnitConversion::None); + ExpressionNode::ReductionContext reductionContext(context, Preferences::ComplexFormat::Real, Preferences::AngleUnit::Degree, 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, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) { +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, complexFormat, angleUnit); + 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 = 2; + Unit units[numberOfUnits] = {Unit::Pound(), Unit::Ounce()}; + constexpr static double factors[numberOfUnits] = {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); } template Evaluation UnitNode::templatedApproximate(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const;