Files
Upsilon/poincare/src/unit_convert.cpp
2020-12-22 17:32:40 +01:00

140 lines
5.6 KiB
C++

#include <poincare/unit_convert.h>
#include <poincare/complex.h>
#include <poincare/context.h>
#include <poincare/division.h>
#include <poincare/float.h>
#include <poincare/infinity.h>
#include <poincare/multiplication.h>
#include <poincare/symbol.h>
#include <poincare/undefined.h>
#include <assert.h>
#include <math.h>
#include <stdlib.h>
namespace Poincare {
Expression UnitConvertNode::removeUnit(Expression * unit) {
/* Warning: removeUnit of a UnitConvert doesn't make much sense but we
* implement a 'dummy' version since UnitConvert still exists among the
* reduced expression. */
childAtIndex(1)->removeUnit(unit);
return UnitConvert(this).replaceWithUndefinedInPlace();
}
Expression UnitConvertNode::shallowBeautify(ReductionContext * reductionContext) {
return UnitConvert(this).shallowBeautify(reductionContext);
}
void UnitConvertNode::deepReduceChildren(ExpressionNode::ReductionContext reductionContext) {
UnitConvert(this).deepReduceChildren(reductionContext);
}
void UnitConvertNode::deepBeautifyChildren(ExpressionNode::ReductionContext reductionContext) {
UnitConvert(this).deepBeautifyChildren(reductionContext);
}
template<typename T>
Evaluation<T> UnitConvertNode::templatedApproximate(ApproximationContext approximationContext) const {
/* If we are here, it means that the unit convert node was not shallowReduced.
* Otherwise, it would have been replaced by the division of the value by the
* unit. We thus return Undefined. */
return Complex<T>::Undefined();
}
void UnitConvert::deepReduceChildren(ExpressionNode::ReductionContext reductionContext) {
childAtIndex(0).deepReduce(reductionContext);
ExpressionNode::ReductionContext reductionContextKeepUnitAsIs = ExpressionNode::ReductionContext(
reductionContext.context(),
reductionContext.complexFormat(),
reductionContext.angleUnit(),
reductionContext.unitFormat(),
reductionContext.target(),
ExpressionNode::SymbolicComputation::ReplaceAllSymbolsWithUndefined,
ExpressionNode::UnitConversion::None);
// Don't transform the targeted unit
childAtIndex(1).deepReduce(reductionContextKeepUnitAsIs);
}
void UnitConvert::deepBeautifyChildren(ExpressionNode::ReductionContext reductionContext) {
ExpressionNode::ReductionContext reductionContextKeepUnitAsIs = ExpressionNode::ReductionContext(
reductionContext.context(),
reductionContext.complexFormat(),
reductionContext.angleUnit(),
reductionContext.unitFormat(),
reductionContext.target(),
reductionContext.symbolicComputation(),
ExpressionNode::UnitConversion::None);
defaultDeepBeautifyChildren(reductionContextKeepUnitAsIs);
}
Expression UnitConvert::shallowBeautify(ExpressionNode::ReductionContext * reductionContext) {
// Discard cases like 4 -> _m/_km
{
ExpressionNode::ReductionContext reductionContextWithUnits = ExpressionNode::ReductionContext(
reductionContext->context(),
reductionContext->complexFormat(),
reductionContext->angleUnit(),
reductionContext->unitFormat(),
reductionContext->target(),
ExpressionNode::SymbolicComputation::ReplaceAllSymbolsWithUndefined);
Expression unit;
Expression childWithoutUnit = childAtIndex(1).clone().reduceAndRemoveUnit(reductionContextWithUnits, &unit);
if (childWithoutUnit.isUndefined() || unit.isUninitialized()) {
// There is no unit on the right
return replaceWithUndefinedInPlace();
}
}
// Find the unit
Expression unit;
childAtIndex(1).removeUnit(&unit);
if (unit.isUninitialized()) {
// There is no unit on the right
return replaceWithUndefinedInPlace();
}
/* Handle temperatures, as converting between Kelvin, Celsius and Fahrenheit
* cannot be done with a division. */
if (unit.type() == ExpressionNode::Type::Unit) {
Unit unitRef = static_cast<Unit &>(unit);
if (unitRef.representative()->dimensionVector() == Unit::TemperatureRepresentative::Default().dimensionVector()) {
Expression result = Unit::ConvertTemperatureUnits(childAtIndex(0), unitRef, *reductionContext);
replaceWithInPlace(result);
return result;
}
}
// Divide the left member by the new unit
Expression division = Division::Builder(childAtIndex(0), unit.clone());
Expression divisionUnit;
division = division.reduceAndRemoveUnit(*reductionContext, &divisionUnit);
if (!divisionUnit.isUninitialized()) {
// The left and right members are not homogeneous
return replaceWithUndefinedInPlace();
}
Expression result = Multiplication::Builder(division, unit);
replaceWithInPlace(result);
ExpressionNode::ReductionContext reductionContextWithoutUnits = ExpressionNode::ReductionContext(
reductionContext->context(),
reductionContext->complexFormat(),
reductionContext->angleUnit(),
reductionContext->unitFormat(),
reductionContext->target(),
ExpressionNode::SymbolicComputation::ReplaceAllSymbolsWithUndefined,
ExpressionNode::UnitConversion::None);
result = result.shallowReduce(reductionContextWithoutUnits);
result = result.shallowBeautify(&reductionContextWithoutUnits);
*reductionContext = ExpressionNode::ReductionContext(
reductionContext->context(),
reductionContext->complexFormat(),
reductionContext->angleUnit(),
reductionContext->unitFormat(),
reductionContext->target(),
reductionContext->symbolicComputation(),
ExpressionNode::UnitConversion::None);
return result;
}
template Evaluation<float> UnitConvertNode::templatedApproximate<float>(ApproximationContext approximationContext) const;
template Evaluation<double> UnitConvertNode::templatedApproximate<double>(ApproximationContext approximationContext) const;
}