mirror of
https://github.com/UpsilonNumworks/Upsilon.git
synced 2026-03-20 14:20:39 +01:00
Merge branch 'master' into python_turtle
This commit is contained in:
@@ -86,4 +86,9 @@ $(app_objs): $(app_image_objs)
|
||||
|
||||
epsilon.$(EXE): $(app_objs) $(app_image_objs)
|
||||
|
||||
TO_REMOVE := apps/main.o apps/i18n.o
|
||||
TMP := $(app_objs) $(app_image_objs)
|
||||
VAR := $(filter-out $(TO_REMOVE), $(TMP))
|
||||
test.$(EXE): $(VAR)
|
||||
|
||||
products += epsilon.$(EXE) $(app_objs) $(call INLINER_PRODUCTS,$(app_images))
|
||||
|
||||
@@ -152,14 +152,25 @@ bool AppsContainer::dispatchEvent(Ion::Events::Event event) {
|
||||
if (event == Ion::Events::USBEnumeration) {
|
||||
if (Ion::USB::isPlugged()) {
|
||||
App::Snapshot * activeSnapshot = (activeApp() == nullptr ? appSnapshotAtIndex(0) : activeApp()->snapshot());
|
||||
/* Just after a software update, the battery timer does not have time to
|
||||
* fire before the calculator enters DFU mode. As the DFU mode blocks the
|
||||
* event loop, we update the battery state "manually" here. */
|
||||
updateBatteryState();
|
||||
switchTo(usbConnectedAppSnapshot());
|
||||
Ion::USB::DFU();
|
||||
switchTo(activeSnapshot);
|
||||
didProcessEvent = true;
|
||||
if (activeApp() == nullptr || activeApp()->prepareForExit()) {
|
||||
/* Just after a software update, the battery timer does not have time to
|
||||
* fire before the calculator enters DFU mode. As the DFU mode blocks the
|
||||
* event loop, we update the battery state "manually" here. */
|
||||
updateBatteryState();
|
||||
switchTo(usbConnectedAppSnapshot());
|
||||
Ion::USB::DFU();
|
||||
switchTo(activeSnapshot);
|
||||
didProcessEvent = true;
|
||||
} else {
|
||||
/* activeApp()->prepareForExit() returned false, which means that the
|
||||
* app needs another event loop to prepare for being switched off.
|
||||
* Discard the current enumeration interruption.
|
||||
* The USB host tries a few times in a row to enumerate the device, so
|
||||
* hopefully the device will get another enumeration event soon and this
|
||||
* time the device will be ready to go in DFU mode. Otherwise, the user
|
||||
* needs to re-plug the device to go into DFU mode. */
|
||||
Ion::USB::clearEnumerationInterrupt();
|
||||
}
|
||||
} else {
|
||||
/* Sometimes, the device gets an ENUMDNE interrupts when being unplugged
|
||||
* from a non-USB communicating host (e.g. a USB charger). The interrupt
|
||||
@@ -212,6 +223,7 @@ bool AppsContainer::processEvent(Ion::Events::Event event) {
|
||||
}
|
||||
|
||||
void AppsContainer::switchTo(App::Snapshot * snapshot) {
|
||||
assert(activeApp() == nullptr || activeApp()->prepareForExit());
|
||||
if (activeApp() && snapshot != activeApp()->snapshot()) {
|
||||
resetShiftAlphaStatus();
|
||||
}
|
||||
|
||||
@@ -24,7 +24,5 @@ i18n_files += $(addprefix apps/calculation/,\
|
||||
tests += $(addprefix apps/calculation/test/,\
|
||||
calculation_store.cpp\
|
||||
)
|
||||
test_objs += $(addprefix apps/calculation/, calculation.o calculation_store.o)
|
||||
|
||||
app_images += apps/calculation/calculation_icon.png
|
||||
|
||||
|
||||
@@ -46,23 +46,23 @@ bool App::Snapshot::lockOnConsole() const {
|
||||
|
||||
void App::Snapshot::setOpt(const char * name, char * value) {
|
||||
if (strcmp(name, "script") == 0) {
|
||||
m_scriptStore.deleteAllScripts();
|
||||
char * separator = strchr(value, ':');
|
||||
if (!separator) {
|
||||
return;
|
||||
}
|
||||
*separator = 0;
|
||||
const char * scriptName = value;
|
||||
/* We include the 0 in the scriptContent to represent the importation
|
||||
* status. It is set to 1 after addScriptFromTemplate. Indeed, this '/0'
|
||||
* char has two goals: ending the scriptName and representing the
|
||||
* importation status; we cannot set it to 1 before adding the script to
|
||||
* storage. */
|
||||
const char * scriptContent = separator;
|
||||
Code::ScriptTemplate script(scriptName, scriptContent);
|
||||
m_scriptStore.addScriptFromTemplate(&script);
|
||||
m_scriptStore.scriptNamed(scriptName).toggleImportationStatus(); // set Importation Status to 1
|
||||
m_scriptStore.deleteAllScripts();
|
||||
char * separator = strchr(value, ':');
|
||||
if (!separator) {
|
||||
return;
|
||||
}
|
||||
*separator = 0;
|
||||
const char * scriptName = value;
|
||||
/* We include the 0 in the scriptContent to represent the importation
|
||||
* status. It is set to 1 after addScriptFromTemplate. Indeed, this '/0'
|
||||
* char has two goals: ending the scriptName and representing the
|
||||
* importation status; we cannot set it to 1 before adding the script to
|
||||
* storage. */
|
||||
const char * scriptContent = separator;
|
||||
Code::ScriptTemplate script(scriptName, scriptContent);
|
||||
m_scriptStore.addScriptFromTemplate(&script);
|
||||
m_scriptStore.scriptNamed(scriptName).toggleImportationStatus(); // set Importation Status to 1
|
||||
return;
|
||||
}
|
||||
if (strcmp(name, "lock-on-console") == 0) {
|
||||
m_lockOnConsole = true;
|
||||
@@ -88,15 +88,15 @@ App::App(Container * container, Snapshot * snapshot) :
|
||||
}
|
||||
|
||||
App::~App() {
|
||||
assert(!m_consoleController.inputRunLoopActive());
|
||||
deinitPython();
|
||||
}
|
||||
|
||||
bool App::handleEvent(Ion::Events::Event event) {
|
||||
if (event == Ion::Events::Home && m_consoleController.inputRunLoopActive()) {
|
||||
// We need to return true here because we want to actually exit from the
|
||||
// input run loop, which requires ending a dispatchEvent cycle.
|
||||
m_consoleController.askInputRunLoopTermination();
|
||||
m_consoleController.interrupt();
|
||||
/* We need to return true here because we want to actually exit from the
|
||||
* input run loop, which requires ending a dispatchEvent cycle. */
|
||||
m_consoleController.terminateInputLoop();
|
||||
if (m_modalViewController.isDisplayingModal()) {
|
||||
m_modalViewController.dismissModalViewController();
|
||||
}
|
||||
|
||||
@@ -37,6 +37,13 @@ public:
|
||||
ScriptStore m_scriptStore;
|
||||
};
|
||||
~App();
|
||||
bool prepareForExit() override {
|
||||
if (m_consoleController.inputRunLoopActive()) {
|
||||
m_consoleController.terminateInputLoop();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
StackViewController * stackViewController() { return &m_codeStackViewController; }
|
||||
ConsoleController * consoleController() { return &m_consoleController; }
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ ConsoleController::ConsoleController(Responder * parentResponder, App * pythonDe
|
||||
m_sandboxController(this, this),
|
||||
m_inputRunLoopActive(false)
|
||||
#if EPSILON_GETOPT
|
||||
, m_locked(lockOnConsole)
|
||||
, m_locked(lockOnConsole)
|
||||
#endif
|
||||
{
|
||||
m_selectableTableView.setMargins(0, Metric::CommonRightMargin, 0, Metric::TitleBarExternHorizontalMargin);
|
||||
@@ -81,21 +81,30 @@ void ConsoleController::runAndPrintForCommand(const char * command) {
|
||||
m_consoleStore.deleteLastLineIfEmpty();
|
||||
}
|
||||
|
||||
void ConsoleController::terminateInputLoop() {
|
||||
assert(m_inputRunLoopActive);
|
||||
m_inputRunLoopActive = false;
|
||||
interrupt();
|
||||
}
|
||||
|
||||
const char * ConsoleController::inputText(const char * prompt) {
|
||||
AppsContainer * a = (AppsContainer *)(app()->container());
|
||||
m_inputRunLoopActive = true;
|
||||
|
||||
// Set the prompt text
|
||||
m_selectableTableView.reloadData();
|
||||
m_selectableTableView.selectCellAtLocation(0, m_consoleStore.numberOfLines());
|
||||
m_editCell.setPrompt(prompt);
|
||||
m_editCell.setText("");
|
||||
|
||||
// Run new input loop
|
||||
a->redrawWindow();
|
||||
a->runWhile([](void * a){
|
||||
ConsoleController * c = static_cast<ConsoleController *>(a);
|
||||
return c->inputRunLoopActive();
|
||||
}, this);
|
||||
|
||||
// Reset the prompt line
|
||||
flushOutputAccumulationBufferToStore();
|
||||
m_consoleStore.deleteLastLineIfEmpty();
|
||||
m_editCell.setPrompt(sStandardPromptText);
|
||||
@@ -145,9 +154,8 @@ bool ConsoleController::handleEvent(Ion::Events::Event event) {
|
||||
}
|
||||
#if EPSILON_GETOPT
|
||||
if (m_locked && (event == Ion::Events::Home || event == Ion::Events::Back)) {
|
||||
if (inputRunLoopActive()) {
|
||||
askInputRunLoopTermination();
|
||||
interrupt();
|
||||
if (m_inputRunLoopActive) {
|
||||
terminateInputLoop();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -234,10 +242,10 @@ bool ConsoleController::textFieldShouldFinishEditing(TextField * textField, Ion:
|
||||
}
|
||||
|
||||
bool ConsoleController::textFieldDidReceiveEvent(TextField * textField, Ion::Events::Event event) {
|
||||
if (event == Ion::Events::Up && inputRunLoopActive()) {
|
||||
askInputRunLoopTermination();
|
||||
// We need to return true here because we want to actually exit from the
|
||||
// input run loop, which requires ending a dispatchEvent cycle.
|
||||
if (event == Ion::Events::Up && m_inputRunLoopActive) {
|
||||
m_inputRunLoopActive = false;
|
||||
/* We need to return true here because we want to actually exit from the
|
||||
* input run loop, which requires ending a dispatchEvent cycle. */
|
||||
return true;
|
||||
}
|
||||
if (event == Ion::Events::Up) {
|
||||
@@ -251,8 +259,8 @@ bool ConsoleController::textFieldDidReceiveEvent(TextField * textField, Ion::Eve
|
||||
}
|
||||
|
||||
bool ConsoleController::textFieldDidFinishEditing(TextField * textField, const char * text, Ion::Events::Event event) {
|
||||
if (inputRunLoopActive()) {
|
||||
askInputRunLoopTermination();
|
||||
if (m_inputRunLoopActive) {
|
||||
m_inputRunLoopActive = false;
|
||||
return false;
|
||||
}
|
||||
runAndPrintForCommand(text);
|
||||
@@ -266,8 +274,8 @@ bool ConsoleController::textFieldDidFinishEditing(TextField * textField, const c
|
||||
}
|
||||
|
||||
bool ConsoleController::textFieldDidAbortEditing(TextField * textField) {
|
||||
if (inputRunLoopActive()) {
|
||||
askInputRunLoopTermination();
|
||||
if (m_inputRunLoopActive) {
|
||||
m_inputRunLoopActive = false;
|
||||
} else {
|
||||
#if EPSILON_GETOPT
|
||||
/* In order to lock the console controller, we disable poping controllers
|
||||
|
||||
@@ -31,8 +31,8 @@ public:
|
||||
void autoImport();
|
||||
void autoImportScript(Script script, bool force = false);
|
||||
void runAndPrintForCommand(const char * command);
|
||||
bool inputRunLoopActive() { return m_inputRunLoopActive; }
|
||||
void askInputRunLoopTermination() { m_inputRunLoopActive = false; }
|
||||
bool inputRunLoopActive() const { return m_inputRunLoopActive; }
|
||||
void terminateInputLoop();
|
||||
|
||||
// ViewController
|
||||
View * view() override { return &m_selectableTableView; }
|
||||
|
||||
@@ -39,7 +39,6 @@ i18n_files += $(addprefix apps/probability/,\
|
||||
tests += $(addprefix apps/probability/test/,\
|
||||
erf_inv.cpp\
|
||||
)
|
||||
test_objs += $(addprefix apps/probability/law/, erf_inv.o)
|
||||
|
||||
app_images += apps/probability/probability_icon.png
|
||||
|
||||
|
||||
@@ -45,33 +45,5 @@ i18n_files += $(addprefix apps/regression/,\
|
||||
tests += $(addprefix apps/regression/test/,\
|
||||
model.cpp\
|
||||
)
|
||||
test_objs += $(addprefix apps/regression/,\
|
||||
regression_context.o\
|
||||
store.o\
|
||||
)
|
||||
test_objs += $(addprefix apps/shared/,\
|
||||
store_context.o\
|
||||
)
|
||||
test_objs += $(addprefix apps/shared/,\
|
||||
curve_view_cursor.o\
|
||||
curve_view_range.o\
|
||||
double_pair_store.o\
|
||||
interactive_curve_view_range.o\
|
||||
interactive_curve_view_range_delegate.o\
|
||||
memoized_curve_view_range.o\
|
||||
store_context.o\
|
||||
)
|
||||
test_objs += $(addprefix apps/regression/model/,\
|
||||
cubic_model.o\
|
||||
exponential_model.o\
|
||||
linear_model.o\
|
||||
logarithmic_model.o\
|
||||
logistic_model.o\
|
||||
model.o\
|
||||
power_model.o\
|
||||
quadratic_model.o\
|
||||
quartic_model.o\
|
||||
trigonometric_model.o\
|
||||
)
|
||||
|
||||
app_images += apps/regression/regression_icon.png
|
||||
|
||||
@@ -23,6 +23,11 @@ public:
|
||||
void viewWillAppear() override;
|
||||
void selectRegressionCurve();
|
||||
int selectedSeriesIndex() const { return *m_selectedSeriesIndex; }
|
||||
|
||||
// moveCursorHorizontally and Vertically are public to be used in tests
|
||||
bool moveCursorHorizontally(int direction) override;
|
||||
bool moveCursorVertically(int direction) override;
|
||||
|
||||
private:
|
||||
constexpr static int k_maxLegendLength = 16;
|
||||
constexpr static int k_maxNumberOfCharacters = 50;
|
||||
@@ -35,7 +40,6 @@ private:
|
||||
|
||||
// SimpleInteractiveCurveViewController
|
||||
void reloadBannerView() override;
|
||||
bool moveCursorHorizontally(int direction) override;
|
||||
Shared::InteractiveCurveViewRange * interactiveCurveViewRange() override;
|
||||
Shared::CurveView * curveView() override;
|
||||
bool handleEnter() override;
|
||||
@@ -43,7 +47,6 @@ private:
|
||||
// InteractiveCurveViewController
|
||||
void initRangeParameters() override;
|
||||
void initCursorParameters() override;
|
||||
bool moveCursorVertically(int direction) override;
|
||||
uint32_t modelVersion() override;
|
||||
uint32_t rangeVersion() override;
|
||||
bool isCursorVisible() override;
|
||||
|
||||
176
apps/regression/test/graph.cpp
Normal file
176
apps/regression/test/graph.cpp
Normal file
@@ -0,0 +1,176 @@
|
||||
#include <quiz.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include "../model/model.h"
|
||||
#include "../graph_controller.h"
|
||||
#include "../regression_context.h"
|
||||
#include "../store.h"
|
||||
|
||||
using namespace Poincare;
|
||||
using namespace Regression;
|
||||
|
||||
void load_in_store(
|
||||
Regression::Store * store,
|
||||
double * x0, double * y0, int numberOfPoints0,
|
||||
double * x1 = nullptr, double * y1 = nullptr, int numberOfPoints1 = 0,
|
||||
double * x2 = nullptr, double * y2 = nullptr, int numberOfPoints2 = 0)
|
||||
{
|
||||
// Set the points and the regression type
|
||||
if (numberOfPoints0 != 0 && x0 != nullptr && y0 != nullptr) {
|
||||
for (int i = 0; i < numberOfPoints0; i++) {
|
||||
store->set(x0[i], 0, 0, i);
|
||||
store->set(y0[i], 0, 1, i);
|
||||
}
|
||||
}
|
||||
if (numberOfPoints1 != 0 && x1 != nullptr && y1 != nullptr) {
|
||||
for (int i = 0; i < numberOfPoints1; i++) {
|
||||
store->set(x1[i], 1, 0, i);
|
||||
store->set(y1[i], 1, 1, i);
|
||||
}
|
||||
}
|
||||
if (numberOfPoints2 != 0 && x2 != nullptr && y2 != nullptr) {
|
||||
for (int i = 0; i < numberOfPoints2; i++) {
|
||||
store->set(x2[i], 2, 0, i);
|
||||
store->set(y2[i], 2, 1, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class NavigationEvent {
|
||||
public:
|
||||
enum class Direction {
|
||||
Up,
|
||||
Right,
|
||||
Down,
|
||||
Left
|
||||
};
|
||||
NavigationEvent(Direction d, int series, int dot) :
|
||||
direction(d),
|
||||
expectedSelectedSeries(series),
|
||||
expectedSelectedDot(dot)
|
||||
{}
|
||||
Direction direction;
|
||||
int expectedSelectedSeries;
|
||||
int expectedSelectedDot;
|
||||
};
|
||||
|
||||
void assert_navigation_is(
|
||||
int numberOfEvents,
|
||||
NavigationEvent * events,
|
||||
int startingDotSelection,
|
||||
int startingSeriesSelection,
|
||||
double * x0, double * y0, int numberOfPoints0,
|
||||
double * x1 = nullptr, double * y1 = nullptr, int numberOfPoints1 = 0,
|
||||
double * x2 = nullptr, double * y2 = nullptr, int numberOfPoints2 = 0)
|
||||
{
|
||||
assert(startingDotSelection >= 0);
|
||||
Store store;
|
||||
Shared::CurveViewCursor cursor;
|
||||
uint32_t dummy;
|
||||
|
||||
int selectedDotIndex = startingDotSelection;
|
||||
int selectedSeriesIndex = startingSeriesSelection;
|
||||
|
||||
load_in_store(&store, x0, y0, numberOfPoints0, x1, y1, numberOfPoints1, x2, y2, numberOfPoints2);
|
||||
|
||||
if (selectedDotIndex < store.numberOfPairsOfSeries(selectedSeriesIndex)) {
|
||||
cursor.moveTo(
|
||||
store.get(selectedSeriesIndex, 0, selectedDotIndex),
|
||||
store.get(selectedSeriesIndex, 1, selectedDotIndex));
|
||||
} else {
|
||||
cursor.moveTo(
|
||||
store.meanOfColumn(selectedSeriesIndex, 0),
|
||||
store.meanOfColumn(selectedSeriesIndex, 1));
|
||||
}
|
||||
|
||||
AppsContainerStorage container;
|
||||
// We do not really care about the snapshot
|
||||
App app(&container, container.appSnapshotAtIndex(0));
|
||||
|
||||
GraphController graphController(
|
||||
&app, &app, nullptr,
|
||||
&store,
|
||||
&cursor,
|
||||
&dummy, &dummy,
|
||||
&selectedDotIndex,
|
||||
&selectedSeriesIndex);
|
||||
|
||||
|
||||
for (int i = 0; i < numberOfEvents; i++) {
|
||||
NavigationEvent event = events[i];
|
||||
if (event.direction == NavigationEvent::Direction::Up) {
|
||||
graphController.moveCursorVertically(1);
|
||||
} else if (event.direction == NavigationEvent::Direction::Right) {
|
||||
graphController.moveCursorHorizontally(1);
|
||||
} else if (event.direction == NavigationEvent::Direction::Down) {
|
||||
graphController.moveCursorVertically(-1);
|
||||
} else {
|
||||
assert(event.direction == NavigationEvent::Direction::Left);
|
||||
graphController.moveCursorHorizontally(-1);
|
||||
}
|
||||
quiz_assert(event.expectedSelectedDot == selectedDotIndex);
|
||||
quiz_assert(event.expectedSelectedSeries == selectedSeriesIndex);
|
||||
}
|
||||
}
|
||||
|
||||
QUIZ_CASE(regression_navigation_1) {
|
||||
constexpr int numberOfPoints0 = 4;
|
||||
double x0[numberOfPoints0] = {2.0, 3.0, 2.0, 1.0};
|
||||
double y0[numberOfPoints0] = {0.0, 1.0, 0.5, 5.0};
|
||||
|
||||
constexpr int numberOfPoints1 = 3;
|
||||
double x1[numberOfPoints1] = {1.0, 10.0, 2.0};
|
||||
double y1[numberOfPoints1] = {0.0, 25.0, 0.5};
|
||||
|
||||
constexpr int numberOfEvents = 7;
|
||||
NavigationEvent events[numberOfEvents] = {
|
||||
NavigationEvent(NavigationEvent::Direction::Down, 0, -1),
|
||||
NavigationEvent(NavigationEvent::Direction::Down, 0, 2),
|
||||
NavigationEvent(NavigationEvent::Direction::Down, 1, 2),
|
||||
NavigationEvent(NavigationEvent::Direction::Down, 0, 0),
|
||||
NavigationEvent(NavigationEvent::Direction::Down, 1, 0),
|
||||
NavigationEvent(NavigationEvent::Direction::Down, 1, -1),
|
||||
NavigationEvent(NavigationEvent::Direction::Down, 1, -1)
|
||||
};
|
||||
assert_navigation_is(numberOfEvents, events, numberOfPoints0, 0, x0, y0, numberOfPoints0, x1, y1, numberOfPoints1);
|
||||
}
|
||||
|
||||
QUIZ_CASE(regression_navigation_2) {
|
||||
constexpr int numberOfPoints0 = 4;
|
||||
double x0[numberOfPoints0] = {8.0, 9.0, 10.0, 10.0};
|
||||
double y0[numberOfPoints0] = {2.0, 5.0, 1.0, 2.0};
|
||||
|
||||
constexpr int numberOfPoints1 = 3;
|
||||
double x1[numberOfPoints1] = {10.0, 12.0, 5.0};
|
||||
double y1[numberOfPoints1] = {2.0, 0.0, 8.0};
|
||||
|
||||
constexpr int numberOfEvents = 6;
|
||||
NavigationEvent events[numberOfEvents] = {
|
||||
/* FIXME
|
||||
* Because of double computation error, the regression curve of the series 0
|
||||
* is above its mean point.
|
||||
NavigationEvent(NavigationEvent::Direction::Down, 1, -1),
|
||||
* */
|
||||
NavigationEvent(NavigationEvent::Direction::Down, 0, -1),
|
||||
NavigationEvent(NavigationEvent::Direction::Down, 0, numberOfPoints0),
|
||||
NavigationEvent(NavigationEvent::Direction::Down, 0, -1),
|
||||
NavigationEvent(NavigationEvent::Direction::Down, 0, 3),
|
||||
NavigationEvent(NavigationEvent::Direction::Down, 1, 0),
|
||||
NavigationEvent(NavigationEvent::Direction::Down, 0, 2)
|
||||
};
|
||||
assert_navigation_is(numberOfEvents, events, numberOfPoints1, 1, x0, y0, numberOfPoints0, x1, y1, numberOfPoints1);
|
||||
}
|
||||
|
||||
QUIZ_CASE(regression_navigation_3) {
|
||||
constexpr int numberOfPoints = 4;
|
||||
double x[numberOfPoints] = {1.0, 2.0, 3.0, 4.0};
|
||||
double y[numberOfPoints] = {2.0, 2.0, 2.0, 2.0};
|
||||
|
||||
constexpr int numberOfEvents = 3;
|
||||
NavigationEvent events[numberOfEvents] = {
|
||||
NavigationEvent(NavigationEvent::Direction::Down, 0, -1),
|
||||
NavigationEvent(NavigationEvent::Direction::Down, 1, -1),
|
||||
NavigationEvent(NavigationEvent::Direction::Down, 2, -1),
|
||||
};
|
||||
assert_navigation_is(numberOfEvents, events, 2, 0, x, y, numberOfPoints, x, y, numberOfPoints, x, y, numberOfPoints);
|
||||
}
|
||||
@@ -34,7 +34,5 @@ i18n_files += $(addprefix apps/sequence/,\
|
||||
tests += $(addprefix apps/sequence/test/,\
|
||||
sequence.cpp\
|
||||
)
|
||||
test_objs += $(addprefix apps/sequence/, sequence.o sequence_store.o sequence_context.o cache_context.o)
|
||||
test_objs += $(addprefix apps/shared/, expression_model.o expression_model_store.o function.o function_store.o)
|
||||
|
||||
app_images += apps/sequence/sequence_icon.png
|
||||
|
||||
@@ -96,14 +96,14 @@ void Sequence::setInitialRank(int rank) {
|
||||
|
||||
Poincare::Expression Sequence::firstInitialConditionExpression(Context * context) const {
|
||||
if (m_firstInitialConditionExpression.isUninitialized()) {
|
||||
m_firstInitialConditionExpression = PoincareHelpers::ParseAndReduce(m_firstInitialConditionText, *context);
|
||||
m_firstInitialConditionExpression = PoincareHelpers::ParseAndSimplify(m_firstInitialConditionText, *context);
|
||||
}
|
||||
return m_firstInitialConditionExpression;
|
||||
}
|
||||
|
||||
Poincare::Expression Sequence::secondInitialConditionExpression(Context * context) const {
|
||||
if (m_secondInitialConditionExpression.isUninitialized()) {
|
||||
m_secondInitialConditionExpression = PoincareHelpers::ParseAndReduce(m_secondInitialConditionText, *context);
|
||||
m_secondInitialConditionExpression = PoincareHelpers::ParseAndSimplify(m_secondInitialConditionText, *context);
|
||||
}
|
||||
return m_secondInitialConditionExpression;
|
||||
}
|
||||
|
||||
@@ -86,5 +86,3 @@ app_objs += $(addprefix apps/shared/,\
|
||||
vertical_cursor_view.o\
|
||||
zoom_parameter_controller.o\
|
||||
)
|
||||
|
||||
test_objs += $(addprefix apps/shared/, storage_expression_model.o storage_function.o storage_cartesian_function.o)
|
||||
|
||||
@@ -21,7 +21,7 @@ const char * ExpressionModel::text() const {
|
||||
|
||||
Poincare::Expression ExpressionModel::expression(Poincare::Context * context) const {
|
||||
if (m_expression.isUninitialized()) {
|
||||
m_expression = PoincareHelpers::ParseAndReduce(m_text, *context);
|
||||
m_expression = PoincareHelpers::ParseAndSimplify(m_text, *context);
|
||||
}
|
||||
return m_expression;
|
||||
}
|
||||
|
||||
@@ -37,10 +37,6 @@ inline T ApproximateToScalar(const char * text, Poincare::Context & context) {
|
||||
return Poincare::Expression::approximateToScalar<T>(text, context, Poincare::Preferences::sharedPreferences()->angleUnit());
|
||||
}
|
||||
|
||||
inline Poincare::Expression ParseAndReduce(const char * text, Poincare::Context & context) {
|
||||
return Poincare::Expression::ParseAndReduce(text, context, Poincare::Preferences::sharedPreferences()->angleUnit());
|
||||
}
|
||||
|
||||
inline Poincare::Expression ParseAndSimplify(const char * text, Poincare::Context & context) {
|
||||
return Poincare::Expression::ParseAndSimplify(text, context, Poincare::Preferences::sharedPreferences()->angleUnit());
|
||||
}
|
||||
|
||||
@@ -39,7 +39,12 @@ Expression StorageExpressionModel::expressionReduced(Poincare::Context * context
|
||||
if (m_expression.isUninitialized()) {
|
||||
assert(!isNull());
|
||||
Ion::Storage::Record::Data recordData = value();
|
||||
m_expression = Expression::ExpressionFromAddress(expressionAddressForValue(recordData), expressionSizeForValue(recordData)).reduce(*context, Preferences::sharedPreferences()->angleUnit());
|
||||
m_expression = Expression::ExpressionFromAddress(expressionAddressForValue(recordData), expressionSizeForValue(recordData));
|
||||
PoincareHelpers::Simplify(&m_expression, *context);
|
||||
// simplify might return an uninitialized Expression if interrupted
|
||||
if (m_expression.isUninitialized()) {
|
||||
m_expression = Expression::ExpressionFromAddress(expressionAddressForValue(recordData), expressionSizeForValue(recordData));
|
||||
}
|
||||
}
|
||||
return m_expression;
|
||||
}
|
||||
@@ -82,8 +87,7 @@ Ion::Storage::Record::ErrorStatus StorageExpressionModel::setContent(const char
|
||||
// Compute the expression to store, without replacing symbols
|
||||
expressionToStore = Expression::Parse(c);
|
||||
if (!expressionToStore.isUninitialized()) {
|
||||
Symbol xUnknown = Symbol(Symbol::SpecialSymbols::UnknownX);
|
||||
expressionToStore = expressionToStore.replaceSymbolWithExpression(Symbol("x", 1), xUnknown);
|
||||
expressionToStore = expressionToStore.replaceUnknown(Symbol('x')); //TODO Beware of non x unknowns! (for instance whe sequences are in the storage)
|
||||
}
|
||||
}
|
||||
return setExpressionContent(expressionToStore);
|
||||
|
||||
@@ -8,10 +8,9 @@ namespace Shared {
|
||||
|
||||
StorageExpressionModelListController::StorageExpressionModelListController(Responder * parentResponder, I18n::Message text) :
|
||||
ViewController(parentResponder),
|
||||
m_addNewModel(),
|
||||
m_memoizedCellHeight {-1, -1, -1, -1, -1},
|
||||
m_cumulatedHeightForSelectedIndex(-1)
|
||||
m_addNewModel()
|
||||
{
|
||||
resetMemoization();
|
||||
m_addNewModel.setMessage(text);
|
||||
}
|
||||
|
||||
@@ -20,7 +19,7 @@ void StorageExpressionModelListController::tableSelectionDidChange(int previousS
|
||||
int currentSelectedRow = selectedRow();
|
||||
|
||||
// The previously selected cell's height might have changed.
|
||||
m_memoizedCellHeight[currentSelectedMemoizedIndex] = -1;
|
||||
m_memoizedCellHeight[currentSelectedMemoizedIndex] = k_resetedMemoizedValue;
|
||||
|
||||
// Update m_cumulatedHeightForSelectedIndex if we scrolled one cell up/down
|
||||
if (currentSelectedRow == previousSelectedRow + 1) {
|
||||
@@ -29,7 +28,7 @@ void StorageExpressionModelListController::tableSelectionDidChange(int previousS
|
||||
for (int i = 0; i < k_memoizedCellHeightsCount - 1; i++) {
|
||||
m_memoizedCellHeight[i] = m_memoizedCellHeight[i+1];
|
||||
}
|
||||
m_memoizedCellHeight[k_memoizedCellHeightsCount-1] = -1;
|
||||
m_memoizedCellHeight[k_memoizedCellHeightsCount-1] = k_resetedMemoizedValue;
|
||||
// Update m_cumulatedHeightForSelectedIndex
|
||||
if (previousSelectedRow >= 0) {
|
||||
m_cumulatedHeightForSelectedIndex+= memoizedRowHeight(previousSelectedRow);
|
||||
@@ -43,7 +42,7 @@ void StorageExpressionModelListController::tableSelectionDidChange(int previousS
|
||||
for (int i = k_memoizedCellHeightsCount - 1; i > 0; i--) {
|
||||
m_memoizedCellHeight[i] = m_memoizedCellHeight[i-1];
|
||||
}
|
||||
m_memoizedCellHeight[0] = -1;
|
||||
m_memoizedCellHeight[0] = k_resetedMemoizedValue;
|
||||
// Update m_cumulatedHeightForSelectedIndex
|
||||
if (currentSelectedRow >= 0) {
|
||||
m_cumulatedHeightForSelectedIndex-= memoizedRowHeight(currentSelectedRow);
|
||||
@@ -63,7 +62,7 @@ KDCoordinate StorageExpressionModelListController::memoizedRowHeight(int j) {
|
||||
constexpr int halfMemoizationCount = k_memoizedCellHeightsCount/2;
|
||||
if (j >= currentSelectedRow - halfMemoizationCount && j <= currentSelectedRow + halfMemoizationCount) {
|
||||
int memoizedIndex = j - (currentSelectedRow - halfMemoizationCount);
|
||||
if (m_memoizedCellHeight[memoizedIndex] < 0) {
|
||||
if (m_memoizedCellHeight[memoizedIndex] == k_resetedMemoizedValue) {
|
||||
m_memoizedCellHeight[memoizedIndex] = expressionRowHeight(j);
|
||||
}
|
||||
return m_memoizedCellHeight[memoizedIndex];
|
||||
@@ -83,7 +82,7 @@ KDCoordinate StorageExpressionModelListController::memoizedCumulatedHeightFromIn
|
||||
return notMemoizedCumulatedHeightFromIndex(j);
|
||||
}
|
||||
// Recompute the memoized cumulatedHeight if needed
|
||||
if (m_cumulatedHeightForSelectedIndex < 0) {
|
||||
if (m_cumulatedHeightForSelectedIndex == k_resetedMemoizedValue) {
|
||||
m_cumulatedHeightForSelectedIndex = notMemoizedCumulatedHeightFromIndex(currentSelectedRow);
|
||||
}
|
||||
/* Compute the wanted cumulated height by adding/removing memoized cell
|
||||
@@ -226,9 +225,9 @@ bool StorageExpressionModelListController::isAddEmptyRow(int j) {
|
||||
}
|
||||
|
||||
void StorageExpressionModelListController::resetMemoization() {
|
||||
m_cumulatedHeightForSelectedIndex = -1;
|
||||
m_cumulatedHeightForSelectedIndex = k_resetedMemoizedValue;
|
||||
for (int i = 0; i < k_memoizedCellHeightsCount; i++) {
|
||||
m_memoizedCellHeight[i] = -1;
|
||||
m_memoizedCellHeight[i] = k_resetedMemoizedValue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -35,11 +35,28 @@ protected:
|
||||
virtual StorageExpressionModelStore * modelStore() = 0;
|
||||
virtual InputViewController * inputController() = 0;
|
||||
EvenOddMessageTextCell m_addNewModel;
|
||||
protected:
|
||||
// Memoization
|
||||
static constexpr int k_memoizedCellHeightsCount = 7;
|
||||
/* We use memoization to speed up indexFromHeight(offset) in the children
|
||||
* classes: if offset is "around" the memoized cumulatedHeightForIndex, we can
|
||||
* compute its value easily by adding/substracting memoized row heights. We
|
||||
* thus need to memoize 3 cells (see under for explanation on the 3) above the
|
||||
* selected one, and 3 under, which gives 7 cells.
|
||||
* 3 is the maximal number of non selected visible rows if the selected cell
|
||||
* is completely [on top/at the bottom] of the screen. To compute this value:
|
||||
* (ScreenHeight - Metric::TitleBarHeight - Metric::TabHeight - ButtonRowHeight
|
||||
* - currentSelectedRowHeight) / Metric::StoreRowHeight
|
||||
* = (240-18-27-20-50)/50 = 2.5 */
|
||||
static_assert(StorageExpressionModelListController::k_memoizedCellHeightsCount % 2 == 1, "StorageExpressionModelListController::k_memoizedCellHeightsCount should be odd.");
|
||||
/* We memoize values for indexes around the selectedRow index.
|
||||
* k_memoizedCellHeightsCount needs to be odd to compute things such as:
|
||||
* constexpr int halfMemoizationCount = k_memoizedCellHeightsCount/2;
|
||||
* if (j < selectedRow - halfMemoizationCount
|
||||
* || j > selectedRow + halfMemoizationCount) { ... } */
|
||||
private:
|
||||
// Memoization
|
||||
static constexpr int k_memoizedCellHeightsCount = 5;
|
||||
static_assert(StorageExpressionModelListController::k_memoizedCellHeightsCount == 5, "Wrong array size in initialization of StorageExpressionModelListController::m_memoizedCellHeight.");
|
||||
static_assert(StorageExpressionModelListController::k_memoizedCellHeightsCount % 2 == 1, "StorageExpressionModelListController::k_memoizedCellHeightsCount should be odd to be able to compute the middle element.");
|
||||
static constexpr int k_resetedMemoizedValue = -1;
|
||||
void resetMemoization();
|
||||
virtual KDCoordinate notMemoizedCumulatedHeightFromIndex(int j) = 0;
|
||||
KDCoordinate m_memoizedCellHeight[k_memoizedCellHeightsCount];
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
namespace Shared {
|
||||
|
||||
static inline int max(int x, int y) { return x > y ? x : y; }
|
||||
static inline int min(int x, int y) { return x < y ? x : y; }
|
||||
|
||||
StorageFunctionListController::StorageFunctionListController(Responder * parentResponder, ButtonRowController * header, ButtonRowController * footer, I18n::Message text) :
|
||||
StorageExpressionModelListController(parentResponder, text),
|
||||
@@ -81,6 +82,42 @@ int StorageFunctionListController::indexFromCumulatedWidth(KDCoordinate offsetX)
|
||||
}
|
||||
}
|
||||
|
||||
int StorageFunctionListController::indexFromCumulatedHeight(KDCoordinate offsetY) {
|
||||
if (offsetY == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* We use memoization to speed up this method: if offsetY is "around" the
|
||||
* memoized cumulatedHeightForIndex, we can compute its value easily by
|
||||
* adding/substracting memoized row heights. */
|
||||
|
||||
int currentSelectedRow = selectedRow();
|
||||
int rowsCount = numberOfRows();
|
||||
if (rowsCount <= 1 || currentSelectedRow < 1) {
|
||||
return TableViewDataSource::indexFromCumulatedHeight(offsetY);
|
||||
}
|
||||
|
||||
KDCoordinate currentCumulatedHeight = cumulatedHeightFromIndex(currentSelectedRow);
|
||||
if (offsetY > currentCumulatedHeight) {
|
||||
int iMax = min(k_memoizedCellHeightsCount/2 + 1, rowsCount - currentSelectedRow);
|
||||
for (int i = 0; i < iMax; i++) {
|
||||
currentCumulatedHeight+= rowHeight(currentSelectedRow + i);
|
||||
if (offsetY <= currentCumulatedHeight) {
|
||||
return currentSelectedRow + i;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
int iMax = min(k_memoizedCellHeightsCount/2, currentSelectedRow);
|
||||
for (int i = 1; i <= iMax; i++) {
|
||||
currentCumulatedHeight-= rowHeight(currentSelectedRow-i);
|
||||
if (offsetY > currentCumulatedHeight) {
|
||||
return currentSelectedRow - i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return TableViewDataSource::indexFromCumulatedHeight(offsetY);
|
||||
}
|
||||
|
||||
int StorageFunctionListController::typeAtLocation(int i, int j) {
|
||||
if (isAddEmptyRow(j)){
|
||||
return i + 2;
|
||||
|
||||
@@ -26,6 +26,7 @@ public:
|
||||
KDCoordinate cumulatedWidthFromIndex(int i) override;
|
||||
KDCoordinate cumulatedHeightFromIndex(int j) override;
|
||||
int indexFromCumulatedWidth(KDCoordinate offsetX) override;
|
||||
int indexFromCumulatedHeight(KDCoordinate offsetY) override;
|
||||
int typeAtLocation(int i, int j) override;
|
||||
HighlightCell * reusableCell(int index, int type) override;
|
||||
int reusableCellCount(int type) override;
|
||||
|
||||
@@ -23,7 +23,5 @@ i18n_files += $(addprefix apps/solver/,\
|
||||
tests += $(addprefix apps/solver/test/,\
|
||||
equation_store.cpp\
|
||||
)
|
||||
test_objs += $(addprefix apps/solver/, equation.o equation_store.o)
|
||||
test_objs += $(addprefix apps/shared/, expression_model.o expression_model_store.o)
|
||||
|
||||
app_images += apps/solver/solver_icon.png
|
||||
|
||||
@@ -34,6 +34,5 @@ i18n_files += $(addprefix apps/statistics/,\
|
||||
tests += $(addprefix apps/statistics/test/,\
|
||||
store.cpp\
|
||||
)
|
||||
test_objs += $(addprefix apps/statistics/, store.o)
|
||||
|
||||
app_images += apps/statistics/stat_icon.png
|
||||
|
||||
@@ -50,6 +50,9 @@ public:
|
||||
void setFirstResponder(Responder * responder);
|
||||
Responder * firstResponder();
|
||||
virtual bool processEvent(Ion::Events::Event event);
|
||||
/* prepareForExit returns true if the app can be switched off in the current
|
||||
* runloop step, else it prepares for a switch off and returns false. */
|
||||
virtual bool prepareForExit() { return true; }
|
||||
void displayModalViewController(ViewController * vc, float verticalAlignment, float horizontalAlignment,
|
||||
KDCoordinate topMargin = 0, KDCoordinate leftMargin = 0, KDCoordinate bottomMargin = 0, KDCoordinate rightMargin = 0);
|
||||
void dismissModalViewController();
|
||||
|
||||
@@ -97,6 +97,7 @@ objs += $(addprefix poincare/src/,\
|
||||
nth_root.o\
|
||||
number.o\
|
||||
opposite.o\
|
||||
parametered_expression_helper.o\
|
||||
parenthesis.o\
|
||||
permute_coefficient.o\
|
||||
power.o\
|
||||
|
||||
@@ -22,6 +22,7 @@ public:
|
||||
// Properties
|
||||
Type type() const override { return Type::Derivative; }
|
||||
int polynomialDegree(Context & context, const char * symbolName) const override;
|
||||
Expression replaceUnknown(const Symbol & symbol) override;
|
||||
|
||||
private:
|
||||
// Layout
|
||||
|
||||
@@ -14,6 +14,7 @@ namespace Poincare {
|
||||
|
||||
class Context;
|
||||
class SymbolAbstract;
|
||||
class Symbol;
|
||||
|
||||
class Expression : public TreeHandle {
|
||||
friend class AbsoluteValue;
|
||||
@@ -154,6 +155,8 @@ public:
|
||||
static constexpr int k_maxNumberOfPolynomialCoefficients = k_maxPolynomialDegree+1;
|
||||
int getPolynomialReducedCoefficients(const char * symbolName, Expression coefficients[], Context & context, Preferences::AngleUnit angleUnit) const;
|
||||
Expression replaceSymbolWithExpression(const SymbolAbstract & symbol, const Expression & expression) { return node()->replaceSymbolWithExpression(symbol, expression); }
|
||||
Expression replaceUnknown(const Symbol & symbol);
|
||||
Expression defaultReplaceUnknown(const Symbol & symbol);
|
||||
|
||||
/* Comparison */
|
||||
/* isIdenticalTo is the "easy" equality, it returns true if both trees have
|
||||
@@ -167,7 +170,6 @@ public:
|
||||
int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode = Preferences::PrintFloatMode::Decimal, int numberOfSignificantDigits = PrintFloat::k_numberOfStoredSignificantDigits) const;
|
||||
|
||||
/* Simplification */
|
||||
static Expression ParseAndReduce(const char * text, Context & context, Preferences::AngleUnit);
|
||||
static Expression ParseAndSimplify(const char * text, Context & context, Preferences::AngleUnit angleUnit);
|
||||
Expression simplify(Context & context, Preferences::AngleUnit angleUnit);
|
||||
Expression reduce(Context & context, Preferences::AngleUnit angleUnit);
|
||||
|
||||
@@ -13,6 +13,7 @@ namespace Poincare {
|
||||
* 'this' outdated. They should only be called in a wrapper on Expression. */
|
||||
|
||||
class SymbolAbstract;
|
||||
class Symbol;
|
||||
|
||||
class ExpressionNode : public TreeNode {
|
||||
friend class AdditionNode;
|
||||
@@ -103,6 +104,7 @@ public:
|
||||
virtual Sign sign() const { return Sign::Unknown; }
|
||||
virtual bool isNumber() const { return false; }
|
||||
/*!*/ virtual Expression replaceSymbolWithExpression(const SymbolAbstract & symbol, const Expression & expression);
|
||||
/*!*/ virtual Expression replaceUnknown(const Symbol & symbol);
|
||||
/*!*/ virtual Expression setSign(Sign s, Context & context, Preferences::AngleUnit angleUnit);
|
||||
virtual int polynomialDegree(Context & context, const char * symbolName) const;
|
||||
/*!*/ virtual int getPolynomialCoefficients(Context & context, const char * symbolName, Expression coefficients[]) const;
|
||||
|
||||
@@ -21,6 +21,8 @@ public:
|
||||
// ExpressionNode
|
||||
Type type() const override { return Type::Integral; }
|
||||
int polynomialDegree(Context & context, const char * symbolName) const override;
|
||||
Expression replaceUnknown(const Symbol & symbol) override;
|
||||
|
||||
private:
|
||||
// Layout
|
||||
Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override;
|
||||
|
||||
28
poincare/include/poincare/parametered_expression_helper.h
Normal file
28
poincare/include/poincare/parametered_expression_helper.h
Normal file
@@ -0,0 +1,28 @@
|
||||
#ifndef POINCARE_PARAMETERED_EXPRESSION_HELPER_H
|
||||
#define POINCARE_PARAMETERED_EXPRESSION_HELPER_H
|
||||
|
||||
#include <poincare/expression.h>
|
||||
|
||||
namespace Poincare {
|
||||
|
||||
class SymbolAbstract;
|
||||
class Symbol;
|
||||
|
||||
// Parametered expressions are Integral, Derivative, Sum and Product.
|
||||
|
||||
namespace ParameteredExpressionHelper {
|
||||
/* We sometimes replace 'x' by 'UnknownX' to differentiate between a variable
|
||||
* and a function parameter. For instance, when defining the function
|
||||
* f(x)=cos(x) in Graph, we want x to be an unknown, instead of having f be
|
||||
* the constant function equal to cos(user variable that is named x).
|
||||
*
|
||||
* In parametered expression, we do not want to replace all the 'x' with
|
||||
* unknowns: for instance, we want to change f(x)=diff(cos(x),x,x) into
|
||||
* f(X)=diff(cos(x),x,X), X being an unknown. ReplaceUnknownInExpression does
|
||||
* that. */
|
||||
Expression ReplaceUnknownInExpression(Expression e, const Symbol & symbolToReplace);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -16,6 +16,8 @@ public:
|
||||
#endif
|
||||
|
||||
Type type() const override { return Type::Product; }
|
||||
Expression replaceUnknown(const Symbol & symbol) override;
|
||||
|
||||
private:
|
||||
float emptySequenceValue() const override { return 1.0f; }
|
||||
Layout createSequenceLayout(Layout argumentLayout, Layout symbolLayout, Layout subscriptLayout, Layout superscriptLayout) const override;
|
||||
|
||||
@@ -16,6 +16,8 @@ public:
|
||||
#endif
|
||||
|
||||
Type type() const override { return Type::Sum; }
|
||||
Expression replaceUnknown(const Symbol & symbol) override;
|
||||
|
||||
private:
|
||||
float emptySequenceValue() const override { return 0.0f; }
|
||||
Layout createSequenceLayout(Layout argumentLayout, Layout symbolLayout, Layout subscriptLayout, Layout superscriptLayout) const override;
|
||||
|
||||
@@ -20,6 +20,7 @@ public:
|
||||
// Expression Properties
|
||||
Type type() const override { return Type::Symbol; }
|
||||
Expression replaceSymbolWithExpression(const SymbolAbstract & symbol, const Expression & expression) override;
|
||||
Expression replaceUnknown(const Symbol & symbol) override;
|
||||
int polynomialDegree(Context & context, const char * symbolName) const override;
|
||||
int getPolynomialCoefficients(Context & context, const char * symbolName, Expression coefficients[]) const override;
|
||||
int getVariables(Context & context, isVariableTest isVariable, char * variables, int maxSizeVariable) const override;
|
||||
@@ -77,6 +78,7 @@ public:
|
||||
// Expression
|
||||
Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target);
|
||||
Expression replaceSymbolWithExpression(const SymbolAbstract & symbol, const Expression & expression);
|
||||
Expression replaceUnknown(const Symbol & symbol);
|
||||
int getPolynomialCoefficients(Context & context, const char * symbolName, Expression coefficients[]) const;
|
||||
Expression shallowReplaceReplaceableSymbols(Context & context);
|
||||
private:
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
#include <poincare/derivative.h>
|
||||
#include <poincare/symbol.h>
|
||||
#include <poincare/layout_helper.h>
|
||||
#include <poincare/parametered_expression_helper.h>
|
||||
#include <poincare/serialization_helper.h>
|
||||
#include <poincare/simplification_helper.h>
|
||||
#include <poincare/symbol.h>
|
||||
#include <poincare/undefined.h>
|
||||
#include <cmath>
|
||||
#include <assert.h>
|
||||
@@ -26,6 +27,10 @@ int DerivativeNode::polynomialDegree(Context & context, const char * symbolName)
|
||||
return ExpressionNode::polynomialDegree(context, symbolName);
|
||||
}
|
||||
|
||||
Expression DerivativeNode::replaceUnknown(const Symbol & symbol) {
|
||||
return ParameteredExpressionHelper::ReplaceUnknownInExpression(Derivative(this), symbol);
|
||||
}
|
||||
|
||||
Layout DerivativeNode::createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const {
|
||||
return LayoutHelper::Prefix(this, floatDisplayMode, numberOfSignificantDigits, Derivative::s_functionHelper.name());
|
||||
}
|
||||
|
||||
@@ -281,6 +281,18 @@ int Expression::getPolynomialReducedCoefficients(const char * symbolName, Expres
|
||||
return degree;
|
||||
}
|
||||
|
||||
Expression Expression::replaceUnknown(const Symbol & symbol) {
|
||||
return node()->replaceUnknown(symbol);
|
||||
}
|
||||
|
||||
Expression Expression::defaultReplaceUnknown(const Symbol & symbol) {
|
||||
int nbChildren = numberOfChildren();
|
||||
for (int i = 0; i < nbChildren; i++) {
|
||||
childAtIndex(i).replaceUnknown(symbol);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
/* Comparison */
|
||||
|
||||
bool Expression::isIdenticalTo(const Expression e) const {
|
||||
@@ -312,14 +324,6 @@ int Expression::serialize(char * buffer, int bufferSize, Preferences::PrintFloat
|
||||
|
||||
/* Simplification */
|
||||
|
||||
Expression Expression::ParseAndReduce(const char * text, Context & context, Preferences::AngleUnit angleUnit) {
|
||||
Expression exp = Parse(text);
|
||||
if (exp.isUninitialized()) {
|
||||
return Undefined();
|
||||
}
|
||||
return exp.reduce(context, angleUnit);
|
||||
}
|
||||
|
||||
Expression Expression::ParseAndSimplify(const char * text, Context & context, Preferences::AngleUnit angleUnit) {
|
||||
Expression exp = Parse(text);
|
||||
if (exp.isUninitialized()) {
|
||||
@@ -423,7 +427,7 @@ U Expression::approximateToScalar(Context& context, Preferences::AngleUnit angle
|
||||
|
||||
template<typename U>
|
||||
U Expression::approximateToScalar(const char * text, Context& context, Preferences::AngleUnit angleUnit) {
|
||||
Expression exp = ParseAndReduce(text, context, angleUnit);
|
||||
Expression exp = ParseAndSimplify(text, context, angleUnit);
|
||||
return exp.approximateToScalar<U>(context, angleUnit);
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,10 @@ Expression ExpressionNode::replaceSymbolWithExpression(const SymbolAbstract & sy
|
||||
return Expression(this).defaultReplaceSymbolWithExpression(symbol, expression);
|
||||
}
|
||||
|
||||
Expression ExpressionNode::replaceUnknown(const Symbol & symbol) {
|
||||
return Expression(this).defaultReplaceUnknown(symbol);
|
||||
}
|
||||
|
||||
Expression ExpressionNode::setSign(Sign s, Context & context, Preferences::AngleUnit angleUnit) {
|
||||
assert(false);
|
||||
return Expression();
|
||||
|
||||
@@ -323,7 +323,7 @@ T Integer::approximate() const {
|
||||
*/
|
||||
|
||||
if (isInfinity()) {
|
||||
return INFINITY;
|
||||
return m_negative ? -INFINITY : INFINITY;
|
||||
}
|
||||
|
||||
native_uint_t lastDigit = m_numberOfDigits > 0 ? digit(m_numberOfDigits-1) : 0;
|
||||
@@ -334,7 +334,7 @@ T Integer::approximate() const {
|
||||
/* Escape case if the exponent is too big to be stored */
|
||||
assert(m_numberOfDigits > 0);
|
||||
if (((int)m_numberOfDigits-1)*32+numberOfBitsInLastDigit-1> IEEE754<T>::maxExponent()-IEEE754<T>::exponentOffset()) {
|
||||
return INFINITY;
|
||||
return m_negative ? -INFINITY : INFINITY;
|
||||
}
|
||||
exponent += (m_numberOfDigits-1)*32;
|
||||
exponent += numberOfBitsInLastDigit-1;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <poincare/integral.h>
|
||||
#include <poincare/complex.h>
|
||||
#include <poincare/integral_layout.h>
|
||||
#include <poincare/parametered_expression_helper.h>
|
||||
#include <poincare/serialization_helper.h>
|
||||
#include <poincare/symbol.h>
|
||||
#include <poincare/undefined.h>
|
||||
@@ -26,6 +27,10 @@ int IntegralNode::polynomialDegree(Context & context, const char * symbolName) c
|
||||
return ExpressionNode::polynomialDegree(context, symbolName);
|
||||
}
|
||||
|
||||
Expression IntegralNode::replaceUnknown(const Symbol & symbol) {
|
||||
return ParameteredExpressionHelper::ReplaceUnknownInExpression(Integral(this), symbol);
|
||||
}
|
||||
|
||||
Layout IntegralNode::createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const {
|
||||
return IntegralLayout(
|
||||
childAtIndex(0)->createLayout(floatDisplayMode, numberOfSignificantDigits),
|
||||
|
||||
33
poincare/src/parametered_expression_helper.cpp
Normal file
33
poincare/src/parametered_expression_helper.cpp
Normal file
@@ -0,0 +1,33 @@
|
||||
#include <poincare/parametered_expression_helper.h>
|
||||
#include <poincare/symbol.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
namespace Poincare {
|
||||
|
||||
Expression ParameteredExpressionHelper::ReplaceUnknownInExpression(Expression e, const Symbol & symbolToReplace) {
|
||||
assert(!e.isUninitialized());
|
||||
assert(e.type() == ExpressionNode::Type::Integral
|
||||
|| e.type() == ExpressionNode::Type::Derivative
|
||||
|| e.type() == ExpressionNode::Type::Sum
|
||||
|| e.type() == ExpressionNode::Type::Product);
|
||||
assert(!symbolToReplace.isUninitialized());
|
||||
assert(symbolToReplace.type() == ExpressionNode::Type::Symbol);
|
||||
|
||||
int numberOfChildren = e.numberOfChildren();
|
||||
assert(numberOfChildren > 2);
|
||||
|
||||
Expression child1 = e.childAtIndex(1);
|
||||
assert(child1.type() == ExpressionNode::Type::Symbol);
|
||||
|
||||
Symbol& parameterChild = static_cast<Symbol &>(child1);
|
||||
if (strcmp(parameterChild.name(), symbolToReplace.name()) != 0) {
|
||||
return e.defaultReplaceUnknown(symbolToReplace);
|
||||
}
|
||||
for (int i = 2; i < numberOfChildren; i++) {
|
||||
e.childAtIndex(i).replaceUnknown(symbolToReplace);
|
||||
}
|
||||
return e;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
#include <poincare/product.h>
|
||||
#include <poincare/multiplication.h>
|
||||
#include <poincare/parametered_expression_helper.h>
|
||||
#include <poincare/product_layout.h>
|
||||
#include <poincare/layout_helper.h>
|
||||
#include <poincare/serialization_helper.h>
|
||||
@@ -13,6 +14,10 @@ namespace Poincare {
|
||||
|
||||
constexpr Expression::FunctionHelper Product::s_functionHelper;
|
||||
|
||||
Expression ProductNode::replaceUnknown(const Symbol & symbol) {
|
||||
return ParameteredExpressionHelper::ReplaceUnknownInExpression(Product(this), symbol);
|
||||
}
|
||||
|
||||
Layout ProductNode::createSequenceLayout(Layout argumentLayout, Layout symbolLayout, Layout subscriptLayout, Layout superscriptLayout) const {
|
||||
return ProductLayout(argumentLayout, symbolLayout, subscriptLayout, superscriptLayout);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include <poincare/addition.h>
|
||||
#include <poincare/sum_layout.h>
|
||||
#include <poincare/layout_helper.h>
|
||||
#include <poincare/parametered_expression_helper.h>
|
||||
#include <poincare/serialization_helper.h>
|
||||
extern "C" {
|
||||
#include <assert.h>
|
||||
@@ -13,6 +14,10 @@ namespace Poincare {
|
||||
|
||||
constexpr Expression::FunctionHelper Sum::s_functionHelper;
|
||||
|
||||
Expression SumNode::replaceUnknown(const Symbol & symbol) {
|
||||
return ParameteredExpressionHelper::ReplaceUnknownInExpression(Sum(this), symbol);
|
||||
}
|
||||
|
||||
Layout SumNode::createSequenceLayout(Layout argumentLayout, Layout symbolLayout, Layout subscriptLayout, Layout superscriptLayout) const {
|
||||
return SumLayout(argumentLayout, symbolLayout, subscriptLayout, superscriptLayout);
|
||||
}
|
||||
|
||||
@@ -27,6 +27,10 @@ Expression SymbolNode::replaceSymbolWithExpression(const SymbolAbstract & symbol
|
||||
return Symbol(this).replaceSymbolWithExpression(symbol, expression);
|
||||
}
|
||||
|
||||
Expression SymbolNode::replaceUnknown(const Symbol & symbol) {
|
||||
return Symbol(this).replaceUnknown(symbol);
|
||||
}
|
||||
|
||||
int SymbolNode::polynomialDegree(Context & context, const char * symbolName) const {
|
||||
if (strcmp(m_name,symbolName) == 0) {
|
||||
return 1;
|
||||
@@ -180,6 +184,12 @@ Expression Symbol::replaceSymbolWithExpression(const SymbolAbstract & symbol, co
|
||||
return *this;
|
||||
}
|
||||
|
||||
Expression Symbol::replaceUnknown(const Symbol & symbol) {
|
||||
assert(!symbol.isUninitialized());
|
||||
assert(symbol.type() == ExpressionNode::Type::Symbol);
|
||||
return replaceSymbolWithExpression(symbol, Symbol(SpecialSymbols::UnknownX));
|
||||
}
|
||||
|
||||
int Symbol::getPolynomialCoefficients(Context & context, const char * symbolName, Expression coefficients[]) const {
|
||||
if (strcmp(name(), symbolName) == 0) {
|
||||
coefficients[0] = Rational(0);
|
||||
|
||||
Reference in New Issue
Block a user