mirror of
https://github.com/UpsilonNumworks/Upsilon.git
synced 2026-01-18 16:27:34 +01:00
[poincare] Unit conversion
This commit is contained in:
@@ -144,6 +144,7 @@ poincare_src += $(addprefix poincare/src/,\
|
||||
trigonometry_cheat_table.cpp \
|
||||
undefined.cpp \
|
||||
unit.cpp \
|
||||
unit_convert.cpp \
|
||||
unreal.cpp \
|
||||
variable_context.cpp \
|
||||
)
|
||||
|
||||
@@ -100,6 +100,7 @@ class Expression : public TreeHandle {
|
||||
friend class Trigonometry;
|
||||
friend class TrigonometryCheatTable;
|
||||
friend class Unit;
|
||||
friend class UnitConvert;
|
||||
|
||||
friend class AdditionNode;
|
||||
friend class DerivativeNode;
|
||||
@@ -196,6 +197,8 @@ public:
|
||||
static constexpr int k_maxNumberOfPolynomialCoefficients = k_maxPolynomialDegree+1;
|
||||
int getPolynomialReducedCoefficients(const char * symbolName, Expression coefficients[], Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::SymbolicComputation symbolicComputation) const;
|
||||
Expression replaceSymbolWithExpression(const SymbolAbstract & symbol, const Expression & expression) { return node()->replaceSymbolWithExpression(symbol, expression); }
|
||||
bool beautifiedExpressionHasUnits() const { return node()->beautifiedExpressionHasUnits(); } // This must be called on a beautified expression
|
||||
bool isUnitsOnly(Context * context) const;
|
||||
|
||||
/* Complex */
|
||||
static bool EncounteredComplex();
|
||||
@@ -395,6 +398,7 @@ private:
|
||||
|
||||
/* Properties */
|
||||
int defaultGetPolynomialCoefficients(Context * context, const char * symbol, Expression expression[]) const;
|
||||
bool reducedExpressionIsUnitsOnly() const { return node()->reducedExpressionIsUnitsOnly(); } // This must be called on a reduced expression
|
||||
|
||||
/* Builder */
|
||||
static bool IsZero(const Expression e);
|
||||
|
||||
@@ -40,6 +40,7 @@ public:
|
||||
Constant,
|
||||
Symbol,
|
||||
Store,
|
||||
UnitConvert,
|
||||
Equal,
|
||||
Sine,
|
||||
Cosine,
|
||||
@@ -126,7 +127,8 @@ public:
|
||||
enum class SymbolicComputation {
|
||||
ReplaceAllSymbolsWithDefinitionsOrUndefined = 0,
|
||||
ReplaceAllDefinedSymbolsWithDefinition = 1,
|
||||
ReplaceDefinedFunctionsWithDefinitions = 2
|
||||
ReplaceDefinedFunctionsWithDefinitions = 2,
|
||||
ReplaceAllSymbolsWithUndefinedAndDoNotReplaceUnits = 3 // Used in Expression::isUnitsOnly
|
||||
};
|
||||
enum class Sign {
|
||||
Negative = -1,
|
||||
@@ -174,6 +176,8 @@ public:
|
||||
virtual int getVariables(Context * context, isVariableTest isVariable, char * variables, int maxSizeVariable) const;
|
||||
virtual float characteristicXRange(Context * context, Preferences::AngleUnit angleUnit) const;
|
||||
bool isOfType(Type * types, int length) const;
|
||||
virtual bool beautifiedExpressionHasUnits() const { return false; } // This must be called on a beautified expression
|
||||
virtual bool reducedExpressionIsUnitsOnly() const { return false; } // This must be called on a reduced expression
|
||||
|
||||
/* Simplification */
|
||||
/* SimplificationOrder returns:
|
||||
|
||||
@@ -25,6 +25,8 @@ public:
|
||||
int polynomialDegree(Context * context, const char * symbolName) const override;
|
||||
int getPolynomialCoefficients(Context * context, const char * symbolName, Expression coefficients[], ExpressionNode::SymbolicComputation symbolicComputation) const override;
|
||||
bool childAtIndexNeedsUserParentheses(const Expression & child, int childIndex) const override;
|
||||
bool beautifiedExpressionHasUnits() const override;
|
||||
bool reducedExpressionIsUnitsOnly() const override;
|
||||
|
||||
// Approximation
|
||||
template<typename T> static Complex<T> compute(const std::complex<T> c, const std::complex<T> d, Preferences::ComplexFormat complexFormat) { return Complex<T>::Builder(c*d); }
|
||||
@@ -65,6 +67,7 @@ class Multiplication : public NAryExpression {
|
||||
friend class AdditionNode;
|
||||
friend class Addition;
|
||||
friend class Power;
|
||||
friend class UnitConvert;
|
||||
public:
|
||||
Multiplication(const MultiplicationNode * n) : NAryExpression(n) {}
|
||||
static Multiplication Builder() { return TreeHandle::NAryBuilder<Multiplication, MultiplicationNode>(); }
|
||||
|
||||
@@ -32,6 +32,8 @@ public:
|
||||
|
||||
int polynomialDegree(Context * context, const char * symbolName) const override;
|
||||
int getPolynomialCoefficients(Context * context, const char * symbolName, Expression coefficients[], ExpressionNode::SymbolicComputation symbolicComputation) const override;
|
||||
bool beautifiedExpressionHasUnits() const override { return reducedExpressionIsUnitsOnly(); }
|
||||
bool reducedExpressionIsUnitsOnly() const override;
|
||||
|
||||
template<typename T> static Complex<T> compute(const std::complex<T> c, const std::complex<T> d, Preferences::ComplexFormat complexFormat);
|
||||
|
||||
|
||||
@@ -112,6 +112,8 @@ public:
|
||||
// Expression Properties
|
||||
Type type() const override { return Type::Unit; }
|
||||
Sign sign(Context * context) const override;
|
||||
bool beautifiedExpressionHasUnits() const override { return true; }
|
||||
bool reducedExpressionIsUnitsOnly() const override { return true; }
|
||||
|
||||
/* Layout */
|
||||
Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override;
|
||||
|
||||
55
poincare/include/poincare/unit_convert.h
Normal file
55
poincare/include/poincare/unit_convert.h
Normal file
@@ -0,0 +1,55 @@
|
||||
#ifndef POINCARE_UNIT_CONVERT_H
|
||||
#define POINCARE_UNIT_CONVERT_H
|
||||
|
||||
#include <poincare/expression.h>
|
||||
#include <poincare/unit.h>
|
||||
#include <poincare/evaluation.h>
|
||||
|
||||
namespace Poincare {
|
||||
|
||||
class UnitConvertNode /*final*/ : public ExpressionNode {
|
||||
public:
|
||||
|
||||
// TreeNode
|
||||
size_t size() const override { return sizeof(UnitConvertNode); }
|
||||
int numberOfChildren() const override { return 2; }
|
||||
#if POINCARE_TREE_LOG
|
||||
virtual void logNodeName(std::ostream & stream) const override {
|
||||
stream << "UnivtConvert";
|
||||
}
|
||||
#endif
|
||||
|
||||
// ExpressionNode
|
||||
Type type() const override { return Type::UnitConvert; }
|
||||
int polynomialDegree(Context * context, const char * symbolName) const override { return -1; }
|
||||
|
||||
private:
|
||||
// Simplification
|
||||
void deepReduceChildren(ExpressionNode::ReductionContext reductionContext) override {}
|
||||
Expression shallowReduce(ReductionContext reductionContext) override;
|
||||
LayoutShape leftLayoutShape() const override { assert(false); return LayoutShape::MoreLetters; };
|
||||
// Layout
|
||||
Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override;
|
||||
int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override;
|
||||
// Evalutation
|
||||
Evaluation<float> approximate(SinglePrecision p, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { return templatedApproximate<float>(context, complexFormat, angleUnit); }
|
||||
Evaluation<double> approximate(DoublePrecision p, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { return templatedApproximate<double>(context, complexFormat, angleUnit); }
|
||||
template<typename T> Evaluation<T> templatedApproximate(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const;
|
||||
};
|
||||
|
||||
class UnitConvert final : public Expression {
|
||||
friend class UnitConvertNode;
|
||||
public:
|
||||
UnitConvert(const UnitConvertNode * n) : Expression(n) {}
|
||||
static UnitConvert Builder(Expression value, Expression unit) { return TreeHandle::FixedArityBuilder<UnitConvert, UnitConvertNode>(ArrayBuilder<TreeHandle>(value, unit).array(), 2); }
|
||||
|
||||
// Expression
|
||||
Expression shallowReduce(ExpressionNode::ReductionContext reductionContext);
|
||||
|
||||
private:
|
||||
UnitConvertNode * node() const { return static_cast<UnitConvertNode *>(Expression::node()); }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -84,6 +84,7 @@
|
||||
#include <poincare/tangent.h>
|
||||
#include <poincare/undefined.h>
|
||||
#include <poincare/unit.h>
|
||||
#include <poincare/unit_convert.h>
|
||||
#include <poincare/unreal.h>
|
||||
#include <poincare/variable_context.h>
|
||||
|
||||
|
||||
@@ -453,6 +453,14 @@ int Expression::getPolynomialReducedCoefficients(const char * symbolName, Expres
|
||||
return degree;
|
||||
}
|
||||
|
||||
bool Expression::isUnitsOnly(Context * context) const {
|
||||
if (type() == ExpressionNode::Type::Unit) {
|
||||
return true;
|
||||
}
|
||||
Expression thisBeautified = clone().reduce(ExpressionNode::ReductionContext(context, Preferences::ComplexFormat::Real, Preferences::AngleUnit::Degree, ExpressionNode::ReductionTarget::SystemForApproximation, ExpressionNode::SymbolicComputation::ReplaceAllSymbolsWithUndefinedAndDoNotReplaceUnits)); // The values do not really matter except for the symbolicComputation
|
||||
return thisBeautified.reducedExpressionIsUnitsOnly();
|
||||
}
|
||||
|
||||
/* Complex */
|
||||
|
||||
bool Expression::EncounteredComplex() {
|
||||
@@ -684,8 +692,13 @@ void Expression::simplifyAndApproximate(Expression * simplifiedExpression, Expre
|
||||
if (approximateExpression) {
|
||||
static_cast<Matrix *>(approximateExpression)->setDimensions(m.numberOfRows(), m.numberOfColumns());
|
||||
}
|
||||
} else if (e.type() == ExpressionNode::Type::UnitConvert) {
|
||||
/* Case 2: the initial expression is a unit convert, so we already beautified the result. */
|
||||
*simplifiedExpression = e;
|
||||
*approximateExpression = e;
|
||||
return;
|
||||
} else {
|
||||
/* Case 2: the reduced expression is scalar or too complex to respect the
|
||||
/* Case 3: the reduced expression is scalar or too complex to respect the
|
||||
* complex format. */
|
||||
return e.beautifyAndApproximateScalar(simplifiedExpression, approximateExpression, userReductionContext, context, complexFormat, angleUnit);
|
||||
}
|
||||
|
||||
@@ -62,6 +62,16 @@ bool MultiplicationNode::childAtIndexNeedsUserParentheses(const Expression & chi
|
||||
return child.isOfType(types, 2);
|
||||
}
|
||||
|
||||
bool MultiplicationNode::beautifiedExpressionHasUnits() const {
|
||||
assert(numberOfChildren() > 0);
|
||||
return Expression(this).childAtIndex(numberOfChildren() - 1).beautifiedExpressionHasUnits();
|
||||
}
|
||||
|
||||
bool MultiplicationNode::reducedExpressionIsUnitsOnly() const {
|
||||
assert(numberOfChildren() > 0);
|
||||
return Expression(this).childAtIndex(0).reducedExpressionIsUnitsOnly();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
MatrixComplex<T> MultiplicationNode::computeOnMatrices(const MatrixComplex<T> m, const MatrixComplex<T> n, Preferences::ComplexFormat complexFormat) {
|
||||
if (m.numberOfColumns() != n.numberOfRows()) {
|
||||
|
||||
@@ -60,7 +60,7 @@ Expression Parser::parseUntil(Token::Type stoppingType) {
|
||||
typedef void (Parser::*TokenParser)(Expression & leftHandSide, Token::Type stoppingType);
|
||||
static constexpr TokenParser tokenParsers[] = {
|
||||
&Parser::parseUnexpected, // Token::EndOfStream
|
||||
&Parser::parseStore, // Token::Store
|
||||
&Parser::parseStoreOrUnitConvert, // Token::Store
|
||||
&Parser::parseEqual, // Token::Equal
|
||||
&Parser::parseUnexpected, // Token::RightSystemParenthesis
|
||||
&Parser::parseUnexpected, // Token::RightBracket
|
||||
@@ -275,30 +275,42 @@ void Parser::parseEqual(Expression & leftHandSide, Token::Type stoppingType) {
|
||||
}
|
||||
}
|
||||
|
||||
void Parser::parseStore(Expression & leftHandSide, Token::Type stoppingType) {
|
||||
void Parser::parseStoreOrUnitConvert(Expression & leftHandSide, Token::Type stoppingType) {
|
||||
if (leftHandSide.isUninitialized()) {
|
||||
m_status = Status::Error; // Left-hand side missing.
|
||||
return;
|
||||
}
|
||||
// At this point, m_currentToken is Token::Store.
|
||||
popToken();
|
||||
if (!m_currentToken.is(Token::Identifier) || IsReservedName(m_currentToken.text(), m_currentToken.length())) {
|
||||
m_status = Status::Error; // The right-hand side of Token::Store must be symbol or function that is not reserved.
|
||||
bool parseId = m_nextToken.is(Token::Identifier) && !IsReservedName(m_nextToken.text(), m_nextToken.length());
|
||||
if (parseId) {
|
||||
popToken();
|
||||
// Try parsing a store
|
||||
Expression rightHandSide;
|
||||
parseCustomIdentifier(rightHandSide, m_currentToken.text(), m_currentToken.length(), true);
|
||||
if (m_status != Status::Progress) {
|
||||
return;
|
||||
}
|
||||
if (!m_nextToken.is(Token::EndOfStream)
|
||||
|| !(rightHandSide.type() == ExpressionNode::Type::Symbol
|
||||
|| (rightHandSide.type() == ExpressionNode::Type::Function
|
||||
&& rightHandSide.childAtIndex(0).type() == ExpressionNode::Type::Symbol)))
|
||||
{
|
||||
m_status = Status::Error; // Store expects a single symbol or function.
|
||||
return;
|
||||
}
|
||||
leftHandSide = Store::Builder(leftHandSide, static_cast<SymbolAbstract&>(rightHandSide));
|
||||
return;
|
||||
}
|
||||
Expression rightHandSide;
|
||||
parseCustomIdentifier(rightHandSide, m_currentToken.text(), m_currentToken.length(), true);
|
||||
// Try parsing a unit convert
|
||||
Expression rightHandSide = parseUntil(stoppingType);
|
||||
if (m_status != Status::Progress) {
|
||||
return;
|
||||
}
|
||||
if (!m_nextToken.is(Token::EndOfStream) ||
|
||||
!( rightHandSide.type() == ExpressionNode::Type::Symbol ||
|
||||
(rightHandSide.type() == ExpressionNode::Type::Function && rightHandSide.childAtIndex(0).type() == ExpressionNode::Type::Symbol)))
|
||||
{
|
||||
m_status = Status::Error; // Store expects a single symbol or function.
|
||||
if (!m_nextToken.is(Token::EndOfStream) || !rightHandSide.isUnitsOnly(m_context)) {
|
||||
m_status = Status::Error; // Store expects a single symbol or function or a unit.
|
||||
return;
|
||||
}
|
||||
leftHandSide = Store::Builder(leftHandSide, static_cast<SymbolAbstract&>(rightHandSide));
|
||||
leftHandSide = UnitConvert::Builder(leftHandSide, rightHandSide);
|
||||
}
|
||||
|
||||
bool Parser::parseBinaryOperator(const Expression & leftHandSide, Expression & rightHandSide, Token::Type stoppingType) {
|
||||
|
||||
@@ -64,7 +64,7 @@ private:
|
||||
void parseCaret(Expression & leftHandSide, Token::Type stoppingType = (Token::Type)0);
|
||||
void parseCaretWithParenthesis(Expression & leftHandSide, Token::Type stoppingType = (Token::Type)0);
|
||||
void parseEqual(Expression & leftHandSide, Token::Type stoppingType = (Token::Type)0);
|
||||
void parseStore(Expression & leftHandSide, Token::Type stoppingType = (Token::Type)0);
|
||||
void parseStoreOrUnitConvert(Expression & leftHandSide, Token::Type stoppingType = (Token::Type)0);
|
||||
void parseLeftSuperscript(Expression & leftHandSide, Token::Type stoppingType = (Token::Type)0);
|
||||
|
||||
// Parsing helpers
|
||||
|
||||
@@ -85,6 +85,10 @@ int PowerNode::getPolynomialCoefficients(Context * context, const char * symbolN
|
||||
return Power(this).getPolynomialCoefficients(context, symbolName, coefficients);
|
||||
}
|
||||
|
||||
bool PowerNode::reducedExpressionIsUnitsOnly() const {
|
||||
return childAtIndex(0)->type() == ExpressionNode::Type::Unit;
|
||||
}
|
||||
|
||||
bool PowerNode::isReal(Context * context) const {
|
||||
Expression base(childAtIndex(0));
|
||||
Expression index(childAtIndex(1));
|
||||
|
||||
@@ -362,6 +362,7 @@ template Sum TreeHandle::FixedArityBuilder<Sum, SumNode>(TreeHandle*, size_t);
|
||||
template SumLayout TreeHandle::FixedArityBuilder<SumLayout, SumLayoutNode>(TreeHandle*, size_t);
|
||||
template Tangent TreeHandle::FixedArityBuilder<Tangent, TangentNode>(TreeHandle*, size_t);
|
||||
template Undefined TreeHandle::FixedArityBuilder<Undefined, UndefinedNode>(TreeHandle*, size_t);
|
||||
template UnitConvert TreeHandle::FixedArityBuilder<UnitConvert, UnitConvertNode>(TreeHandle*, size_t);
|
||||
template Unreal TreeHandle::FixedArityBuilder<Unreal, UnrealNode>(TreeHandle*, size_t);
|
||||
template MatrixLayout TreeHandle::NAryBuilder<MatrixLayout, MatrixLayoutNode>(TreeHandle*, size_t);
|
||||
|
||||
|
||||
@@ -221,6 +221,9 @@ Unit Unit::Builder(const Dimension * dimension, const Representative * represent
|
||||
}
|
||||
|
||||
Expression Unit::shallowReduce(ExpressionNode::ReductionContext reductionContext) {
|
||||
if (reductionContext.symbolicComputation() == ExpressionNode::SymbolicComputation::ReplaceAllSymbolsWithUndefinedAndDoNotReplaceUnits) {
|
||||
return *this;
|
||||
}
|
||||
UnitNode * unitNode = static_cast<UnitNode *>(node());
|
||||
const Dimension * dim = unitNode->dimension();
|
||||
const Representative * rep = unitNode->representative();
|
||||
|
||||
74
poincare/src/unit_convert.cpp
Normal file
74
poincare/src/unit_convert.cpp
Normal file
@@ -0,0 +1,74 @@
|
||||
#include <poincare/unit_convert.h>
|
||||
#include <poincare/code_point_layout.h>
|
||||
#include <poincare/complex.h>
|
||||
#include <poincare/context.h>
|
||||
#include <poincare/division.h>
|
||||
#include <poincare/float.h>
|
||||
#include <poincare/horizontal_layout.h>
|
||||
#include <poincare/infinity.h>
|
||||
#include <poincare/multiplication.h>
|
||||
#include <poincare/serialization_helper.h>
|
||||
#include <poincare/symbol.h>
|
||||
#include <poincare/undefined.h>
|
||||
#include <ion.h>
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <utility>
|
||||
|
||||
namespace Poincare {
|
||||
|
||||
Expression UnitConvertNode::shallowReduce(ReductionContext reductionContext) {
|
||||
return UnitConvert(this).shallowReduce(reductionContext);
|
||||
}
|
||||
|
||||
int UnitConvertNode::serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const {
|
||||
//TODO LEA factorize with Store
|
||||
constexpr int stringMaxSize = CodePoint::MaxCodePointCharLength + 1;
|
||||
char string[stringMaxSize];
|
||||
SerializationHelper::CodePoint(string, stringMaxSize, UCodePointRightwardsArrow);
|
||||
return SerializationHelper::Infix(this, buffer, bufferSize, floatDisplayMode, numberOfSignificantDigits, string);
|
||||
}
|
||||
|
||||
Layout UnitConvertNode::createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const {
|
||||
//TODO LEA factorize with Store
|
||||
HorizontalLayout result = HorizontalLayout::Builder();
|
||||
result.addOrMergeChildAtIndex(childAtIndex(0)->createLayout(floatDisplayMode, numberOfSignificantDigits), 0, false);
|
||||
result.addChildAtIndex(CodePointLayout::Builder(UCodePointRightwardsArrow), result.numberOfChildren(), result.numberOfChildren(), nullptr);
|
||||
result.addOrMergeChildAtIndex(childAtIndex(1)->createLayout(floatDisplayMode, numberOfSignificantDigits), result.numberOfChildren(), false);
|
||||
return std::move(result);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Evaluation<T> UnitConvertNode::templatedApproximate(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) 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();
|
||||
}
|
||||
|
||||
Expression UnitConvert::shallowReduce(ExpressionNode::ReductionContext reductionContext) {
|
||||
// UnitConvert the expression.
|
||||
Expression finalUnit = childAtIndex(1).clone();
|
||||
Expression division = Division::Builder(childAtIndex(0), childAtIndex(1));
|
||||
division = division.simplify(reductionContext);
|
||||
if (division.beautifiedExpressionHasUnits()) {
|
||||
return replaceWithUndefinedInPlace();
|
||||
}
|
||||
double floatValue = division.approximateToScalar<double>(reductionContext.context(), reductionContext.complexFormat(), reductionContext.angleUnit());
|
||||
if (std::isinf(floatValue)) {
|
||||
return Infinity::Builder(false); //FIXME sign?
|
||||
}
|
||||
if (std::isnan(floatValue)) {
|
||||
return Undefined::Builder();
|
||||
}
|
||||
division = Multiplication::Builder(Float<double>::Builder(floatValue), finalUnit);
|
||||
static_cast<Multiplication &>(division).mergeMultiplicationChildrenInPlace();
|
||||
replaceWithInPlace(division);
|
||||
return division;
|
||||
}
|
||||
|
||||
template Evaluation<float> UnitConvertNode::templatedApproximate<float>(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const;
|
||||
template Evaluation<double> UnitConvertNode::templatedApproximate<double>(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const;
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user