mirror of
https://github.com/UpsilonNumworks/Upsilon.git
synced 2026-01-18 16:27:34 +01:00
[poincare] Handle rational unit exponents
Change-Id: Id710702dbed19d34992da90978d5823d68abb80a
This commit is contained in:
committed by
Émilie Feral
parent
09e39ad890
commit
51f1cdb076
@@ -26,6 +26,9 @@ typedef int64_t int_fast64_t;
|
|||||||
|
|
||||||
typedef uint8_t uint_least8_t;
|
typedef uint8_t uint_least8_t;
|
||||||
|
|
||||||
|
#define INT8_MAX 0x7f
|
||||||
|
#define INT8_MIN (-INT8_MAX-1)
|
||||||
|
|
||||||
#define UINT8_MAX 0xff
|
#define UINT8_MAX 0xff
|
||||||
#define UINT16_MAX 0xffff
|
#define UINT16_MAX 0xffff
|
||||||
|
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ public:
|
|||||||
bool canParse(const char * symbol, size_t length,
|
bool canParse(const char * symbol, size_t length,
|
||||||
const Prefix * * prefix) const;
|
const Prefix * * prefix) const;
|
||||||
int serialize(char * buffer, int bufferSize, const Prefix * prefix) const;
|
int serialize(char * buffer, int bufferSize, const Prefix * prefix) const;
|
||||||
const Prefix * bestPrefixForValue(double & value, const int exponent) const;
|
const Prefix * bestPrefixForValue(double & value, const double exponent) const;
|
||||||
private:
|
private:
|
||||||
const char * m_rootSymbol;
|
const char * m_rootSymbol;
|
||||||
const char * m_definition;
|
const char * m_definition;
|
||||||
@@ -793,7 +793,7 @@ private:
|
|||||||
UnitNode * node() const { return static_cast<UnitNode *>(Expression::node()); }
|
UnitNode * node() const { return static_cast<UnitNode *>(Expression::node()); }
|
||||||
bool isSI() const;
|
bool isSI() const;
|
||||||
static void ChooseBestMultipleForValue(Expression * units, double * value, bool tuneRepresentative, ExpressionNode::ReductionContext reductionContext);
|
static void ChooseBestMultipleForValue(Expression * units, double * value, bool tuneRepresentative, ExpressionNode::ReductionContext reductionContext);
|
||||||
void chooseBestMultipleForValue(double * value, const int exponent, bool tuneRepresentative, ExpressionNode::ReductionContext reductionContext);
|
void chooseBestMultipleForValue(double * value, const double exponent, bool tuneRepresentative, ExpressionNode::ReductionContext reductionContext);
|
||||||
Expression removeUnit(Expression * unit);
|
Expression removeUnit(Expression * unit);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -391,6 +391,10 @@ Expression Multiplication::shallowBeautify(ExpressionNode::ReductionContext redu
|
|||||||
* - Repeat those steps until no more simplification is possible.
|
* - Repeat those steps until no more simplification is possible.
|
||||||
*/
|
*/
|
||||||
Multiplication unitsAccu = Multiplication::Builder();
|
Multiplication unitsAccu = Multiplication::Builder();
|
||||||
|
/* If exponents are not integers, FromBaseUnits will return the closest
|
||||||
|
* representation of units with base units and integer exponents.
|
||||||
|
* It cause no problem because once the best derived units are found,
|
||||||
|
* units is divided then multiplied by them. */
|
||||||
Unit::Dimension::Vector<Integer> unitsExponents = Unit::Dimension::Vector<Integer>::FromBaseUnits(units);
|
Unit::Dimension::Vector<Integer> unitsExponents = Unit::Dimension::Vector<Integer>::FromBaseUnits(units);
|
||||||
Unit::Dimension::Vector<Integer>::Metrics unitsMetrics = unitsExponents.metrics();
|
Unit::Dimension::Vector<Integer>::Metrics unitsMetrics = unitsExponents.metrics();
|
||||||
Unit::Dimension::Vector<Integer> bestRemainderExponents;
|
Unit::Dimension::Vector<Integer> bestRemainderExponents;
|
||||||
|
|||||||
@@ -418,8 +418,8 @@ Expression Power::shallowReduce(ExpressionNode::ReductionContext reductionContex
|
|||||||
}
|
}
|
||||||
assert(index == childAtIndex(1));
|
assert(index == childAtIndex(1));
|
||||||
if (base.hasUnit()) {
|
if (base.hasUnit()) {
|
||||||
if (index.type() != ExpressionNode::Type::Rational || !static_cast<Rational &>(index).isInteger()) {
|
if (index.type() != ExpressionNode::Type::Rational) {
|
||||||
// The exponent must be an Integer
|
// The exponent must be an Rational
|
||||||
return replaceWithUndefinedInPlace();
|
return replaceWithUndefinedInPlace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -995,8 +995,8 @@ Expression Power::shallowBeautify(ExpressionNode::ReductionContext reductionCont
|
|||||||
p.shallowReduce(reductionContext);
|
p.shallowReduce(reductionContext);
|
||||||
return d.shallowBeautify(reductionContext);
|
return d.shallowBeautify(reductionContext);
|
||||||
}
|
}
|
||||||
// Step 2: Turn a^(1/n) into root(a, n)
|
// Step 2: Turn a^(1/n) into root(a, n), unless base is a unit
|
||||||
if (childAtIndex(1).type() == ExpressionNode::Type::Rational && childAtIndex(1).convert<Rational>().signedIntegerNumerator().isOne()) {
|
if (childAtIndex(1).type() == ExpressionNode::Type::Rational && childAtIndex(1).convert<Rational>().signedIntegerNumerator().isOne() && childAtIndex(0).type() != ExpressionNode::Type::Unit) {
|
||||||
Integer index = childAtIndex(1).convert<Rational>().integerDenominator();
|
Integer index = childAtIndex(1).convert<Rational>().integerDenominator();
|
||||||
// Special case: a^(1/2) --> sqrt(a)
|
// Special case: a^(1/2) --> sqrt(a)
|
||||||
if (index.isEqualTo(Integer(2))) {
|
if (index.isEqualTo(Integer(2))) {
|
||||||
|
|||||||
@@ -40,7 +40,6 @@ Expression SquareRootNode::shallowReduce(ReductionContext reductionContext) {
|
|||||||
Expression SquareRoot::shallowReduce(ExpressionNode::ReductionContext reductionContext) {
|
Expression SquareRoot::shallowReduce(ExpressionNode::ReductionContext reductionContext) {
|
||||||
{
|
{
|
||||||
Expression e = Expression::defaultShallowReduce();
|
Expression e = Expression::defaultShallowReduce();
|
||||||
e = e.defaultHandleUnitsInChildren();
|
|
||||||
if (e.isUndefined()) {
|
if (e.isUndefined()) {
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include <poincare/power.h>
|
#include <poincare/power.h>
|
||||||
#include <poincare/rational.h>
|
#include <poincare/rational.h>
|
||||||
#include <poincare/layout_helper.h>
|
#include <poincare/layout_helper.h>
|
||||||
|
#include <limits.h>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@@ -16,8 +17,6 @@
|
|||||||
|
|
||||||
namespace Poincare {
|
namespace Poincare {
|
||||||
|
|
||||||
static inline int absInt(int x) { return x >= 0 ? x : -x; }
|
|
||||||
|
|
||||||
int UnitNode::Prefix::serialize(char * buffer, int bufferSize) const {
|
int UnitNode::Prefix::serialize(char * buffer, int bufferSize) const {
|
||||||
assert(bufferSize >= 0);
|
assert(bufferSize >= 0);
|
||||||
return std::min<int>(strlcpy(buffer, m_symbol, bufferSize), bufferSize - 1);
|
return std::min<int>(strlcpy(buffer, m_symbol, bufferSize), bufferSize - 1);
|
||||||
@@ -57,20 +56,20 @@ int UnitNode::Representative::serialize(char * buffer, int bufferSize, const Pre
|
|||||||
return length;
|
return length;
|
||||||
}
|
}
|
||||||
|
|
||||||
const UnitNode::Prefix * UnitNode::Representative::bestPrefixForValue(double & value, const int exponent) const {
|
const UnitNode::Prefix * UnitNode::Representative::bestPrefixForValue(double & value, const double exponent) const {
|
||||||
if (!isPrefixable()) {
|
if (!isPrefixable()) {
|
||||||
return &Unit::EmptyPrefix;
|
return &Unit::EmptyPrefix;
|
||||||
}
|
}
|
||||||
const Prefix * bestPre = nullptr;
|
const Prefix * bestPre = nullptr;
|
||||||
unsigned int diff = -1;
|
double diff = -1.0;
|
||||||
/* Find the 'Prefix' with the most adequate 'exponent' for the order of
|
/* Find the 'Prefix' with the most adequate 'exponent' for the order of
|
||||||
* magnitude of 'value'.
|
* magnitude of 'value'.
|
||||||
*/
|
*/
|
||||||
const int orderOfMagnitude = IEEE754<double>::exponentBase10(std::fabs(value));
|
const double orderOfMagnitude = IEEE754<double>::exponentBase10(std::fabs(value));
|
||||||
for (size_t i = 0; i < m_outputPrefixesLength; i++) {
|
for (size_t i = 0; i < m_outputPrefixesLength; i++) {
|
||||||
const Prefix * pre = m_outputPrefixes[i];
|
const Prefix * pre = m_outputPrefixes[i];
|
||||||
unsigned int newDiff = absInt(orderOfMagnitude - pre->exponent() * exponent);
|
double newDiff = std::abs(orderOfMagnitude - pre->exponent() * exponent);
|
||||||
if (newDiff < diff) {
|
if (newDiff < diff || diff < 0.0) {
|
||||||
diff = newDiff;
|
diff = newDiff;
|
||||||
bestPre = pre;
|
bestPre = pre;
|
||||||
}
|
}
|
||||||
@@ -112,6 +111,8 @@ Unit::Dimension::Vector<int8_t>::Metrics UnitNode::Dimension::Vector<int8_t>::me
|
|||||||
|
|
||||||
template<>
|
template<>
|
||||||
Unit::Dimension::Vector<Integer> UnitNode::Dimension::Vector<Integer>::FromBaseUnits(const Expression baseUnits) {
|
Unit::Dimension::Vector<Integer> UnitNode::Dimension::Vector<Integer>::FromBaseUnits(const Expression baseUnits) {
|
||||||
|
/* Returns the vector of Base units with integer exponents. If rational, the
|
||||||
|
* closest integer will be used. */
|
||||||
Vector<Integer> vector;
|
Vector<Integer> vector;
|
||||||
int numberOfFactors;
|
int numberOfFactors;
|
||||||
int factorIndex = 0;
|
int factorIndex = 0;
|
||||||
@@ -128,8 +129,20 @@ Unit::Dimension::Vector<Integer> UnitNode::Dimension::Vector<Integer>::FromBaseU
|
|||||||
Integer exponent(1);
|
Integer exponent(1);
|
||||||
if (factor.type() == ExpressionNode::Type::Power) {
|
if (factor.type() == ExpressionNode::Type::Power) {
|
||||||
Expression exp = factor.childAtIndex(1);
|
Expression exp = factor.childAtIndex(1);
|
||||||
assert(exp.type() == ExpressionNode::Type::Rational && static_cast<Rational &>(exp).isInteger());
|
assert(exp.type() == ExpressionNode::Type::Rational);
|
||||||
exponent = static_cast<Rational &>(exp).signedIntegerNumerator();
|
// Using the closest integer to the exponent.
|
||||||
|
double exponent_double = static_cast<const Rational &>(exp).node()->templatedApproximate<double>();
|
||||||
|
if (std::fabs(exponent_double) < INT_MAX / 2) {
|
||||||
|
// Exponent can be safely casted as int
|
||||||
|
exponent = (int)std::round(exponent_double);
|
||||||
|
assert(std::fabs(exponent_double - exponent.approximate<double>()) <= 0.5);
|
||||||
|
} else {
|
||||||
|
/* Base units vector will ignore this coefficient, that could have been
|
||||||
|
* casted as int8_t in CanSimplifyUnitProduct, leading to homogeneous,
|
||||||
|
* but badly formatted units. Any way, the missing exponent won't affect
|
||||||
|
* CanSimplifyUnitProduct as homogeneity is conserved. */
|
||||||
|
exponent = 0;
|
||||||
|
}
|
||||||
factor = factor.childAtIndex(0);
|
factor = factor.childAtIndex(0);
|
||||||
}
|
}
|
||||||
// Fill the vector with the unit's exponent
|
// Fill the vector with the unit's exponent
|
||||||
@@ -184,7 +197,7 @@ int UnitNode::simplificationOrderSameType(const ExpressionNode * e, bool ascendi
|
|||||||
if (dimdiff != 0) {
|
if (dimdiff != 0) {
|
||||||
return dimdiff;
|
return dimdiff;
|
||||||
}
|
}
|
||||||
// This works because reprensentatives are ordered in a table
|
// This works because representatives are ordered in a table
|
||||||
const ptrdiff_t repdiff = eNode->representative() - m_representative;
|
const ptrdiff_t repdiff = eNode->representative() - m_representative;
|
||||||
if (repdiff != 0) {
|
if (repdiff != 0) {
|
||||||
/* We order representatives in the reverse order as how they're stored in
|
/* We order representatives in the reverse order as how they're stored in
|
||||||
@@ -341,31 +354,25 @@ Expression Unit::shallowBeautify(ExpressionNode::ReductionContext reductionConte
|
|||||||
void Unit::ChooseBestMultipleForValue(Expression * units, double * value, bool tuneRepresentative, ExpressionNode::ReductionContext reductionContext) {
|
void Unit::ChooseBestMultipleForValue(Expression * units, double * value, bool tuneRepresentative, ExpressionNode::ReductionContext reductionContext) {
|
||||||
// Identify the first Unit factor and its exponent
|
// Identify the first Unit factor and its exponent
|
||||||
Expression firstFactor = *units;
|
Expression firstFactor = *units;
|
||||||
int exponent = 1;
|
double exponent = 1.0;
|
||||||
if (firstFactor.type() == ExpressionNode::Type::Multiplication) {
|
if (firstFactor.type() == ExpressionNode::Type::Multiplication) {
|
||||||
firstFactor = firstFactor.childAtIndex(0);
|
firstFactor = firstFactor.childAtIndex(0);
|
||||||
}
|
}
|
||||||
if (firstFactor.type() == ExpressionNode::Type::Power) {
|
if (firstFactor.type() == ExpressionNode::Type::Power) {
|
||||||
Expression exp = firstFactor.childAtIndex(1);
|
Expression exp = firstFactor.childAtIndex(1);
|
||||||
firstFactor = firstFactor.childAtIndex(0);
|
firstFactor = firstFactor.childAtIndex(0);
|
||||||
assert(exp.type() == ExpressionNode::Type::Rational && static_cast<Rational &>(exp).isInteger());
|
assert(exp.type() == ExpressionNode::Type::Rational);
|
||||||
Integer expInt = static_cast<Rational &>(exp).signedIntegerNumerator();
|
exponent = static_cast<const Rational &>(exp).node()->templatedApproximate<double>();
|
||||||
if (expInt.isExtractable()) {
|
|
||||||
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);
|
assert(firstFactor.type() == ExpressionNode::Type::Unit);
|
||||||
// Choose its multiple and update value accordingly
|
// Choose its multiple and update value accordingly
|
||||||
if (exponent != 0) {
|
if (exponent != 0.0) {
|
||||||
static_cast<Unit&>(firstFactor).chooseBestMultipleForValue(value, exponent, tuneRepresentative, reductionContext);
|
static_cast<Unit&>(firstFactor).chooseBestMultipleForValue(value, exponent, tuneRepresentative, reductionContext);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Unit::chooseBestMultipleForValue(double * value, const int exponent, bool tuneRepresentative, ExpressionNode::ReductionContext reductionContext) {
|
void Unit::chooseBestMultipleForValue(double * value, const double exponent, bool tuneRepresentative, ExpressionNode::ReductionContext reductionContext) {
|
||||||
assert(!std::isnan(*value) && exponent != 0);
|
assert(!std::isnan(*value) && exponent != 0.0);
|
||||||
if (*value == 0 || *value == 1.0 || std::isinf(*value)) {
|
if (*value == 0 || *value == 1.0 || std::isinf(*value)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -442,7 +449,8 @@ bool Unit::IsSI(Expression & e) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (e.type() == ExpressionNode::Type::Power) {
|
if (e.type() == ExpressionNode::Type::Power) {
|
||||||
assert(e.childAtIndex(1).type() == ExpressionNode::Type::Rational && e.childAtIndex(1).convert<Rational>().isInteger());
|
// Rational exponents are accepted in IS system
|
||||||
|
assert(e.childAtIndex(1).type() == ExpressionNode::Type::Rational);
|
||||||
Expression child = e.childAtIndex(0);
|
Expression child = e.childAtIndex(0);
|
||||||
assert(child.type() == ExpressionNode::Type::Unit);
|
assert(child.type() == ExpressionNode::Type::Unit);
|
||||||
return IsSI(child);
|
return IsSI(child);
|
||||||
|
|||||||
@@ -319,7 +319,8 @@ QUIZ_CASE(poincare_simplification_units) {
|
|||||||
assert_parsed_expression_simplify_to("log(undef)*_s", "undef");
|
assert_parsed_expression_simplify_to("log(undef)*_s", "undef");
|
||||||
|
|
||||||
/* Units with invalid exponent */
|
/* Units with invalid exponent */
|
||||||
assert_parsed_expression_simplify_to("_s^(1/2)", "undef");
|
assert_parsed_expression_simplify_to("_s^(_s)", "undef");
|
||||||
|
assert_parsed_expression_simplify_to("_s^(π)", "undef");
|
||||||
|
|
||||||
/* Inhomogeneous expressions */
|
/* Inhomogeneous expressions */
|
||||||
assert_parsed_expression_simplify_to("1+_s", "undef");
|
assert_parsed_expression_simplify_to("1+_s", "undef");
|
||||||
@@ -416,7 +417,6 @@ QUIZ_CASE(poincare_simplification_units) {
|
|||||||
assert_parsed_expression_simplify_to("tanh(_s)", "undef");
|
assert_parsed_expression_simplify_to("tanh(_s)", "undef");
|
||||||
assert_parsed_expression_simplify_to("trace(_s)", "undef");
|
assert_parsed_expression_simplify_to("trace(_s)", "undef");
|
||||||
assert_parsed_expression_simplify_to("transpose(_s)", "undef");
|
assert_parsed_expression_simplify_to("transpose(_s)", "undef");
|
||||||
assert_parsed_expression_simplify_to("√(_s)", "undef");
|
|
||||||
|
|
||||||
/* Valid expressions */
|
/* Valid expressions */
|
||||||
assert_parsed_expression_simplify_to("-2×_A", "-2×_A");
|
assert_parsed_expression_simplify_to("-2×_A", "-2×_A");
|
||||||
|
|||||||
Reference in New Issue
Block a user