mirror of
https://github.com/UpsilonNumworks/Upsilon.git
synced 2026-01-19 00:37:25 +01:00
288 lines
9.8 KiB
C++
288 lines
9.8 KiB
C++
#include <poincare/rational.h>
|
|
#include <poincare/arithmetic.h>
|
|
#include <poincare/code_point_layout.h>
|
|
#include <poincare/fraction_layout.h>
|
|
#include <poincare/infinity.h>
|
|
#include <poincare/opposite.h>
|
|
#include <poincare/serialization_helper.h>
|
|
#include <assert.h>
|
|
#include <math.h>
|
|
#include <utility>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
namespace Poincare {
|
|
|
|
/* Rational Node */
|
|
|
|
RationalNode::RationalNode(const native_uint_t * numeratorDigits, uint8_t numeratorSize, const native_uint_t * denominatorDigits, uint8_t denominatorSize, bool negative) :
|
|
m_negative(negative),
|
|
m_numberOfDigitsNumerator(numeratorSize),
|
|
m_numberOfDigitsDenominator(denominatorSize)
|
|
{
|
|
if (numeratorDigits) {
|
|
memcpy(m_digits, numeratorDigits, numeratorSize*sizeof(native_uint_t));
|
|
}
|
|
if (denominatorDigits) {
|
|
memcpy(m_digits + numeratorSize, denominatorDigits, denominatorSize*sizeof(native_uint_t));
|
|
}
|
|
}
|
|
|
|
Integer RationalNode::signedNumerator() const {
|
|
return Integer::BuildInteger((native_uint_t *)m_digits, m_numberOfDigitsNumerator, m_negative);
|
|
}
|
|
|
|
Integer RationalNode::unsignedNumerator() const {
|
|
return Integer::BuildInteger((native_uint_t *)m_digits, m_numberOfDigitsNumerator, false);
|
|
}
|
|
|
|
Integer RationalNode::denominator() const {
|
|
return Integer::BuildInteger(((native_uint_t *)m_digits+m_numberOfDigitsNumerator), m_numberOfDigitsDenominator, false);
|
|
}
|
|
|
|
// Tree Node
|
|
|
|
static size_t RationalSize(uint8_t numeratorNumberOfDigits, uint8_t denominatorNumberOfDigits) {
|
|
uint8_t realNumeratorSize = numeratorNumberOfDigits > Integer::k_maxNumberOfDigits ? 0 : numeratorNumberOfDigits;
|
|
uint8_t realDenominatorSize = denominatorNumberOfDigits > Integer::k_maxNumberOfDigits ? 0 : denominatorNumberOfDigits;
|
|
return sizeof(RationalNode) + sizeof(native_uint_t)*(realNumeratorSize + realDenominatorSize);
|
|
}
|
|
|
|
size_t RationalNode::size() const {
|
|
return RationalSize(m_numberOfDigitsNumerator, m_numberOfDigitsDenominator);
|
|
}
|
|
|
|
// Serialization Node
|
|
|
|
#if POINCARE_TREE_LOG
|
|
void RationalNode::logAttributes(std::ostream & stream) const {
|
|
stream << " negative=\"" << m_negative << "\"";
|
|
stream << " numerator=\"";
|
|
this->signedNumerator().logInteger(stream);
|
|
stream << "\"";
|
|
stream << " denominator=\"";
|
|
this->denominator().logInteger(stream);
|
|
stream << "\"";
|
|
}
|
|
#endif
|
|
|
|
int RationalNode::serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const {
|
|
if (bufferSize == 0) {
|
|
return -1;
|
|
}
|
|
buffer[bufferSize-1] = 0;
|
|
int numberOfChar = signedNumerator().serialize(buffer, bufferSize);
|
|
if (numberOfChar >= bufferSize-1) {
|
|
return numberOfChar;
|
|
}
|
|
if (isInteger()) {
|
|
return numberOfChar;
|
|
}
|
|
numberOfChar += SerializationHelper::CodePoint(buffer + numberOfChar, bufferSize - numberOfChar, '/');
|
|
if (numberOfChar >= bufferSize-1) {
|
|
return numberOfChar;
|
|
}
|
|
numberOfChar += denominator().serialize(buffer+numberOfChar, bufferSize-numberOfChar);
|
|
return numberOfChar;
|
|
}
|
|
|
|
// Expression subclassing
|
|
|
|
Expression RationalNode::setSign(Sign s, ReductionContext reductionContext) {
|
|
assert(s == ExpressionNode::Sign::Positive || s == ExpressionNode::Sign::Negative);
|
|
return Rational(this).setSign(s);
|
|
}
|
|
|
|
// Layout
|
|
|
|
Layout RationalNode::createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const {
|
|
Layout numeratorLayout = signedNumerator().createLayout();
|
|
if (isInteger()) {
|
|
return numeratorLayout;
|
|
}
|
|
Layout denominatorLayout = denominator().createLayout();
|
|
return FractionLayout::Builder(numeratorLayout, denominatorLayout);
|
|
}
|
|
|
|
// Approximation
|
|
|
|
template<typename T> T RationalNode::templatedApproximate() const {
|
|
T n = signedNumerator().approximate<T>();
|
|
T d = denominator().approximate<T>();
|
|
return n/d;
|
|
}
|
|
|
|
// Comparison
|
|
|
|
int RationalNode::NaturalOrder(const RationalNode * i, const RationalNode * j) {
|
|
if (Number(i).sign() == Sign::Negative && Number(j).sign() == Sign::Positive) {
|
|
return -1;
|
|
}
|
|
if (Number(i).sign() == Sign::Positive && Number(j).sign() == Sign::Negative) {
|
|
return 1;
|
|
}
|
|
Integer i1 = Integer::Multiplication(i->signedNumerator(), j->denominator());
|
|
Integer i2 = Integer::Multiplication(i->denominator(), j->signedNumerator());
|
|
return Integer::NaturalOrder(i1, i2);
|
|
}
|
|
|
|
int RationalNode::simplificationOrderSameType(const ExpressionNode * e, bool ascending, bool canBeInterrupted, bool ignoreParentheses) const {
|
|
if (!ascending) {
|
|
return e->simplificationOrderSameType(this, true, canBeInterrupted, ignoreParentheses);
|
|
}
|
|
assert(e->type() == ExpressionNode::Type::Rational);
|
|
const RationalNode * other = static_cast<const RationalNode *>(e);
|
|
return NaturalOrder(this, other);
|
|
}
|
|
|
|
// Simplification
|
|
|
|
Expression RationalNode::shallowReduce(ReductionContext reductionContext) {
|
|
return Rational(this).shallowReduce();
|
|
}
|
|
|
|
Expression RationalNode::shallowBeautify(ReductionContext reductionContext) {
|
|
return Rational(this).shallowBeautify();
|
|
}
|
|
|
|
Expression RationalNode::denominator(ReductionContext reductionContext) const {
|
|
return Rational(this).denominator();
|
|
}
|
|
|
|
/* Rational */
|
|
|
|
// Constructors
|
|
|
|
Rational Rational::Builder(Integer & num, Integer & den) {
|
|
assert(!den.isZero());
|
|
if (!num.isOne() && !den.isOne()) {
|
|
// Avoid computing GCD if possible
|
|
Integer gcd = Arithmetic::GCD(num, den);
|
|
num = Integer::Division(num, gcd).quotient;
|
|
den = Integer::Division(den, gcd).quotient;
|
|
}
|
|
bool negative = (!num.isNegative() && den.isNegative()) || (!den.isNegative() && num.isNegative());
|
|
return Rational::Builder(num.digits(), num.numberOfDigits(), den.digits(), den.numberOfDigits(), negative);
|
|
}
|
|
|
|
Rational Rational::Builder(const Integer & numerator) {
|
|
native_uint_t one = 1;
|
|
return Rational::Builder(numerator.digits(), numerator.numberOfDigits(), &one, 1, numerator.isNegative());
|
|
}
|
|
|
|
Rational Rational::Builder(native_int_t i) {
|
|
native_uint_t one = 1;
|
|
if (i == 0) {
|
|
return Rational::Builder(nullptr, 0, &one, 1, false);
|
|
}
|
|
native_uint_t absI = i < 0 ? -i : i;
|
|
return Rational::Builder(&absI, 1, &one, 1, i < 0);
|
|
}
|
|
|
|
Rational Rational::Builder(native_int_t i, native_int_t j) {
|
|
Integer iInteger(i);
|
|
Integer jInteger(j);
|
|
return Rational::Builder(iInteger, jInteger);
|
|
}
|
|
|
|
Rational Rational::Builder(const char * iString, const char * jString) {
|
|
Integer iInteger(iString);
|
|
Integer jInteger(jString);
|
|
return Rational::Builder(iInteger, jInteger);
|
|
}
|
|
|
|
bool Rational::numeratorOrDenominatorIsInfinity() const {
|
|
return signedIntegerNumerator().isOverflow() || integerDenominator().isOverflow();
|
|
}
|
|
|
|
// Basic operations
|
|
|
|
Rational Rational::Addition(const Rational & i, const Rational & j) {
|
|
Integer newNumerator = Integer::Addition(Integer::Multiplication(i.signedIntegerNumerator(), j.integerDenominator()), Integer::Multiplication(j.signedIntegerNumerator(), i.integerDenominator()));
|
|
Integer newDenominator = Integer::Multiplication(i.integerDenominator(), j.integerDenominator());
|
|
return Rational::Builder(newNumerator, newDenominator);
|
|
}
|
|
|
|
Rational Rational::Multiplication(const Rational & i, const Rational & j) {
|
|
Integer newNumerator = Integer::Multiplication(i.signedIntegerNumerator(), j.signedIntegerNumerator());
|
|
Integer newDenominator = Integer::Multiplication(i.integerDenominator(), j.integerDenominator());
|
|
return Rational::Builder(newNumerator, newDenominator);
|
|
}
|
|
|
|
Rational Rational::IntegerPower(const Rational & i, const Integer & j) {
|
|
Integer absJ = j;
|
|
absJ.setNegative(false);
|
|
Integer newNumerator = Integer::Power(i.signedIntegerNumerator(), absJ);
|
|
Integer newDenominator = Integer::Power(i.integerDenominator(), absJ);
|
|
if (j.isNegative()) {
|
|
return Rational::Builder(newDenominator, newNumerator);
|
|
}
|
|
return Rational::Builder(newNumerator, newDenominator);
|
|
}
|
|
|
|
Rational Rational::Builder(const native_uint_t * i, uint8_t numeratorSize, const native_uint_t * j, uint8_t denominatorSize, bool negative) {
|
|
void * bufferNode = TreePool::sharedPool()->alloc(RationalSize(numeratorSize, denominatorSize));
|
|
RationalNode * node = new (bufferNode) RationalNode(i, numeratorSize, j, denominatorSize, negative);
|
|
TreeHandle h = TreeHandle::BuildWithGhostChildren(node);
|
|
return static_cast<Rational &>(h);
|
|
}
|
|
|
|
Expression Rational::shallowReduce() {
|
|
// FIXME:
|
|
/* Infinite Rational should not exist as they aren't parsed and are supposed
|
|
* to be turn in Float if they should appear. We assert(false) so far, but
|
|
* the right behaviour would be to find the right float to represent them.
|
|
* However at that point, it is too late to find it. The issue is earlier... */
|
|
#if 0
|
|
if (unsignedIntegerNumerator().isOverflow() && integerDenominator().isOverflow()) {
|
|
assert(false);
|
|
return Undefined::Builder();
|
|
}
|
|
// Turn into Infinite if the numerator is too big.
|
|
if (unsignedIntegerNumerator().isOverflow()) {
|
|
assert(false);
|
|
return Infinity::Builder(sign(&context) == ExpressionNode::Sign::Negative);
|
|
}
|
|
// Turn into 0 if the denominator is too big.
|
|
if (integerDenominator().isOverflow()) {
|
|
assert(false);
|
|
return Rational::Builder(0);
|
|
}
|
|
#endif
|
|
assert(!numeratorOrDenominatorIsInfinity());
|
|
return *this;
|
|
}
|
|
|
|
Expression Rational::shallowBeautify() {
|
|
if (sign() == ExpressionNode::Sign::Negative) {
|
|
Expression abs = setSign(ExpressionNode::Sign::Positive);
|
|
Opposite o = Opposite::Builder();
|
|
replaceWithInPlace(o);
|
|
o.replaceChildAtIndexInPlace(0, abs);
|
|
return std::move(o);
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
Expression Rational::denominator() const {
|
|
Integer d = integerDenominator();
|
|
if (d.isOne()) {
|
|
return Expression();
|
|
}
|
|
if (d.isOverflow()) {
|
|
return Infinity::Builder(false);
|
|
}
|
|
return Rational::Builder(d);
|
|
}
|
|
|
|
Expression Rational::setSign(ExpressionNode::Sign s) {
|
|
assert(s == ExpressionNode::Sign::Positive || s == ExpressionNode::Sign::Negative);
|
|
node()->setNegative(s == ExpressionNode::Sign::Negative);
|
|
return *this;
|
|
}
|
|
|
|
template float RationalNode::templatedApproximate() const;
|
|
template double RationalNode::templatedApproximate() const;
|
|
|
|
}
|