mirror of
https://github.com/UpsilonNumworks/Upsilon.git
synced 2026-01-18 16:27:34 +01:00
[apps/calculation] Calculations now hold 4 texts: input, exact output
and 2 approximate outputs - one with the maximal number of significant digits and one with the number of significant digits asked by the user. This enables to find the approximate output without going through the approximation routine again.
This commit is contained in:
@@ -121,7 +121,7 @@ void IllustratedListController::setExpression(Poincare::Expression e) {
|
||||
int IllustratedListController::textAtIndex(char * buffer, size_t bufferSize, int index) {
|
||||
ScrollableThreeExpressionsCell * myCell = static_cast<ScrollableThreeExpressionsCell *>(m_listController.selectableTableView()->selectedCell());
|
||||
Shared::ExpiringPointer<Calculation> c = m_calculationStore.calculationAtIndex(index-1);
|
||||
const char * text = myCell->selectedSubviewPosition() == ScrollableThreeExpressionsView::SubviewPosition::Right ? c->approximateOutputText() : c->exactOutputText();
|
||||
const char * text = myCell->selectedSubviewPosition() == ScrollableThreeExpressionsView::SubviewPosition::Right ? c->approximateOutputText(Calculation::NumberOfSignificantDigits::Maximal) : c->exactOutputText();
|
||||
return strlcpy(buffer, text, bufferSize);
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ void TrigonometryListController::setExpression(Poincare::Expression e) {
|
||||
m_calculationStore.push("θ", context);
|
||||
|
||||
// Set trigonometry illustration
|
||||
float angle = Shared::PoincareHelpers::ApproximateToScalar<float>(m_calculationStore.calculationAtIndex(0)->approximateOutput(context), context);
|
||||
float angle = Shared::PoincareHelpers::ApproximateToScalar<float>(m_calculationStore.calculationAtIndex(0)->approximateOutput(context, Calculation::NumberOfSignificantDigits::Maximal), context);
|
||||
m_model.setAngle(angle);
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,8 @@ static inline KDCoordinate maxCoordinate(KDCoordinate x, KDCoordinate y) { retur
|
||||
|
||||
bool Calculation::operator==(const Calculation& c) {
|
||||
return strcmp(inputText(), c.inputText()) == 0
|
||||
&& strcmp(approximateOutputText(), c.approximateOutputText()) == 0
|
||||
&& strcmp(approximateOutputText(NumberOfSignificantDigits::Maximal), c.approximateOutputText(NumberOfSignificantDigits::Maximal)) == 0
|
||||
&& strcmp(approximateOutputText(NumberOfSignificantDigits::UserDefined), c.approximateOutputText(NumberOfSignificantDigits::UserDefined)) == 0
|
||||
/* Some calculations can make appear trigonometric functions in their
|
||||
* exact output. Their argument will be different with the angle unit
|
||||
* preferences but both input and approximate output will be the same.
|
||||
@@ -28,8 +29,8 @@ bool Calculation::operator==(const Calculation& c) {
|
||||
|
||||
Calculation * Calculation::next() const {
|
||||
const char * result = reinterpret_cast<const char *>(this) + sizeof(Calculation);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
result = result + strlen(result) + 1; // Pass inputText, exactOutputText, ApproximateOutputText
|
||||
for (int i = 0; i < k_numberOfExpressions; i++) {
|
||||
result = result + strlen(result) + 1; // Pass inputText, exactOutputText, ApproximateOutputText x2
|
||||
}
|
||||
return reinterpret_cast<Calculation *>(const_cast<char *>(result));
|
||||
}
|
||||
@@ -41,9 +42,13 @@ void Calculation::tidy() {
|
||||
m_expandedHeight = -1;
|
||||
}
|
||||
|
||||
const char * Calculation::approximateOutputText() const {
|
||||
const char * Calculation::approximateOutputText(NumberOfSignificantDigits numberOfSignificantDigits) const {
|
||||
const char * exactOutput = exactOutputText();
|
||||
return exactOutput + strlen(exactOutput) + 1;
|
||||
const char * approximateOutputTextWithMaxNumberOfDigits = exactOutput + strlen(exactOutput) + 1;
|
||||
if (numberOfSignificantDigits == NumberOfSignificantDigits::Maximal) {
|
||||
return approximateOutputTextWithMaxNumberOfDigits;
|
||||
}
|
||||
return approximateOutputTextWithMaxNumberOfDigits + strlen(approximateOutputTextWithMaxNumberOfDigits) + 1;
|
||||
}
|
||||
|
||||
Expression Calculation::input() {
|
||||
@@ -60,18 +65,39 @@ Expression Calculation::exactOutput() {
|
||||
return exactOutput;
|
||||
}
|
||||
|
||||
Expression Calculation::approximateOutput(Context * context) {
|
||||
Expression exp = Expression::Parse(approximateOutputText(), nullptr);
|
||||
Expression Calculation::approximateOutput(Context * context, NumberOfSignificantDigits numberOfSignificantDigits) {
|
||||
Expression exp = Expression::Parse(approximateOutputText(numberOfSignificantDigits), nullptr);
|
||||
assert(!exp.isUninitialized());
|
||||
/* Warning:
|
||||
* Since quite old versions of Epsilon, the Expression 'exp' was used to be
|
||||
* approximated again to ensure its content was in the expected form. That is
|
||||
* currently the case (see Poincare::Expression::simplifyAndApproximate). So
|
||||
* 'exp' does not need to be approximated. Moreover since the approximate
|
||||
* output may contain units and that a Poincare::Unit approximates to undef,
|
||||
* thus it must not be approximated. If another behavior is desired, the
|
||||
* previous considerations should be taken into account. */
|
||||
return exp;
|
||||
* approximated again to ensure its content was in the expected form - a
|
||||
* linear combination of Decimal.
|
||||
* However, since the approximate output may contain units and that a
|
||||
* Poincare::Unit approximates to undef, thus it must not be approximated
|
||||
* anymore.
|
||||
* We have to keep two serializations of the approximation outputs:
|
||||
* - one with the maximal significant digits, to be used by 'ans' or when
|
||||
* handling 'OK' event on the approximation output.
|
||||
* - one with the displayed number of significant digits that we parse to
|
||||
* create the displayed layout. If we used the other serialization to
|
||||
* create the layout, the result of the parsing could be an Integer which
|
||||
* does not take the number of significant digits into account when creating
|
||||
* its layout. This would lead to wrong number of significant digits in the
|
||||
* layout.
|
||||
* For instance:
|
||||
* Number of asked significant digits: 7
|
||||
* Input: "123456780", Approximate output: "1.234567E8"
|
||||
*
|
||||
* |--------------------------------------------------------------------------------------|
|
||||
* | Number of significant digits | Approximate text | Parse expression | Layout |
|
||||
* |------------------------------+------------------+---------------------+--------------|
|
||||
* | Maximal | "123456780" | Integer(123456780) | "123456780" |
|
||||
* |------------------------------+------------------+---------------------+--------------|
|
||||
* | User defined | "1.234567E8" | Decimal(1.234567E8) | "1.234567E8" |
|
||||
* |--------------------------------------------------------------------------------------|
|
||||
*
|
||||
*/
|
||||
return exp;
|
||||
}
|
||||
|
||||
Layout Calculation::createInputLayout() {
|
||||
@@ -91,7 +117,7 @@ Layout Calculation::createExactOutputLayout(bool * couldNotCreateExactLayout) {
|
||||
Layout Calculation::createApproximateOutputLayout(Context * context, bool * couldNotCreateApproximateLayout) {
|
||||
Poincare::ExceptionCheckpoint ecp;
|
||||
if (ExceptionRun(ecp)) {
|
||||
return PoincareHelpers::CreateLayout(approximateOutput(context));
|
||||
return PoincareHelpers::CreateLayout(approximateOutput(context, NumberOfSignificantDigits::UserDefined));
|
||||
} else {
|
||||
*couldNotCreateApproximateLayout = true;
|
||||
return Layout();
|
||||
@@ -220,18 +246,18 @@ Calculation::DisplayOutput Calculation::displayOutput(Context * context) {
|
||||
}, context, true))
|
||||
{
|
||||
m_displayOutput = DisplayOutput::ApproximateOnly;
|
||||
} else if (strcmp(exactOutputText(), approximateOutputText()) == 0) {
|
||||
/* If the exact and approximate results' texts are equal and their layouts
|
||||
* too, do not display the exact result. If the two layouts are not equal
|
||||
* because of the number of significant digits, we display both. */
|
||||
m_displayOutput = exactAndApproximateDisplayedOutputsAreEqual(context) == Calculation::EqualSign::Equal ? DisplayOutput::ApproximateOnly : DisplayOutput::ExactAndApproximate;
|
||||
} else if (strcmp(exactOutputText(), approximateOutputText(NumberOfSignificantDigits::UserDefined)) == 0) {
|
||||
/* If the exact and approximate results' texts are equal (with the
|
||||
* UserDefined number of significant digits), do not display the exact
|
||||
* result. Indeed, in this case, the layouts are identical. */
|
||||
m_displayOutput = DisplayOutput::ApproximateOnly;
|
||||
} else if (strcmp(exactOutputText(), Undefined::Name()) == 0
|
||||
|| strcmp(approximateOutputText(), Unreal::Name()) == 0
|
||||
|| strcmp(approximateOutputText(NumberOfSignificantDigits::Maximal), Unreal::Name()) == 0
|
||||
|| exactOutput().type() == ExpressionNode::Type::Undefined)
|
||||
{
|
||||
// If the approximate result is 'unreal' or the exact result is 'undef'
|
||||
m_displayOutput = DisplayOutput::ApproximateOnly;
|
||||
} else if (strcmp(approximateOutputText(), Undefined::Name()) == 0
|
||||
} else if (strcmp(approximateOutputText(NumberOfSignificantDigits::Maximal), Undefined::Name()) == 0
|
||||
&& strcmp(inputText(), exactOutputText()) == 0)
|
||||
{
|
||||
/* If the approximate result is 'undef' and the input and exactOutput are
|
||||
@@ -278,7 +304,7 @@ Calculation::EqualSign Calculation::exactAndApproximateDisplayedOutputsAreEqual(
|
||||
exactOutputExpression = Undefined::Builder();
|
||||
}
|
||||
Preferences::ComplexFormat complexFormat = Expression::UpdatedComplexFormatWithTextInput(preferences->complexFormat(), m_inputText);
|
||||
m_equalSign = exactOutputExpression.isEqualToItsApproximationLayout(approximateOutput(context), buffer, bufferSize, complexFormat, preferences->angleUnit(), preferences->displayMode(), preferences->numberOfSignificantDigits(), context) ? EqualSign::Equal : EqualSign::Approximation;
|
||||
m_equalSign = exactOutputExpression.isEqualToItsApproximationLayout(approximateOutput(context, NumberOfSignificantDigits::UserDefined), buffer, bufferSize, complexFormat, preferences->angleUnit(), preferences->displayMode(), preferences->numberOfSignificantDigits(), context) ? EqualSign::Equal : EqualSign::Approximation;
|
||||
return m_equalSign;
|
||||
} else {
|
||||
/* Do not override m_equalSign in case there is enough room in the pool
|
||||
|
||||
@@ -19,6 +19,7 @@ class CalculationStore;
|
||||
* */
|
||||
|
||||
class Calculation {
|
||||
friend CalculationStore;
|
||||
public:
|
||||
enum class EqualSign : uint8_t {
|
||||
Unknown,
|
||||
@@ -63,14 +64,19 @@ public:
|
||||
void tidy();
|
||||
|
||||
// Texts
|
||||
enum class NumberOfSignificantDigits {
|
||||
Maximal,
|
||||
UserDefined
|
||||
};
|
||||
const char * inputText() const { return m_inputText; }
|
||||
const char * exactOutputText() const { return m_inputText + strlen(m_inputText) + 1; }
|
||||
const char * approximateOutputText() const;
|
||||
// See comment in approximateOutput implementation explaining the need of two approximateOutputTexts
|
||||
const char * approximateOutputText(NumberOfSignificantDigits numberOfSignificantDigits) const;
|
||||
|
||||
// Expressions
|
||||
Poincare::Expression input();
|
||||
Poincare::Expression exactOutput();
|
||||
Poincare::Expression approximateOutput(Poincare::Context * context);
|
||||
Poincare::Expression approximateOutput(Poincare::Context * context, NumberOfSignificantDigits numberOfSignificantDigits);
|
||||
|
||||
// Layouts
|
||||
Poincare::Layout createInputLayout();
|
||||
@@ -89,6 +95,7 @@ public:
|
||||
// Additional Information
|
||||
AdditionalInformationType additionalInformationType(Poincare::Context * context);
|
||||
private:
|
||||
static constexpr int k_numberOfExpressions = 4;
|
||||
static constexpr KDCoordinate k_heightComputationFailureHeight = 50;
|
||||
static constexpr const char * k_maximalIntegerWithAdditionalInformation = "10000000000000000";
|
||||
/* Buffers holding text expressions have to be longer than the text written
|
||||
|
||||
@@ -79,7 +79,7 @@ ExpiringPointer<Calculation> CalculationStore::push(const char * text, Context *
|
||||
const char * inputSerialization = nextSerializationLocation;
|
||||
{
|
||||
Expression input = Expression::Parse(text, context).replaceSymbolWithExpression(Symbol::Ans(), ans);
|
||||
if (!serializeExpression(input, nextSerializationLocation, &newCalculationsLocation)) {
|
||||
if (!pushSerializeExpression(input, nextSerializationLocation, &newCalculationsLocation)) {
|
||||
/* If the input does not fit in the store (event if the current
|
||||
* calculation is the only calculation), just replace the calculation with
|
||||
* undef. */
|
||||
@@ -89,16 +89,27 @@ ExpiringPointer<Calculation> CalculationStore::push(const char * text, Context *
|
||||
}
|
||||
|
||||
// Compute and serialize the outputs
|
||||
/* The serialized outputs are:
|
||||
* - the exact ouput
|
||||
* - the approximate output with the maximal number of significant digits
|
||||
* - the approximate output with the displayed number of significant digits */
|
||||
{
|
||||
Expression outputs[] = {Expression(), Expression()};
|
||||
// Outputs hold exact output, approximate output and its duplicate
|
||||
constexpr static int numberOfOutputs = Calculation::k_numberOfExpressions - 1;
|
||||
Expression outputs[numberOfOutputs] = {Expression(), Expression(), Expression()};
|
||||
PoincareHelpers::ParseAndSimplifyAndApproximate(inputSerialization, &(outputs[0]), &(outputs[1]), context, false);
|
||||
for (int i = 0; i < 2; i++) {
|
||||
if (!serializeExpression(outputs[i], nextSerializationLocation, &newCalculationsLocation)) {
|
||||
outputs[2] = outputs[1];
|
||||
int numberOfSignificantDigits = Poincare::PrintFloat::k_numberOfStoredSignificantDigits;
|
||||
for (int i = 0; i < numberOfOutputs; i++) {
|
||||
if (i == numberOfOutputs - 1) {
|
||||
numberOfSignificantDigits = Poincare::Preferences::sharedPreferences()->numberOfSignificantDigits();
|
||||
}
|
||||
if (!pushSerializeExpression(outputs[i], nextSerializationLocation, &newCalculationsLocation, numberOfSignificantDigits)) {
|
||||
/* If the exat/approximate output does not fit in the store (event if the
|
||||
* current calculation is the only calculation), replace the output with
|
||||
* undef if it fits, else replace the whole calcualtion with undef. */
|
||||
Expression undef = Undefined::Builder();
|
||||
if (!serializeExpression(undef, nextSerializationLocation, &newCalculationsLocation)) {
|
||||
if (!pushSerializeExpression(undef, nextSerializationLocation, &newCalculationsLocation)) {
|
||||
return emptyStoreAndPushUndef(context);
|
||||
}
|
||||
}
|
||||
@@ -165,7 +176,7 @@ Expression CalculationStore::ansExpression(Context * context) {
|
||||
Expression e = mostRecentCalculation->exactOutput();
|
||||
bool exactOuptutInvolvesStoreEqual = e.type() == ExpressionNode::Type::Store || e.type() == ExpressionNode::Type::Equal;
|
||||
if (mostRecentCalculation->input().recursivelyMatches(Expression::IsApproximate, context) || exactOuptutInvolvesStoreEqual) {
|
||||
return mostRecentCalculation->approximateOutput(context);
|
||||
return mostRecentCalculation->approximateOutput(context, Calculation::NumberOfSignificantDigits::Maximal);
|
||||
}
|
||||
return mostRecentCalculation->exactOutput();
|
||||
}
|
||||
@@ -182,12 +193,20 @@ Calculation * CalculationStore::bufferCalculationAtIndex(int i) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool CalculationStore::serializeExpression(Expression e, char * location, char * * newCalculationsLocation) {
|
||||
bool CalculationStore::pushSerializeExpression(Expression e, char * location, char * * newCalculationsLocation, int numberOfSignificantDigits) {
|
||||
assert(m_slidedBuffer);
|
||||
return pushExpression(
|
||||
[](char * location, size_t locationSize, void * e) {
|
||||
return PoincareHelpers::Serialize(*(Expression *)e, location, locationSize) < (int)locationSize-1;
|
||||
}, &e, location, newCalculationsLocation);
|
||||
assert(*newCalculationsLocation <= m_buffer + k_bufferSize);
|
||||
bool expressionIsPushed = false;
|
||||
while (true) {
|
||||
size_t locationSize = *newCalculationsLocation - location;
|
||||
expressionIsPushed = (PoincareHelpers::Serialize(e, location, locationSize, numberOfSignificantDigits) < (int)locationSize-1);
|
||||
if (expressionIsPushed || *newCalculationsLocation >= m_buffer + k_bufferSize) {
|
||||
break;
|
||||
}
|
||||
*newCalculationsLocation = *newCalculationsLocation + deleteLastCalculation();
|
||||
assert(*newCalculationsLocation <= m_buffer + k_bufferSize);
|
||||
}
|
||||
return expressionIsPushed;
|
||||
}
|
||||
|
||||
char * CalculationStore::slideCalculationsToEndOfBuffer() {
|
||||
@@ -230,20 +249,6 @@ const char * CalculationStore::lastCalculationPosition(const char * calculations
|
||||
return reinterpret_cast<const char *>(c);
|
||||
}
|
||||
|
||||
bool CalculationStore::pushExpression(ValueCreator valueCreator, Expression * expression, char * location, char * * newCalculationsLocation) {
|
||||
assert(*newCalculationsLocation <= m_buffer + k_bufferSize);
|
||||
bool expressionIsPushed = false;
|
||||
while (true) {
|
||||
expressionIsPushed = valueCreator(location, *newCalculationsLocation - location, expression);
|
||||
if (expressionIsPushed || *newCalculationsLocation >= m_buffer + k_bufferSize) {
|
||||
break;
|
||||
}
|
||||
*newCalculationsLocation = *newCalculationsLocation + deleteLastCalculation();
|
||||
assert(*newCalculationsLocation <= m_buffer + k_bufferSize);
|
||||
}
|
||||
return expressionIsPushed;
|
||||
}
|
||||
|
||||
Shared::ExpiringPointer<Calculation> CalculationStore::emptyStoreAndPushUndef(Context * context) {
|
||||
/* We end up here as a result of a failed calculation push. The store
|
||||
* attributes are not necessarily clean, so we need to reset them. */
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "calculation.h"
|
||||
#include <apps/shared/expiring_pointer.h>
|
||||
#include <poincare/print_float.h>
|
||||
|
||||
namespace Calculation {
|
||||
|
||||
@@ -35,7 +36,7 @@ public:
|
||||
void tidy();
|
||||
private:
|
||||
static constexpr int k_maxNumberOfCalculations = 25;
|
||||
static constexpr int k_bufferSize = 10 * 3 * Constant::MaxSerializedExpressionSize;
|
||||
static constexpr int k_bufferSize = 10 * Calculation::k_numberOfExpressions * Constant::MaxSerializedExpressionSize;
|
||||
|
||||
class CalculationIterator {
|
||||
public:
|
||||
@@ -55,12 +56,10 @@ private:
|
||||
|
||||
Calculation * bufferCalculationAtIndex(int i);
|
||||
int remainingBufferSize() const { assert(m_bufferEnd >= m_buffer); return k_bufferSize - (m_bufferEnd - m_buffer); }
|
||||
bool serializeExpression(Poincare::Expression e, char * location, char * * newCalculationsLocation);
|
||||
bool pushSerializeExpression(Poincare::Expression e, char * location, char * * newCalculationsLocation, int numberOfSignificantDigits = Poincare::PrintFloat::k_numberOfStoredSignificantDigits);
|
||||
char * slideCalculationsToEndOfBuffer(); // returns the new position of the calculations
|
||||
size_t deleteLastCalculation(const char * calculationsStart = nullptr);
|
||||
const char * lastCalculationPosition(const char * calculationsStart) const;
|
||||
typedef bool (*ValueCreator)(char * location, size_t locationSize, void * e);
|
||||
bool pushExpression(ValueCreator valueCrator, Poincare::Expression * expression, char * location, char * * newCalculationsLocation);
|
||||
Shared::ExpiringPointer<Calculation> emptyStoreAndPushUndef(Poincare::Context * context);
|
||||
|
||||
char m_buffer[k_bufferSize];
|
||||
|
||||
@@ -80,7 +80,7 @@ bool HistoryController::handleEvent(Ion::Events::Event event) {
|
||||
if (outputSubviewPosition == ScrollableTwoExpressionsView::SubviewPosition::Right
|
||||
&& !calculation->shouldOnlyDisplayExactOutput())
|
||||
{
|
||||
editController->insertTextBody(calculation->approximateOutputText());
|
||||
editController->insertTextBody(calculation->approximateOutputText(Calculation::NumberOfSignificantDigits::Maximal));
|
||||
} else {
|
||||
editController->insertTextBody(calculation->exactOutputText());
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ QUIZ_CASE(calculation_ans) {
|
||||
store.push("ans+0.22", &globalContext);
|
||||
lastCalculation = store.calculationAtIndex(0);
|
||||
quiz_assert(lastCalculation->displayOutput(&globalContext) == ::Calculation::Calculation::DisplayOutput::ExactAndApproximateToggle);
|
||||
quiz_assert(strcmp(lastCalculation->approximateOutputText(),"2.6366666666667") == 0);
|
||||
quiz_assert(strcmp(lastCalculation->approximateOutputText(Calculation::NumberOfSignificantDigits::Maximal),"2.6366666666667") == 0);
|
||||
|
||||
store.deleteAll();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user