mirror of
https://github.com/UpsilonNumworks/Upsilon.git
synced 2026-01-19 00:37:25 +01:00
[poincare] Partially implement simplification for UnitConversion::Classic
This commit is contained in:
@@ -414,6 +414,9 @@ public:
|
||||
Representative::Prefixable::Yes,
|
||||
NegativePrefixes),
|
||||
};
|
||||
static const Representative constexpr * SecondRepresentative = &TimeRepresentatives[0];
|
||||
static const Representative constexpr * HourRepresentative = &TimeRepresentatives[2];
|
||||
static const Representative constexpr * MeterRepresentative = &DistanceRepresentatives[0];
|
||||
static constexpr const Dimension DimensionTable[] = {
|
||||
/* The current table is sorted from most to least simple units.
|
||||
* The order determines the behavior of simplification.
|
||||
@@ -718,6 +721,9 @@ public:
|
||||
&EmptyPrefix
|
||||
),
|
||||
};
|
||||
static const Dimension constexpr * TimeDimension = &DimensionTable[0] ;
|
||||
static const Dimension constexpr * DistanceDimension = &DimensionTable[1];
|
||||
|
||||
static constexpr const Unit::Dimension * DimensionTableUpperBound =
|
||||
DimensionTable + sizeof(DimensionTable)/sizeof(Dimension);
|
||||
static bool CanParse(const char * symbol, size_t length,
|
||||
@@ -725,11 +731,18 @@ public:
|
||||
|
||||
Unit(const UnitNode * node) : Expression(node) {}
|
||||
static Unit Builder(const Dimension * dimension, const Representative * representative, const Prefix * prefix);
|
||||
static Unit Kilometer() { return Builder(DistanceDimension, MeterRepresentative, &KiloPrefix); }
|
||||
static Unit Hour() { return Builder(TimeDimension, HourRepresentative, &EmptyPrefix); }
|
||||
|
||||
static bool IsISSpeed(Expression & e);
|
||||
bool isMeter() const;
|
||||
bool isSecond() const;
|
||||
|
||||
// Simplification
|
||||
Expression shallowReduce(ExpressionNode::ReductionContext reductionContext);
|
||||
void chooseBestMultipleForValue(double & value, const int exponent, ExpressionNode::ReductionContext reductionContext);
|
||||
|
||||
static constexpr double MeterPerSecondToKilometerPerHourFactor = 60.0*60.0/1000.0;
|
||||
private:
|
||||
UnitNode * node() const { return static_cast<UnitNode *>(Expression::node()); }
|
||||
Expression removeUnit(Expression * unit);
|
||||
|
||||
@@ -355,12 +355,13 @@ Expression Multiplication::shallowBeautify(ExpressionNode::ReductionContext redu
|
||||
return std::move(o);
|
||||
}
|
||||
|
||||
Expression result;
|
||||
|
||||
// Step 2: Handle the units
|
||||
Expression self = *this;
|
||||
Expression units;
|
||||
self = removeUnit(&units);
|
||||
|
||||
Expression result;
|
||||
if (!units.isUninitialized()) {
|
||||
ExpressionNode::UnitConversion unitConversionMode = reductionContext.unitConversion();
|
||||
if (unitConversionMode == ExpressionNode::UnitConversion::Default || unitConversionMode == ExpressionNode::UnitConversion::Classic) {
|
||||
@@ -438,48 +439,56 @@ Expression Multiplication::shallowBeautify(ExpressionNode::ReductionContext redu
|
||||
// If the value is undefined, return "undef" without any unit
|
||||
result = Undefined::Builder();
|
||||
} else {
|
||||
if (unitConversionMode == ExpressionNode::UnitConversion::Classic) {
|
||||
// TODO create new result 1h 23min 24secondes ?
|
||||
// Utiliser store ?
|
||||
} else {
|
||||
// Find the right unit prefix when the value ≠ 0
|
||||
if (unitConversionMode == ExpressionNode::UnitConversion::Default && value != 0.0 && value != 1.0 && !std::isinf(value)) {
|
||||
// Identify the first Unit factor and its exponent
|
||||
Expression firstFactor = units;
|
||||
int exponent = 1;
|
||||
if (firstFactor.type() == ExpressionNode::Type::Multiplication) {
|
||||
firstFactor = firstFactor.childAtIndex(0);
|
||||
}
|
||||
if (firstFactor.type() == ExpressionNode::Type::Power) {
|
||||
Expression exp = firstFactor.childAtIndex(1);
|
||||
firstFactor = firstFactor.childAtIndex(0);
|
||||
assert(exp.type() == ExpressionNode::Type::Rational && static_cast<Rational &>(exp).isInteger());
|
||||
Integer expInt = static_cast<Rational &>(exp).signedIntegerNumerator();
|
||||
if (expInt.isLowerThan(Integer(Integer::k_maxExtractableInteger))) {
|
||||
exponent = expInt.extractedInt();
|
||||
} else {
|
||||
// The exponent is too large to be extracted, so do not try to use it.
|
||||
exponent = 0;
|
||||
}
|
||||
}
|
||||
assert(firstFactor.type() == ExpressionNode::Type::Unit);
|
||||
// Choose its multiple and update value accordingly
|
||||
if (exponent != 0) {
|
||||
static_cast<Unit&>(firstFactor).chooseBestMultipleForValue(value, exponent, reductionContext);
|
||||
}
|
||||
} else if (unitConversionMode == ExpressionNode::UnitConversion::Classic) {
|
||||
if (Unit::IsISSpeed(units)) {
|
||||
value *= Unit::MeterPerSecondToKilometerPerHourFactor;
|
||||
units = Multiplication::Builder(
|
||||
Unit::Kilometer(),
|
||||
Power::Builder(
|
||||
Unit::Hour(),
|
||||
Rational::Builder(-1)
|
||||
)
|
||||
);
|
||||
}
|
||||
// TODO: what to do if no classic conversion?
|
||||
}
|
||||
if (result.isUninitialized()) {
|
||||
// Build final Expression
|
||||
Expression resultWithoutUnit;
|
||||
if (std::isinf(value)) {
|
||||
resultWithoutUnit = Infinity::Builder(value < 0.0);
|
||||
} else {
|
||||
// Find the right unit prefix when the value ≠ 0
|
||||
if (unitConversionMode == ExpressionNode::UnitConversion::Default && value != 0.0 && value != 1.0) {
|
||||
// Identify the first Unit factor and its exponent
|
||||
Expression firstFactor = units;
|
||||
int exponent = 1;
|
||||
if (firstFactor.type() == ExpressionNode::Type::Multiplication) {
|
||||
firstFactor = firstFactor.childAtIndex(0);
|
||||
}
|
||||
if (firstFactor.type() == ExpressionNode::Type::Power) {
|
||||
Expression exp = firstFactor.childAtIndex(1);
|
||||
firstFactor = firstFactor.childAtIndex(0);
|
||||
assert(exp.type() == ExpressionNode::Type::Rational && static_cast<Rational &>(exp).isInteger());
|
||||
Integer expInt = static_cast<Rational &>(exp).signedIntegerNumerator();
|
||||
if (expInt.isLowerThan(Integer(Integer::k_maxExtractableInteger))) {
|
||||
exponent = expInt.extractedInt();
|
||||
} else {
|
||||
// The exponent is too large to be extracted, so do not try to use it.
|
||||
exponent = 0;
|
||||
}
|
||||
}
|
||||
assert(firstFactor.type() == ExpressionNode::Type::Unit);
|
||||
// Choose its multiple and update value accordingly
|
||||
if (exponent != 0) {
|
||||
static_cast<Unit&>(firstFactor).chooseBestMultipleForValue(value, exponent, reductionContext);
|
||||
}
|
||||
}
|
||||
resultWithoutUnit = Float<double>::Builder(value);
|
||||
}
|
||||
// Build final Expression
|
||||
result = Multiplication::Builder(resultWithoutUnit, units);
|
||||
static_cast<Multiplication &>(result).mergeSameTypeChildrenInPlace();
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
// Step 3: Create a Division if relevant
|
||||
Expression numer, denom;
|
||||
|
||||
@@ -259,7 +259,12 @@ constexpr const Unit::Representative
|
||||
Unit::CatalyticActivityRepresentatives[],
|
||||
Unit::SurfaceRepresentatives[],
|
||||
Unit::VolumeRepresentatives[];
|
||||
const Unit::Representative constexpr * Unit::SecondRepresentative;
|
||||
const Unit::Representative constexpr * Unit::HourRepresentative;
|
||||
const Unit::Representative constexpr * Unit::MeterRepresentative;
|
||||
constexpr const Unit::Dimension Unit::DimensionTable[];
|
||||
const Unit::Dimension constexpr * Unit::TimeDimension;
|
||||
const Unit::Dimension constexpr * Unit::DistanceDimension;
|
||||
constexpr const Unit::Dimension * Unit::DimensionTableUpperBound;
|
||||
|
||||
bool Unit::CanParse(const char * symbol, size_t length,
|
||||
@@ -348,6 +353,23 @@ Expression Unit::removeUnit(Expression * unit) {
|
||||
return one;
|
||||
}
|
||||
|
||||
bool Unit::isSecond() const {
|
||||
return node()->dimension() == TimeDimension && node()->representative() == SecondRepresentative && node()->prefix() == &EmptyPrefix;
|
||||
}
|
||||
|
||||
bool Unit::isMeter() const {
|
||||
return node()->dimension() == DistanceDimension && node()->representative() == MeterRepresentative && node()->prefix() == &EmptyPrefix;
|
||||
}
|
||||
|
||||
bool Unit::IsISSpeed(Expression & e) {
|
||||
// Form m*s^-1
|
||||
return e.type() == ExpressionNode::Type::Multiplication && e.numberOfChildren() == 2 &&
|
||||
e.childAtIndex(0).type() == ExpressionNode::Type::Unit && e.childAtIndex(0).convert<Unit>().isMeter() &&
|
||||
e.childAtIndex(1).type() == ExpressionNode::Type::Power &&
|
||||
e.childAtIndex(1).childAtIndex(1).type() == ExpressionNode::Type::Rational && e.childAtIndex(1).childAtIndex(1).convert<const Rational>().isMinusOne() &&
|
||||
e.childAtIndex(1).childAtIndex(0).type() == ExpressionNode::Type::Unit && e.childAtIndex(1).childAtIndex(0).convert<Unit>().isSecond();
|
||||
}
|
||||
|
||||
template Evaluation<float> UnitNode::templatedApproximate<float>(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const;
|
||||
template Evaluation<double> UnitNode::templatedApproximate<double>(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const;
|
||||
|
||||
|
||||
@@ -1284,7 +1284,7 @@ QUIZ_CASE(poincare_simplification_unit_conversion) {
|
||||
assert_parsed_expression_simplify_to("1000000_cm", "10×_km", User, Degree, Cartesian, ReplaceAllDefinedSymbolsWithDefinition, DefaultUnitConversion);
|
||||
assert_parsed_expression_simplify_to("1000000_cm", "1000000×_cm", User, Degree, Cartesian, ReplaceAllDefinedSymbolsWithDefinition, NoUnitConversion);
|
||||
assert_parsed_expression_simplify_to("1000000_cm", "10000×_m", User, Degree, Cartesian, ReplaceAllDefinedSymbolsWithDefinition, InternationalSystemUnitConversion);
|
||||
//assert_parsed_expression_simplify_to("10_m/h", "0.01km/h", SystemForApproximation, Degree, Cartesian, ReplaceAllDefinedSymbolsWithDefinition, ClassicUnitConversion);
|
||||
assert_parsed_expression_simplify_to("10_m/_h", "0.01×_km×_h^\x12-1\x13", User, Degree, Cartesian, ReplaceAllDefinedSymbolsWithDefinition, ClassicUnitConversion);
|
||||
}
|
||||
|
||||
QUIZ_CASE(poincare_simplification_user_function) {
|
||||
|
||||
Reference in New Issue
Block a user