diff --git a/apps/graph/values/values_controller.cpp b/apps/graph/values/values_controller.cpp index ad48293ee..d5783efce 100644 --- a/apps/graph/values/values_controller.cpp +++ b/apps/graph/values/values_controller.cpp @@ -237,10 +237,10 @@ int ValuesController::numberOfValuesColumns() { ContinuousFunction::PlotType ValuesController::plotTypeAtColumn(int * i) const { int plotTypeIndex = 0; - while (plotTypeIndex < ContinuousFunction::k_numberOfPlotTypes && *i >= numberOfColumnsForPlotType(plotTypeIndex)) { + while (*i >= numberOfColumnsForPlotType(plotTypeIndex)) { *i -= numberOfColumnsForPlotType(plotTypeIndex++); + assert(plotTypeIndex < ContinuousFunction::k_numberOfPlotTypes); } - assert(plotTypeIndex < ContinuousFunction::k_numberOfPlotTypes); return static_cast(plotTypeIndex); } @@ -255,9 +255,10 @@ int ValuesController::absoluteColumnForValuesColumn(int column) { int valuesColumns = 0; int plotTypeIndex = 0; do { - abscissaColumns++; assert(plotTypeIndex < Shared::ContinuousFunction::k_numberOfPlotTypes); - valuesColumns += m_numberOfValuesColumnsForType[plotTypeIndex++]; + const int numberOfValuesColumnsForType = m_numberOfValuesColumnsForType[plotTypeIndex++]; + valuesColumns += numberOfValuesColumnsForType; + abscissaColumns += (numberOfValuesColumnsForType > 0); } while (valuesColumns <= column); return column + abscissaColumns; } diff --git a/apps/shared/values_controller.cpp b/apps/shared/values_controller.cpp index 8dd252be1..ec0a2c4bd 100644 --- a/apps/shared/values_controller.cpp +++ b/apps/shared/values_controller.cpp @@ -237,14 +237,13 @@ void ValuesController::didChangeCell(int column, int row) { // the first row is never reloaded as it corresponds to title row assert(row > 0); // Conversion of coordinates from absolute table to values table - int valuesRow = valuesRowForAbsoluteRow(row); - if (m_firstMemoizedRow > valuesRow || valuesRow >= m_firstMemoizedRow + k_maxNumberOfDisplayableRows) { + int memoizedRow = valuesRowForAbsoluteRow(row) - m_firstMemoizedRow; + if (0 > memoizedRow || memoizedRow >= k_maxNumberOfDisplayableRows) { // The changed row is out of the memoized table return; } // Update the memoization of rows linked to the changed cell - int memoizedRow = valuesRow - m_firstMemoizedRow; int nbOfMemoizedColumns = numberOfMemoizedColumn(); for (int i = column+1; i < column+numberOfColumnsForAbscissaColumn(column); i++) { int memoizedI = valuesColumnForAbsoluteColumn(i) - m_firstMemoizedColumn; @@ -291,10 +290,10 @@ void ValuesController::resetMemoization() { } char * ValuesController::memoizedBufferForCell(int i, int j) { + const int nbOfMemoizedColumns = numberOfMemoizedColumn(); // Conversion of coordinates from absolute table to values table int valuesI = valuesColumnForAbsoluteColumn(i); int valuesJ = valuesRowForAbsoluteRow(j); - int nbOfMemoizedColumns = numberOfMemoizedColumn(); /* Compute the required offset to apply to the memoized table in order to * display cell (i,j) */ int offsetI = 0; @@ -316,7 +315,7 @@ char * ValuesController::memoizedBufferForCell(int i, int j) { m_firstMemoizedColumn = m_firstMemoizedColumn + offsetI; m_firstMemoizedRow = m_firstMemoizedRow + offsetJ; // Shift already memoized cells - int numberOfMemoizedCell = k_maxNumberOfDisplayableRows*numberOfMemoizedColumn(); + const int numberOfMemoizedCell = k_maxNumberOfDisplayableRows * nbOfMemoizedColumns; size_t moveLength = (numberOfMemoizedCell - absInt(offset))*valuesCellBufferSize()*sizeof(char); if (offset > 0 && offset < numberOfMemoizedCell) { memmove(memoizedBufferAtIndex(offset), memoizedBufferAtIndex(0), moveLength); diff --git a/build/toolchain.android.mak b/build/toolchain.android.mak index f24462a72..2249be610 100644 --- a/build/toolchain.android.mak +++ b/build/toolchain.android.mak @@ -9,20 +9,26 @@ endif NDK_TOOLCHAIN_PATH = $(NDK_PATH)/toolchains/llvm/prebuilt/$(NDK_HOST_TAG)/bin +# No 64 bit device has ever shipped with an API level < 21. Consequently, there +# is no toolchain for those archs on those API levels. Let's enforce NDK_VERSION +# at 21 for these archs, and 16 for the others. + ifeq ($(NDK_ABI),armeabi-v7a) NDK_TARGET = armv7a-linux-androideabi + NDK_VERSION = 16 else ifeq ($(NDK_ABI),arm64-v8a) NDK_TARGET = aarch64-linux-android + NDK_VERSION = 21 else ifeq ($(NDK_ABI),x86) NDK_TARGET = i686-linux-android + NDK_VERSION = 16 else ifeq ($(NDK_ABI),x86_64) NDK_TARGET = x86_64-linux-android + NDK_VERSION = 21 endif ifdef NDK_TARGET -NDK_VERSION ?= 21 - CC = $(NDK_TOOLCHAIN_PATH)/$(NDK_TARGET)$(NDK_VERSION)-clang CXX = $(NDK_TOOLCHAIN_PATH)/$(NDK_TARGET)$(NDK_VERSION)-clang++ LD = $(NDK_TOOLCHAIN_PATH)/$(NDK_TARGET)$(NDK_VERSION)-clang++ diff --git a/build/toolchain.emscripten.mak b/build/toolchain.emscripten.mak index b2fa4b714..b582851bb 100644 --- a/build/toolchain.emscripten.mak +++ b/build/toolchain.emscripten.mak @@ -10,7 +10,6 @@ _IonEventsEmscriptenKeyUp \ _IonEventsEmscriptenPushEvent \ _IonSimulatorCallbackDidScanKeyboard \ __Z8ion_mainiPKPKc \ -__Z8ion_mainiPPc \ __ZN10Invocation7performEPv \ __ZN11MicroPython20ExecutionEnvironment7runCodeEPKc \ __ZN13AppsContainer13dispatchEventEN3Ion6Events5EventE \ diff --git a/ion/src/simulator/android/build.gradle b/ion/src/simulator/android/build.gradle index 7b1f27a45..eb4c8e160 100644 --- a/ion/src/simulator/android/build.gradle +++ b/ion/src/simulator/android/build.gradle @@ -22,11 +22,11 @@ allprojects { apply plugin: 'com.android.application' android { - compileSdkVersion 28 + compileSdkVersion 29 defaultConfig { applicationId "com.numworks.calculator" minSdkVersion 16 - targetSdkVersion 26 + targetSdkVersion 28 def (major, minor, patch) = System.getenv('EPSILON_VERSION').toLowerCase().tokenize('.').collect{it.toInteger()} versionCode major*1000000 + minor*10000 + patch * 100 versionName System.getenv('EPSILON_VERSION') diff --git a/ion/src/simulator/shared/events_stdin.cpp b/ion/src/simulator/shared/events_stdin.cpp index b3a00f3e2..4aaa96093 100644 --- a/ion/src/simulator/shared/events_stdin.cpp +++ b/ion/src/simulator/shared/events_stdin.cpp @@ -4,6 +4,8 @@ #include "events.h" #include +#include + #include #include diff --git a/ion/src/simulator/shared/main_headless.cpp b/ion/src/simulator/shared/main_headless.cpp index 8dd954729..0f1fee2de 100644 --- a/ion/src/simulator/shared/main_headless.cpp +++ b/ion/src/simulator/shared/main_headless.cpp @@ -14,7 +14,11 @@ #endif constexpr int kHeapSize = 131072; +#if DEBUG +constexpr int kStackSize = 32768*2; // In DEBUG mode, we increase the stack to be able to pass the tests +#else constexpr int kStackSize = 32768; +#endif char heap[kHeapSize]; extern "C" { diff --git a/poincare/include/poincare/symbol.h b/poincare/include/poincare/symbol.h index 147bf923c..e80f66836 100644 --- a/poincare/include/poincare/symbol.h +++ b/poincare/include/poincare/symbol.h @@ -34,7 +34,7 @@ public: /* Simplification */ Expression shallowReduce(ReductionContext reductionContext) override; Expression shallowReplaceReplaceableSymbols(Context * context) override; - LayoutShape leftLayoutShape() const override { return strlen(m_name) > 1 ? LayoutShape::MoreLetters : LayoutShape::OneLetter; }; + LayoutShape leftLayoutShape() const override; /* Approximation */ Evaluation approximate(SinglePrecision p, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, complexFormat, angleUnit); } diff --git a/poincare/src/decimal.cpp b/poincare/src/decimal.cpp index 3167630b6..73aaf4c9b 100644 --- a/poincare/src/decimal.cpp +++ b/poincare/src/decimal.cpp @@ -16,15 +16,41 @@ namespace Poincare { static inline int maxInt(int x, int y) { return x > y ? x : y; } -void removeZeroAtTheEnd(Integer * i, int minimalNumbersOfDigits = 1) { +void removeZeroAtTheEnd(Integer * i, int minimalNumbersOfDigits = -1) { + /* Remove the zeroes at the end of an integer, respecting the minimum number + * of digits asked for. + * + * For instance : + * + * i = 1000 + * removeZeroAtTheEnd(&i, 2) + * assert(i==10) + * + * i = 1000 + * removeZeroAtTheEnd(&i, -1) + * assert(i==1) + */ + if (i->isZero()) { return; } + + /* If we check the number of digits, we want *i to stay outside of the + * interval ]-10^numberDigits; 10^numberDigits[. */ + const bool shouldCheckMinimalNumberOfDigits = minimalNumbersOfDigits > 0; + Integer minimum = shouldCheckMinimalNumberOfDigits ? + Integer((int64_t)std::pow(10.0, minimalNumbersOfDigits-1)) : + Integer::Overflow(false); + Integer minusMinimum = shouldCheckMinimalNumberOfDigits ? + Integer(-(int64_t)std::pow(10.0, minimalNumbersOfDigits-1)) : + Integer::Overflow(false); + Integer base = Integer(10); - Integer minimum = Integer((int64_t)std::pow(10.0, minimalNumbersOfDigits-1)); - Integer minusMinimum = Integer(-(int64_t)std::pow(10.0, minimalNumbersOfDigits-1)); IntegerDivision d = Integer::Division(*i, base); - while (d.remainder.isZero() && (Integer::NaturalOrder(*i, minimum) > 0 || Integer::NaturalOrder(*i, minusMinimum) < 0)) { + while (d.remainder.isZero()) { + if (shouldCheckMinimalNumberOfDigits && (Integer::NaturalOrder(d.quotient, minimum) < 0 && Integer::NaturalOrder(d.quotient, minusMinimum) > 0)) { + break; + } *i = d.quotient; d = Integer::Division(*i, base); } @@ -136,7 +162,7 @@ int DecimalNode::convertToText(char * buffer, int bufferSize, Preferences::Print } } int exponentForEngineeringNotation = 0; - int minimalNumberOfMantissaDigits = 1; + int minimalNumberOfMantissaDigits = -1; bool removeZeroes = true; if (mode == Preferences::PrintFloatMode::Engineering) { exponentForEngineeringNotation = PrintFloat::EngineeringExponentFromBase10Exponent(exponent); diff --git a/poincare/src/power.cpp b/poincare/src/power.cpp index e5ffc985a..77bba4c05 100644 --- a/poincare/src/power.cpp +++ b/poincare/src/power.cpp @@ -625,18 +625,22 @@ Expression Power::shallowReduce(ExpressionNode::ReductionContext reductionContex * --> tan(2)+tan(2)*[tan(2)^1/2]^(-1)/tan(2) * --> tan(2)^(3/2)+tan(2)^(3/2)*[tan(2)^1/2]^(-1)/tan(2)^3/2 * --> ... - * Indeed, we have to apply the rule (a^b)^c -> a^(b*c) as soon as c is an - * integer. + * Indeed, we have to apply the rule (a^b)^c -> a^(b*c) as soon as c is -1. */ if (baseType == ExpressionNode::Type::Power) { Power powerBase = static_cast(base); + bool cInteger = indexType == ExpressionNode::Type::Rational && static_cast(index).isInteger(); bool applyRule = powerBase.childAtIndex(0).sign(reductionContext.context()) == ExpressionNode::Sign::Positive // a > 0 - || (indexType == ExpressionNode::Type::Rational && static_cast(index).isInteger()); // c integer + || cInteger; // c integer + bool cMinusOne = cInteger && static_cast(index).isMinusOne(); /* If the complexFormat is real, we check that the inner power is defined * before applying the rule (a^b)^c -> a^(b*c). Otherwise, we return - * 'unreal' or we do nothing. */ - if (reductionContext.complexFormat() == Preferences::ComplexFormat::Real) { + * 'unreal' or we do nothing. + * We escape this additional check if c = -1 for two reasons: + * - (a^b)^(-1) has to be reduced to avoid infinite loop discussed above; + * - if a^b is unreal, a^(-b) also. */ + if (!cMinusOne && reductionContext.complexFormat() == Preferences::ComplexFormat::Real) { Expression approximation = powerBase.approximate(reductionContext.context(), reductionContext.complexFormat(), reductionContext.angleUnit()); if (approximation.type() == ExpressionNode::Type::Unreal) { // The inner power is unreal, return "unreal" diff --git a/poincare/src/symbol.cpp b/poincare/src/symbol.cpp index 1062d1e67..625523b2d 100644 --- a/poincare/src/symbol.cpp +++ b/poincare/src/symbol.cpp @@ -107,6 +107,15 @@ Expression SymbolNode::shallowReplaceReplaceableSymbols(Context * context) { return Symbol(this).shallowReplaceReplaceableSymbols(context); } +ExpressionNode::LayoutShape SymbolNode::leftLayoutShape() const { + UTF8Decoder decoder(m_name); + decoder.nextCodePoint(); + if (decoder.nextCodePoint() == UCodePointNull) { // nextCodePoint asserts that the first character is non-null + return LayoutShape::OneLetter; + } + return LayoutShape::MoreLetters; +} + template Evaluation SymbolNode::templatedApproximate(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { Symbol s(this); diff --git a/poincare/test/expression_serialization.cpp b/poincare/test/expression_serialization.cpp index 3c069758f..abe0d4c4a 100644 --- a/poincare/test/expression_serialization.cpp +++ b/poincare/test/expression_serialization.cpp @@ -81,6 +81,7 @@ QUIZ_CASE(poincare_serialization_decimal) { assert_expression_serialize_to(d10, "1.235ᴇ-1", ScientificMode, 4); assert_expression_serialize_to(d10, "123.5ᴇ-3", EngineeringMode, 4); + assert_expression_serialize_to(Decimal::Builder(0.25), "250ᴇ-3", EngineeringMode); assert_expression_serialize_to(Decimal::Builder(-1.23456789E30), "-1.23456789ᴇ30", ScientificMode, 14); assert_expression_serialize_to(Decimal::Builder(1.23456789E30), "1.23456789ᴇ30", ScientificMode, 14); assert_expression_serialize_to(Decimal::Builder(-1.23456789E-30), "-1.23456789ᴇ-30", ScientificMode, 14); diff --git a/poincare/test/simplification.cpp b/poincare/test/simplification.cpp index 23e1820aa..2699ddc57 100644 --- a/poincare/test/simplification.cpp +++ b/poincare/test/simplification.cpp @@ -107,6 +107,7 @@ QUIZ_CASE(poincare_simplification_infinity) { } QUIZ_CASE(poincare_simplification_addition) { + assert_parsed_expression_simplify_to("1/x^2+3", "\u00123×x^2+1\u0013/x^2", User, Radian, Real); assert_parsed_expression_simplify_to("1+x", "x+1"); assert_parsed_expression_simplify_to("1/2+1/3+1/4+1/5+1/6+1/7", "223/140"); assert_parsed_expression_simplify_to("1+x+4-i-2x", "-i-x+5");