mirror of
https://github.com/UpsilonNumworks/Upsilon.git
synced 2026-01-19 00:37:25 +01:00
When tuning the representative without tuning the prefix, the prefix would not be reset to its default value, causing a problem for masses, whose base prefix depend on the representative (k for grams, none for the rest). Fixes a crash when typing sqrt(_N) with country set to US. Change-Id: Iade900de5fb31e06fdfafef34ae27da3f36983fa
943 lines
44 KiB
C++
943 lines
44 KiB
C++
#include <poincare/unit.h>
|
|
#include <poincare/addition.h>
|
|
#include <poincare/float.h>
|
|
#include <poincare/layout_helper.h>
|
|
#include <poincare/multiplication.h>
|
|
#include <poincare/power.h>
|
|
#include <poincare/rational.h>
|
|
#include <poincare/undefined.h>
|
|
#include <algorithm>
|
|
#include <assert.h>
|
|
#include <limits.h>
|
|
#include <utility>
|
|
|
|
namespace Poincare {
|
|
|
|
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_kelvinRepresentativeIndex,
|
|
Unit::k_celsiusRepresentativeIndex,
|
|
Unit::k_fahrenheitRepresentativeIndex,
|
|
Unit::k_electronVoltRepresentativeIndex,
|
|
Unit::k_wattRepresentativeIndex,
|
|
Unit::k_hectareRepresentativeIndex,
|
|
Unit::k_acreRepresentativeIndex,
|
|
Unit::k_literRepresentativeIndex,
|
|
Unit::k_cupRepresentativeIndex,
|
|
Unit::k_pintRepresentativeIndex,
|
|
Unit::k_quartRepresentativeIndex,
|
|
Unit::k_gallonRepresentativeIndex;
|
|
|
|
// UnitNode::Prefix
|
|
const UnitNode::Prefix * UnitNode::Prefix::Prefixes() {
|
|
return Unit::k_prefixes;
|
|
}
|
|
|
|
const UnitNode::Prefix * UnitNode::Prefix::EmptyPrefix() {
|
|
return Prefixes() + Unit::k_emptyPrefixIndex;
|
|
}
|
|
|
|
// UnitNode::Vector
|
|
template<>
|
|
size_t UnitNode::Vector<int>::supportSize() const {
|
|
size_t supportSize = 0;
|
|
for (int i = 0; i < k_numberOfBaseUnits; i++) {
|
|
if (coefficientAtIndex(i) == 0) {
|
|
continue;
|
|
}
|
|
supportSize++;
|
|
}
|
|
return supportSize;
|
|
}
|
|
|
|
template<>
|
|
void UnitNode::Vector<int>::addAllCoefficients(const Vector<int> other, int factor) {
|
|
for (int i = 0; i < UnitNode::k_numberOfBaseUnits; i++) {
|
|
setCoefficientAtIndex(i, coefficientAtIndex(i) + other.coefficientAtIndex(i) * factor);
|
|
}
|
|
}
|
|
|
|
template<>
|
|
UnitNode::Vector<int> UnitNode::Vector<int>::FromBaseUnits(const Expression baseUnits) {
|
|
/* Returns the vector of Base units with integer exponents. If rational, the
|
|
* closest integer will be used. */
|
|
Vector<int> vector = {
|
|
.time = 0,
|
|
.distance = 0,
|
|
.mass = 0,
|
|
.current = 0,
|
|
.temperature = 0,
|
|
.amountOfSubstance = 0,
|
|
.luminuousIntensity = 0,
|
|
};
|
|
int numberOfFactors;
|
|
int factorIndex = 0;
|
|
Expression factor;
|
|
if (baseUnits.type() == ExpressionNode::Type::Multiplication) {
|
|
numberOfFactors = baseUnits.numberOfChildren();
|
|
factor = baseUnits.childAtIndex(0);
|
|
} else {
|
|
numberOfFactors = 1;
|
|
factor = baseUnits;
|
|
}
|
|
do {
|
|
// Get the unit's exponent
|
|
int exponent = 1;
|
|
if (factor.type() == ExpressionNode::Type::Power) {
|
|
Expression exp = factor.childAtIndex(1);
|
|
assert(exp.type() == ExpressionNode::Type::Rational);
|
|
// Using the closest integer to the exponent.
|
|
float exponentFloat = static_cast<const Rational &>(exp).node()->templatedApproximate<float>();
|
|
if (exponentFloat != std::round(exponentFloat)) {
|
|
/* If non-integer exponents are found, we round a null vector so that
|
|
* Multiplication::shallowBeautify will not attempt to find derived
|
|
* units. */
|
|
return vector;
|
|
}
|
|
/* We limit to INT_MAX / 3 because an exponent might get bigger with
|
|
* simplification. As a worst case scenario, (_s²_m²_kg/_A²)^n should be
|
|
* simplified to (_s^5_S)^n. If 2*n is under INT_MAX, 5*n might not. */
|
|
if (std::fabs(exponentFloat) < INT_MAX / 3) {
|
|
// Exponent can be safely casted as int
|
|
exponent = static_cast<int>(std::round(exponentFloat));
|
|
assert(std::fabs(exponentFloat - static_cast<float>(exponent)) <= 0.5f);
|
|
} else {
|
|
/* Base units vector will ignore this coefficient, to avoid exponent
|
|
* overflow. In any way, shallowBeautify will conserve homogeneity. */
|
|
exponent = 0;
|
|
}
|
|
factor = factor.childAtIndex(0);
|
|
}
|
|
// Fill the vector with the unit's exponent
|
|
assert(factor.type() == ExpressionNode::Type::Unit);
|
|
vector.addAllCoefficients(static_cast<Unit &>(factor).node()->representative()->dimensionVector(), exponent);
|
|
if (++factorIndex >= numberOfFactors) {
|
|
break;
|
|
}
|
|
factor = baseUnits.childAtIndex(factorIndex);
|
|
} while (true);
|
|
return vector;
|
|
}
|
|
|
|
template<>
|
|
Expression UnitNode::Vector<int>::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<Multiplication &>(result).addChildAtIndexInPlace(e, numberOfChildren, numberOfChildren);
|
|
numberOfChildren++;
|
|
}
|
|
assert(numberOfChildren > 0);
|
|
result = static_cast<Multiplication &>(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<int> 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<float>()) {
|
|
order = 0.0f;
|
|
}
|
|
if (std::fabs(otherOrder) < Expression::Epsilon<float>()) {
|
|
otherOrder = 0.0f;
|
|
}
|
|
if (std::fabs(std::fabs(order) - std::fabs(otherOrder)) <= 3.0f + Expression::Epsilon<float>() && 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<int>(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<int>(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
|
|
&& candidate[i].canParse(symbol, potentialPrefixLength, prefix)) {
|
|
*representative = (candidate + i);
|
|
return true;
|
|
}
|
|
}
|
|
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 (strlen(prefixSymbol) == length
|
|
&& 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<double>::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<double>()) {
|
|
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);
|
|
if (reductionContext.unitFormat() == Preferences::UnitFormat::Metric) {
|
|
return 0;
|
|
}
|
|
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, sizeof(splitUnits)/sizeof(Unit), 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);
|
|
if (reductionContext.unitFormat() == Preferences::UnitFormat::Metric) {
|
|
return 0;
|
|
}
|
|
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, sizeof(splitUnits)/sizeof(Unit), reductionContext);
|
|
return 1;
|
|
}
|
|
|
|
double UnitNode::TemperatureRepresentative::ConvertTemperatures(double value, const Representative * source, const Representative * target) {
|
|
assert(source->dimensionVector() == TemperatureRepresentative::Default().dimensionVector());
|
|
assert(target->dimensionVector() == TemperatureRepresentative::Default().dimensionVector());
|
|
if (source == target) {
|
|
return value;
|
|
}
|
|
constexpr double origin[] = {0, k_celsiusOrigin, k_fahrenheitOrigin};
|
|
assert(sizeof(origin) == source->numberOfRepresentatives() * sizeof(double));
|
|
double sourceOrigin = origin[source - source->representativesOfSameDimension()];
|
|
double targetOrigin = origin[target - target->representativesOfSameDimension()];
|
|
/* (T + origin) * ration converts T to Kelvin.
|
|
* T/ratio - origin converts T from Kelvin. */
|
|
return (value + sourceOrigin) * source->ratio() / target->ratio() - targetOrigin;
|
|
}
|
|
|
|
int UnitNode::TemperatureRepresentative::setAdditionalExpressions(double value, Expression * dest, int availableLength, ExpressionNode::ReductionContext reductionContext) const {
|
|
assert(availableLength >= 2);
|
|
const Representative * celsius = TemperatureRepresentative::Default().representativesOfSameDimension() + Unit::k_celsiusRepresentativeIndex;
|
|
const Representative * fahrenheit = TemperatureRepresentative::Default().representativesOfSameDimension() + Unit::k_fahrenheitRepresentativeIndex;
|
|
const Representative * kelvin = TemperatureRepresentative::Default().representativesOfSameDimension() + Unit::k_kelvinRepresentativeIndex;
|
|
const Representative * targets[] = {
|
|
reductionContext.unitFormat() == Preferences::UnitFormat::Metric ? celsius : fahrenheit,
|
|
reductionContext.unitFormat() == Preferences::UnitFormat::Metric ? fahrenheit : celsius,
|
|
kelvin};
|
|
int numberOfExpressionsSet = 0;
|
|
int numberOfTargets = sizeof(targets) / sizeof(Representative *);
|
|
for (int i = 0; i < numberOfTargets; i++) {
|
|
if (targets[i] == this) {
|
|
continue;
|
|
}
|
|
dest[numberOfExpressionsSet++] = Multiplication::Builder(
|
|
Float<double>::Builder(TemperatureRepresentative::ConvertTemperatures(value, this, targets[i])),
|
|
Unit::Builder(targets[i], Prefix::EmptyPrefix()));
|
|
}
|
|
assert(numberOfExpressionsSet == 2);
|
|
return numberOfExpressionsSet;
|
|
}
|
|
|
|
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<double>::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<double>::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);
|
|
Expression * destMetric;
|
|
Expression * destImperial = nullptr;
|
|
if (reductionContext.unitFormat() == Preferences::UnitFormat::Metric) {
|
|
destMetric = dest;
|
|
} else {
|
|
destImperial = dest;
|
|
destMetric = dest + 1;
|
|
}
|
|
// 1. Convert to hectares
|
|
const Representative * hectare = representativesOfSameDimension() + Unit::k_hectareRepresentativeIndex;
|
|
*destMetric = Multiplication::Builder(Float<double>::Builder(value / hectare->ratio()), Unit::Builder(hectare, Prefix::EmptyPrefix()));
|
|
// 2. Convert to acres
|
|
if (!destImperial) {
|
|
return 1;
|
|
}
|
|
const Representative * acre = representativesOfSameDimension() + Unit::k_acreRepresentativeIndex;
|
|
*destImperial = Multiplication::Builder(Float<double>::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);
|
|
Expression * destMetric;
|
|
Expression * destImperial = nullptr;
|
|
if (reductionContext.unitFormat() == Preferences::UnitFormat::Metric) {
|
|
destMetric = dest;
|
|
} else {
|
|
destImperial = dest;
|
|
destMetric = dest + 1;
|
|
}
|
|
// 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<double>::Builder(adjustedValue * pow(10., -literPrefix->exponent())),
|
|
Unit::Builder(liter, literPrefix));
|
|
// 2. Convert to imperial volumes
|
|
if (!destImperial) {
|
|
return 1;
|
|
}
|
|
const Unit splitUnits[] = {
|
|
Unit::Builder(representativesOfSameDimension() + Unit::k_cupRepresentativeIndex, Prefix::EmptyPrefix()),
|
|
Unit::Builder(representativesOfSameDimension() + Unit::k_pintRepresentativeIndex, Prefix::EmptyPrefix()),
|
|
Unit::Builder(representativesOfSameDimension() + Unit::k_quartRepresentativeIndex, Prefix::EmptyPrefix()),
|
|
Unit::Builder(representativesOfSameDimension() + Unit::k_gallonRepresentativeIndex, Prefix::EmptyPrefix()),
|
|
};
|
|
*destImperial = Unit::BuildSplit(value, splitUnits, sizeof(splitUnits)/sizeof(Unit), reductionContext);
|
|
return 2;
|
|
}
|
|
|
|
int UnitNode::SpeedRepresentative::setAdditionalExpressions(double value, Expression * dest, int availableLength, ExpressionNode::ReductionContext reductionContext) const {
|
|
assert(availableLength >= 2);
|
|
Expression * destMetric;
|
|
Expression * destImperial = nullptr;
|
|
if (reductionContext.unitFormat() == Preferences::UnitFormat::Metric) {
|
|
destMetric = dest;
|
|
} else {
|
|
destImperial = dest;
|
|
destMetric = dest + 1;
|
|
}
|
|
// 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<double>::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
|
|
if (!destImperial) {
|
|
return 1;
|
|
}
|
|
const Representative * mile = DistanceRepresentative::Default().representativesOfSameDimension() + Unit::k_mileRepresentativeIndex;
|
|
*destImperial = Multiplication::Builder(
|
|
Float<double>::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;
|
|
}
|
|
|
|
Expression UnitNode::removeUnit(Expression * unit) {
|
|
return Unit(this).removeUnit(unit);
|
|
}
|
|
|
|
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 µ
|
|
* which would change the required size?*/
|
|
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 {
|
|
assert(bufferSize >= 0);
|
|
int underscoreLength = std::min<int>(strlcpy(buffer, "_", bufferSize), bufferSize - 1);
|
|
buffer += underscoreLength;
|
|
bufferSize -= underscoreLength;
|
|
return underscoreLength + m_representative->serialize(buffer, bufferSize, m_prefix);
|
|
}
|
|
|
|
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<const UnitNode *>(e);
|
|
Vector<int> v = representative()->dimensionVector();
|
|
Vector<int> 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) {
|
|
return Unit(this).shallowReduce(reductionContext);
|
|
}
|
|
|
|
Expression UnitNode::shallowBeautify(ReductionContext reductionContext) {
|
|
return Unit(this).shallowBeautify(reductionContext);
|
|
}
|
|
|
|
template<typename T>
|
|
Evaluation<T> UnitNode::templatedApproximate(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const {
|
|
return Complex<T>::Undefined();
|
|
}
|
|
|
|
// 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<Unit &>(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;
|
|
}
|
|
|
|
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<Rational &>(childExponent).approximateToScalar<double>(reductionContext.context(), reductionContext.complexFormat(), reductionContext.angleUnit());
|
|
factor = factor.childAtIndex(0);
|
|
}
|
|
assert(factor.type() == ExpressionNode::Type::Unit);
|
|
if (exponent == 0.f) {
|
|
/* Finding the best representative for a unit with exponent 0 doesn't
|
|
* really make sense, and should only happen with a weak ReductionTarget
|
|
* (such as in Graph app), that only rely on approximations. We keep the
|
|
* unit unchanged as it will approximate to undef anyway. */
|
|
return;
|
|
}
|
|
static_cast<Unit &>(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, Preferences::UnitFormat unitFormat) {
|
|
UnitNode::Vector<int> vector = UnitNode::Vector<int>::FromBaseUnits(unit);
|
|
const Representative * representative = Representative::RepresentativeForDimension(vector);
|
|
return representative != nullptr
|
|
&& ((unit.type() == ExpressionNode::Type::Unit && !static_cast<Unit &>(unit).isBaseUnit())
|
|
|| representative->hasAdditionalExpressions(value, unitFormat));
|
|
}
|
|
|
|
int Unit::SetAdditionalExpressions(Expression units, double value, Expression * dest, int availableLength, ExpressionNode::ReductionContext reductionContext) {
|
|
const Representative * representative = units.type() == ExpressionNode::Type::Unit ? static_cast<Unit &>(units).node()->representative() : UnitNode::Representative::RepresentativeForDimension(UnitNode::Vector<int>::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<double>()) {
|
|
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<double>()) {
|
|
res.addChildAtIndexInPlace(Multiplication::Builder(Float<double>::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::ConvertTemperatureUnits(Expression e, Unit unit, ExpressionNode::ReductionContext reductionContext) {
|
|
const Representative * targetRepr = unit.representative();
|
|
const Prefix * targetPrefix = unit.node()->prefix();
|
|
assert(unit.representative()->dimensionVector() == TemperatureRepresentative::Default().dimensionVector());
|
|
|
|
Expression startUnit;
|
|
e = e.removeUnit(&startUnit);
|
|
if (startUnit.type() != ExpressionNode::Type::Unit) {
|
|
return Undefined::Builder();
|
|
}
|
|
const Representative * startRepr = static_cast<Unit &>(startUnit).representative();
|
|
if (startRepr->dimensionVector() != TemperatureRepresentative::Default().dimensionVector()) {
|
|
return Undefined::Builder();
|
|
}
|
|
|
|
const Prefix * startPrefix = static_cast<Unit &>(startUnit).node()->prefix();
|
|
double value = e.approximateToScalar<double>(reductionContext.context(), reductionContext.complexFormat(), reductionContext.angleUnit());
|
|
return Multiplication::Builder(
|
|
Float<double>::Builder(TemperatureRepresentative::ConvertTemperatures(value * std::pow(10., startPrefix->exponent()), startRepr, targetRepr) * std::pow(10., - targetPrefix->exponent())),
|
|
unit.clone());
|
|
}
|
|
|
|
Expression Unit::shallowReduce(ExpressionNode::ReductionContext reductionContext) {
|
|
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;
|
|
}
|
|
|
|
/* Handle temperatures : Celsius and Fahrenheit should not be used in
|
|
* calculations, only in conversions and results.
|
|
* These are the seven legal forms for writing non-kelvin temperatures :
|
|
* (1) _°C
|
|
* (2) _°C->_?
|
|
* (3) 123_°C
|
|
* (4) -123_°C
|
|
* (5) 123_°C->_K
|
|
* (6) -123_°C->_K
|
|
* (7) Right member of a unit convert - this is handled above, as
|
|
* UnitConversion is set to None in this case. */
|
|
if (node()->representative()->dimensionVector() == TemperatureRepresentative::Default().dimensionVector()) {
|
|
Expression p = parent();
|
|
if (p.isUninitialized() || p.type() == ExpressionNode::Type::UnitConvert) {
|
|
// Form (1) and (2)
|
|
return *this;
|
|
}
|
|
if (p.type() == ExpressionNode::Type::Multiplication && p.numberOfChildren() == 2) {
|
|
Expression pp = p.parent();
|
|
if (pp.isUninitialized() || pp.type() == UnitNode::Type::UnitConvert) {
|
|
// Form (3) and (5)
|
|
return *this;
|
|
}
|
|
Expression ppp = pp.parent();
|
|
if (pp.type() == UnitNode::Type::Opposite && (ppp.isUninitialized() || ppp.type() == UnitNode::Type::UnitConvert)) {
|
|
// Form (4) and (6)
|
|
return *this;
|
|
}
|
|
}
|
|
return replaceWithUndefinedInPlace();
|
|
}
|
|
|
|
UnitNode * unitNode = node();
|
|
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;
|
|
}
|
|
|
|
Expression Unit::shallowBeautify(ExpressionNode::ReductionContext reductionContext) {
|
|
// Force Float(1) in front of an orphan Unit
|
|
if (parent().isUninitialized() || parent().type() == ExpressionNode::Type::Opposite) {
|
|
Multiplication m = Multiplication::Builder(Float<double>::Builder(1.));
|
|
replaceWithInPlace(m);
|
|
m.addChildAtIndexInPlace(*this, 1, 1);
|
|
return std::move(m);
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
Expression Unit::removeUnit(Expression * unit) {
|
|
*unit = *this;
|
|
Expression one = Rational::Builder(1);
|
|
replaceWithInPlace(one);
|
|
return one;
|
|
}
|
|
|
|
void Unit::chooseBestRepresentativeAndPrefix(double * value, double exponent, ExpressionNode::ReductionContext reductionContext, bool optimizePrefix) {
|
|
assert(exponent != 0.f);
|
|
|
|
if ((std::isinf(*value) || (*value == 0.0 && node()->representative()->dimensionVector() != TemperatureRepresentative::Default().dimensionVector()))) {
|
|
/* Use the base unit to represent an infinite or null value, as all units
|
|
* are equivalent.
|
|
* This is not true for temperatures (0 K != 0°C != 0°F). */
|
|
node()->setRepresentative(node()->representative()->representativesOfSameDimension());
|
|
node()->setPrefix(node()->representative()->basePrefix());
|
|
return;
|
|
}
|
|
// 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 (!optimizePrefix) {
|
|
bestPrefix = bestRepresentative->basePrefix();
|
|
}
|
|
|
|
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);
|
|
}
|
|
if (bestPrefix != node()->prefix()) {
|
|
*value = *value * std::pow(10., exponent * (node()->prefix()->exponent() - bestPrefix->exponent()));
|
|
node()->setPrefix(bestPrefix);
|
|
}
|
|
}
|
|
|
|
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;
|
|
|
|
}
|