diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 000000000..c9f1c1e8d --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,25 @@ +--- +name: Bug report +about: Epsilon is not working like it should? Let us know! +labels: 'bug' + +--- +#### Describe the bug +A clear and concise description of what the bug is. Please describe a **single** bug per issue. Feel free to create multiple issues though! + +#### Screenshots +Please provide at least one screenshot of the issue happening. This is by far the best way to quickly show any issue! To attach a screenshot, just go to our [online simulator](https://www.numworks.com/simulator), navigate to reproduce your issue, and click the "screenshot" button. Then drag'n'drop the file here! + +#### To Reproduce +Steps to reproduce the behavior: +1. Go to the '...' app +2. Type '....' +3. Scroll down to '....' +4. See error + +#### Expected behavior +A clear and concise description of what you expected to happen. + +#### Environment + - Epsilon version (Settings > About > Software version). + - The platform(s) on which the problem happens: online simulator, actual device, etc... diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 000000000..6bc39c490 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,22 @@ +--- +name: Feature request +about: Suggest an idea for an improvement of Epsilon +labels: 'enhancement' + +--- +#### Problem you'd like to fix +Is your feature request related to a problem? Please provide a clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +Please describe a **single** improvement per issue. Feel free to open multiple issues though! + +#### Screenshots +If possible, please attach a screenshot. You can go on our [online simulator](https://www.numworks.com/simulator), use the screenshot button, and drag'n'drop the file here. + +#### Describe the solution you'd like +A clear and concise description of what you want to happen. + +#### Describe alternatives you've considered +A clear and concise description of any alternative solutions or features you've considered. + +#### Additional context +Add any other context or screenshots about the feature request here. diff --git a/.gitignore b/.gitignore index cfd17d43a..a49e8c8c0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,52 +1,4 @@ -# No objects files. -*.o -*.elf -*.exe - -# No dependency files -*.d - -# No lex / yacc generated files. -poincare/src/expression_lexer.cpp -poincare/src/expression_lexer.hpp -poincare/src/expression_parser.cpp -poincare/src/expression_parser.hpp - -# No rulegen generated files -poincare/src/simplification/rulegen/rules_tokens.h -poincare/src/simplification/rulegen/rules_lexer.cpp -poincare/src/simplification/rulegen/rules_parser.cpp -poincare/src/simplification/rulegen/rulegen -poincare/src/simplification/demo_ruleset.h - -# Font related generated files. -kandinsky/fonts/rasterizer -kandinsky/src/font_large.cpp -kandinsky/src/font_small.cpp - -# No i18n headers -apps/i18n.h -apps/i18n.cpp - -# No PicView generated files -apps/picview/image.raw -apps/picview/image.c - -# No AST file -*.ast -*.ast.json - # Quiz output quiz/src/symbols.c -# No generated icon & font files -*_icon.cpp -*_icon.h -*_font.c -*_font.h - -# Ignore inliner binary -escher/image/inliner - -# Ignore generated qtsrdefs file -python/port/genhdr/qstrdefs.generated.h +build diff --git a/.travis.yml b/.travis.yml index 07d14f852..e6765d5e1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,9 @@ language: cpp +env: + global: + - MAKEFLAGS="-j 2" + addons: apt: sources: @@ -19,4 +23,4 @@ os: linux script: - set -e - make clean && make epsilon.$EXT test.$EXT -- if [ "$PLATFORM" = "blackbox" ]; then ./test.$EXT; PLATFORM=blackbox make integration_tests; fi +- if [ "$PLATFORM" = "blackbox" ]; then build/blackbox/test.$EXT; PLATFORM=blackbox make integration_tests; fi diff --git a/Makefile b/Makefile index bb10ae979..f96b6f82b 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,27 @@ -include build/config.mak +include scripts/config.mak -default: epsilon.$(EXE) +# Disable default Make rules +.SUFFIXES: + +object_for = $(addprefix $(BUILD_DIR)/,$(addsuffix .o,$(basename $(1)))) + +default: $(BUILD_DIR)/epsilon.$(EXE) + +# Define a standard rule helper +# If passed a last parameter value of with_local_version, we also define an +# extra rule that can build source files within the $(BUILD_DIR). This is useful +# for rules that can be applied for intermediate objects (for example, when +# going .png -> .cpp -> .o). +define rule_for +$(addprefix $$(BUILD_DIR)/,$(strip $(2))): $(strip $(3)) | $$$$(@D)/. + @ echo "$(shell printf "%-8s" $(strip $(1)))$$(@:$$(BUILD_DIR)/%=%)" + $(Q) $(4) +ifeq ($(strip $(5)),with_local_version) +$(addprefix $$(BUILD_DIR)/,$(strip $(2))): $(addprefix $$(BUILD_DIR)/,$(strip $(3))) + @ echo "$(shell printf "%-8s" $(strip $(1)))$$(@:$$(BUILD_DIR)/%=%)" + $(Q) $(4) +endif +endef .PHONY: info info: @@ -10,16 +31,24 @@ info: @echo "EPSILON_APPS = $(EPSILON_APPS)" @echo "EPSILON_I18N = $(EPSILON_I18N)" -# Each sub-Makefile can either add objects to the $(objs) variable or define a -# new executable target. The $(objs) variable lists the objects that will be -# linked to every executable being generated. Each Makefile is also responsible -# for keeping the $(product) variable updated. This variable lists all files -# that could be generated during the build and that needs to be cleaned up -# afterwards. +# Since we're building out-of-tree, we need to make sure the output directories +# are created, otherwise the receipes will fail (e.g. gcc will fail to create +# "output/foo/bar.o" because the directory "output/foo" doesn't exist). +# We need to mark those directories as precious, otherwise Make will try to get +# rid of them upon completion (and fail, since those folders won't be empty). +.PRECIOUS: $(BUILD_DIR)/. $(BUILD_DIR)%/. +$(BUILD_DIR)/.: + $(Q) mkdir -p $(dir $@) +$(BUILD_DIR)%/.: + $(Q) mkdir -p $(dir $@) -products := +# To make objects dependent on their directory, we need a second expansion +.SECONDEXPANSION: + +# Each sub-Makefile can either add sources to the $(src) variable or define a +# new executable target. The $(src) variable lists the sources that will be +# built and linked to every executable being generated. -# Library Makefiles ifeq ($(USE_LIBA),0) include liba/Makefile.bridge else @@ -34,43 +63,61 @@ include python/Makefile include escher/Makefile # Executable Makefiles include apps/Makefile -include build/struct_layout/Makefile -include build/scenario/Makefile +include scripts/struct_layout/Makefile +include scripts/scenario/Makefile include quiz/Makefile # Quiz needs to be included at the end -products += $(objs) - -all_objs = $(filter %.o, $(products)) -dependencies = $(all_objs:.o=.d) --include $(dependencies) -products += $(dependencies) - -$(all_objs): $(generated_headers) - -epsilon.$(EXE): $(objs) -test.$(EXE): $(objs) - +objs = $(call object_for,$(src)) .SECONDARY: $(objs) -%.$(EXE): - @echo "LD $@" - $(Q) $(LD) $^ $(LDFLAGS) -o $@ -%.o: %.c - @echo "CC $@" - $(Q) $(CC) $(SFLAGS) $(CFLAGS) -c $< -o $@ +# Load source-based dependencies +# Compilers can generate Makefiles that states the dependencies of a given +# objet to other source and headers. This serve no purpose for a clean build, +# but allows correct yet optimal incremental builds. +-include $(objs:.o=.d) -%.o: %.s - @echo "AS $@" - $(Q) $(CC) $(SFLAGS) -c $< -o $@ +# Define rules for executables +# Those can be built directly with make executable.exe as a shortcut. They also +# depends on $(objs) -%.o: %.cpp - @echo "CXX $@" - $(Q) $(CXX) $(SFLAGS) $(CXXFLAGS) -c $< -o $@ +executables = epsilon test flasher + +define rules_for_executable +$$(BUILD_DIR)/$(1).$$(EXE): $$(objs) +.PHONY: $(1).$$(EXE) +$(1).$$(EXE): $$(BUILD_DIR)/$(1).$$(EXE) +endef + +$(foreach executable,$(executables),$(eval $(call rules_for_executable,$(executable)))) + +# Define standard compilation rules + +$(eval $(call rule_for, \ + AS, %.o, %.s, \ + $$(CC) $$(SFLAGS) -c $$< -o $$@ \ +)) + +$(eval $(call rule_for, \ + CC, %.o, %.c, \ + $$(CC) $$(SFLAGS) $$(CFLAGS) -c $$< -o $$@, \ + with_local_version \ +)) + +$(eval $(call rule_for, \ + CXX, %.o, %.cpp, \ + $$(CC) $$(SFLAGS) $$(CXXFLAGS) -c $$< -o $$@, \ + with_local_version \ +)) + +$(eval $(call rule_for, \ + LD, %.$$(EXE), , \ + $$(LD) $$^ $$(LDFLAGS) -o $$@ \ +)) .PHONY: clean clean: @echo "CLEAN" - $(Q) rm -f $(products) + $(Q) rm -rf $(BUILD_DIR) .PHONY: cowsay_% cowsay_%: @@ -86,4 +133,4 @@ cowsay_%: .PHONY: clena clena: cowsay_CLENA clean --include build/targets.$(PLATFORM).mak +-include scripts/targets.$(PLATFORM).mak diff --git a/apps/Makefile b/apps/Makefile index 98141b448..34eeb7463 100644 --- a/apps/Makefile +++ b/apps/Makefile @@ -10,26 +10,24 @@ apps = # (path to the apps header). $(foreach i,${EPSILON_APPS},$(eval include apps/$(i)/Makefile)) -app_objs += $(addprefix apps/,\ - apps_container.o\ - apps_container_storage.o\ - apps_window.o\ - battery_timer.o\ - battery_view.o\ - constant.o\ - backlight_dimming_timer.o\ - empty_battery_window.o\ - exam_pop_up_controller.o\ - global_preferences.o\ - i18n.o\ - lock_view.o\ - main.o\ - math_toolbox.o\ - shift_alpha_lock_view.o\ - suspend_timer.o\ - title_bar_view.o\ - variable_box_controller.o\ - variable_box_empty_controller.o\ +app_src += $(addprefix apps/,\ + apps_container.cpp \ + apps_container_storage.cpp \ + apps_window.cpp \ + backlight_dimming_timer.cpp \ + battery_timer.cpp \ + battery_view.cpp \ + constant.cpp \ + empty_battery_window.cpp \ + exam_pop_up_controller.cpp \ + global_preferences.cpp \ + lock_view.cpp \ + math_toolbox.cpp \ + shift_alpha_lock_view.cpp \ + suspend_timer.cpp \ + title_bar_view.cpp \ + variable_box_controller.cpp \ + variable_box_empty_controller.cpp \ ) snapshots_declaration = $(foreach i,$(apps),$(i)::Snapshot m_snapshot$(subst :,,$(i))Snapshot;) @@ -40,8 +38,12 @@ snapshots_count = $(words $(apps)) snapshot_includes = $(foreach i,$(app_headers),-include $(i) ) epsilon_app_names = '$(foreach i,${EPSILON_APPS},"$(i)", )' -apps/apps_container_storage.o apps/main.o: CXXFLAGS += $(snapshot_includes) -DAPPS_CONTAINER_APPS_DECLARATION="$(apps_declaration)" -DAPPS_CONTAINER_SNAPSHOT_DECLARATIONS="$(snapshots_declaration)" -DAPPS_CONTAINER_SNAPSHOT_CONSTRUCTORS="$(snapshots_construction)" -DAPPS_CONTAINER_SNAPSHOT_LIST="$(snapshots_list)" -DAPPS_CONTAINER_SNAPSHOT_COUNT=$(snapshots_count) -DEPSILON_APPS_NAMES=$(epsilon_app_names) +$(call object_for,apps/apps_container_storage.cpp apps/main.cpp): CXXFLAGS += $(snapshot_includes) -DAPPS_CONTAINER_APPS_DECLARATION="$(apps_declaration)" -DAPPS_CONTAINER_SNAPSHOT_DECLARATIONS="$(snapshots_declaration)" -DAPPS_CONTAINER_SNAPSHOT_CONSTRUCTORS="$(snapshots_construction)" -DAPPS_CONTAINER_SNAPSHOT_LIST="$(snapshots_list)" -DAPPS_CONTAINER_SNAPSHOT_COUNT=$(snapshots_count) -DEPSILON_APPS_NAMES=$(epsilon_app_names) +# I18n file generation + +# The header is refered to as so make sure it's findable this way +SFLAGS += -I$(BUILD_DIR) i18n_files += $(addprefix apps/language_,$(addsuffix .universal.i18n, $(EPSILON_I18N))) i18n_files += $(addprefix apps/,\ @@ -63,32 +65,35 @@ i18n_files += $(addprefix apps/,\ variables.pt.i18n\ ) -apps/i18n.h: apps/i18n.cpp -apps/i18n.cpp: $(i18n_files) - @echo "I18N $@" - $(Q) $(PYTHON) apps/i18n.py --header $(subst .cpp,.h,$@) --implementation $@ --locales $(EPSILON_I18N) --files $^ +$(eval $(call rule_for, \ + I18N, \ + apps/i18n.cpp, \ + $(i18n_files), \ + $$(PYTHON) apps/i18n.py --header $$(subst .cpp,.h,$$@) --implementation $$@ --locales $$(EPSILON_I18N) --files $$^ \ +)) -$(app_objs): apps/i18n.h +$(BUILD_DIR)/apps/i18n.h: $(BUILD_DIR)/apps/i18n.cpp -products += apps/i18n.h apps/i18n.cpp +# Handle PNG files -app_images += apps/exam_icon.png +$(eval $(call depends_on_image,apps/title_bar_view.cpp,apps/exam_icon.png)) -# Tracking which source file uses which image is painful. But we need to ensure -# that a .png file has been inlined before building any source file that uses -# said image (because it will expect the ".h" file to be there). -# As a shortcut, we simply say that every app file depends on every image. In -# practice, this forces all the images to be before the app. +# Handle epsilon-only sources +# Other executables may want to do their own i18n and main. That's the case for +# the test runner. Let's add those files to a specific epsilon-only src. When +# building apps/i18n.o, the extension doesn't really matter since it'll be +# processed by the object_for function. -app_image_objs := $(app_images:.png=.o) -.SECONDARY: $(app_images:.png=.cpp) -$(app_objs): $(app_image_objs) +epsilon_src += $(addprefix apps/, \ + main.cpp \ + i18n.py \ +) -epsilon.$(EXE): $(app_objs) $(app_image_objs) +all_app_src = $(app_src) $(epsilon_src) -TO_REMOVE := apps/main.o apps/i18n.o -TMP := $(app_objs) $(app_image_objs) -VAR := $(filter-out $(TO_REMOVE), $(TMP)) -test.$(EXE): $(VAR) +$(call object_for,$(all_app_src)): $(BUILD_DIR)/apps/i18n.h +$(call object_for,$(all_app_src)): $(BUILD_DIR)/python/port/genhdr/qstrdefs.generated.h -products += epsilon.$(EXE) $(app_objs) $(call INLINER_PRODUCTS,$(app_images)) +$(BUILD_DIR)/epsilon.$(EXE): $(call object_for,$(epsilon_src)) + +src += $(app_src) diff --git a/apps/apps_container.cpp b/apps/apps_container.cpp index 2b038e6e5..d5dd6ad98 100644 --- a/apps/apps_container.cpp +++ b/apps/apps_container.cpp @@ -84,7 +84,7 @@ AppsContainer::AppsContainer() : * We just remove the circuit breaker for now. * TODO: Put the Poincare circuit breaker back on epsilon's web emulator */ #else - Poincare::Expression::setCircuitBreaker(AppsContainer::poincareCircuitBreaker); + Poincare::Expression::SetCircuitBreaker(AppsContainer::poincareCircuitBreaker); #endif Ion::Storage::sharedStorage()->setDelegate(this); } @@ -152,18 +152,19 @@ 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()); - if (activeApp() == nullptr || activeApp()->prepareForExit()) { + if (switchTo(usbConnectedAppSnapshot())) { /* 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); + bool switched = switchTo(activeSnapshot); + assert(switched); + (void) switched; // Silence compilation warning about unused variable. didProcessEvent = true; } else { - /* activeApp()->prepareForExit() returned false, which means that the - * app needs another event loop to prepare for being switched off. + /* We could not switch apps, which means that the current 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 @@ -222,8 +223,7 @@ bool AppsContainer::processEvent(Ion::Events::Event event) { return false; } -void AppsContainer::switchTo(App::Snapshot * snapshot) { - assert(activeApp() == nullptr || activeApp()->prepareForExit()); +bool AppsContainer::switchTo(App::Snapshot * snapshot) { if (activeApp() && snapshot != activeApp()->snapshot()) { resetShiftAlphaStatus(); } @@ -235,7 +235,7 @@ void AppsContainer::switchTo(App::Snapshot * snapshot) { if (snapshot) { m_window.setTitle(snapshot->descriptor()->upperName()); } - Container::switchTo(snapshot); + return Container::switchTo(snapshot); } void AppsContainer::run() { @@ -252,15 +252,14 @@ void AppsContainer::run() { /* Normal execution. The exception checkpoint must be created before * switching to the first app, because the first app might create nodes on * the pool. */ + bool switched = #if EPSILON_ONBOARDING_APP - switchTo(onBoardingAppSnapshot()); + switchTo(onBoardingAppSnapshot()); #else - if (numberOfApps() == 2) { - switchTo(appSnapshotAtIndex(1)); - } else { - switchTo(appSnapshotAtIndex(0)); - } + switchTo(appSnapshotAtIndex(numberOfApps() == 2 ? 1 : 0)); #endif + assert(switched); + (void) switched; // Silence compilation warning about unused variable. } else { // Exception if (activeApp() != nullptr) { @@ -277,7 +276,9 @@ void AppsContainer::run() { * history here, we will be stuck outside the calculation app. */ activeApp()->snapshot()->reset(); } - switchTo(appSnapshotAtIndex(0)); + bool switched = switchTo(appSnapshotAtIndex(0)); + assert(switched); + (void) switched; // Silence compilation warning about unused variable. Poincare::Tidy(); activeApp()->displayWarning(I18n::Message::PoolMemoryFull1, I18n::Message::PoolMemoryFull2, true); } diff --git a/apps/apps_container.h b/apps/apps_container.h index afa3ef224..5f23fbd1a 100644 --- a/apps/apps_container.h +++ b/apps/apps_container.h @@ -16,11 +16,6 @@ #include "backlight_dimming_timer.h" #include "shared/global_context.h" -#define USE_PIC_VIEW_APP 0 -#if USE_PIC_VIEW_APP -#include "picview/picview_app.h" -#endif - #ifdef EPSILON_BOOT_PROMPT #include "on_boarding/pop_up_controller.h" #endif @@ -42,7 +37,7 @@ public: VariableBoxController * variableBoxController(); void suspend(bool checkIfPowerKeyReleased = false); virtual bool dispatchEvent(Ion::Events::Event event) override; - void switchTo(App::Snapshot * snapshot) override; + bool switchTo(App::Snapshot * snapshot) override; void run() override; bool updateBatteryState(); void refreshPreferences(); @@ -70,9 +65,6 @@ private: AppsWindow m_window; EmptyBatteryWindow m_emptyBatteryWindow; -#if USE_PIC_VIEW_APP - PicViewApp m_picViewApp; -#endif Shared::GlobalContext m_globalContext; MathToolbox m_mathToolbox; VariableBoxController m_variableBoxController; diff --git a/apps/calculation/Makefile b/apps/calculation/Makefile index 7b797bea7..c8960bb30 100644 --- a/apps/calculation/Makefile +++ b/apps/calculation/Makefile @@ -1,16 +1,16 @@ apps += Calculation::App app_headers += apps/calculation/app.h -app_objs += $(addprefix apps/calculation/,\ - app.o\ - calculation.o\ - calculation_store.o\ - edit_expression_controller.o\ - expression_field.o\ - history_view_cell.o\ - history_controller.o\ - scrollable_expression_view.o\ - selectable_table_view.o\ +app_src += $(addprefix apps/calculation/,\ + app.cpp \ + calculation.cpp \ + calculation_store.cpp \ + edit_expression_controller.cpp \ + expression_field.cpp \ + history_view_cell.cpp \ + history_controller.cpp \ + scrollable_expression_view.cpp \ + selectable_table_view.cpp \ ) i18n_files += $(addprefix apps/calculation/,\ @@ -25,4 +25,4 @@ tests += $(addprefix apps/calculation/test/,\ calculation_store.cpp\ ) -app_images += apps/calculation/calculation_icon.png +$(eval $(call depends_on_image,apps/calculation/app.cpp,apps/calculation/calculation_icon.png)) diff --git a/apps/calculation/app.cpp b/apps/calculation/app.cpp index 5be4fa94f..44c2a938e 100644 --- a/apps/calculation/app.cpp +++ b/apps/calculation/app.cpp @@ -1,7 +1,7 @@ #include "app.h" #include "../apps_container.h" #include "calculation_icon.h" -#include "../i18n.h" +#include #include using namespace Poincare; diff --git a/apps/calculation/calculation.cpp b/apps/calculation/calculation.cpp index 4e1105697..1bf479107 100644 --- a/apps/calculation/calculation.cpp +++ b/apps/calculation/calculation.cpp @@ -3,6 +3,7 @@ #include "../shared/poincare_helpers.h" #include #include +#include #include #include @@ -22,7 +23,14 @@ Calculation::Calculation() : bool Calculation::operator==(const Calculation& c) { return strcmp(m_inputText, c.m_inputText) == 0 - && strcmp(m_approximateOutputText, c.m_approximateOutputText) == 0; + && strcmp(m_approximateOutputText, c.m_approximateOutputText) == 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. + * For example, i^(sqrt(3)) = cos(sqrt(3)*pi/2)+i*sin(sqrt(3)*pi/2) if + * angle unit is radian and i^(sqrt(3)) = cos(sqrt(3)*90+i*sin(sqrt(3)*90) + * in degree. */ + && strcmp(m_exactOutputText, c.m_exactOutputText) == 0; } void Calculation::reset() { @@ -41,9 +49,10 @@ void Calculation::setContent(const char * c, Context * context, Expression ansEx * to keep Ans symbol in the calculation store. */ PoincareHelpers::Serialize(input, m_inputText, sizeof(m_inputText)); } - Expression exactOutput = PoincareHelpers::ParseAndSimplify(m_inputText, *context); + Expression exactOutput; + Expression approximateOutput; + PoincareHelpers::ParseAndSimplifyAndApproximate(m_inputText, &exactOutput, &approximateOutput, *context); PoincareHelpers::Serialize(exactOutput, m_exactOutputText, sizeof(m_exactOutputText)); - Expression approximateOutput = PoincareHelpers::Approximate(exactOutput, *context); PoincareHelpers::Serialize(approximateOutput, m_approximateOutputText, sizeof(m_approximateOutputText)); } @@ -116,7 +125,7 @@ Expression Calculation::exactOutput() { * 'cos(pi/4) = 0.999906' (which is true in degree). */ Expression exactOutput = Expression::Parse(m_exactOutputText); if (exactOutput.isUninitialized()) { - return Undefined(); + return Undefined::Builder(); } return exactOutput; } @@ -142,21 +151,21 @@ bool Calculation::shouldOnlyDisplayApproximateOutput(Context * context) { } if (strcmp(m_exactOutputText, m_approximateOutputText) == 0) { /* If the exact and approximate results' texts are equal and their layouts - * too, do not display the exact result. If, because of the number of - * significant digits, the two layouts are not equal, we display both. */ + * too, do not display the exact result. If the two layouts are not equal + * because of the number of significant digits, we display both. */ return exactAndApproximateDisplayedOutputsAreEqual(context) == Calculation::EqualSign::Equal; } - if (strcmp(m_exactOutputText, Undefined::Name()) == 0) { + if (strcmp(m_exactOutputText, Undefined::Name()) == 0 || strcmp(m_approximateOutputText, Unreal::Name()) == 0) { + // If the approximate result is 'unreal' or the exact result is 'undef' return true; } return input().isApproximate(*context) || exactOutput().isApproximate(*context); } bool Calculation::shouldOnlyDisplayExactOutput() { - /* If the approximateOutput is undef, we not not want to display it. - * This prevents: + /* If the approximateOutput is undef, do not display it. This prevents: * x->f(x) from displaying x = undef - * x+x form displaying 2x = undef */ + * x+x from displaying 2x = undef */ return strcmp(m_approximateOutputText, Undefined::Name()) == 0; } @@ -167,11 +176,12 @@ Calculation::EqualSign Calculation::exactAndApproximateDisplayedOutputsAreEqual( constexpr int bufferSize = Constant::MaxSerializedExpressionSize; char buffer[bufferSize]; Preferences * preferences = Preferences::sharedPreferences(); - Expression exactOutputExpression = Expression::ParseAndSimplify(m_exactOutputText, *context, preferences->angleUnit()); + Expression exactOutputExpression = PoincareHelpers::ParseAndSimplify(m_exactOutputText, *context); if (exactOutputExpression.isUninitialized()) { - exactOutputExpression = Undefined(); + exactOutputExpression = Undefined::Builder(); } - m_equalSign = exactOutputExpression.isEqualToItsApproximationLayout(approximateOutput(context), buffer, bufferSize, preferences->angleUnit(), preferences->displayMode(), preferences->numberOfSignificantDigits(), *context) ? EqualSign::Equal : EqualSign::Approximation; + 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; return m_equalSign; } diff --git a/apps/calculation/calculation_store.cpp b/apps/calculation/calculation_store.cpp index b3f747c9d..d235445d1 100644 --- a/apps/calculation/calculation_store.cpp +++ b/apps/calculation/calculation_store.cpp @@ -88,14 +88,14 @@ void CalculationStore::tidy() { Expression CalculationStore::ansExpression(Context * context) { if (numberOfCalculations() == 0) { - return Rational(0); + return Rational::Builder(0); } Calculation * lastCalculation = calculationAtIndex(numberOfCalculations()-1); /* Special case: the exact output is a Store/Equal expression. - * Store/Equal expression must be final root of an expression. - * To avoid turning 'ans->A' in '2->A->A' (or 2->A=A) which cannot be parsed), - * ans is replaced by the approximation output in when any Store or Equal - * expression appears.*/ + * Store/Equal expression can only be at the root of an expression. + * To avoid turning 'ans->A' in '2->A->A' or '2=A->A' (which cannot be + * parsed), ans is replaced by the approximation output when any Store or + * Equal expression appears. */ bool exactOuptutInvolvesStoreEqual = lastCalculation->exactOutput().recursivelyMatches([](const Expression e, Context & context, bool replaceSymbols) { return e.type() == ExpressionNode::Type::Store || e.type() == ExpressionNode::Type::Equal; }, *context, false); diff --git a/apps/calculation/history_controller.cpp b/apps/calculation/history_controller.cpp index 741304ac0..ba3ec25cd 100644 --- a/apps/calculation/history_controller.cpp +++ b/apps/calculation/history_controller.cpp @@ -150,7 +150,7 @@ KDCoordinate HistoryController::rowHeight(int j) { } Calculation * calculation = m_calculationStore->calculationAtIndex(j); App * calculationApp = (App *)app(); - return calculation->height(calculationApp->localContext()) + 3*HistoryViewCell::k_digitVerticalMargin; + return calculation->height(calculationApp->localContext()) + 4 * Metric::CommonSmallMargin; } int HistoryController::typeAtLocation(int i, int j) { diff --git a/apps/calculation/history_view_cell.cpp b/apps/calculation/history_view_cell.cpp index 9bc595365..ff9cb69a4 100644 --- a/apps/calculation/history_view_cell.cpp +++ b/apps/calculation/history_view_cell.cpp @@ -10,9 +10,9 @@ namespace Calculation { /* HistoryViewCellDataSource */ HistoryViewCellDataSource::HistoryViewCellDataSource() : - m_selectedSubviewType(HistoryViewCellDataSource::SubviewType::Output) {} + m_selectedSubviewType(SubviewType::Output) {} -void HistoryViewCellDataSource::setSelectedSubviewType(HistoryViewCellDataSource::SubviewType subviewType, HistoryViewCell * cell) { +void HistoryViewCellDataSource::setSelectedSubviewType(SubviewType subviewType, HistoryViewCell * cell) { m_selectedSubviewType = subviewType; if (cell) { cell->setHighlighted(cell->isHighlighted()); @@ -39,17 +39,18 @@ Shared::ScrollableExactApproximateExpressionsView * HistoryViewCell::outputView( void HistoryViewCell::setEven(bool even) { EvenOddCell::setEven(even); m_inputView.setBackgroundColor(backgroundColor()); + m_scrollableOutputView.setBackgroundColor(backgroundColor()); m_scrollableOutputView.evenOddCell()->setEven(even); } void HistoryViewCell::setHighlighted(bool highlight) { assert(m_dataSource); m_highlighted = highlight; - m_inputView.setBackgroundColor(backgroundColor()); + m_inputView.setExpressionBackgroundColor(backgroundColor()); m_scrollableOutputView.evenOddCell()->setHighlighted(false); if (isHighlighted()) { if (m_dataSource->selectedSubviewType() == HistoryViewCellDataSource::SubviewType::Input) { - m_inputView.setBackgroundColor(Palette::Select); + m_inputView.setExpressionBackgroundColor(Palette::Select); } else { m_scrollableOutputView.evenOddCell()->setHighlighted(true); } @@ -93,20 +94,21 @@ View * HistoryViewCell::subviewAtIndex(int index) { } void HistoryViewCell::layoutSubviews() { - KDCoordinate width = bounds().width(); - KDCoordinate height = bounds().height(); + KDCoordinate maxFrameWidth = bounds().width(); KDSize inputSize = m_inputView.minimalSizeForOptimalDisplay(); - if (inputSize.width() + Metric::HistoryHorizontalMargin > width) { - m_inputView.setFrame(KDRect(Metric::HistoryHorizontalMargin, k_digitVerticalMargin, width - Metric::HistoryHorizontalMargin, inputSize.height())); - } else { - m_inputView.setFrame(KDRect(Metric::HistoryHorizontalMargin, k_digitVerticalMargin, inputSize.width(), inputSize.height())); - } + m_inputView.setFrame(KDRect( + 0, + 0, + min(maxFrameWidth, inputSize.width()), + inputSize.height() + )); KDSize outputSize = m_scrollableOutputView.minimalSizeForOptimalDisplay(); - if (outputSize.width() + Metric::HistoryHorizontalMargin > width) { - m_scrollableOutputView.setFrame(KDRect(Metric::HistoryHorizontalMargin, inputSize.height() + 2*k_digitVerticalMargin, width - Metric::HistoryHorizontalMargin, height - inputSize.height() - 3*k_digitVerticalMargin)); - } else { - m_scrollableOutputView.setFrame(KDRect(width - outputSize.width() - Metric::HistoryHorizontalMargin, inputSize.height() + 2*k_digitVerticalMargin, outputSize.width(), height - inputSize.height() - 3*k_digitVerticalMargin)); - } + m_scrollableOutputView.setFrame(KDRect( + max(0, maxFrameWidth - outputSize.width()), + inputSize.height(), + min(maxFrameWidth, outputSize.width()), + bounds().height() - inputSize.height() + )); } void HistoryViewCell::setCalculation(Calculation * calculation) { diff --git a/apps/calculation/history_view_cell.h b/apps/calculation/history_view_cell.h index f3a0eade1..55053d6dc 100644 --- a/apps/calculation/history_view_cell.h +++ b/apps/calculation/history_view_cell.h @@ -17,7 +17,7 @@ public: Output }; HistoryViewCellDataSource(); - void setSelectedSubviewType(HistoryViewCellDataSource::SubviewType subviewType, HistoryViewCell * cell = nullptr); + void setSelectedSubviewType(SubviewType subviewType, HistoryViewCell * cell = nullptr); SubviewType selectedSubviewType() { return m_selectedSubviewType; } private: SubviewType m_selectedSubviewType; @@ -42,7 +42,6 @@ public: void layoutSubviews() override; void didBecomeFirstResponder() override; bool handleEvent(Ion::Events::Event event) override; - constexpr static KDCoordinate k_digitVerticalMargin = 5; Shared::ScrollableExactApproximateExpressionsView * outputView(); private: constexpr static KDCoordinate k_resultWidth = 80; diff --git a/apps/calculation/scrollable_expression_view.cpp b/apps/calculation/scrollable_expression_view.cpp index 35cfe08de..4860699ad 100644 --- a/apps/calculation/scrollable_expression_view.cpp +++ b/apps/calculation/scrollable_expression_view.cpp @@ -8,11 +8,17 @@ ScrollableExpressionView::ScrollableExpressionView(Responder * parentResponder) ScrollableView(parentResponder, &m_expressionView, this), m_expressionView() { + setDecoratorType(ScrollView::Decorator::Type::Arrows); + setMargins( + Metric::CommonSmallMargin, + Metric::CommonLargeMargin, + Metric::CommonSmallMargin, + Metric::CommonLargeMargin + ); } void ScrollableExpressionView::setLayout(Layout layout) { m_expressionView.setLayout(layout); - layoutSubviews(); } void ScrollableExpressionView::setBackgroundColor(KDColor backgroundColor) { @@ -20,8 +26,8 @@ void ScrollableExpressionView::setBackgroundColor(KDColor backgroundColor) { ScrollableView::setBackgroundColor(backgroundColor); } -KDSize ScrollableExpressionView::minimalSizeForOptimalDisplay() const { - return m_expressionView.minimalSizeForOptimalDisplay(); +void ScrollableExpressionView::setExpressionBackgroundColor(KDColor backgroundColor) { + m_expressionView.setBackgroundColor(backgroundColor); } } diff --git a/apps/calculation/scrollable_expression_view.h b/apps/calculation/scrollable_expression_view.h index d4ac8e330..15ce343bf 100644 --- a/apps/calculation/scrollable_expression_view.h +++ b/apps/calculation/scrollable_expression_view.h @@ -10,7 +10,7 @@ public: ScrollableExpressionView(Responder * parentResponder); void setLayout(Poincare::Layout layout); void setBackgroundColor(KDColor backgroundColor) override; - KDSize minimalSizeForOptimalDisplay() const override; + void setExpressionBackgroundColor(KDColor backgroundColor); private: ExpressionView m_expressionView; }; diff --git a/apps/calculation/selectable_table_view.cpp b/apps/calculation/selectable_table_view.cpp index 958ac8a15..c00bed4b1 100644 --- a/apps/calculation/selectable_table_view.cpp +++ b/apps/calculation/selectable_table_view.cpp @@ -8,7 +8,7 @@ CalculationSelectableTableView::CalculationSelectableTableView(Responder * paren { setVerticalCellOverlap(0); setMargins(0); - setShowsIndicators(false); + setDecoratorType(ScrollView::Decorator::Type::None); } void CalculationSelectableTableView::scrollToCell(int i, int j) { diff --git a/apps/calculation/test/calculation_store.cpp b/apps/calculation/test/calculation_store.cpp index c621fcac4..151d6254a 100644 --- a/apps/calculation/test/calculation_store.cpp +++ b/apps/calculation/test/calculation_store.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include "../calculation_store.h" @@ -13,7 +14,7 @@ void assert_store_is(CalculationStore * store, const char * result[10]) { } } -QUIZ_CASE(calculation_store) { +QUIZ_CASE(calculation_store_ring_buffer) { Shared::GlobalContext globalContext; CalculationStore store; quiz_assert(CalculationStore::k_maxNumberOfCalculations == 10); @@ -50,7 +51,7 @@ QUIZ_CASE(calculation_store) { store.deleteAll(); } -QUIZ_CASE(calculation_display_exact_approximate) { +QUIZ_CASE(calculation_ans) { Shared::GlobalContext globalContext; CalculationStore store; @@ -65,49 +66,84 @@ QUIZ_CASE(calculation_display_exact_approximate) { quiz_assert(lastCalculation->shouldOnlyDisplayApproximateOutput(&globalContext) == true); quiz_assert(strcmp(lastCalculation->approximateOutputText(),"2.6366666666667") == 0); - store.deleteAll(); - store.push("1/2", &globalContext); - lastCalculation = store.calculationAtIndex(1); - quiz_assert(lastCalculation->exactAndApproximateDisplayedOutputsAreEqual(&globalContext) == ::Calculation::Calculation::EqualSign::Equal); - quiz_assert(lastCalculation->shouldOnlyDisplayExactOutput() == false); - quiz_assert(lastCalculation->shouldOnlyDisplayApproximateOutput(&globalContext) == false); - - store.deleteAll(); - store.push("1/3", &globalContext); - lastCalculation = store.calculationAtIndex(1); - quiz_assert(lastCalculation->exactAndApproximateDisplayedOutputsAreEqual(&globalContext) == ::Calculation::Calculation::EqualSign::Approximation); - quiz_assert(lastCalculation->shouldOnlyDisplayExactOutput() == false); - quiz_assert(lastCalculation->shouldOnlyDisplayApproximateOutput(&globalContext) == false); - - store.deleteAll(); - store.push("1/0", &globalContext); - lastCalculation = store.calculationAtIndex(1); - quiz_assert(lastCalculation->shouldOnlyDisplayExactOutput() == true); - quiz_assert(strcmp(lastCalculation->approximateOutputText(),"undef") == 0); - - store.deleteAll(); - store.push("2x-x", &globalContext); - lastCalculation = store.calculationAtIndex(1); - quiz_assert(lastCalculation->shouldOnlyDisplayExactOutput() == true); - quiz_assert(strcmp(lastCalculation->exactOutputText(),"x") == 0); - - store.deleteAll(); - store.push("[[1,2,3]]", &globalContext); - lastCalculation = store.calculationAtIndex(1); - quiz_assert(lastCalculation->shouldOnlyDisplayExactOutput() == false); - quiz_assert(lastCalculation->shouldOnlyDisplayApproximateOutput(&globalContext) == true); - - store.deleteAll(); - store.push("[[1,x,3]]", &globalContext); - lastCalculation = store.calculationAtIndex(1); - quiz_assert(lastCalculation->shouldOnlyDisplayExactOutput() == false); - quiz_assert(lastCalculation->shouldOnlyDisplayApproximateOutput(&globalContext) == true); - - store.deleteAll(); - store.push("28^7", &globalContext); - lastCalculation = store.calculationAtIndex(1); - quiz_assert(lastCalculation->shouldOnlyDisplayExactOutput() == false); - quiz_assert(lastCalculation->shouldOnlyDisplayApproximateOutput(&globalContext) == false); - store.deleteAll(); } + +void assertCalculationDisplay(const char * input, bool displayExactOutput, bool displayApproximateOutput, ::Calculation::Calculation::EqualSign sign, const char * exactOutput, const char * approximateOutput, Context * context, CalculationStore * store) { + char buffer[500]; + strlcpy(buffer, input, sizeof(buffer)); + translate_in_special_chars(buffer); + store->push(buffer, context); + ::Calculation::Calculation * lastCalculation = store->calculationAtIndex(1); + quiz_assert(lastCalculation->shouldOnlyDisplayExactOutput() == displayExactOutput); + quiz_assert(lastCalculation->shouldOnlyDisplayApproximateOutput(context) == displayApproximateOutput); + if (sign != ::Calculation::Calculation::EqualSign::Unknown) { + quiz_assert(lastCalculation->exactAndApproximateDisplayedOutputsAreEqual(context) == sign); + } + if (exactOutput) { + strlcpy(buffer, exactOutput, sizeof(buffer)); + translate_in_special_chars(buffer); + quiz_assert(strcmp(lastCalculation->exactOutputText(), buffer) == 0); + } + if (approximateOutput) { + strlcpy(buffer, approximateOutput, sizeof(buffer)); + translate_in_special_chars(buffer); + quiz_assert(strcmp(lastCalculation->approximateOutputText(),buffer) == 0); + } + store->deleteAll(); +} + +QUIZ_CASE(calculation_display_exact_approximate) { + Shared::GlobalContext globalContext; + CalculationStore store; + + assertCalculationDisplay("1/2", false, false, ::Calculation::Calculation::EqualSign::Equal, nullptr, nullptr, &globalContext, &store); + assertCalculationDisplay("1/3", false, false, ::Calculation::Calculation::EqualSign::Approximation, nullptr, nullptr, &globalContext, &store); + assertCalculationDisplay("1/0", true, false, ::Calculation::Calculation::EqualSign::Unknown, "undef", "undef", &globalContext, &store); + assertCalculationDisplay("2x-x", true, false, ::Calculation::Calculation::EqualSign::Unknown, "x", "undef", &globalContext, &store); + assertCalculationDisplay("[[1,2,3]]", false, true, ::Calculation::Calculation::EqualSign::Unknown, nullptr, nullptr, &globalContext, &store); + assertCalculationDisplay("[[1,x,3]]", false, true, ::Calculation::Calculation::EqualSign::Unknown, nullptr, nullptr, &globalContext, &store); + assertCalculationDisplay("28^7", false, false, ::Calculation::Calculation::EqualSign::Unknown, nullptr, nullptr, &globalContext, &store); + assertCalculationDisplay("3+R(2)>a", false, false, ::Calculation::Calculation::EqualSign::Approximation, "R(2)+3", nullptr, &globalContext, &store); + Ion::Storage::sharedStorage()->recordNamed("a.exp").destroy(); + assertCalculationDisplay("3+2>a", false, true, ::Calculation::Calculation::EqualSign::Equal, "5", "5", &globalContext, &store); + Ion::Storage::sharedStorage()->recordNamed("a.exp").destroy(); + assertCalculationDisplay("3>a", false, true, ::Calculation::Calculation::EqualSign::Equal, "3", "3", &globalContext, &store); + Ion::Storage::sharedStorage()->recordNamed("a.exp").destroy(); + assertCalculationDisplay("3+x>f(x)", true, false, ::Calculation::Calculation::EqualSign::Unknown, "x+3", nullptr, &globalContext, &store); + Ion::Storage::sharedStorage()->recordNamed("f.func").destroy(); +} + +QUIZ_CASE(calculation_complex_format) { + Shared::GlobalContext globalContext; + CalculationStore store; + + Poincare::Preferences::sharedPreferences()->setComplexFormat(Poincare::Preferences::ComplexFormat::Real); + assertCalculationDisplay("1+I", false, true, ::Calculation::Calculation::EqualSign::Unknown, nullptr, "1+I", &globalContext, &store); + assertCalculationDisplay("R(-1)", false, true, ::Calculation::Calculation::EqualSign::Unknown, "unreal", nullptr, &globalContext, &store); + assertCalculationDisplay("ln(-2)", false, true, ::Calculation::Calculation::EqualSign::Unknown, nullptr, "unreal", &globalContext, &store); + assertCalculationDisplay("R(-1)*R(-1)", false, true, ::Calculation::Calculation::EqualSign::Unknown, nullptr, "unreal", &globalContext, &store); + assertCalculationDisplay("(-8)^(1/3)", false, true, ::Calculation::Calculation::EqualSign::Unknown, nullptr, "-2", &globalContext, &store); + assertCalculationDisplay("(-8)^(2/3)", false, true, ::Calculation::Calculation::EqualSign::Unknown, nullptr, "4", &globalContext, &store); + assertCalculationDisplay("(-2)^(1/4)", false, true, ::Calculation::Calculation::EqualSign::Unknown, nullptr, "unreal", &globalContext, &store); + + Poincare::Preferences::sharedPreferences()->setComplexFormat(Poincare::Preferences::ComplexFormat::Cartesian); + assertCalculationDisplay("1+I", false, true, ::Calculation::Calculation::EqualSign::Unknown, nullptr, "1+I", &globalContext, &store); + assertCalculationDisplay("R(-1)", false, true, ::Calculation::Calculation::EqualSign::Unknown, nullptr, "I", &globalContext, &store); + assertCalculationDisplay("ln(-2)", false, false, ::Calculation::Calculation::EqualSign::Approximation, "ln(-2)", nullptr, &globalContext, &store); + assertCalculationDisplay("R(-1)*R(-1)", false, true, ::Calculation::Calculation::EqualSign::Unknown, nullptr, "-1", &globalContext, &store); + assertCalculationDisplay("(-8)^(1/3)", false, false, ::Calculation::Calculation::EqualSign::Approximation, "1+R(3)*I", nullptr, &globalContext, &store); + assertCalculationDisplay("(-8)^(2/3)", false, false, ::Calculation::Calculation::EqualSign::Approximation, "-2+2*R(3)*I", nullptr, &globalContext, &store); + assertCalculationDisplay("(-2)^(1/4)", false, false, ::Calculation::Calculation::EqualSign::Approximation, "root(8,4)/2+root(8,4)/2*I", nullptr, &globalContext, &store); + + Poincare::Preferences::sharedPreferences()->setComplexFormat(Poincare::Preferences::ComplexFormat::Polar); + assertCalculationDisplay("1+I", false, false, ::Calculation::Calculation::EqualSign::Approximation, "R(2)*X^(P/4*I)", nullptr, &globalContext, &store); + assertCalculationDisplay("R(-1)", false, false, ::Calculation::Calculation::EqualSign::Approximation, "X^(P/2*I)", nullptr, &globalContext, &store); + assertCalculationDisplay("ln(-2)", false, false, ::Calculation::Calculation::EqualSign::Approximation, "ln(-2)", nullptr, &globalContext, &store); + assertCalculationDisplay("R(-1)*R(-1)", false, false, ::Calculation::Calculation::EqualSign::Unknown, nullptr, "X^(3.1415926535898*I)", &globalContext, &store); + assertCalculationDisplay("(-8)^(1/3)", false, false, ::Calculation::Calculation::EqualSign::Approximation, "2*X^(P/3*I)", nullptr, &globalContext, &store); + assertCalculationDisplay("(-8)^(2/3)", false, false, ::Calculation::Calculation::EqualSign::Approximation, "4*X^((2*P)/3*I)", nullptr, &globalContext, &store); + assertCalculationDisplay("(-2)^(1/4)", false, false, ::Calculation::Calculation::EqualSign::Approximation, "root(2,4)*X^(P/4*I)", nullptr, &globalContext, &store); + + Poincare::Preferences::sharedPreferences()->setComplexFormat(Poincare::Preferences::ComplexFormat::Cartesian); +} diff --git a/apps/code/Makefile b/apps/code/Makefile index 25b63e4ee..891aa24d2 100644 --- a/apps/code/Makefile +++ b/apps/code/Makefile @@ -1,26 +1,26 @@ apps += Code::App app_headers += apps/code/app.h -app_objs += $(addprefix apps/code/,\ - app.o\ - console_controller.o\ - console_edit_cell.o\ - console_line_cell.o\ - console_store.o\ - editor_controller.o\ - editor_view.o\ - helpers.o\ - menu_controller.o\ - python_toolbox.o\ - python_text_area.o\ - sandbox_controller.o\ - script.o\ - script_name_cell.o\ - script_node_cell.o\ - script_parameter_controller.o\ - script_store.o\ - script_template.o\ - variable_box_controller.o\ +app_src += $(addprefix apps/code/,\ + app.cpp \ + console_controller.cpp \ + console_edit_cell.cpp \ + console_line_cell.cpp \ + console_store.cpp \ + editor_controller.cpp \ + editor_view.cpp \ + helpers.cpp \ + menu_controller.cpp \ + python_toolbox.cpp \ + python_text_area.cpp \ + sandbox_controller.cpp \ + script.cpp \ + script_name_cell.cpp \ + script_node_cell.cpp \ + script_parameter_controller.cpp \ + script_store.cpp \ + script_template.cpp \ + variable_box_controller.cpp \ ) i18n_files += $(addprefix apps/code/,\ @@ -44,4 +44,4 @@ i18n_files += $(addprefix apps/code/,\ toolbox.universal.i18n\ ) -app_images += apps/code/code_icon.png +$(eval $(call depends_on_image,apps/code/app.cpp,apps/code/code_icon.png)) diff --git a/apps/code/app.cpp b/apps/code/app.cpp index bedd3d0f1..16dfa0150 100644 --- a/apps/code/app.cpp +++ b/apps/code/app.cpp @@ -1,7 +1,7 @@ #include "app.h" #include "../apps_container.h" #include "code_icon.h" -#include "../i18n.h" +#include #include "helpers.h" namespace Code { diff --git a/apps/code/base.de.i18n b/apps/code/base.de.i18n index 70b704e82..155cd5ccd 100644 --- a/apps/code/base.de.i18n +++ b/apps/code/base.de.i18n @@ -5,4 +5,4 @@ ExecuteScript = "Skript ausfuehren" AutoImportScript = "Automatischer Import in Konsole" DeleteScript = "Skript loeschen" FunctionsAndVariables = "Funktionen und Variablen" -NonCompliantName = "Erlaubte Zeichen: a-z, 0-9, _" +AllowedCharactersaz09 = "Erlaubte Zeichen: a-z, 0-9, _" diff --git a/apps/code/base.en.i18n b/apps/code/base.en.i18n index 7a4a9c4bc..8bf1e3c9f 100644 --- a/apps/code/base.en.i18n +++ b/apps/code/base.en.i18n @@ -5,4 +5,4 @@ ExecuteScript = "Execute script" AutoImportScript = "Auto import in shell" DeleteScript = "Delete script" FunctionsAndVariables = "Functions and variables" -NonCompliantName = "Allowed characters: a-z, 0-9, _" +AllowedCharactersaz09 = "Allowed characters: a-z, 0-9, _" diff --git a/apps/code/base.es.i18n b/apps/code/base.es.i18n index eaae8c3bd..1f6fc5aaf 100644 --- a/apps/code/base.es.i18n +++ b/apps/code/base.es.i18n @@ -5,4 +5,4 @@ ExecuteScript = "Ejecutar el archivo" AutoImportScript = "Importacion auto en interprete" DeleteScript = "Eliminar el archivo" FunctionsAndVariables = "Funciones y variables" -NonCompliantName = "Caracteres permitidos : a-z, 0-9, _" +AllowedCharactersaz09 = "Caracteres permitidos : a-z, 0-9, _" diff --git a/apps/code/base.fr.i18n b/apps/code/base.fr.i18n index b063007dd..876bac18c 100644 --- a/apps/code/base.fr.i18n +++ b/apps/code/base.fr.i18n @@ -5,4 +5,4 @@ ExecuteScript = "Executer le script" AutoImportScript = "Importation auto dans la console" DeleteScript = "Supprimer le script" FunctionsAndVariables = "Fonctions et variables" -NonCompliantName = "Caractères autorisés : a-z, 0-9, _" +AllowedCharactersaz09 = "Caractères autorisés : a-z, 0-9, _" diff --git a/apps/code/base.pt.i18n b/apps/code/base.pt.i18n index 4d083b061..29acc7c3c 100644 --- a/apps/code/base.pt.i18n +++ b/apps/code/base.pt.i18n @@ -5,4 +5,4 @@ ExecuteScript = "Executar o script" AutoImportScript = "Importacao auto no interpretador" DeleteScript = "Eliminar o script" FunctionsAndVariables = "Funções e variáveis" -NonCompliantName = "Caracteres permitidos : a-z, 0-9, _" +AllowedCharactersaz09 = "Caracteres permitidos : a-z, 0-9, _" diff --git a/apps/code/console_controller.cpp b/apps/code/console_controller.cpp index 13acc6965..d325669be 100644 --- a/apps/code/console_controller.cpp +++ b/apps/code/console_controller.cpp @@ -91,25 +91,51 @@ 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); + const char * promptText = prompt; + char * s = const_cast(prompt); + + if (promptText != nullptr) { + /* Set the prompt text. If the prompt text has a '\n', put the prompt text in + * the history until the last '\n', and put the remaining prompt text in the + * edit cell's prompt. */ + char * lastCarriageReturn = nullptr; + while (*s != 0) { + if (*s == '\n') { + lastCarriageReturn = s; + } + s++; + } + if (lastCarriageReturn != nullptr) { + printText(prompt, lastCarriageReturn-prompt+1); + promptText = lastCarriageReturn+1; + } + } + + m_editCell.setPrompt(promptText); m_editCell.setText(""); - // Run new input loop + // Reload the history + m_selectableTableView.reloadData(); + m_selectableTableView.selectCellAtLocation(0, m_consoleStore.numberOfLines()); a->redrawWindow(); + + // Launch a new input loop a->runWhile([](void * a){ ConsoleController * c = static_cast(a); return c->inputRunLoopActive(); }, this); - // Reset the prompt line + // Handle the input text + if (promptText != nullptr) { + printText(promptText, s - promptText); + } + const char * text = m_editCell.text(); + printText(text, strlen(text)); flushOutputAccumulationBufferToStore(); - m_consoleStore.deleteLastLineIfEmpty(); + m_editCell.setPrompt(sStandardPromptText); - return m_editCell.text(); + return text; } void ConsoleController::viewWillAppear() { @@ -242,7 +268,11 @@ bool ConsoleController::textFieldShouldFinishEditing(TextField * textField, Ion: } bool ConsoleController::textFieldDidReceiveEvent(TextField * textField, Ion::Events::Event event) { - if (event == Ion::Events::Up && m_inputRunLoopActive) { + if (m_inputRunLoopActive + && (event == Ion::Events::Up + || event == Ion::Events::OK + || event == Ion::Events::EXE)) + { 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. */ @@ -302,6 +332,13 @@ void ConsoleController::displaySandbox() { stackViewController()->push(&m_sandboxController); } +void ConsoleController::hideSandbox() { + if (!sandboxIsDisplayed()) { + return; + } + m_sandboxController.hide(); +} + void ConsoleController::resetSandbox() { if (!sandboxIsDisplayed()) { return; @@ -313,28 +350,33 @@ void ConsoleController::resetSandbox() { * The text argument is not always null-terminated. */ void ConsoleController::printText(const char * text, size_t length) { size_t textCutIndex = firstNewLineCharIndex(text, length); - // If there is no new line in text, just append it to the output accumulation - // buffer. if (textCutIndex >= length) { + /* If there is no new line in text, just append it to the output + * accumulation buffer. */ appendTextToOutputAccumulationBuffer(text, length); return; } - // If there is a new line in the middle of the text, we have to store at least - // two new console lines in the console store. if (textCutIndex < length - 1) { + /* If there is a new line in the middle of the text, we have to store at + * least two new console lines in the console store. */ printText(text, textCutIndex + 1); printText(&text[textCutIndex+1], length - (textCutIndex + 1)); return; } - // If there is a new line at the end of the text, we have to store the line in - // the console store. - if (textCutIndex == length - 1) { - appendTextToOutputAccumulationBuffer(text, length-1); - flushOutputAccumulationBufferToStore(); - } + /* There is a new line at the end of the text, we have to store the line in + * the console store. */ + assert(textCutIndex == length - 1); + appendTextToOutputAccumulationBuffer(text, length-1); + flushOutputAccumulationBufferToStore(); } void ConsoleController::autoImportScript(Script script, bool force) { + if (sandboxIsDisplayed()) { + /* The sandbox might be displayed, for instance if we are auto-importing + * several scripts that draw at importation. In this case, we want to remove + * the sandbox. */ + hideSandbox(); + } if (script.importationStatus() || force) { // Step 1 - Create the command "from scriptName import *". @@ -357,7 +399,7 @@ void ConsoleController::autoImportScript(Script script, bool force) { // Step 2 - Run the command runAndPrintForCommand(command); } - if (force) { + if (!sandboxIsDisplayed() && force) { m_selectableTableView.reloadData(); m_selectableTableView.selectCellAtLocation(0, m_consoleStore.numberOfLines()); m_editCell.setEditing(true); diff --git a/apps/code/console_controller.h b/apps/code/console_controller.h index 2a672ba33..d39f6edfe 100644 --- a/apps/code/console_controller.h +++ b/apps/code/console_controller.h @@ -62,6 +62,7 @@ public: // MicroPython::ExecutionEnvironment void displaySandbox() override; + void hideSandbox() override; void resetSandbox() override; void printText(const char * text, size_t length) override; const char * inputText(const char * prompt) override; diff --git a/apps/code/console_line_cell.cpp b/apps/code/console_line_cell.cpp index 7593591b1..5a696d57a 100644 --- a/apps/code/console_line_cell.cpp +++ b/apps/code/console_line_cell.cpp @@ -31,10 +31,6 @@ ConsoleLineCell::ScrollableConsoleLineView::ScrollableConsoleLineView(Responder { } -KDSize ConsoleLineCell::ScrollableConsoleLineView::minimalSizeForOptimalDisplay() const { - return m_consoleLineView.minimalSizeForOptimalDisplay(); -} - ConsoleLineCell::ConsoleLineCell(Responder * parentResponder) : HighlightCell(), Responder(parentResponder), diff --git a/apps/code/console_line_cell.h b/apps/code/console_line_cell.h index e192e82cc..8cb3ef58a 100644 --- a/apps/code/console_line_cell.h +++ b/apps/code/console_line_cell.h @@ -48,7 +48,6 @@ private: }; ScrollableConsoleLineView(Responder * parentResponder); - KDSize minimalSizeForOptimalDisplay() const override; ConsoleLineView * consoleLineView() { return &m_consoleLineView; } private: ConsoleLineView m_consoleLineView; diff --git a/apps/code/console_store.cpp b/apps/code/console_store.cpp index 186ead226..910ea0bb8 100644 --- a/apps/code/console_store.cpp +++ b/apps/code/console_store.cpp @@ -1,21 +1,10 @@ #include "console_store.h" -#include #include namespace Code { static inline int min(int x, int y) { return (x 0); - m_history[0] = 0; -} - void ConsoleStore::startNewSession() { if (k_historySize < 1) { return; diff --git a/apps/code/console_store.h b/apps/code/console_store.h index 245ecbcef..f5ae9c2b7 100644 --- a/apps/code/console_store.h +++ b/apps/code/console_store.h @@ -2,14 +2,15 @@ #define CODE_CONSOLE_STORE_H #include "console_line.h" +#include #include namespace Code { class ConsoleStore { public: - ConsoleStore(); - void clear(); + ConsoleStore() : m_history{0} {} + void clear() { assert(k_historySize > 0); m_history[0] = 0; } void startNewSession(); ConsoleLine lineAtIndex(int i) const; int numberOfLines() const; diff --git a/apps/code/menu_controller.cpp b/apps/code/menu_controller.cpp index 73c81f01d..bf4533456 100644 --- a/apps/code/menu_controller.cpp +++ b/apps/code/menu_controller.cpp @@ -1,6 +1,6 @@ #include "menu_controller.h" #include "app.h" -#include "../i18n.h" +#include #include "../apps_container.h" #include #include @@ -26,7 +26,7 @@ MenuController::MenuController(Responder * parentResponder, App * pythonDelegate m_shouldDisplayAddScriptRow(true) { m_selectableTableView.setMargins(0); - m_selectableTableView.setShowsIndicators(false); + m_selectableTableView.setDecoratorType(ScrollView::Decorator::Type::None); m_addNewScriptCell.setMessage(I18n::Message::AddScript); for (int i = 0; i < k_maxNumberOfDisplayableScriptCells; i++) { m_scriptCells[i].setParentResponder(&m_selectableTableView); @@ -49,7 +49,7 @@ void MenuController::willExitResponderChain(Responder * nextFirstResponder) { TextField * tf = static_cast(m_selectableTableView.selectedCell())->textField(); if (tf->isEditing()) { tf->setEditing(false, false); - textFieldDidAbortEditing(tf); + privateTextFieldDidAbortEditing(tf, false); } } } @@ -69,7 +69,8 @@ void MenuController::didBecomeFirstResponder() { assert(m_selectableTableView.selectedRow() < m_scriptStore->numberOfScripts() + 1); app()->setFirstResponder(&m_selectableTableView); #if EPSILON_GETOPT - if (consoleController()->locked() && consoleController()->loadPythonEnvironment()) { + if (consoleController()->locked()) { + consoleController()->setAutoImport(true); stackViewController()->push(consoleController()); return; } @@ -337,7 +338,7 @@ bool MenuController::textFieldDidFinishEditing(TextField * textField, const char } else if (error == Script::ErrorStatus::NameTaken) { app()->displayWarning(I18n::Message::NameTaken); } else if (error == Script::ErrorStatus::NonCompliantName) { - app()->displayWarning(I18n::Message::NonCompliantName); + app()->displayWarning(I18n::Message::AllowedCharactersaz09, I18n::Message::NameCannotStartWithNumber); } else { assert(error == Script::ErrorStatus::NotEnoughSpaceAvailable); app()->displayWarning(I18n::Message::NameTooLong); @@ -345,34 +346,6 @@ bool MenuController::textFieldDidFinishEditing(TextField * textField, const char return false; } -bool MenuController::textFieldDidAbortEditing(TextField * textField) { - Script script = m_scriptStore->scriptAtIndex(m_selectableTableView.selectedRow()); - const char * scriptName = script.fullName(); - if (strlen(scriptName) <= 1 + strlen(ScriptStore::k_scriptExtension)) { - // The previous text was an empty name. Use a numbered default script name. - char numberedDefaultName[Script::k_defaultScriptNameMaxSize]; - bool foundDefaultName = Script::DefaultName(numberedDefaultName, Script::k_defaultScriptNameMaxSize); - if (!foundDefaultName) { - // If we did not find a default name, delete the script - deleteScript(script); - return true; - } - Script::ErrorStatus error = script.setBaseNameWithExtension(numberedDefaultName, ScriptStore::k_scriptExtension); - scriptName = m_scriptStore->scriptAtIndex(m_selectableTableView.selectedRow()).fullName(); - /* Because we use the numbered default name, the name should not be - * already taken. Plus, the script could be added only if the storage has - * enough available space to add a script named 'script99.py' */ - (void) error; // Silence the "variable unused" warning if assertions are not enabled - assert(error == Script::ErrorStatus::None); - updateAddScriptRowDisplay(); - } - textField->setText(scriptName); - m_selectableTableView.selectCellAtLocation(m_selectableTableView.selectedColumn(), m_selectableTableView.selectedRow()); - app()->setFirstResponder(&m_selectableTableView); - static_cast(const_cast(app()->container()))->setShiftAlphaStatus(Ion::Events::ShiftAlphaStatus::Default); - return true; -} - bool MenuController::textFieldDidHandleEvent(TextField * textField, bool returnValue, bool textSizeDidChange) { int scriptExtensionLength = 1 + strlen(ScriptStore::k_scriptExtension); if (textField->isEditing() && textField->cursorLocation() > textField->draftTextLength() - scriptExtensionLength) { @@ -410,4 +383,39 @@ void MenuController::updateAddScriptRowDisplay() { m_selectableTableView.reloadData(); } +bool MenuController::privateTextFieldDidAbortEditing(TextField * textField, bool menuControllerStaysInResponderChain) { + /* If menuControllerStaysInResponderChain is false, we do not want to use + * methods that might call setFirstResponder, because we might be in the + * middle of another setFirstResponder call. */ + Script script = m_scriptStore->scriptAtIndex(m_selectableTableView.selectedRow()); + const char * scriptName = script.fullName(); + if (strlen(scriptName) <= 1 + strlen(ScriptStore::k_scriptExtension)) { + // The previous text was an empty name. Use a numbered default script name. + char numberedDefaultName[Script::k_defaultScriptNameMaxSize]; + bool foundDefaultName = Script::DefaultName(numberedDefaultName, Script::k_defaultScriptNameMaxSize); + if (!foundDefaultName) { + // If we did not find a default name, delete the script + deleteScript(script); + return true; + } + Script::ErrorStatus error = script.setBaseNameWithExtension(numberedDefaultName, ScriptStore::k_scriptExtension); + scriptName = m_scriptStore->scriptAtIndex(m_selectableTableView.selectedRow()).fullName(); + /* Because we use the numbered default name, the name should not be + * already taken. Plus, the script could be added only if the storage has + * enough available space to add a script named 'script99.py' */ + (void) error; // Silence the "variable unused" warning if assertions are not enabled + assert(error == Script::ErrorStatus::None); + if (menuControllerStaysInResponderChain) { + updateAddScriptRowDisplay(); + } + } + textField->setText(scriptName); + if (menuControllerStaysInResponderChain) { + m_selectableTableView.selectCellAtLocation(m_selectableTableView.selectedColumn(), m_selectableTableView.selectedRow()); + app()->setFirstResponder(&m_selectableTableView); + } + static_cast(const_cast(app()->container()))->setShiftAlphaStatus(Ion::Events::ShiftAlphaStatus::Default); + return true; +} + } diff --git a/apps/code/menu_controller.h b/apps/code/menu_controller.h index b4383549f..7b94014f4 100644 --- a/apps/code/menu_controller.h +++ b/apps/code/menu_controller.h @@ -52,7 +52,9 @@ public: bool textFieldShouldFinishEditing(TextField * textField, Ion::Events::Event event) override; bool textFieldDidReceiveEvent(TextField * textField, Ion::Events::Event event) override; bool textFieldDidFinishEditing(TextField * textField, const char * text, Ion::Events::Event event) override; - bool textFieldDidAbortEditing(TextField * textField) override; + bool textFieldDidAbortEditing(TextField * textField) override { + return privateTextFieldDidAbortEditing(textField, true); + } bool textFieldDidHandleEvent(TextField * textField, bool returnValue, bool textSizeDidChange) override; /* ButtonRowDelegate */ @@ -74,6 +76,7 @@ private: void editScriptAtIndex(int scriptIndex); void numberedDefaultScriptName(char * buffer); void updateAddScriptRowDisplay(); + bool privateTextFieldDidAbortEditing(TextField * textField, bool menuControllerStaysInResponderChain); ScriptStore * m_scriptStore; ScriptNameCell m_scriptCells[k_maxNumberOfDisplayableScriptCells]; EvenOddCellWithEllipsis m_scriptParameterCells[k_maxNumberOfDisplayableScriptCells]; diff --git a/apps/code/sandbox_controller.cpp b/apps/code/sandbox_controller.cpp index ce49f38b4..12b5f0332 100644 --- a/apps/code/sandbox_controller.cpp +++ b/apps/code/sandbox_controller.cpp @@ -20,6 +20,10 @@ void SandboxController::reset() { redrawWindow(); } +void SandboxController::hide() { + stackViewController()->pop(); +} + void SandboxController::viewWillAppear() { assert(m_executionEnvironment != nullptr); m_executionEnvironment->setSandboxIsDisplayed(true); diff --git a/apps/code/sandbox_controller.h b/apps/code/sandbox_controller.h index df0b81999..c50725789 100644 --- a/apps/code/sandbox_controller.h +++ b/apps/code/sandbox_controller.h @@ -14,6 +14,7 @@ public: SandboxController(Responder * parentResponder, MicroPython::ExecutionEnvironment * executionEnvironment); StackViewController * stackViewController(); void reset(); + void hide(); // ViewController View * view() override { return &m_solidColorView; } diff --git a/apps/code/script.cpp b/apps/code/script.cpp index f0d8072bc..1c97a9083 100644 --- a/apps/code/script.cpp +++ b/apps/code/script.cpp @@ -37,17 +37,38 @@ bool Script::DefaultName(char buffer[], size_t bufferSize) { return false; } +bool isSmallLetterOrUnderscoreChar(const char c) { + return (c >= 'a' && c <= 'z') || c == '_'; +} +bool isNumberChar(const char c) { + return c >= '0' && c <= '9'; +} + bool Script::nameCompliant(const char * name) { - /* The name format is [a-z0-9_\.]+ */ - const char * currentChar = name; - while (*currentChar != 0) { - if ((*currentChar >= 'a' && *currentChar <= 'z') || *currentChar == '_' || (*currentChar >= '0' && *currentChar <= '9') || *currentChar == '.') { - currentChar++; - continue; - } + /* We allow here the empty script name ".py", because it is the name used to + * create a new empty script. When naming or renaming a script, we check + * elsewhere that the name is no longer empty. + * The name format is ([a-z_][a-z0-9_]*)*\.py + * + * We do not allow upper cases in the script names because script names are + * used in the URLs of the NumWorks workshop website and we do not want + * problems with case sensitivity. */ + const char * c = name; + if (*c == 0 || (!isSmallLetterOrUnderscoreChar(*c) && *c != '.')) { + /* The name cannot be empty. Its first letter must be in [a-z_] or the + * extension dot. */ return false; } - return name != currentChar; + while (*c != 0) { + if (*c == '.' && strcmp(c+1, ScriptStore::k_scriptExtension) == 0) { + return true; + } + if (!isSmallLetterOrUnderscoreChar(*c) && !isNumberChar(*c)) { + return false; + } + c++; + } + return false; } bool Script::importationStatus() const { diff --git a/apps/code/script_name_cell.h b/apps/code/script_name_cell.h index 08719b3f7..914432114 100644 --- a/apps/code/script_name_cell.h +++ b/apps/code/script_name_cell.h @@ -38,7 +38,7 @@ public: private: constexpr static size_t k_extensionLength = 1+ScriptStore::k_scriptExtensionLength; // '.' + "py" - constexpr static KDCoordinate k_leftMargin = Metric::HistoryHorizontalMargin; + constexpr static KDCoordinate k_leftMargin = Metric::CommonLargeMargin; // View int numberOfSubviews() const override { return 1; } diff --git a/apps/code/script_parameter_controller.h b/apps/code/script_parameter_controller.h index 5d6fc6dcf..1fef1d752 100644 --- a/apps/code/script_parameter_controller.h +++ b/apps/code/script_parameter_controller.h @@ -2,7 +2,7 @@ #define CODE_SCRIPT_PARAMETER_CONTROLLER_H #include -#include "../i18n.h" +#include #include "script_store.h" namespace Code { diff --git a/apps/empty_battery_window.cpp b/apps/empty_battery_window.cpp index bae9b0f00..b0e58e2dd 100644 --- a/apps/empty_battery_window.cpp +++ b/apps/empty_battery_window.cpp @@ -1,6 +1,6 @@ #include "empty_battery_window.h" #include "global_preferences.h" -#include "i18n.h" +#include extern "C" { #include } diff --git a/apps/exam_pop_up_controller.cpp b/apps/exam_pop_up_controller.cpp index 915fcf547..b72bca2b5 100644 --- a/apps/exam_pop_up_controller.cpp +++ b/apps/exam_pop_up_controller.cpp @@ -1,6 +1,6 @@ #include "exam_pop_up_controller.h" #include "apps_container.h" -#include "i18n.h" +#include #include "global_preferences.h" #include diff --git a/apps/global_preferences.h b/apps/global_preferences.h index 07bbd5320..24ff19581 100644 --- a/apps/global_preferences.h +++ b/apps/global_preferences.h @@ -1,7 +1,7 @@ #ifndef APPS_GLOBAL_PREFERENCES_H #define APPS_GLOBAL_PREFERENCES_H -#include "i18n.h" +#include class GlobalPreferences { public: diff --git a/apps/graph/Makefile b/apps/graph/Makefile index 9c631beff..8e7b1ef68 100644 --- a/apps/graph/Makefile +++ b/apps/graph/Makefile @@ -1,27 +1,27 @@ apps += Graph::App app_headers += apps/graph/app.h -app_objs += $(addprefix apps/graph/,\ - app.o\ - storage_cartesian_function_store.o\ - graph/banner_view.o\ - graph/calculation_graph_controller.o\ - graph/calculation_parameter_controller.o\ - graph/curve_parameter_controller.o\ - graph/extremum_graph_controller.o\ - graph/graph_controller.o\ - graph/graph_controller_helper.o\ - graph/graph_view.o\ - graph/integral_graph_controller.o\ - graph/intersection_graph_controller.o\ - graph/root_graph_controller.o\ - graph/tangent_graph_controller.o\ - list/list_parameter_controller.o\ - list/storage_list_controller.o\ - list/text_field_function_title_cell.o\ - values/storage_derivative_parameter_controller.o\ - values/storage_function_parameter_controller.o\ - values/storage_values_controller.o\ +app_src += $(addprefix apps/graph/,\ + app.cpp \ + storage_cartesian_function_store.cpp \ + graph/banner_view.cpp \ + graph/calculation_graph_controller.cpp \ + graph/calculation_parameter_controller.cpp \ + graph/curve_parameter_controller.cpp \ + graph/extremum_graph_controller.cpp \ + graph/graph_controller.cpp \ + graph/graph_controller_helper.cpp \ + graph/graph_view.cpp \ + graph/integral_graph_controller.cpp \ + graph/intersection_graph_controller.cpp \ + graph/root_graph_controller.cpp \ + graph/tangent_graph_controller.cpp \ + list/list_parameter_controller.cpp \ + list/storage_list_controller.cpp \ + list/text_field_function_title_cell.cpp \ + values/storage_derivative_parameter_controller.cpp \ + values/storage_function_parameter_controller.cpp \ + values/storage_values_controller.cpp \ ) i18n_files += $(addprefix apps/graph/,\ @@ -32,4 +32,4 @@ i18n_files += $(addprefix apps/graph/,\ base.pt.i18n\ ) -app_images += apps/graph/graph_icon.png +$(eval $(call depends_on_image,apps/graph/app.cpp,apps/graph/graph_icon.png)) diff --git a/apps/graph/app.cpp b/apps/graph/app.cpp index 82ffd6d16..c150b7f97 100644 --- a/apps/graph/app.cpp +++ b/apps/graph/app.cpp @@ -1,7 +1,7 @@ #include "app.h" #include "../apps_container.h" #include "graph_icon.h" -#include "../i18n.h" +#include using namespace Poincare; using namespace Shared; diff --git a/apps/graph/base.de.i18n b/apps/graph/base.de.i18n index 108ebd0a7..ed06c9cbc 100644 --- a/apps/graph/base.de.i18n +++ b/apps/graph/base.de.i18n @@ -21,4 +21,3 @@ DerivativeFunctionColumn = "Spalte der Ableitungsfunktion" HideDerivativeColumn = "Ableitungsfunktion ausblenden" AllowedCharactersAZaz09 = "Erlaubte Zeichen: A-Z, a-z, 0-9, _" ReservedName = "Reserviertes Wort" -NameCannotStartWithNumber = "Ein name darf nicht mit einer Zahl beginnen" diff --git a/apps/graph/base.en.i18n b/apps/graph/base.en.i18n index 36f2820dd..34847cd07 100644 --- a/apps/graph/base.en.i18n +++ b/apps/graph/base.en.i18n @@ -21,4 +21,3 @@ DerivativeFunctionColumn = "Derivative function column" HideDerivativeColumn = "Hide the derivative function" AllowedCharactersAZaz09 = "Allowed characters: A-Z, a-z, 0-9, _" ReservedName = "Reserved name" -NameCannotStartWithNumber = "A name cannot start with a number" diff --git a/apps/graph/base.es.i18n b/apps/graph/base.es.i18n index 6386083e2..23d2e66a6 100644 --- a/apps/graph/base.es.i18n +++ b/apps/graph/base.es.i18n @@ -21,4 +21,3 @@ DerivativeFunctionColumn = "Columna de la derivada" HideDerivativeColumn = "Ocultar la derivada" AllowedCharactersAZaz09 = "Caracteres permitidos : A-Z, a-z, 0-9, _" ReservedName = "Nombre reservado" -NameCannotStartWithNumber = "Un nombre no puede empezar con un número" diff --git a/apps/graph/base.fr.i18n b/apps/graph/base.fr.i18n index b27e0ee06..4d3a06807 100644 --- a/apps/graph/base.fr.i18n +++ b/apps/graph/base.fr.i18n @@ -21,4 +21,3 @@ DerivativeFunctionColumn = "Colonne de la fonction derivee" HideDerivativeColumn = "Masquer la fonction derivee" AllowedCharactersAZaz09 = "Caractères autorisés : A-Z, a-z, 0-9, _" ReservedName = "Nom réservé" -NameCannotStartWithNumber = "Un nom ne peut pas commencer par un chiffre" diff --git a/apps/graph/base.pt.i18n b/apps/graph/base.pt.i18n index 1c702aee8..693d1c8ca 100644 --- a/apps/graph/base.pt.i18n +++ b/apps/graph/base.pt.i18n @@ -21,4 +21,3 @@ DerivativeFunctionColumn = "Coluna da funcao derivada" HideDerivativeColumn = "Esconder funcao derivada" AllowedCharactersAZaz09 = "Caracteres permitidos : A-Z, a-z, 0-9, _" ReservedName = "Nome reservado" -NameCannotStartWithNumber = "Um nome não pode começar com um número" diff --git a/apps/graph/graph/banner_view.cpp b/apps/graph/graph/banner_view.cpp index aebd501d1..0ce02d0b2 100644 --- a/apps/graph/graph/banner_view.cpp +++ b/apps/graph/graph/banner_view.cpp @@ -1,5 +1,5 @@ #include "banner_view.h" -#include "../../i18n.h" +#include namespace Graph { diff --git a/apps/graph/graph/calculation_parameter_controller.h b/apps/graph/graph/calculation_parameter_controller.h index a875c759e..9c483122a 100644 --- a/apps/graph/graph/calculation_parameter_controller.h +++ b/apps/graph/graph/calculation_parameter_controller.h @@ -10,7 +10,7 @@ #include "root_graph_controller.h" #include "graph_view.h" #include "banner_view.h" -#include "../../i18n.h" +#include namespace Graph { diff --git a/apps/graph/graph/curve_parameter_controller.cpp b/apps/graph/graph/curve_parameter_controller.cpp index e5e9da038..2361724e5 100644 --- a/apps/graph/graph/curve_parameter_controller.cpp +++ b/apps/graph/graph/curve_parameter_controller.cpp @@ -1,6 +1,6 @@ #include "curve_parameter_controller.h" #include "graph_controller.h" -#include "../../i18n.h" +#include #include using namespace Shared; diff --git a/apps/graph/graph/graph_controller.cpp b/apps/graph/graph/graph_controller.cpp index 605dea77f..caf46381a 100644 --- a/apps/graph/graph/graph_controller.cpp +++ b/apps/graph/graph/graph_controller.cpp @@ -5,6 +5,8 @@ using namespace Shared; namespace Graph { +static inline float max(float x, float y) { return (x>y ? x : y); } + GraphController::GraphController(Responder * parentResponder, InputEventHandlerDelegate * inputEventHandlerDelegate, StorageCartesianFunctionStore * functionStore, Shared::InteractiveCurveViewRange * curveViewRange, CurveViewCursor * cursor, int * indexFunctionSelectedByCursor, uint32_t * modelVersion, uint32_t * rangeVersion, Poincare::Preferences::AngleUnit * angleUnitVersion, ButtonRowController * header) : StorageFunctionGraphController(parentResponder, inputEventHandlerDelegate, header, curveViewRange, &m_view, cursor, indexFunctionSelectedByCursor, modelVersion, rangeVersion, angleUnitVersion), m_bannerView(), @@ -37,17 +39,17 @@ void GraphController::setDisplayDerivativeInBanner(bool displayDerivative) { m_displayDerivativeInBanner = displayDerivative; } -float GraphController::interestingXRange() { +float GraphController::interestingXHalfRange() const { float characteristicRange = 0.0f; TextFieldDelegateApp * myApp = (TextFieldDelegateApp *)app(); for (int i = 0; i < functionStore()->numberOfActiveFunctions(); i++) { ExpiringPointer f = functionStore()->modelForRecord(functionStore()->activeRecordAtIndex(i)); float fRange = f->expressionReduced(myApp->localContext()).characteristicXRange(*(myApp->localContext()), Poincare::Preferences::sharedPreferences()->angleUnit()); if (!std::isnan(fRange)) { - characteristicRange = fRange > characteristicRange ? fRange : characteristicRange; + characteristicRange = max(fRange, characteristicRange); } } - return (characteristicRange > 0.0f ? 1.6f*characteristicRange : 10.0f); + return (characteristicRange > 0.0f ? 1.6f*characteristicRange : InteractiveCurveViewRangeDelegate::interestingXHalfRange()); } int GraphController::estimatedBannerNumberOfLines() const { diff --git a/apps/graph/graph/graph_controller.h b/apps/graph/graph/graph_controller.h index d14ee8741..58586d727 100644 --- a/apps/graph/graph/graph_controller.h +++ b/apps/graph/graph/graph_controller.h @@ -20,7 +20,7 @@ public: void viewWillAppear() override; bool displayDerivativeInBanner() const; void setDisplayDerivativeInBanner(bool displayDerivative); - float interestingXRange() override; + float interestingXHalfRange() const override; private: int estimatedBannerNumberOfLines() const override; void selectFunctionWithCursor(int functionIndex) override; diff --git a/apps/graph/list/storage_list_controller.cpp b/apps/graph/list/storage_list_controller.cpp index 6a4f94827..aee95cac7 100644 --- a/apps/graph/list/storage_list_controller.cpp +++ b/apps/graph/list/storage_list_controller.cpp @@ -1,6 +1,6 @@ #include "storage_list_controller.h" #include "../app.h" -#include "../../i18n.h" +#include #include #include #include @@ -33,13 +33,20 @@ const char * StorageListController::title() { void StorageListController::renameSelectedFunction() { assert(selectedColumn() == 0); assert(selectedRow() >= 0 && selectedRow() < numberOfRows()-1); // TODO change if sometimes the addFunction row is not displayed + + // Increase the size of the name column + computeTitlesColumnWidth(true); + selectableTableView()->reloadData(); + static_cast(const_cast(app()->container()))->setShiftAlphaStatus(Ion::Events::ShiftAlphaStatus::AlphaLock); TextFieldFunctionTitleCell * selectedTitleCell = (TextFieldFunctionTitleCell *)(selectableTableView()->selectedCell()); + selectedTitleCell->setHorizontalAlignment(1.0f); app()->setFirstResponder(selectedTitleCell); selectedTitleCell->setEditing(true); } bool StorageListController::textFieldDidFinishEditing(TextField * textField, const char * text, Ion::Events::Event event) { + assert(textField != nullptr); // Compute the new name size_t textLength = strlen(text); size_t argumentLength = StorageFunction::k_parenthesedArgumentLength; @@ -107,6 +114,10 @@ bool StorageListController::textFieldDidFinishEditing(TextField * textField, con } bool StorageListController::textFieldDidAbortEditing(TextField * textField) { + assert(textField != nullptr); + // Put the name column back to normal size + computeTitlesColumnWidth(); + selectableTableView()->reloadData(); ExpiringPointer function = modelStore()->modelForRecord(modelStore()->recordAtIndex(selectedRow())); setFunctionNameInTextField(function, textField); m_selectableTableView.selectedCell()->setHighlighted(true); @@ -120,6 +131,7 @@ bool StorageListController::textFieldShouldFinishEditing(TextField * textField, } bool StorageListController::textFieldDidReceiveEvent(TextField * textField, Ion::Events::Event event) { + assert(textField != nullptr); if (textField->isEditing() && textField->shouldFinishEditing(event)) { return false; } @@ -145,8 +157,14 @@ HighlightCell * StorageListController::expressionCells(int index) { } void StorageListController::willDisplayTitleCellAtIndex(HighlightCell * cell, int j) { + assert(cell != nullptr); + assert(j >= 0 && j < modelStore()->numberOfModels()); TextFieldFunctionTitleCell * titleCell = static_cast(cell); + // Update the corresponding expression cell in order to get the baseline + StorageExpressionModelListController::willDisplayExpressionCellAtIndex(m_selectableTableView.cellAtLocation(1, j), j); + titleCell->setBaseline(baseline(j)); if (!titleCell->isEditing()) { + // Set name and color if the name is not being edited ExpiringPointer function = modelStore()->modelForRecord(modelStore()->recordAtIndex(j)); setFunctionNameInTextField(function, titleCell->textField()); KDColor functionNameColor = function->isActive() ? function->color() : Palette::GreyDark; @@ -155,6 +173,8 @@ void StorageListController::willDisplayTitleCellAtIndex(HighlightCell * cell, in } void StorageListController::willDisplayExpressionCellAtIndex(HighlightCell * cell, int j) { + assert(cell != nullptr); + assert(j >= 0 && j < modelStore()->numberOfModels()); Shared::StorageFunctionListController::willDisplayExpressionCellAtIndex(cell, j); FunctionExpressionCell * myCell = (FunctionExpressionCell *)cell; ExpiringPointer f = modelStore()->modelForRecord(modelStore()->recordAtIndex(j)); @@ -163,9 +183,20 @@ void StorageListController::willDisplayExpressionCellAtIndex(HighlightCell * cel } void StorageListController::setFunctionNameInTextField(ExpiringPointer function, TextField * textField) { + assert(textField != nullptr); char bufferName[BufferTextView::k_maxNumberOfChar]; function->nameWithArgument(bufferName, BufferTextView::k_maxNumberOfChar, modelStore()->symbol()); textField->setText(bufferName); } +KDCoordinate StorageListController::privateBaseline(int j) const { + assert(j >= 0 && j < const_cast(this)->modelStore()->numberOfModels()); + Shared::FunctionExpressionCell * cell = static_cast((const_cast(&m_selectableTableView))->cellAtLocation(1, j)); + Poincare::Layout layout = cell->layout(); + if (layout.isUninitialized()) { + return -1; + } + return 0.5*(const_cast(this)->rowHeight(j)-layout.layoutSize().height())+layout.baseline(); +} + } diff --git a/apps/graph/list/storage_list_controller.h b/apps/graph/list/storage_list_controller.h index eabe0c52f..a8c65d9fa 100644 --- a/apps/graph/list/storage_list_controller.h +++ b/apps/graph/list/storage_list_controller.h @@ -33,6 +33,7 @@ private: return static_cast(app()); } void setFunctionNameInTextField(Shared::ExpiringPointer function, TextField * textField); + KDCoordinate privateBaseline(int j) const override; TextFieldFunctionTitleCell m_functionTitleCells[k_maxNumberOfDisplayableRows]; Shared::FunctionExpressionCell m_expressionCells[k_maxNumberOfDisplayableRows]; ListParameterController m_parameterController; diff --git a/apps/graph/list/text_field_function_title_cell.cpp b/apps/graph/list/text_field_function_title_cell.cpp index 4cd6dd5b7..405053675 100644 --- a/apps/graph/list/text_field_function_title_cell.cpp +++ b/apps/graph/list/text_field_function_title_cell.cpp @@ -4,11 +4,15 @@ namespace Graph { +static inline float min(float x, float y) { return (xy ? x : y); } + TextFieldFunctionTitleCell::TextFieldFunctionTitleCell(StorageListController * listController, Orientation orientation, const KDFont * font) : Shared::FunctionTitleCell(orientation), Responder(listController), - m_textField(Shared::StorageFunction::k_parenthesedArgumentLength, this, m_textFieldBuffer, m_textFieldBuffer, k_textFieldBufferSize, nullptr, listController, false, font, 0.5f, 0.5f) - {} + m_textField(Shared::StorageFunction::k_parenthesedArgumentLength, this, m_textFieldBuffer, m_textFieldBuffer, k_textFieldBufferSize, nullptr, listController, false, font, 1.0f, 0.5f) +{ +} void TextFieldFunctionTitleCell::setHighlighted(bool highlight) { EvenOddCell::setHighlighted(highlight); @@ -44,8 +48,21 @@ void TextFieldFunctionTitleCell::setText(const char * title) { m_textField.setText(title); } +void TextFieldFunctionTitleCell::setHorizontalAlignment(float alignment) { + assert(alignment >= 0.0f && alignment <= 1.0f); + m_textField.setAlignment(alignment, verticalAlignment()); +} + void TextFieldFunctionTitleCell::layoutSubviews() { - m_textField.setFrame(textFieldFrame()); + KDRect frame = subviewFrame(); + m_textField.setFrame(frame); + KDCoordinate maxTextFieldX = frame.width() - m_textField.minimalSizeForOptimalDisplay().width(); + float horizontalAlignment = max( + 0.0f, + min( + 1.0f, + ((float)(maxTextFieldX - k_textFieldRightMargin))/((float)maxTextFieldX))); + m_textField.setAlignment(horizontalAlignment, verticalAlignment()); } void TextFieldFunctionTitleCell::didBecomeFirstResponder() { @@ -54,12 +71,10 @@ void TextFieldFunctionTitleCell::didBecomeFirstResponder() { } } -KDRect TextFieldFunctionTitleCell::textFieldFrame() const { - KDRect textFrame(0, k_colorIndicatorThickness, bounds().width(), bounds().height() - k_colorIndicatorThickness); - if (m_orientation == Orientation::VerticalIndicator){ - textFrame = KDRect(k_colorIndicatorThickness, 0, bounds().width() - k_colorIndicatorThickness-k_separatorThickness, bounds().height()-k_separatorThickness); - } - return textFrame; +float TextFieldFunctionTitleCell::verticalAlignmentGivenExpressionBaselineAndRowHeight(KDCoordinate expressionBaseline, KDCoordinate rowHeight) const { + assert(m_orientation == Orientation::VerticalIndicator); + KDCoordinate glyphHeight = font()->glyphSize().height(); + return ((float)(expressionBaseline - glyphHeight/2))/((float)rowHeight+1-glyphHeight); } } diff --git a/apps/graph/list/text_field_function_title_cell.h b/apps/graph/list/text_field_function_title_cell.h index f719668d6..3a8771ba2 100644 --- a/apps/graph/list/text_field_function_title_cell.h +++ b/apps/graph/list/text_field_function_title_cell.h @@ -16,6 +16,7 @@ public: void setEditing(bool editing); bool isEditing() const; void setText(const char * textContent); + void setHorizontalAlignment(float alignment); // FunctionTitleCell void setColor(KDColor color) override; @@ -38,10 +39,10 @@ public: // Responder void didBecomeFirstResponder() override; -protected: - KDRect textFieldFrame() const; private: + constexpr static KDCoordinate k_textFieldRightMargin = 4; constexpr static int k_textFieldBufferSize = Shared::StorageFunction::k_maxNameWithArgumentSize; + float verticalAlignmentGivenExpressionBaselineAndRowHeight(KDCoordinate expressionBaseline, KDCoordinate rowHeight) const override; Shared::TextFieldWithExtension m_textField; char m_textFieldBuffer[k_textFieldBufferSize]; }; diff --git a/apps/graph/values/storage_function_parameter_controller.cpp b/apps/graph/values/storage_function_parameter_controller.cpp index 667ab51a0..737cafada 100644 --- a/apps/graph/values/storage_function_parameter_controller.cpp +++ b/apps/graph/values/storage_function_parameter_controller.cpp @@ -19,7 +19,9 @@ bool StorageFunctionParameterController::handleEvent(Ion::Events::Event event) { switch (selectedRow()) { case 0: { - function()->setDisplayDerivative(!function()->displayDerivative()); + bool isDisplayingDerivative = function()->displayDerivative(); + function()->setDisplayDerivative(!isDisplayingDerivative); + m_valuesController->selectCellAtLocation(isDisplayingDerivative ? m_selectedFunctionColumn : m_selectedFunctionColumn + 1, m_valuesController->selectedRow()); m_selectableTableView.reloadData(); return true; } @@ -57,9 +59,7 @@ int StorageFunctionParameterController::reusableCellCount() { void StorageFunctionParameterController::viewWillAppear() { StorageValuesFunctionParameterController::viewWillAppear(); - if (function()->displayDerivative()) { - m_valuesController->selectCellAtLocation(m_valuesController->selectedColumn()+1, m_valuesController->selectedRow()); - } + m_selectedFunctionColumn = m_valuesController->selectedColumn(); } void StorageFunctionParameterController::willDisplayCellForIndex(HighlightCell * cell, int index) { diff --git a/apps/graph/values/storage_function_parameter_controller.h b/apps/graph/values/storage_function_parameter_controller.h index 205320927..c629c73b6 100644 --- a/apps/graph/values/storage_function_parameter_controller.h +++ b/apps/graph/values/storage_function_parameter_controller.h @@ -27,6 +27,8 @@ private: #endif MessageTableCellWithSwitch m_displayDerivativeColumn; StorageValuesController * m_valuesController; + // Index of the column corresponding to the function in the values controller + int m_selectedFunctionColumn; }; } diff --git a/apps/hardware_test/Makefile b/apps/hardware_test/Makefile index 209104e22..f6a490b0d 100644 --- a/apps/hardware_test/Makefile +++ b/apps/hardware_test/Makefile @@ -1,15 +1,15 @@ -app_objs += $(addprefix apps/hardware_test/,\ - app.o\ - arrow_view.o\ - battery_test_controller.o\ - code_128b_view.o\ - keyboard_test_controller.o\ - keyboard_view.o\ - led_test_controller.o\ - pattern.o\ - pattern_view.o\ - pop_up_controller.o\ - screen_test_controller.o\ - screen_test_controller.o\ - serial_number_controller.o\ +app_src += $(addprefix apps/hardware_test/,\ + app.cpp \ + arrow_view.cpp \ + battery_test_controller.cpp \ + code_128b_view.cpp \ + keyboard_test_controller.cpp \ + keyboard_view.cpp \ + led_test_controller.cpp \ + pattern.cpp \ + pattern_view.cpp \ + pop_up_controller.cpp \ + screen_test_controller.cpp \ + screen_test_controller.cpp \ + serial_number_controller.cpp \ ) diff --git a/apps/hardware_test/pop_up_controller.cpp b/apps/hardware_test/pop_up_controller.cpp index 92905900c..791846dcf 100644 --- a/apps/hardware_test/pop_up_controller.cpp +++ b/apps/hardware_test/pop_up_controller.cpp @@ -1,5 +1,5 @@ #include "pop_up_controller.h" -#include "../i18n.h" +#include #include "../apps_container.h" #include @@ -41,7 +41,9 @@ PopUpController::ContentView::ContentView(Responder * parentResponder) : m_okButton(this, I18n::Message::Ok, Invocation([](void * context, void * sender) { PopUpController::ContentView * view = (PopUpController::ContentView *)context; AppsContainer * appsContainer = (AppsContainer *)view->app()->container(); - appsContainer->switchTo(appsContainer->hardwareTestAppSnapshot()); + bool switched = appsContainer->switchTo(appsContainer->hardwareTestAppSnapshot()); + assert(switched); + (void) switched; // Silence compilation warning about unused variable. return true; }, this), KDFont::SmallFont), m_warningTextView(KDFont::SmallFont, I18n::Message::Warning, 0.5, 0.5, KDColorWhite, KDColorBlack), diff --git a/apps/home/Makefile b/apps/home/Makefile index ac3e309e1..b6c5428b9 100644 --- a/apps/home/Makefile +++ b/apps/home/Makefile @@ -1,13 +1,13 @@ -app_objs += $(addprefix apps/home/,\ - app.o\ - app_cell.o\ - controller.o\ +app_src += $(addprefix apps/home/,\ + app.cpp \ + app_cell.cpp \ + controller.cpp \ ) i18n_files += $(addprefix apps/home/,\ - base.de.i18n\ - base.en.i18n\ - base.es.i18n\ - base.fr.i18n\ - base.pt.i18n\ + base.de.i18n \ + base.en.i18n \ + base.es.i18n \ + base.fr.i18n \ + base.pt.i18n \ ) diff --git a/apps/home/app.cpp b/apps/home/app.cpp index 6404f410a..b7fa2ff0b 100644 --- a/apps/home/app.cpp +++ b/apps/home/app.cpp @@ -1,5 +1,5 @@ #include "app.h" -#include "../i18n.h" +#include #include "../apps_container.h" extern "C" { diff --git a/apps/home/controller.cpp b/apps/home/controller.cpp index 37857cb24..10f0075a5 100644 --- a/apps/home/controller.cpp +++ b/apps/home/controller.cpp @@ -11,9 +11,8 @@ Controller::ContentView::ContentView(Controller * controller, SelectableTableVie { m_selectableTableView.setVerticalCellOverlap(0); m_selectableTableView.setMargins(0, k_sideMargin, k_bottomMargin, k_sideMargin); - m_selectableTableView.setColorsBackground(false); - m_selectableTableView.setIndicatorThickness(k_indicatorThickness); - m_selectableTableView.verticalScrollIndicator()->setMargin(k_indicatorMargin); + m_selectableTableView.setBackgroundColor(KDColorWhite); + static_cast(m_selectableTableView.decorator())->verticalBar()->setMargin(k_indicatorMargin); } SelectableTableView * Controller::ContentView::selectableTableView() { @@ -58,7 +57,9 @@ Controller::Controller(Responder * parentResponder, ::AppsContainer * container, bool Controller::handleEvent(Ion::Events::Event event) { if (event == Ion::Events::OK || event == Ion::Events::EXE) { - m_container->switchTo(m_container->appSnapshotAtIndex(m_selectionDataSource->selectedRow()*k_numberOfColumns+m_selectionDataSource->selectedColumn()+1)); + bool switched = m_container->switchTo(m_container->appSnapshotAtIndex(m_selectionDataSource->selectedRow()*k_numberOfColumns+m_selectionDataSource->selectedColumn()+1)); + assert(switched); + (void) switched; // Silence compilation warning about unused variable. return true; } diff --git a/apps/home/controller.h b/apps/home/controller.h index 183dbda07..ae3be69e5 100644 --- a/apps/home/controller.h +++ b/apps/home/controller.h @@ -43,7 +43,6 @@ private: AppsContainer * m_container; static constexpr KDCoordinate k_sideMargin = 4; static constexpr KDCoordinate k_bottomMargin = 14; - static constexpr KDCoordinate k_indicatorThickness = 15; static constexpr KDCoordinate k_indicatorMargin = 61; static constexpr int k_numberOfColumns = 3; static constexpr int k_maxNumberOfCells = 16; diff --git a/apps/i18n.py b/apps/i18n.py index f83321a01..93ec034c9 100644 --- a/apps/i18n.py +++ b/apps/i18n.py @@ -116,7 +116,7 @@ def print_header(data, path, locales): def print_implementation(data, path, locales): f = open(path, 'w') f.write("#include \"i18n.h\"\n") - f.write("#include \"global_preferences.h\"\n") + f.write("#include \n") f.write("#include \n\n"); f.write("namespace I18n {\n\n") diff --git a/apps/on_boarding/Makefile b/apps/on_boarding/Makefile index b11b5d216..c013f074e 100644 --- a/apps/on_boarding/Makefile +++ b/apps/on_boarding/Makefile @@ -1,9 +1,9 @@ -app_objs += $(addprefix apps/on_boarding/,\ - app.o\ - language_controller.o\ - logo_controller.o\ - logo_view.o\ - pop_up_controller.o\ +app_src += $(addprefix apps/on_boarding/,\ + app.cpp \ + language_controller.cpp \ + logo_controller.cpp \ + logo_view.cpp \ + pop_up_controller.cpp \ ) i18n_files += $(addprefix apps/on_boarding/,\ @@ -14,5 +14,4 @@ i18n_files += $(addprefix apps/on_boarding/,\ base.pt.i18n\ ) -app_images += apps/on_boarding/logo_icon.png - +$(eval $(call depends_on_image,apps/on_boarding/logo_view.cpp,apps/on_boarding/logo_icon.png)) diff --git a/apps/on_boarding/logo_controller.cpp b/apps/on_boarding/logo_controller.cpp index 9fa2f10cb..3068da16c 100644 --- a/apps/on_boarding/logo_controller.cpp +++ b/apps/on_boarding/logo_controller.cpp @@ -1,5 +1,4 @@ #include "logo_controller.h" -#include "logo_icon.h" namespace OnBoarding { diff --git a/apps/on_boarding/pop_up_controller.cpp b/apps/on_boarding/pop_up_controller.cpp index ccc1e84e6..d9596863b 100644 --- a/apps/on_boarding/pop_up_controller.cpp +++ b/apps/on_boarding/pop_up_controller.cpp @@ -55,7 +55,9 @@ bool PopUpController::handleEvent(Ion::Events::Event event) { app()->dismissModalViewController(); AppsContainer * appsContainer = (AppsContainer *)app()->container(); if (appsContainer->activeApp()->snapshot() == appsContainer->onBoardingAppSnapshot()) { - appsContainer->switchTo(appsContainer->appSnapshotAtIndex(0)); + bool switched = appsContainer->switchTo(appsContainer->appSnapshotAtIndex(0)); + assert(switched); + (void) switched; // Silence compilation warning about unused variable. } return true; } diff --git a/apps/on_boarding/pop_up_controller.h b/apps/on_boarding/pop_up_controller.h index 52d26b398..38eac9a73 100644 --- a/apps/on_boarding/pop_up_controller.h +++ b/apps/on_boarding/pop_up_controller.h @@ -2,7 +2,7 @@ #define ON_BOARDING_POP_UP_CONTROLLER_H #include -#include "../i18n.h" +#include #include "../shared/message_view.h" #include "../shared/ok_view.h" diff --git a/apps/picview/Makefile b/apps/picview/Makefile deleted file mode 100644 index f08668d38..000000000 --- a/apps/picview/Makefile +++ /dev/null @@ -1,18 +0,0 @@ -app_objs += $(addprefix apps/picview/,\ - pic_view.o\ - picview_app.o\ - picview_controller.o\ -) - -apps/picview/pic_view.cpp: apps/picview/image.c - -apps/picview/image.c: apps/picview/image.raw - @echo "RAW2C $@" - @echo "const /* Needed otherwise the image will eat up all RAM */" > $@ - @xxd -i $^ >> $@ - -apps/picview/image.raw: apps/picview/image.png - @echo "PNG2RAW $@" - @ffmpeg -loglevel panic -vcodec png -i $^ -vcodec rawvideo -f rawvideo -pix_fmt rgb565 $@ - -products += $(addprefix apps/picview/, image.raw image.c) diff --git a/apps/picview/pic_view.cpp b/apps/picview/pic_view.cpp deleted file mode 100644 index a12779f97..000000000 --- a/apps/picview/pic_view.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include "pic_view.h" -#include - -PicView::PicView() : - View() { -} - -#include "image.c" - -void PicView::drawRect(KDContext * ctx, KDRect rect) const { - KDColor * pixels = (KDColor *)apps_picview_image_raw; - assert(apps_picview_image_raw_len == bounds().width() * bounds().height() * sizeof(KDColor)); - ctx->fillRectWithPixels(bounds(), pixels, nullptr); -} diff --git a/apps/picview/pic_view.h b/apps/picview/pic_view.h deleted file mode 100644 index 2beb68f96..000000000 --- a/apps/picview/pic_view.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef PICVIEW_PIC_VIEW_H -#define PICVIEW_PIC_VIEW_H - -#include - -class PicView : public View { -public: - PicView(); - void drawRect(KDContext * ctx, KDRect rect) const override; -}; - -#endif diff --git a/apps/picview/picview_app.cpp b/apps/picview/picview_app.cpp deleted file mode 100644 index 09e94215a..000000000 --- a/apps/picview/picview_app.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "picview_app.h" - -PicViewApp::PicViewApp(Container * container) : - ::App(container, &m_picViewController), - m_picViewController(PicViewController(&m_modalViewController)) -{ -} diff --git a/apps/picview/picview_app.h b/apps/picview/picview_app.h deleted file mode 100644 index 8040b0f25..000000000 --- a/apps/picview/picview_app.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef PICVIEW_PICVIEW_APP_H -#define PICVIEW_PICVIEW_APP_H - -#include -#include "picview_controller.h" - -class PicViewApp : public App { -public: - PicViewApp(Container * container); -private: - PicViewController m_picViewController; -}; - -#endif diff --git a/apps/picview/picview_controller.cpp b/apps/picview/picview_controller.cpp deleted file mode 100644 index b4c6610ea..000000000 --- a/apps/picview/picview_controller.cpp +++ /dev/null @@ -1,18 +0,0 @@ -#include "picview_controller.h" - -PicViewController::PicViewController(Responder * parentResponder) : - ViewController(parentResponder), - m_view(PicView()) -{ -} - -View * PicViewController::view() { - return &m_view; -} - -/* - -const char * PicViewController::title() { - return "PicView"; -} -*/ diff --git a/apps/picview/picview_controller.h b/apps/picview/picview_controller.h deleted file mode 100644 index 846596aa2..000000000 --- a/apps/picview/picview_controller.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef PICVIEW_PICVIEW_CONTROLLER_H -#define PICVIEW_PICVIEW_CONTROLLER_H - -#include -#include "pic_view.h" - -class PicViewController : public ViewController { -public: - PicViewController(Responder * parentResponder); - View * view() override; -private: - PicView m_view; -}; - -#endif diff --git a/apps/probability/Makefile b/apps/probability/Makefile index 7b0765032..2087afa1c 100644 --- a/apps/probability/Makefile +++ b/apps/probability/Makefile @@ -1,31 +1,30 @@ apps += Probability::App app_headers += apps/probability/app.h -app_objs += $(addprefix apps/probability/,\ - app.o\ - calculation/calculation.o\ - calculation/discrete_calculation.o\ - calculation/left_integral_calculation.o\ - calculation/right_integral_calculation.o\ - calculation/finite_integral_calculation.o\ - calculation_controller.o\ - calculation_cell.o\ - calculation_type_controller.o\ - cell.o\ - image_cell.o\ - law/binomial_law.o\ - law/erf_inv.o\ - law/exponential_law.o\ - law/law.o\ - law/normal_law.o\ - law/one_parameter_law.o\ - law/poisson_law.o\ - law/two_parameter_law.o\ - law/uniform_law.o\ - law_controller.o\ - law_curve_view.o\ - parameters_controller.o\ - responder_image_cell.o\ +app_src += $(addprefix apps/probability/,\ + app.cpp \ + calculation/calculation.cpp \ + calculation/discrete_calculation.cpp \ + calculation/left_integral_calculation.cpp \ + calculation/right_integral_calculation.cpp \ + calculation/finite_integral_calculation.cpp \ + calculation_controller.cpp \ + calculation_cell.cpp \ + calculation_type_controller.cpp \ + cell.cpp \ + image_cell.cpp \ + law/binomial_law.cpp \ + law/erf_inv.cpp \ + law/exponential_law.cpp \ + law/law.cpp \ + law/normal_law.cpp \ + law/poisson_law.cpp \ + law/two_parameter_law.cpp \ + law/uniform_law.cpp \ + law_controller.cpp \ + law_curve_view.cpp \ + parameters_controller.cpp \ + responder_image_cell.cpp \ ) i18n_files += $(addprefix apps/probability/,\ @@ -40,25 +39,30 @@ tests += $(addprefix apps/probability/test/,\ erf_inv.cpp\ ) -app_images += apps/probability/probability_icon.png +# Image dependencies -app_images += $(addprefix apps/probability/images/,\ - binomial_icon.png\ - calcul1_icon.png\ - calcul2_icon.png\ - calcul3_icon.png\ - calcul4_icon.png\ - exponential_icon.png\ - focused_binomial_icon.png\ - focused_calcul1_icon.png\ - focused_calcul2_icon.png\ - focused_calcul3_icon.png\ - focused_calcul4_icon.png\ - focused_exponential_icon.png\ - focused_normal_icon.png\ - focused_poisson_icon.png\ - focused_uniform_icon.png\ - normal_icon.png\ - poisson_icon.png\ - uniform_icon.png\ -) +$(eval $(call depends_on_image,apps/probability/app.cpp,apps/probability/probability_icon.png)) + +$(eval $(call depends_on_image,apps/probability/law_controller.cpp,$(addprefix apps/probability/images/, \ + binomial_icon.png \ + exponential_icon.png \ + focused_binomial_icon.png \ + focused_exponential_icon.png \ + focused_normal_icon.png \ + focused_poisson_icon.png \ + focused_uniform_icon.png \ + normal_icon.png \ + poisson_icon.png \ + uniform_icon.png \ +))) + +$(eval $(call depends_on_image,$(addprefix apps/probability/,calculation_type_controller.cpp calculation_controller.cpp),$(addprefix apps/probability/images/, \ + calcul1_icon.png \ + calcul2_icon.png \ + calcul3_icon.png \ + calcul4_icon.png \ + focused_calcul1_icon.png \ + focused_calcul2_icon.png \ + focused_calcul3_icon.png \ + focused_calcul4_icon.png \ +))) diff --git a/apps/probability/app.cpp b/apps/probability/app.cpp index fcc3cbef1..ae2e2f693 100644 --- a/apps/probability/app.cpp +++ b/apps/probability/app.cpp @@ -1,5 +1,5 @@ #include "app.h" -#include "../i18n.h" +#include #include "probability_icon.h" #include diff --git a/apps/probability/calculation/calculation.cpp b/apps/probability/calculation/calculation.cpp index 69da88e76..80c5f1a6c 100644 --- a/apps/probability/calculation/calculation.cpp +++ b/apps/probability/calculation/calculation.cpp @@ -4,11 +4,6 @@ namespace Probability { -Calculation::Calculation(): - m_law(nullptr) -{ -} - void Calculation::setLaw(Law * law) { m_law = law; compute(0); diff --git a/apps/probability/calculation/calculation.h b/apps/probability/calculation/calculation.h index e30c42819..111f38599 100644 --- a/apps/probability/calculation/calculation.h +++ b/apps/probability/calculation/calculation.h @@ -13,7 +13,7 @@ public: RightIntegral, Discrete, }; - Calculation(); + Calculation() : m_law(nullptr) {} virtual ~Calculation() = default; virtual Type type() = 0; void setLaw(Law * law); diff --git a/apps/probability/calculation/discrete_calculation.cpp b/apps/probability/calculation/discrete_calculation.cpp index f9f15fb1b..46a166b7f 100644 --- a/apps/probability/calculation/discrete_calculation.cpp +++ b/apps/probability/calculation/discrete_calculation.cpp @@ -13,18 +13,6 @@ DiscreteCalculation::DiscreteCalculation() : compute(0); } -Calculation::Type DiscreteCalculation::type() { - return Type::Discrete; -} - -int DiscreteCalculation::numberOfParameters() { - return 2; -} - -int DiscreteCalculation::numberOfEditableParameters() { - return 1; -} - I18n::Message DiscreteCalculation::legendForParameterAtIndex(int index) { assert(index >= 0 && index < 2); if (index == 0) { @@ -49,14 +37,6 @@ double DiscreteCalculation::parameterAtIndex(int index) { return m_result; } -double DiscreteCalculation::lowerBound() { - return m_abscissa; -} - -double DiscreteCalculation::upperBound() { - return m_abscissa; -} - void DiscreteCalculation::compute(int indexKnownElement) { if (m_law == nullptr) { return; diff --git a/apps/probability/calculation/discrete_calculation.h b/apps/probability/calculation/discrete_calculation.h index e75eea55b..1273fd9dc 100644 --- a/apps/probability/calculation/discrete_calculation.h +++ b/apps/probability/calculation/discrete_calculation.h @@ -5,17 +5,17 @@ namespace Probability { -class DiscreteCalculation : public Calculation { +class DiscreteCalculation final : public Calculation { public: DiscreteCalculation(); - Type type() override; - int numberOfParameters() override; - int numberOfEditableParameters() override; + Type type() override { return Type::Discrete; } + int numberOfParameters() override { return 2; } + int numberOfEditableParameters() override { return 1; } I18n::Message legendForParameterAtIndex(int index) override; void setParameterAtIndex(double f, int index) override; double parameterAtIndex(int index) override; - double lowerBound() override; - double upperBound() override; + double lowerBound() override { return m_abscissa; } + double upperBound() override { return m_abscissa; } private: void compute(int indexKnownElement) override; double m_abscissa; diff --git a/apps/probability/calculation/finite_integral_calculation.cpp b/apps/probability/calculation/finite_integral_calculation.cpp index dc3c14e5a..3774a6439 100644 --- a/apps/probability/calculation/finite_integral_calculation.cpp +++ b/apps/probability/calculation/finite_integral_calculation.cpp @@ -15,14 +15,6 @@ FiniteIntegralCalculation::FiniteIntegralCalculation() : compute(0); } -Calculation::Type FiniteIntegralCalculation::type() { - return Type::FiniteIntegral; -} - -int FiniteIntegralCalculation::numberOfParameters() { - return 3; -} - int FiniteIntegralCalculation::numberOfEditableParameters() { if (m_law->type() == Law::Type::Normal) { return 3; @@ -67,14 +59,6 @@ double FiniteIntegralCalculation::parameterAtIndex(int index) { return m_result; } -double FiniteIntegralCalculation::lowerBound() { - return m_lowerBound; -} - -double FiniteIntegralCalculation::upperBound() { - return m_upperBound; -} - void FiniteIntegralCalculation::compute(int indexKnownElement) { if (m_law == nullptr) { return; diff --git a/apps/probability/calculation/finite_integral_calculation.h b/apps/probability/calculation/finite_integral_calculation.h index 5a6f79700..640279f46 100644 --- a/apps/probability/calculation/finite_integral_calculation.h +++ b/apps/probability/calculation/finite_integral_calculation.h @@ -8,14 +8,14 @@ namespace Probability { class FiniteIntegralCalculation : public Calculation { public: FiniteIntegralCalculation(); - Type type() override; - int numberOfParameters() override; + Type type() override { return Type::FiniteIntegral; } + int numberOfParameters() override { return 3; } int numberOfEditableParameters() override; I18n::Message legendForParameterAtIndex(int index) override; void setParameterAtIndex(double f, int index) override; double parameterAtIndex(int index) override; - double lowerBound() override; - double upperBound() override; + double lowerBound() override { return m_lowerBound; } + double upperBound() override { return m_upperBound; } private: void compute(int indexKnownElement) override; double m_lowerBound; diff --git a/apps/probability/calculation/left_integral_calculation.cpp b/apps/probability/calculation/left_integral_calculation.cpp index 3ddbf59ff..4dd9056a6 100644 --- a/apps/probability/calculation/left_integral_calculation.cpp +++ b/apps/probability/calculation/left_integral_calculation.cpp @@ -13,14 +13,6 @@ LeftIntegralCalculation::LeftIntegralCalculation() : compute(0); } -Calculation::Type LeftIntegralCalculation::type() { - return Type::LeftIntegral; -} - -int LeftIntegralCalculation::numberOfParameters() { - return 2; -} - I18n::Message LeftIntegralCalculation::legendForParameterAtIndex(int index) { assert(index >= 0 && index < 2); if (index == 0) { @@ -48,10 +40,6 @@ double LeftIntegralCalculation::parameterAtIndex(int index) { return m_result; } -double LeftIntegralCalculation::upperBound() { - return m_upperBound; -} - void LeftIntegralCalculation::compute(int indexKnownElement) { if (m_law == nullptr) { return; diff --git a/apps/probability/calculation/left_integral_calculation.h b/apps/probability/calculation/left_integral_calculation.h index ef58a9638..f998cdb12 100644 --- a/apps/probability/calculation/left_integral_calculation.h +++ b/apps/probability/calculation/left_integral_calculation.h @@ -5,15 +5,15 @@ namespace Probability { -class LeftIntegralCalculation : public Calculation { +class LeftIntegralCalculation final : public Calculation { public: LeftIntegralCalculation(); - Type type() override; - int numberOfParameters() override; + Type type() override { return Type::LeftIntegral; } + int numberOfParameters() override { return 2; } I18n::Message legendForParameterAtIndex(int index) override; void setParameterAtIndex(double f, int index) override; double parameterAtIndex(int index) override; - double upperBound() override; + double upperBound() override { return m_upperBound; } private: void compute(int indexKnownElement) override; double m_upperBound; diff --git a/apps/probability/calculation/right_integral_calculation.cpp b/apps/probability/calculation/right_integral_calculation.cpp index 20f8fdd07..48858f584 100644 --- a/apps/probability/calculation/right_integral_calculation.cpp +++ b/apps/probability/calculation/right_integral_calculation.cpp @@ -13,14 +13,6 @@ RightIntegralCalculation::RightIntegralCalculation() : compute(0); } -Calculation::Type RightIntegralCalculation::type() { - return Type::RightIntegral; -} - -int RightIntegralCalculation::numberOfParameters() { - return 2; -} - I18n::Message RightIntegralCalculation::legendForParameterAtIndex(int index) { assert(index >= 0 && index < 2); if (index == 0) { @@ -48,10 +40,6 @@ double RightIntegralCalculation::parameterAtIndex(int index) { return m_result; } -double RightIntegralCalculation::lowerBound() { - return m_lowerBound; -} - void RightIntegralCalculation::compute(int indexKnownElement) { if (m_law == nullptr) { return; diff --git a/apps/probability/calculation/right_integral_calculation.h b/apps/probability/calculation/right_integral_calculation.h index a6fcac127..71f1e5530 100644 --- a/apps/probability/calculation/right_integral_calculation.h +++ b/apps/probability/calculation/right_integral_calculation.h @@ -5,15 +5,15 @@ namespace Probability { -class RightIntegralCalculation : public Calculation { +class RightIntegralCalculation final : public Calculation { public: RightIntegralCalculation(); - Type type() override; - int numberOfParameters() override; + Type type() override { return Type::RightIntegral; } + int numberOfParameters() override { return 2; } I18n::Message legendForParameterAtIndex(int index) override; void setParameterAtIndex(double f, int index) override; double parameterAtIndex(int index) override; - double lowerBound() override; + double lowerBound() override { return m_lowerBound; } private: void compute(int indexKnownElement) override; double m_lowerBound; diff --git a/apps/probability/calculation_cell.cpp b/apps/probability/calculation_cell.cpp index 0caf55d1d..9b18129ac 100644 --- a/apps/probability/calculation_cell.cpp +++ b/apps/probability/calculation_cell.cpp @@ -1,6 +1,6 @@ #include "calculation_cell.h" #include "responder_image_cell.h" -#include "../i18n.h" +#include #include namespace Probability { diff --git a/apps/probability/calculation_controller.cpp b/apps/probability/calculation_controller.cpp index ea4311966..12d3f3de4 100644 --- a/apps/probability/calculation_controller.cpp +++ b/apps/probability/calculation_controller.cpp @@ -66,7 +66,7 @@ CalculationController::CalculationController(Responder * parentResponder, InputE assert(calculation != nullptr); m_selectableTableView.setMargins(k_tableMargin); m_selectableTableView.setVerticalCellOverlap(0); - m_selectableTableView.setShowsIndicators(false); + m_selectableTableView.setDecoratorType(ScrollView::Decorator::Type::None); m_selectableTableView.setBackgroundColor(KDColorWhite); diff --git a/apps/probability/calculation_type_controller.cpp b/apps/probability/calculation_type_controller.cpp index a0b6980b0..aef5c6527 100644 --- a/apps/probability/calculation_type_controller.cpp +++ b/apps/probability/calculation_type_controller.cpp @@ -22,8 +22,7 @@ CalculationTypeController::CalculationTypeController(Responder * parentResponder assert(m_calculation != nullptr); m_selectableTableView.setMargins(0); m_selectableTableView.setVerticalCellOverlap(0); - m_selectableTableView.setShowsIndicators(false); - m_selectableTableView.setColorsBackground(false); + m_selectableTableView.setDecoratorType(ScrollView::Decorator::Type::None); } View * CalculationTypeController::view() { diff --git a/apps/probability/law/binomial_law.cpp b/apps/probability/law/binomial_law.cpp index 2c0f1490c..751247f62 100644 --- a/apps/probability/law/binomial_law.cpp +++ b/apps/probability/law/binomial_law.cpp @@ -4,23 +4,6 @@ namespace Probability { -BinomialLaw::BinomialLaw() : - TwoParameterLaw(20.0, 0.5) -{ -} - -I18n::Message BinomialLaw::title() { - return I18n::Message::BinomialLaw; -} - -Law::Type BinomialLaw::type() const { - return Type::Binomial; -} - -bool BinomialLaw::isContinuous() const { - return false; -} - I18n::Message BinomialLaw::parameterNameAtIndex(int index) { assert(index >= 0 && index < 2); if (index == 0) { diff --git a/apps/probability/law/binomial_law.h b/apps/probability/law/binomial_law.h index 54efc013a..ccd90e701 100644 --- a/apps/probability/law/binomial_law.h +++ b/apps/probability/law/binomial_law.h @@ -5,12 +5,12 @@ namespace Probability { -class BinomialLaw : public TwoParameterLaw { +class BinomialLaw final : public TwoParameterLaw { public: - BinomialLaw(); - I18n::Message title() override; - Type type() const override; - bool isContinuous() const override; + BinomialLaw() : TwoParameterLaw(20.0, 0.5) {} + I18n::Message title() override { return I18n::Message::BinomialLaw; } + Type type() const override { return Type::Binomial; } + bool isContinuous() const override { return false; } float xMin() override; float yMin() override; float xMax() override; diff --git a/apps/probability/law/exponential_law.cpp b/apps/probability/law/exponential_law.cpp index 5615d8e6d..b8111f9d9 100644 --- a/apps/probability/law/exponential_law.cpp +++ b/apps/probability/law/exponential_law.cpp @@ -1,41 +1,15 @@ #include "exponential_law.h" -#include #include #include -#include namespace Probability { -ExponentialLaw::ExponentialLaw() : - OneParameterLaw(1.0f) -{ -} - -I18n::Message ExponentialLaw::title() { - return I18n::Message::ExponentialLaw; -} - -Law::Type ExponentialLaw::type() const { - return Type::Exponential; -} - -bool ExponentialLaw::isContinuous() const { - return true; -} - -I18n::Message ExponentialLaw::parameterNameAtIndex(int index) { - assert(index == 0); - return I18n::Message::Lambda; -} - -I18n::Message ExponentialLaw::parameterDefinitionAtIndex(int index) { - assert(index == 0); - return I18n::Message::LambdaExponentialDefinition; -} - float ExponentialLaw::xMin() { - float max = xMax(); - return - k_displayLeftMarginRatio * max; + return - k_displayLeftMarginRatio * xMax(); +} + +float ExponentialLaw::yMin() { + return -k_displayBottomMarginRatio * yMax(); } float ExponentialLaw::xMax() { @@ -44,11 +18,7 @@ float ExponentialLaw::xMax() { if (result <= 0.0f) { result = 1.0f; } - return result*(1.0f+ k_displayRightMarginRatio); -} - -float ExponentialLaw::yMin() { - return -k_displayBottomMarginRatio*yMax(); + return result * (1.0f + k_displayRightMarginRatio); } float ExponentialLaw::yMax() { @@ -59,14 +29,14 @@ float ExponentialLaw::yMax() { if (result <= 0.0f) { result = 1.0f; } - return result*(1.0f+ k_displayTopMarginRatio); + return result * (1.0f + k_displayTopMarginRatio); } float ExponentialLaw::evaluateAtAbscissa(float x) const { if (x < 0.0f) { return NAN; } - return m_parameter1*std::exp(-m_parameter1*x); + return m_parameter1 * std::exp(-m_parameter1 * x); } bool ExponentialLaw::authorizedValueAtIndex(float x, int index) const { @@ -77,7 +47,7 @@ bool ExponentialLaw::authorizedValueAtIndex(float x, int index) const { } double ExponentialLaw::cumulativeDistributiveFunctionAtAbscissa(double x) const { - return 1.0 - std::exp((double)(-m_parameter1*x)); + return 1.0 - std::exp((double)(-m_parameter1 * x)); } double ExponentialLaw::cumulativeDistributiveInverseForProbability(double * probability) { diff --git a/apps/probability/law/exponential_law.h b/apps/probability/law/exponential_law.h index e9a6e206a..688070a71 100644 --- a/apps/probability/law/exponential_law.h +++ b/apps/probability/law/exponential_law.h @@ -2,21 +2,28 @@ #define PROBABILITE_EXPONENTIAL_LAW_H #include "one_parameter_law.h" +#include namespace Probability { -class ExponentialLaw : public OneParameterLaw { +class ExponentialLaw final : public OneParameterLaw { public: - ExponentialLaw(); - I18n::Message title() override; - Type type() const override; - bool isContinuous() const override; + ExponentialLaw() : OneParameterLaw(1.0f) {} + I18n::Message title() override { return I18n::Message::ExponentialLaw; } + Type type() const override { return Type::Exponential; } + bool isContinuous() const override { return true; } float xMin() override; float yMin() override; float xMax() override; float yMax() override; - I18n::Message parameterNameAtIndex(int index) override; - I18n::Message parameterDefinitionAtIndex(int index) override; + I18n::Message parameterNameAtIndex(int index) override { + assert(index == 0); + return I18n::Message::Lambda; + } + I18n::Message parameterDefinitionAtIndex(int index) override { + assert(index == 0); + return I18n::Message::LambdaExponentialDefinition; + } float evaluateAtAbscissa(float x) const override; bool authorizedValueAtIndex(float x, int index) const override; double cumulativeDistributiveFunctionAtAbscissa(double x) const override; diff --git a/apps/probability/law/law.cpp b/apps/probability/law/law.cpp index 6540e4918..241e2ae62 100644 --- a/apps/probability/law/law.cpp +++ b/apps/probability/law/law.cpp @@ -4,13 +4,8 @@ namespace Probability { -Law::Law() : - Shared::CurveViewRange() -{ -} - float Law::xGridUnit() { - return computeGridUnit(Axis::X, xMin(), xMax()); + return computeGridUnit(Axis::X, xMax() - xMin()); } double Law::cumulativeDistributiveFunctionAtAbscissa(double x) const { diff --git a/apps/probability/law/law.h b/apps/probability/law/law.h index d67d47895..6ebd6f6f3 100644 --- a/apps/probability/law/law.h +++ b/apps/probability/law/law.h @@ -4,13 +4,13 @@ #include #include "../../constant.h" #include "../../shared/curve_view_range.h" -#include "../../i18n.h" +#include namespace Probability { class Law : public Shared::CurveViewRange { public: - Law(); + Law() : Shared::CurveViewRange() {} enum class Type : uint8_t{ Binomial, Uniform, diff --git a/apps/probability/law/normal_law.cpp b/apps/probability/law/normal_law.cpp index 37f1f2572..4e8a51d03 100644 --- a/apps/probability/law/normal_law.cpp +++ b/apps/probability/law/normal_law.cpp @@ -7,57 +7,8 @@ namespace Probability { -NormalLaw::NormalLaw() : - TwoParameterLaw(0.0f, 1.0f) -{ -} - -I18n::Message NormalLaw::title() { - return I18n::Message::NormalLaw; -} - -Law::Type NormalLaw::type() const { - return Type::Normal; -} - -bool NormalLaw::isContinuous() const { - return true; -} - -I18n::Message NormalLaw::parameterNameAtIndex(int index) { - assert(index >= 0 && index < 2); - if (index == 0) { - return I18n::Message::Mu; - } else { - return I18n::Message::Sigma; - } -} - -I18n::Message NormalLaw::parameterDefinitionAtIndex(int index) { - assert(index >= 0 && index < 2); - if (index == 0) { - return I18n::Message::MeanDefinition; - } else { - return I18n::Message::DeviationDefinition; - } -} - -float NormalLaw::xMin() { - if (m_parameter2 == 0.0f) { - return m_parameter1 - 1.0f; - } - return m_parameter1 - 5.0f*std::fabs(m_parameter2); -} - -float NormalLaw::xMax() { - if (m_parameter2 == 0.0f) { - return m_parameter1 + 1.0f; - } - return m_parameter1 + 5.0f*std::fabs(m_parameter2); -} - float NormalLaw::yMin() { - return -k_displayBottomMarginRatio*yMax(); + return - k_displayBottomMarginRatio * yMax(); } float NormalLaw::yMax() { @@ -66,14 +17,30 @@ float NormalLaw::yMax() { if (std::isnan(result) || result <= 0.0f) { result = 1.0f; } - return result*(1.0f+ k_displayTopMarginRatio); + return result * (1.0f + k_displayTopMarginRatio); +} + +I18n::Message NormalLaw::parameterNameAtIndex(int index) { + if (index == 0) { + return I18n::Message::Mu; + } + assert(index == 1); + return I18n::Message::Sigma; +} + +I18n::Message NormalLaw::parameterDefinitionAtIndex(int index) { + if (index == 0) { + return I18n::Message::MeanDefinition; + } + assert(index == 1); + return I18n::Message::DeviationDefinition; } float NormalLaw::evaluateAtAbscissa(float x) const { if (m_parameter2 == 0.0f) { return NAN; } - return (1.0f/(std::fabs(m_parameter2)*std::sqrt(2.0f*M_PI)))*std::exp(-0.5f*std::pow((x-m_parameter1)/m_parameter2,2)); + return (1.0f/(std::fabs(m_parameter2) * std::sqrt(2.0f * M_PI))) * std::exp(-0.5f * std::pow((x - m_parameter1)/m_parameter2, 2)); } bool NormalLaw::authorizedValueAtIndex(float x, int index) const { @@ -94,17 +61,17 @@ void NormalLaw::setParameterAtIndex(float f, int index) { } double NormalLaw::cumulativeDistributiveFunctionAtAbscissa(double x) const { - if (m_parameter2 == 0.0f) { + if (m_parameter2 == 0.0f) { return NAN; } return standardNormalCumulativeDistributiveFunctionAtAbscissa((x-m_parameter1)/std::fabs(m_parameter2)); } double NormalLaw::cumulativeDistributiveInverseForProbability(double * probability) { - if (m_parameter2 == 0.0f) { + if (m_parameter2 == 0.0f) { return NAN; } - return standardNormalCumulativeDistributiveInverseForProbability(*probability)*std::fabs(m_parameter2) + m_parameter1; + return standardNormalCumulativeDistributiveInverseForProbability(*probability) * std::fabs(m_parameter2) + m_parameter1; } double NormalLaw::standardNormalCumulativeDistributiveFunctionAtAbscissa(double abscissa) const { @@ -117,7 +84,7 @@ double NormalLaw::standardNormalCumulativeDistributiveFunctionAtAbscissa(double if (abscissa > k_boundStandardNormalDistribution) { return 1.0; } - return 0.5+0.5*std::erf(abscissa/std::sqrt(2.0)); + return 0.5 + 0.5 * std::erf(abscissa/std::sqrt(2.0)); } double NormalLaw::standardNormalCumulativeDistributiveInverseForProbability(double probability) { @@ -130,7 +97,15 @@ double NormalLaw::standardNormalCumulativeDistributiveInverseForProbability(doub if (probability < 0.5) { return -standardNormalCumulativeDistributiveInverseForProbability(1-probability); } - return std::sqrt(2.0)*erfInv(2.0*probability-1.0); + return std::sqrt(2.0) * erfInv(2.0 * probability - 1.0); +} + +float NormalLaw::xExtremum(bool min) const { + int coefficient = (min ? -1 : 1); + if (m_parameter2 == 0.0f) { + return m_parameter1 + coefficient * 1.0f; + } + return m_parameter1 + coefficient * 5.0f * std::fabs(m_parameter2); } } diff --git a/apps/probability/law/normal_law.h b/apps/probability/law/normal_law.h index b346fb4c3..77ebd4705 100644 --- a/apps/probability/law/normal_law.h +++ b/apps/probability/law/normal_law.h @@ -5,15 +5,15 @@ namespace Probability { -class NormalLaw : public TwoParameterLaw { +class NormalLaw final : public TwoParameterLaw { public: - NormalLaw(); - I18n::Message title() override; - Type type() const override; - bool isContinuous() const override; - float xMin() override; + NormalLaw() : TwoParameterLaw(0.0f, 1.0f) {} + I18n::Message title() override { return I18n::Message::NormalLaw; } + Type type() const override { return Type::Normal; } + bool isContinuous() const override { return true; } + float xMin() override { return xExtremum(true); } float yMin() override; - float xMax() override; + float xMax() override { return xExtremum(false); } float yMax() override; I18n::Message parameterNameAtIndex(int index) override; I18n::Message parameterDefinitionAtIndex(int index) override; @@ -23,14 +23,15 @@ public: double cumulativeDistributiveFunctionAtAbscissa(double x) const override; double cumulativeDistributiveInverseForProbability(double * probability) override; private: - constexpr static double k_maxRatioMuSigma = 1000.0f; - /* For the standard norma law, P(X < y) > 0.9999995 with y >= 4.892 so the + constexpr static double k_maxRatioMuSigma = 1000000.0f; + /* For the standard normal law, P(X < y) > 0.9999995 with y >= 4.892 so the * value displayed is 1. But this is dependent on the fact that we display * only 7 decimal values! */ static_assert(Constant::LargeNumberOfSignificantDigits == 7, "k_maxProbability is ill-defined compared to LargeNumberOfSignificantDigits"); constexpr static double k_boundStandardNormalDistribution = 4.892; double standardNormalCumulativeDistributiveFunctionAtAbscissa(double abscissa) const; double standardNormalCumulativeDistributiveInverseForProbability(double probability); + float xExtremum(bool min) const; }; } diff --git a/apps/probability/law/one_parameter_law.cpp b/apps/probability/law/one_parameter_law.cpp deleted file mode 100644 index 617c60af1..000000000 --- a/apps/probability/law/one_parameter_law.cpp +++ /dev/null @@ -1,25 +0,0 @@ -#include "one_parameter_law.h" -#include - -namespace Probability { - -OneParameterLaw::OneParameterLaw(float parameterValue) : - m_parameter1(parameterValue) -{ -} - -int OneParameterLaw::numberOfParameter() { - return 1; -} - -float OneParameterLaw::parameterValueAtIndex(int index) { - assert(index == 0); - return m_parameter1; -} - -void OneParameterLaw::setParameterAtIndex(float f, int index) { - assert(index == 0); - m_parameter1 = f; -} - -} diff --git a/apps/probability/law/one_parameter_law.h b/apps/probability/law/one_parameter_law.h index 695a1795c..ea45abc58 100644 --- a/apps/probability/law/one_parameter_law.h +++ b/apps/probability/law/one_parameter_law.h @@ -2,15 +2,22 @@ #define PROBABILITE_ONE_PARAMETER_LAW_H #include "law.h" +#include namespace Probability { class OneParameterLaw : public Law { public: - OneParameterLaw(float parameterValue); - int numberOfParameter() override; - float parameterValueAtIndex(int index) override; - void setParameterAtIndex(float f, int index) override; + OneParameterLaw(float parameterValue) : m_parameter1(parameterValue) {} + int numberOfParameter() override { return 1; } + float parameterValueAtIndex(int index) override { + assert(index == 0); + return m_parameter1; + } + void setParameterAtIndex(float f, int index) override { + assert(index == 0); + m_parameter1 = f; + } protected: float m_parameter1; }; diff --git a/apps/probability/law/poisson_law.cpp b/apps/probability/law/poisson_law.cpp index 4cafce3e4..02e801f76 100644 --- a/apps/probability/law/poisson_law.cpp +++ b/apps/probability/law/poisson_law.cpp @@ -5,46 +5,19 @@ namespace Probability { -PoissonLaw::PoissonLaw() : - OneParameterLaw(4.0f) -{ -} - -I18n::Message PoissonLaw::title() { - return I18n::Message::PoissonLaw; -} - -Law::Type PoissonLaw::type() const { - return Type::Poisson; -} - -bool PoissonLaw::isContinuous() const { - return false; -} - -I18n::Message PoissonLaw::parameterNameAtIndex(int index) { - assert(index == 0); - return I18n::Message::Lambda; -} - -I18n::Message PoissonLaw::parameterDefinitionAtIndex(int index) { - assert(index == 0); - return I18n::Message::LambdaPoissonDefinition; -} - float PoissonLaw::xMin() { - return -k_displayLeftMarginRatio*xMax(); -} - -float PoissonLaw::xMax() { - assert(m_parameter1 != 0); - return (m_parameter1 + 5.0f*std::sqrt(m_parameter1))*(1.0f+k_displayRightMarginRatio); + return -k_displayLeftMarginRatio * xMax(); } float PoissonLaw::yMin() { return - k_displayBottomMarginRatio * yMax(); } +float PoissonLaw::xMax() { + assert(m_parameter1 != 0); + return (m_parameter1 + 5.0f * std::sqrt(m_parameter1)) * (1.0f + k_displayRightMarginRatio); +} + float PoissonLaw::yMax() { int maxAbscissa = (int)m_parameter1; assert(maxAbscissa >= 0.0f); @@ -52,7 +25,7 @@ float PoissonLaw::yMax() { if (result <= 0.0f) { result = 1.0f; } - return result*(1.0f+ k_displayTopMarginRatio); + return result * (1.0f + k_displayTopMarginRatio); } bool PoissonLaw::authorizedValueAtIndex(float x, int index) const { @@ -67,7 +40,7 @@ T PoissonLaw::templatedApproximateAtAbscissa(T x) const { if (x < 0) { return NAN; } - T lResult = -(T)m_parameter1+std::floor(x)*std::log((T)m_parameter1)-std::lgamma(std::floor(x)+1); + T lResult = -(T)m_parameter1 + std::floor(x) * std::log((T)m_parameter1) - std::lgamma(std::floor(x) + 1); return std::exp(lResult); } diff --git a/apps/probability/law/poisson_law.h b/apps/probability/law/poisson_law.h index 3ae372a1b..6b8708c46 100644 --- a/apps/probability/law/poisson_law.h +++ b/apps/probability/law/poisson_law.h @@ -5,18 +5,24 @@ namespace Probability { -class PoissonLaw : public OneParameterLaw { +class PoissonLaw final : public OneParameterLaw { public: - PoissonLaw(); - I18n::Message title() override; - Type type() const override; - bool isContinuous() const override; + PoissonLaw() : OneParameterLaw(4.0f) {} + I18n::Message title() override { return I18n::Message::PoissonLaw; } + Type type() const override { return Type::Poisson; } + bool isContinuous() const override { return false; } float xMin() override; float yMin() override; float xMax() override; float yMax() override; - I18n::Message parameterNameAtIndex(int index) override; - I18n::Message parameterDefinitionAtIndex(int index) override; + I18n::Message parameterNameAtIndex(int index) override { + assert(index == 0); + return I18n::Message::Lambda; + } + I18n::Message parameterDefinitionAtIndex(int index) override { + assert(index == 0); + return I18n::Message::LambdaPoissonDefinition; + } float evaluateAtAbscissa(float x) const override { return templatedApproximateAtAbscissa(x); } diff --git a/apps/probability/law/two_parameter_law.cpp b/apps/probability/law/two_parameter_law.cpp index bdee5f63f..8e6c7ac18 100644 --- a/apps/probability/law/two_parameter_law.cpp +++ b/apps/probability/law/two_parameter_law.cpp @@ -3,16 +3,6 @@ namespace Probability { -TwoParameterLaw::TwoParameterLaw(float parameterValue1, float parameterValue2) : - m_parameter1(parameterValue1), - m_parameter2(parameterValue2) -{ -} - -int TwoParameterLaw::numberOfParameter() { - return 2; -} - float TwoParameterLaw::parameterValueAtIndex(int index) { assert(index >= 0 && index < 2); if (index == 0) { diff --git a/apps/probability/law/two_parameter_law.h b/apps/probability/law/two_parameter_law.h index bcfa62c8c..1e1d1fcb3 100644 --- a/apps/probability/law/two_parameter_law.h +++ b/apps/probability/law/two_parameter_law.h @@ -7,8 +7,11 @@ namespace Probability { class TwoParameterLaw : public Law { public: - TwoParameterLaw(float parameterValue1, float parameterValue2); - int numberOfParameter() override; + TwoParameterLaw(float parameterValue1, float parameterValue2) : + m_parameter1(parameterValue1), + m_parameter2(parameterValue2) + {} + int numberOfParameter() override { return 2; } float parameterValueAtIndex(int index) override; void setParameterAtIndex(float f, int index) override; protected: diff --git a/apps/probability/law/uniform_law.cpp b/apps/probability/law/uniform_law.cpp index 9dc323f4a..8a0e072c1 100644 --- a/apps/probability/law/uniform_law.cpp +++ b/apps/probability/law/uniform_law.cpp @@ -5,22 +5,6 @@ namespace Probability { -UniformLaw::UniformLaw() : - TwoParameterLaw(-1.0f, 1.0f) -{ -} - -I18n::Message UniformLaw::title() { - return I18n::Message::UniformLaw; -} - -Law::Type UniformLaw::type() const { - return Type::Uniform; -} - -bool UniformLaw::isContinuous() const { - return true; -} I18n::Message UniformLaw::parameterNameAtIndex(int index) { assert(index >= 0 && index < 2); @@ -45,18 +29,18 @@ float UniformLaw::xMin() { if (m_parameter2 - m_parameter1 < FLT_EPSILON) { return m_parameter1 - 1.0f; } - return m_parameter1 - 0.6f*(m_parameter2 - m_parameter1); + return m_parameter1 - 0.6f * (m_parameter2 - m_parameter1); +} + +float UniformLaw::yMin() { + return -k_displayBottomMarginRatio * yMax(); } float UniformLaw::xMax() { if (m_parameter2 - m_parameter1 < FLT_EPSILON) { return m_parameter1 + 1.0f; } - return m_parameter2 + 0.6f*(m_parameter2 - m_parameter1); -} - -float UniformLaw::yMin() { - return -k_displayBottomMarginRatio*yMax(); + return m_parameter2 + 0.6f * (m_parameter2 - m_parameter1); } float UniformLaw::yMax() { @@ -64,18 +48,18 @@ float UniformLaw::yMax() { if (result <= 0.0f || std::isnan(result) || std::isinf(result)) { result = 1.0f; } - return result*(1.0f+ k_displayTopMarginRatio); + return result * (1.0f+ k_displayTopMarginRatio); } float UniformLaw::evaluateAtAbscissa(float t) const { if (m_parameter2 - m_parameter1 < FLT_EPSILON) { if (m_parameter1 - k_diracWidth<= t && t <= m_parameter2 + k_diracWidth) { - return 2.0f*k_diracMaximum; + return 2.0f * k_diracMaximum; } return 0.0f; } if (m_parameter1 <= t && t <= m_parameter2) { - return (1.0f/(m_parameter2-m_parameter1)); + return (1.0f/(m_parameter2 - m_parameter1)); } return 0.0f; } @@ -93,7 +77,7 @@ bool UniformLaw::authorizedValueAtIndex(float x, int index) const { void UniformLaw::setParameterAtIndex(float f, int index) { TwoParameterLaw::setParameterAtIndex(f, index); if (index == 0 && m_parameter2 < m_parameter1) { - m_parameter2 = m_parameter1+1.0f; + m_parameter2 = m_parameter1 + 1.0f; } } @@ -102,7 +86,7 @@ double UniformLaw::cumulativeDistributiveFunctionAtAbscissa(double x) const { return 0.0; } if (x < m_parameter2) { - return (x-m_parameter1)/(m_parameter2-m_parameter1); + return (x - m_parameter1)/(m_parameter2 - m_parameter1); } return 1.0; } @@ -114,7 +98,7 @@ double UniformLaw::cumulativeDistributiveInverseForProbability(double * probabil if (*probability <= 0.0f) { return m_parameter1; } - return m_parameter1*(1-*probability)+*probability*m_parameter2; + return m_parameter1 * (1 - *probability) + *probability * m_parameter2; } } diff --git a/apps/probability/law/uniform_law.h b/apps/probability/law/uniform_law.h index 7d095fa8a..fc10786e4 100644 --- a/apps/probability/law/uniform_law.h +++ b/apps/probability/law/uniform_law.h @@ -5,12 +5,12 @@ namespace Probability { -class UniformLaw : public TwoParameterLaw { +class UniformLaw final : public TwoParameterLaw { public: - UniformLaw(); - I18n::Message title() override; - Type type() const override; - bool isContinuous() const override; + UniformLaw() : TwoParameterLaw(-1.0f, 1.0f) {} + I18n::Message title() override { return I18n::Message::UniformLaw; } + Type type() const override { return Type::Uniform; } + bool isContinuous() const override { return true; } float xMin() override; float yMin() override; float xMax() override; diff --git a/apps/probability/law_curve_view.cpp b/apps/probability/law_curve_view.cpp index 4fbbd167d..f2d34f077 100644 --- a/apps/probability/law_curve_view.cpp +++ b/apps/probability/law_curve_view.cpp @@ -1,19 +1,12 @@ #include "law_curve_view.h" +#include "law/normal_law.h" #include using namespace Shared; namespace Probability { -LawCurveView::LawCurveView(Law * law, Calculation * calculation) : - CurveView(law, nullptr, nullptr, nullptr), - m_labels{}, - m_law(law), - m_calculation(calculation) -{ - assert(law != nullptr); - assert(calculation != nullptr); -} +constexpr KDColor LawCurveView::k_backgroundColor; void LawCurveView::reload() { CurveView::reload(); @@ -23,9 +16,16 @@ void LawCurveView::reload() { void LawCurveView::drawRect(KDContext * ctx, KDRect rect) const { float lowerBound = m_calculation->lowerBound(); float upperBound = m_calculation->upperBound(); - ctx->fillRect(bounds(), Palette::WallScreen); - drawAxes(ctx, rect, Axis::Horizontal); - drawLabels(ctx, rect, Axis::Horizontal, false); + ctx->fillRect(bounds(), k_backgroundColor); + drawAxis(ctx, rect, Axis::Horizontal); + drawLabels(ctx, rect, Axis::Horizontal, false, false, false, 0, k_backgroundColor); + if (m_law->type() == Law::Type::Normal) { + // Special case for the normal law, which has always the same curve + float pixelColorLowerBound = std::round(floatToPixel(Axis::Horizontal, lowerBound)); + float pixelColorUpperBound = std::round(floatToPixel(Axis::Horizontal, upperBound)); + drawStandardNormal(ctx, rect, pixelColorLowerBound, pixelColorUpperBound); + return; + } if (m_law->isContinuous()) { drawCurve(ctx, rect, EvaluateAtAbscissa, m_law, nullptr, Palette::YellowDark, true, lowerBound, upperBound, true); } else { @@ -45,4 +45,18 @@ float LawCurveView::EvaluateAtAbscissa(float abscissa, void * model, void * cont return law->evaluateAtAbscissa(abscissa); } +void LawCurveView::drawStandardNormal(KDContext * ctx, KDRect rect, float colorLowerBound, float colorUpperBound) const { + // Save the previous curve view range + LawCurveView * constCastedThis = const_cast(this); + CurveViewRange * previousRange = constCastedThis->curveViewRange(); + + // Draw a centered reduced normal curve + NormalLaw n; + constCastedThis->setCurveViewRange(&n); + drawCurve(ctx, rect, EvaluateAtAbscissa, &n, nullptr, Palette::YellowDark, true, pixelToFloat(Axis::Horizontal, colorLowerBound), pixelToFloat(Axis::Horizontal, colorUpperBound), true); + + // Put back the previous curve view range + constCastedThis->setCurveViewRange(previousRange); +} + } diff --git a/apps/probability/law_curve_view.h b/apps/probability/law_curve_view.h index b5b6217c7..2f142aa75 100644 --- a/apps/probability/law_curve_view.h +++ b/apps/probability/law_curve_view.h @@ -12,14 +12,25 @@ namespace Probability { class LawCurveView : public Shared::CurveView { public: - LawCurveView(Law * law, Calculation * calculation); + LawCurveView(Law * law, Calculation * calculation) : + CurveView(law, nullptr, nullptr, nullptr), + m_labels{}, + m_law(law), + m_calculation(calculation) + { + assert(law != nullptr); + assert(calculation != nullptr); + } + void reload() override; void drawRect(KDContext * ctx, KDRect rect) const override; protected: char * label(Axis axis, int index) const override; private: - char m_labels[k_maxNumberOfXLabels][k_labelBufferSize]; static float EvaluateAtAbscissa(float abscissa, void * model, void * context); + static constexpr KDColor k_backgroundColor = Palette::WallScreen; + void drawStandardNormal(KDContext * ctx, KDRect rect, float colorLowerBound, float colorUpperBound) const; + char m_labels[k_maxNumberOfXLabels][k_labelBufferMaxSize]; Law * m_law; Calculation * m_calculation; }; diff --git a/apps/probability/parameters_controller.cpp b/apps/probability/parameters_controller.cpp index 1a2f82ffd..def7ab459 100644 --- a/apps/probability/parameters_controller.cpp +++ b/apps/probability/parameters_controller.cpp @@ -16,7 +16,7 @@ ParametersController::ContentView::ContentView(Responder * parentResponder, Sele } void ParametersController::ContentView::drawRect(KDContext * ctx, KDRect rect) const { - int tableHeight = m_selectableTableView->minimalSizeForOptimalDisplay().height()+ Metric::CommonTopMargin + Metric::CommonBottomMargin; + int tableHeight = m_selectableTableView->minimalSizeForOptimalDisplay().height(); ctx->fillRect(KDRect(0, tableHeight, bounds().width(), bounds().height() - tableHeight), Palette::WallScreen); } @@ -53,7 +53,7 @@ View * ParametersController::ContentView::subviewAtIndex(int index) { void ParametersController::ContentView::layoutSubviews() { KDCoordinate titleHeight = KDFont::SmallFont->glyphSize().height()+k_titleMargin; m_titleView.setFrame(KDRect(0, 0, bounds().width(), titleHeight)); - KDCoordinate tableHeight = m_selectableTableView->minimalSizeForOptimalDisplay().height() + Metric::CommonTopMargin + Metric::CommonBottomMargin; + KDCoordinate tableHeight = m_selectableTableView->minimalSizeForOptimalDisplay().height(); m_selectableTableView->setFrame(KDRect(0, titleHeight, bounds().width(), tableHeight)); KDCoordinate textHeight = KDFont::SmallFont->glyphSize().height(); KDCoordinate defOrigin = (titleHeight+tableHeight)/2+(bounds().height()-textHeight)/2; diff --git a/apps/regression/Makefile b/apps/regression/Makefile index 3065bc27f..450d3a471 100644 --- a/apps/regression/Makefile +++ b/apps/regression/Makefile @@ -1,36 +1,36 @@ apps += Regression::App app_headers += apps/regression/app.h -app_objs += $(addprefix apps/regression/,\ - app.o\ - banner_view.o\ - calculation_controller.o\ - column_title_cell.o\ - even_odd_buffer_text_cell_with_margin.o\ - even_odd_double_buffer_text_cell_with_separator.o\ - go_to_parameter_controller.o\ - graph_controller.o\ - graph_options_controller.o\ - graph_view.o\ - initialisation_parameter_controller.o\ - regression_context.o\ - regression_controller.o\ - store.o\ - store_controller.o\ - store_parameter_controller.o\ +app_src += $(addprefix apps/regression/,\ + app.cpp \ + banner_view.cpp \ + calculation_controller.cpp \ + column_title_cell.cpp \ + even_odd_buffer_text_cell_with_margin.cpp \ + even_odd_double_buffer_text_cell_with_separator.cpp \ + go_to_parameter_controller.cpp \ + graph_controller.cpp \ + graph_options_controller.cpp \ + graph_view.cpp \ + initialisation_parameter_controller.cpp \ + regression_context.cpp \ + regression_controller.cpp \ + store.cpp \ + store_controller.cpp \ + store_parameter_controller.cpp \ ) -app_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_src += $(addprefix apps/regression/model/,\ + cubic_model.cpp \ + exponential_model.cpp \ + linear_model.cpp \ + logarithmic_model.cpp \ + logistic_model.cpp \ + model.cpp \ + power_model.cpp \ + quadratic_model.cpp \ + quartic_model.cpp \ + trigonometric_model.cpp \ ) i18n_files += $(addprefix apps/regression/,\ @@ -46,4 +46,4 @@ tests += $(addprefix apps/regression/test/,\ model.cpp\ ) -app_images += apps/regression/regression_icon.png +$(eval $(call depends_on_image,apps/regression/app.cpp,apps/regression/regression_icon.png)) diff --git a/apps/regression/app.cpp b/apps/regression/app.cpp index 88f6d288e..afd5f27e9 100644 --- a/apps/regression/app.cpp +++ b/apps/regression/app.cpp @@ -1,6 +1,6 @@ #include "app.h" #include "regression_icon.h" -#include "../i18n.h" +#include using namespace Shared; diff --git a/apps/regression/calculation_controller.cpp b/apps/regression/calculation_controller.cpp index 39f0458d9..895256377 100644 --- a/apps/regression/calculation_controller.cpp +++ b/apps/regression/calculation_controller.cpp @@ -25,7 +25,7 @@ CalculationController::CalculationController(Responder * parentResponder, Button m_hideableCell(), m_store(store) { - m_r2Layout = HorizontalLayout(CharLayout('r', KDFont::SmallFont), VerticalOffsetLayout(CharLayout('2', KDFont::SmallFont), VerticalOffsetLayoutNode::Type::Superscript)); + m_r2Layout = HorizontalLayout::Builder(CharLayout::Builder('r', KDFont::SmallFont), VerticalOffsetLayout::Builder(CharLayout::Builder('2', KDFont::SmallFont), VerticalOffsetLayoutNode::Type::Superscript)); m_selectableTableView.setVerticalCellOverlap(0); m_selectableTableView.setBackgroundColor(Palette::WallScreenDark); m_selectableTableView.setMargins(k_margin, k_scrollBarMargin, k_scrollBarMargin, k_margin); diff --git a/apps/regression/graph_controller.cpp b/apps/regression/graph_controller.cpp index 9c3225105..be3298f72 100644 --- a/apps/regression/graph_controller.cpp +++ b/apps/regression/graph_controller.cpp @@ -276,7 +276,9 @@ void GraphController::initCursorParameters() { double x = m_store->meanOfColumn(*m_selectedSeriesIndex, 0); double y = m_store->meanOfColumn(*m_selectedSeriesIndex, 1); m_cursor->moveTo(x, y); - m_store->panToMakePointVisible(x, y, cursorTopMarginRatio(), k_cursorRightMarginRatio, cursorBottomMarginRatio(), k_cursorLeftMarginRatio); + if (m_store->yAuto()) { + m_store->panToMakePointVisible(x, y, cursorTopMarginRatio(), k_cursorRightMarginRatio, cursorBottomMarginRatio(), k_cursorLeftMarginRatio); + } *m_selectedDotIndex = m_store->numberOfPairsOfSeries(*m_selectedSeriesIndex); } diff --git a/apps/regression/graph_view.cpp b/apps/regression/graph_view.cpp index a314f40f6..b08575c6c 100644 --- a/apps/regression/graph_view.cpp +++ b/apps/regression/graph_view.cpp @@ -20,10 +20,8 @@ GraphView::GraphView(Store * store, CurveViewCursor * cursor, BannerView * banne void GraphView::drawRect(KDContext * ctx, KDRect rect) const { ctx->fillRect(rect, KDColorWhite); drawGrid(ctx, rect); - drawAxes(ctx, rect, Axis::Horizontal); - drawAxes(ctx, rect, Axis::Vertical); - drawLabels(ctx, rect, Axis::Horizontal, true); - drawLabels(ctx, rect, Axis::Vertical, true); + drawAxes(ctx, rect); + simpleDrawBothAxesLabels(ctx, rect); for (int series = 0; series < Store::k_numberOfSeries; series++) { if (!m_store->seriesIsEmpty(series)) { KDColor color = Palette::DataColor[series]; diff --git a/apps/regression/graph_view.h b/apps/regression/graph_view.h index c53f997b0..27fe89dc4 100644 --- a/apps/regression/graph_view.h +++ b/apps/regression/graph_view.h @@ -15,8 +15,8 @@ public: private: char * label(Axis axis, int index) const override; Store * m_store; - char m_xLabels[k_maxNumberOfXLabels][k_labelBufferSize]; - char m_yLabels[k_maxNumberOfYLabels][k_labelBufferSize]; + char m_xLabels[k_maxNumberOfXLabels][k_labelBufferMaxSize]; + char m_yLabels[k_maxNumberOfYLabels][k_labelBufferMaxSize]; Responder * m_controller; }; diff --git a/apps/regression/initialisation_parameter_controller.h b/apps/regression/initialisation_parameter_controller.h index efc69276e..19ba8b26a 100644 --- a/apps/regression/initialisation_parameter_controller.h +++ b/apps/regression/initialisation_parameter_controller.h @@ -3,7 +3,7 @@ #include #include "store.h" -#include "../i18n.h" +#include namespace Regression { diff --git a/apps/regression/model/cubic_model.cpp b/apps/regression/model/cubic_model.cpp index bac69876d..5147ff5f5 100644 --- a/apps/regression/model/cubic_model.cpp +++ b/apps/regression/model/cubic_model.cpp @@ -19,30 +19,30 @@ namespace Regression { Layout CubicModel::layout() { if (m_layout.isUninitialized()) { - const Layout layoutChildren[] = { - CharLayout('a', KDFont::SmallFont), - CharLayout(Ion::Charset::MiddleDot, KDFont::SmallFont), - CharLayout('X', KDFont::SmallFont), - VerticalOffsetLayout( - CharLayout('3', KDFont::SmallFont), + Layout layoutChildren[] = { + CharLayout::Builder('a', KDFont::SmallFont), + CharLayout::Builder(Ion::Charset::MiddleDot, KDFont::SmallFont), + CharLayout::Builder('X', KDFont::SmallFont), + VerticalOffsetLayout::Builder( + CharLayout::Builder('3', KDFont::SmallFont), VerticalOffsetLayoutNode::Type::Superscript ), - CharLayout('+', KDFont::SmallFont), - CharLayout('b', KDFont::SmallFont), - CharLayout(Ion::Charset::MiddleDot, KDFont::SmallFont), - CharLayout('X', KDFont::SmallFont), - VerticalOffsetLayout( - CharLayout('2', KDFont::SmallFont), + CharLayout::Builder('+', KDFont::SmallFont), + CharLayout::Builder('b', KDFont::SmallFont), + CharLayout::Builder(Ion::Charset::MiddleDot, KDFont::SmallFont), + CharLayout::Builder('X', KDFont::SmallFont), + VerticalOffsetLayout::Builder( + CharLayout::Builder('2', KDFont::SmallFont), VerticalOffsetLayoutNode::Type::Superscript ), - CharLayout('+', KDFont::SmallFont), - CharLayout('c', KDFont::SmallFont), - CharLayout(Ion::Charset::MiddleDot, KDFont::SmallFont), - CharLayout('X', KDFont::SmallFont), - CharLayout('+', KDFont::SmallFont), - CharLayout('d', KDFont::SmallFont), + CharLayout::Builder('+', KDFont::SmallFont), + CharLayout::Builder('c', KDFont::SmallFont), + CharLayout::Builder(Ion::Charset::MiddleDot, KDFont::SmallFont), + CharLayout::Builder('X', KDFont::SmallFont), + CharLayout::Builder('+', KDFont::SmallFont), + CharLayout::Builder('d', KDFont::SmallFont), }; - m_layout = HorizontalLayout(layoutChildren, 15); + m_layout = HorizontalLayout::Builder(layoutChildren, 15); } return m_layout; } @@ -53,23 +53,23 @@ Expression CubicModel::simplifiedExpression(double * modelCoefficients, Poincare double c = modelCoefficients[2]; double d = modelCoefficients[3]; Expression addChildren[] = { - Multiplication( + Multiplication::Builder( Number::DecimalNumber(a), - Power( - Symbol('x'), - Decimal(3.0))), - Multiplication( + Power::Builder( + Symbol::Builder('x'), + Decimal::Builder(3.0))), + Multiplication::Builder( Number::DecimalNumber(b), - Power( - Symbol('x'), - Decimal(2.0))), - Multiplication( + Power::Builder( + Symbol::Builder('x'), + Decimal::Builder(2.0))), + Multiplication::Builder( Number::DecimalNumber(c), - Symbol('x')), + Symbol::Builder('x')), Number::DecimalNumber(d) }; // a*x^3+b*x^2+c*x+d - Expression result = Addition(addChildren, 4); + Expression result = Addition::Builder(addChildren, 4); PoincareHelpers::Simplify(&result, *context); return result; } diff --git a/apps/regression/model/exponential_model.cpp b/apps/regression/model/exponential_model.cpp index fb7522802..320d99b45 100644 --- a/apps/regression/model/exponential_model.cpp +++ b/apps/regression/model/exponential_model.cpp @@ -11,20 +11,20 @@ namespace Regression { Layout ExponentialModel::layout() { if (m_layout.isUninitialized()) { - const Layout layoutChildren[] = { - CharLayout('a', KDFont::SmallFont), - CharLayout(Ion::Charset::MiddleDot, KDFont::SmallFont), - CharLayout('e', KDFont::SmallFont), - VerticalOffsetLayout( - HorizontalLayout( - CharLayout('b', KDFont::SmallFont), - CharLayout(Ion::Charset::MiddleDot, KDFont::SmallFont), - CharLayout('X', KDFont::SmallFont) + Layout layoutChildren[] = { + CharLayout::Builder('a', KDFont::SmallFont), + CharLayout::Builder(Ion::Charset::MiddleDot, KDFont::SmallFont), + CharLayout::Builder('e', KDFont::SmallFont), + VerticalOffsetLayout::Builder( + HorizontalLayout::Builder( + CharLayout::Builder('b', KDFont::SmallFont), + CharLayout::Builder(Ion::Charset::MiddleDot, KDFont::SmallFont), + CharLayout::Builder('X', KDFont::SmallFont) ), VerticalOffsetLayoutNode::Type::Superscript ) }; - m_layout = HorizontalLayout(layoutChildren, 4); + m_layout = HorizontalLayout::Builder(layoutChildren, 4); } return m_layout; } diff --git a/apps/regression/model/linear_model.cpp b/apps/regression/model/linear_model.cpp index 42fb203ec..92e03af93 100644 --- a/apps/regression/model/linear_model.cpp +++ b/apps/regression/model/linear_model.cpp @@ -11,14 +11,14 @@ namespace Regression { Layout LinearModel::layout() { if (m_layout.isUninitialized()) { - const Layout layoutChildren[] = { - CharLayout('a', KDFont::SmallFont), - CharLayout(Ion::Charset::MiddleDot, KDFont::SmallFont), - CharLayout('X', KDFont::SmallFont), - CharLayout('+', KDFont::SmallFont), - CharLayout('b', KDFont::SmallFont), + Layout layoutChildren[] = { + CharLayout::Builder('a', KDFont::SmallFont), + CharLayout::Builder(Ion::Charset::MiddleDot, KDFont::SmallFont), + CharLayout::Builder('X', KDFont::SmallFont), + CharLayout::Builder('+', KDFont::SmallFont), + CharLayout::Builder('b', KDFont::SmallFont), }; - m_layout = HorizontalLayout(layoutChildren, 5); + m_layout = HorizontalLayout::Builder(layoutChildren, 5); } return m_layout; } diff --git a/apps/regression/model/logarithmic_model.cpp b/apps/regression/model/logarithmic_model.cpp index fa1930d4a..fb7563928 100644 --- a/apps/regression/model/logarithmic_model.cpp +++ b/apps/regression/model/logarithmic_model.cpp @@ -11,18 +11,18 @@ namespace Regression { Layout LogarithmicModel::layout() { if (m_layout.isUninitialized()) { - const Layout layoutChildren[] = { - CharLayout('a', KDFont::SmallFont), - CharLayout(Ion::Charset::MiddleDot, KDFont::SmallFont), - CharLayout('l', KDFont::SmallFont), - CharLayout('n', KDFont::SmallFont), - CharLayout('(', KDFont::SmallFont), - CharLayout('X', KDFont::SmallFont), - CharLayout(')', KDFont::SmallFont), - CharLayout('+', KDFont::SmallFont), - CharLayout('b', KDFont::SmallFont) + Layout layoutChildren[] = { + CharLayout::Builder('a', KDFont::SmallFont), + CharLayout::Builder(Ion::Charset::MiddleDot, KDFont::SmallFont), + CharLayout::Builder('l', KDFont::SmallFont), + CharLayout::Builder('n', KDFont::SmallFont), + CharLayout::Builder('(', KDFont::SmallFont), + CharLayout::Builder('X', KDFont::SmallFont), + CharLayout::Builder(')', KDFont::SmallFont), + CharLayout::Builder('+', KDFont::SmallFont), + CharLayout::Builder('b', KDFont::SmallFont) }; - m_layout = HorizontalLayout(layoutChildren, 9); + m_layout = HorizontalLayout::Builder(layoutChildren, 9); } return m_layout; } diff --git a/apps/regression/model/logistic_model.cpp b/apps/regression/model/logistic_model.cpp index b7649dcc8..ad0ea7503 100644 --- a/apps/regression/model/logistic_model.cpp +++ b/apps/regression/model/logistic_model.cpp @@ -12,26 +12,26 @@ namespace Regression { Layout LogisticModel::layout() { if (m_layout.isUninitialized()) { - const Layout exponentLayoutChildren[] = { - CharLayout('-', KDFont::SmallFont), - CharLayout('b', KDFont::SmallFont), - CharLayout(Ion::Charset::MiddleDot, KDFont::SmallFont), - CharLayout('X', KDFont::SmallFont) + Layout exponentLayoutChildren[] = { + CharLayout::Builder('-', KDFont::SmallFont), + CharLayout::Builder('b', KDFont::SmallFont), + CharLayout::Builder(Ion::Charset::MiddleDot, KDFont::SmallFont), + CharLayout::Builder('X', KDFont::SmallFont) }; - const Layout layoutChildren[] = { - CharLayout('1', KDFont::SmallFont), - CharLayout('+', KDFont::SmallFont), - CharLayout('a', KDFont::SmallFont), - CharLayout(Ion::Charset::MiddleDot, KDFont::SmallFont), - CharLayout('e', KDFont::SmallFont), - VerticalOffsetLayout( - HorizontalLayout(exponentLayoutChildren, 4), + Layout layoutChildren[] = { + CharLayout::Builder('1', KDFont::SmallFont), + CharLayout::Builder('+', KDFont::SmallFont), + CharLayout::Builder('a', KDFont::SmallFont), + CharLayout::Builder(Ion::Charset::MiddleDot, KDFont::SmallFont), + CharLayout::Builder('e', KDFont::SmallFont), + VerticalOffsetLayout::Builder( + HorizontalLayout::Builder(exponentLayoutChildren, 4), VerticalOffsetLayoutNode::Type::Superscript ) }; - m_layout = FractionLayout( - CharLayout('c', KDFont::SmallFont), - HorizontalLayout(layoutChildren, 6) + m_layout = FractionLayout::Builder( + CharLayout::Builder('c', KDFont::SmallFont), + HorizontalLayout::Builder(layoutChildren, 6) ); } return m_layout; diff --git a/apps/regression/model/model.cpp b/apps/regression/model/model.cpp index 2df92feff..3b287687d 100644 --- a/apps/regression/model/model.cpp +++ b/apps/regression/model/model.cpp @@ -19,7 +19,7 @@ double Model::levelSet(double * modelCoefficients, double xMin, double step, dou Expression yExpression = Number::DecimalNumber(y); PoincareHelpers::Simplify(&yExpression, *context); Expression modelExpression = simplifiedExpression(modelCoefficients, context); - double result = modelExpression.nextIntersection("x", xMin, step, xMax, *context, Preferences::sharedPreferences()->angleUnit(), yExpression).abscissa; + double result = PoincareHelpers::NextIntersection(modelExpression, "x", xMin, step, xMax, *context, yExpression).abscissa; return result; } @@ -128,8 +128,8 @@ double Model::alphaPrimeCoefficient(Store * store, int series, double * modelCoe * a'(k,k) = 2*epsilon so that the inversion method does not detect a'(k,k) * as a zero. */ result = alphaCoefficient(store, series, modelCoefficients, k, l)*(1.0+lambda); - if (std::fabs(result) < Expression::epsilon()) { - result = 2*Expression::epsilon(); + if (std::fabs(result) < Expression::Epsilon()) { + result = 2*Expression::Epsilon(); } } else { result = alphaCoefficient(store, series, modelCoefficients, l, k); diff --git a/apps/regression/model/model.h b/apps/regression/model/model.h index 3dd12b0c3..86e586ee5 100644 --- a/apps/regression/model/model.h +++ b/apps/regression/model/model.h @@ -2,7 +2,7 @@ #define REGRESSION_MODEL_H #include -#include "../../i18n.h" +#include #include #include #include diff --git a/apps/regression/model/power_model.cpp b/apps/regression/model/power_model.cpp index 51ebdd6c2..bd790d815 100644 --- a/apps/regression/model/power_model.cpp +++ b/apps/regression/model/power_model.cpp @@ -12,16 +12,16 @@ namespace Regression { Layout PowerModel::layout() { if (m_layout.isUninitialized()) { - const Layout layoutChildren[] = { - CharLayout('a', KDFont::SmallFont), - CharLayout(Ion::Charset::MiddleDot, KDFont::SmallFont), - CharLayout('X', KDFont::SmallFont), - VerticalOffsetLayout( - CharLayout('b', KDFont::SmallFont), + Layout layoutChildren[] = { + CharLayout::Builder('a', KDFont::SmallFont), + CharLayout::Builder(Ion::Charset::MiddleDot, KDFont::SmallFont), + CharLayout::Builder('X', KDFont::SmallFont), + VerticalOffsetLayout::Builder( + CharLayout::Builder('b', KDFont::SmallFont), VerticalOffsetLayoutNode::Type::Superscript ), }; - m_layout = HorizontalLayout(layoutChildren, 4); + m_layout = HorizontalLayout::Builder(layoutChildren, 4); } return m_layout; } diff --git a/apps/regression/model/quadratic_model.cpp b/apps/regression/model/quadratic_model.cpp index c1a3ca6ee..0ee0b1764 100644 --- a/apps/regression/model/quadratic_model.cpp +++ b/apps/regression/model/quadratic_model.cpp @@ -19,22 +19,22 @@ namespace Regression { Layout QuadraticModel::layout() { if (m_layout.isUninitialized()) { - const Layout layoutChildren[] = { - CharLayout('a', KDFont::SmallFont), - CharLayout(Ion::Charset::MiddleDot, KDFont::SmallFont), - CharLayout('X', KDFont::SmallFont), - VerticalOffsetLayout( - CharLayout('2', KDFont::SmallFont), + Layout layoutChildren[] = { + CharLayout::Builder('a', KDFont::SmallFont), + CharLayout::Builder(Ion::Charset::MiddleDot, KDFont::SmallFont), + CharLayout::Builder('X', KDFont::SmallFont), + VerticalOffsetLayout::Builder( + CharLayout::Builder('2', KDFont::SmallFont), VerticalOffsetLayoutNode::Type::Superscript ), - CharLayout('+', KDFont::SmallFont), - CharLayout('b', KDFont::SmallFont), - CharLayout(Ion::Charset::MiddleDot, KDFont::SmallFont), - CharLayout('X', KDFont::SmallFont), - CharLayout('+', KDFont::SmallFont), - CharLayout('c', KDFont::SmallFont), + CharLayout::Builder('+', KDFont::SmallFont), + CharLayout::Builder('b', KDFont::SmallFont), + CharLayout::Builder(Ion::Charset::MiddleDot, KDFont::SmallFont), + CharLayout::Builder('X', KDFont::SmallFont), + CharLayout::Builder('+', KDFont::SmallFont), + CharLayout::Builder('c', KDFont::SmallFont), }; - m_layout = HorizontalLayout(layoutChildren, 10); + m_layout = HorizontalLayout::Builder(layoutChildren, 10); } return m_layout; } @@ -45,17 +45,17 @@ Expression QuadraticModel::simplifiedExpression(double * modelCoefficients, Poin double c = modelCoefficients[2]; // a*x^2+b*x+c Expression addChildren[] = { - Multiplication( + Multiplication::Builder( Number::DecimalNumber(a), - Power( - Symbol('x'), - Decimal(2.0))), - Multiplication( + Power::Builder( + Symbol::Builder('x'), + Decimal::Builder(2.0))), + Multiplication::Builder( Number::DecimalNumber(b), - Symbol('x')), + Symbol::Builder('x')), Number::DecimalNumber(c) }; - Expression result = Addition(addChildren, 3); + Expression result = Addition::Builder(addChildren, 3); PoincareHelpers::Simplify(&result, *context); return result; } diff --git a/apps/regression/model/quartic_model.cpp b/apps/regression/model/quartic_model.cpp index d2e012a39..3f7a4395a 100644 --- a/apps/regression/model/quartic_model.cpp +++ b/apps/regression/model/quartic_model.cpp @@ -19,38 +19,38 @@ namespace Regression { Layout QuarticModel::layout() { if (m_layout.isUninitialized()) { - const Layout layoutChildren[] = { - CharLayout('a', KDFont::SmallFont), - CharLayout(Ion::Charset::MiddleDot, KDFont::SmallFont), - CharLayout('X', KDFont::SmallFont), - VerticalOffsetLayout( - CharLayout('4', KDFont::SmallFont), + Layout layoutChildren[] = { + CharLayout::Builder('a', KDFont::SmallFont), + CharLayout::Builder(Ion::Charset::MiddleDot, KDFont::SmallFont), + CharLayout::Builder('X', KDFont::SmallFont), + VerticalOffsetLayout::Builder( + CharLayout::Builder('4', KDFont::SmallFont), VerticalOffsetLayoutNode::Type::Superscript ), - CharLayout('+', KDFont::SmallFont), - CharLayout('b', KDFont::SmallFont), - CharLayout(Ion::Charset::MiddleDot, KDFont::SmallFont), - CharLayout('X', KDFont::SmallFont), - VerticalOffsetLayout( - CharLayout('3', KDFont::SmallFont), + CharLayout::Builder('+', KDFont::SmallFont), + CharLayout::Builder('b', KDFont::SmallFont), + CharLayout::Builder(Ion::Charset::MiddleDot, KDFont::SmallFont), + CharLayout::Builder('X', KDFont::SmallFont), + VerticalOffsetLayout::Builder( + CharLayout::Builder('3', KDFont::SmallFont), VerticalOffsetLayoutNode::Type::Superscript ), - CharLayout('+', KDFont::SmallFont), - CharLayout('c', KDFont::SmallFont), - CharLayout(Ion::Charset::MiddleDot, KDFont::SmallFont), - CharLayout('X', KDFont::SmallFont), - VerticalOffsetLayout( - CharLayout('2', KDFont::SmallFont), + CharLayout::Builder('+', KDFont::SmallFont), + CharLayout::Builder('c', KDFont::SmallFont), + CharLayout::Builder(Ion::Charset::MiddleDot, KDFont::SmallFont), + CharLayout::Builder('X', KDFont::SmallFont), + VerticalOffsetLayout::Builder( + CharLayout::Builder('2', KDFont::SmallFont), VerticalOffsetLayoutNode::Type::Superscript ), - CharLayout('+', KDFont::SmallFont), - CharLayout('d', KDFont::SmallFont), - CharLayout(Ion::Charset::MiddleDot, KDFont::SmallFont), - CharLayout('X', KDFont::SmallFont), - CharLayout('+', KDFont::SmallFont), - CharLayout('e', KDFont::SmallFont), + CharLayout::Builder('+', KDFont::SmallFont), + CharLayout::Builder('d', KDFont::SmallFont), + CharLayout::Builder(Ion::Charset::MiddleDot, KDFont::SmallFont), + CharLayout::Builder('X', KDFont::SmallFont), + CharLayout::Builder('+', KDFont::SmallFont), + CharLayout::Builder('e', KDFont::SmallFont), }; - m_layout = HorizontalLayout(layoutChildren, 20); + m_layout = HorizontalLayout::Builder(layoutChildren, 20); } return m_layout; } @@ -63,31 +63,31 @@ Expression QuarticModel::simplifiedExpression(double * modelCoefficients, Poinca double e = modelCoefficients[4]; Expression addChildren[] = { // a*x^4 - Multiplication( + Multiplication::Builder( Number::DecimalNumber(a), - Power( - Symbol('x'), - Decimal(4.0))), + Power::Builder( + Symbol::Builder('x'), + Decimal::Builder(4.0))), // b*x^3 - Multiplication( + Multiplication::Builder( Number::DecimalNumber(b), - Power( - Symbol('x'), - Decimal(3.0))), + Power::Builder( + Symbol::Builder('x'), + Decimal::Builder(3.0))), // c*x^2 - Multiplication( + Multiplication::Builder( Number::DecimalNumber(c), - Power( - Symbol('x'), - Decimal(2.0))), + Power::Builder( + Symbol::Builder('x'), + Decimal::Builder(2.0))), // d*x - Multiplication( + Multiplication::Builder( Number::DecimalNumber(d), - Symbol('x')), + Symbol::Builder('x')), // e Number::DecimalNumber(e) }; - Expression result = Addition(addChildren, 5); + Expression result = Addition::Builder(addChildren, 5); PoincareHelpers::Simplify(&result, *context); return result; } diff --git a/apps/regression/model/trigonometric_model.cpp b/apps/regression/model/trigonometric_model.cpp index f7e5380ef..2a605cdde 100644 --- a/apps/regression/model/trigonometric_model.cpp +++ b/apps/regression/model/trigonometric_model.cpp @@ -20,23 +20,23 @@ namespace Regression { Layout TrigonometricModel::layout() { if (m_layout.isUninitialized()) { - const Layout layoutChildren[] = { - CharLayout('a', KDFont::SmallFont), - CharLayout(Ion::Charset::MiddleDot, KDFont::SmallFont), - CharLayout('s', KDFont::SmallFont), - CharLayout('i', KDFont::SmallFont), - CharLayout('n', KDFont::SmallFont), - CharLayout('(', KDFont::SmallFont), - CharLayout('b', KDFont::SmallFont), - CharLayout(Ion::Charset::MiddleDot, KDFont::SmallFont), - CharLayout('X', KDFont::SmallFont), - CharLayout('+', KDFont::SmallFont), - CharLayout('c', KDFont::SmallFont), - CharLayout(')', KDFont::SmallFont), - CharLayout('+', KDFont::SmallFont), - CharLayout('d', KDFont::SmallFont) + Layout layoutChildren[] = { + CharLayout::Builder('a', KDFont::SmallFont), + CharLayout::Builder(Ion::Charset::MiddleDot, KDFont::SmallFont), + CharLayout::Builder('s', KDFont::SmallFont), + CharLayout::Builder('i', KDFont::SmallFont), + CharLayout::Builder('n', KDFont::SmallFont), + CharLayout::Builder('(', KDFont::SmallFont), + CharLayout::Builder('b', KDFont::SmallFont), + CharLayout::Builder(Ion::Charset::MiddleDot, KDFont::SmallFont), + CharLayout::Builder('X', KDFont::SmallFont), + CharLayout::Builder('+', KDFont::SmallFont), + CharLayout::Builder('c', KDFont::SmallFont), + CharLayout::Builder(')', KDFont::SmallFont), + CharLayout::Builder('+', KDFont::SmallFont), + CharLayout::Builder('d', KDFont::SmallFont) }; - m_layout = HorizontalLayout(layoutChildren, 14); + m_layout = HorizontalLayout::Builder(layoutChildren, 14); } return m_layout; } @@ -48,14 +48,14 @@ Expression TrigonometricModel::simplifiedExpression(double * modelCoefficients, double d = modelCoefficients[3]; // a*sin(bx+c)+d Expression result = - Addition( - Multiplication( + Addition::Builder( + Multiplication::Builder( Number::DecimalNumber(a), Sine::Builder( - Addition( - Multiplication( + Addition::Builder( + Multiplication::Builder( Number::DecimalNumber(b), - Symbol('x')), + Symbol::Builder('x')), Number::DecimalNumber(c)))), Number::DecimalNumber(d)); PoincareHelpers::Simplify(&result, *context); diff --git a/apps/regression/regression_context.cpp b/apps/regression/regression_context.cpp index b7d480743..1550cd2bc 100644 --- a/apps/regression/regression_context.cpp +++ b/apps/regression/regression_context.cpp @@ -22,7 +22,7 @@ const Expression RegressionContext::expressionForSymbol(const SymbolAbstract & s assert(m_seriesPairIndex >= 0); assert(m_seriesPairIndex < m_store->numberOfPairsOfSeries(series)); - return Float(m_store->get(series, storeI, m_seriesPairIndex)); + return Float::Builder(m_store->get(series, storeI, m_seriesPairIndex)); } else { return m_parentContext->expressionForSymbol(symbol, clone); } diff --git a/apps/regression/regression_controller.h b/apps/regression/regression_controller.h index 859c7e6b1..19c462fa3 100644 --- a/apps/regression/regression_controller.h +++ b/apps/regression/regression_controller.h @@ -3,7 +3,7 @@ #include "store.h" #include -#include "../i18n.h" +#include namespace Regression { diff --git a/apps/sequence/Makefile b/apps/sequence/Makefile index 76354d74c..149b48e4e 100644 --- a/apps/sequence/Makefile +++ b/apps/sequence/Makefile @@ -1,26 +1,26 @@ apps += Sequence::App app_headers += apps/sequence/app.h -app_objs += $(addprefix apps/sequence/,\ - app.o\ - graph/banner_view.o\ - graph/curve_parameter_controller.o\ - graph/curve_view_range.o\ - graph/go_to_parameter_controller.o\ - graph/graph_controller.o\ - graph/graph_view.o\ - graph/term_sum_controller.o\ - list/list_controller.o\ - list/list_parameter_controller.o\ - list/sequence_toolbox.o\ - list/type_parameter_controller.o\ - values/interval_parameter_controller.o\ - values/values_controller.o\ - cache_context.o\ - sequence.o\ - sequence_context.o\ - sequence_store.o\ - sequence_title_cell.o\ +app_src += $(addprefix apps/sequence/,\ + app.cpp \ + graph/banner_view.cpp \ + graph/curve_parameter_controller.cpp \ + graph/curve_view_range.cpp \ + graph/go_to_parameter_controller.cpp \ + graph/graph_controller.cpp \ + graph/graph_view.cpp \ + graph/term_sum_controller.cpp \ + list/list_controller.cpp \ + list/list_parameter_controller.cpp \ + list/sequence_toolbox.cpp \ + list/type_parameter_controller.cpp \ + values/interval_parameter_controller.cpp \ + values/values_controller.cpp \ + cache_context.cpp \ + sequence.cpp \ + sequence_context.cpp \ + sequence_store.cpp \ + sequence_title_cell.cpp \ ) i18n_files += $(addprefix apps/sequence/,\ @@ -35,4 +35,4 @@ tests += $(addprefix apps/sequence/test/,\ sequence.cpp\ ) -app_images += apps/sequence/sequence_icon.png +$(eval $(call depends_on_image,apps/sequence/app.cpp,apps/sequence/sequence_icon.png)) diff --git a/apps/sequence/cache_context.cpp b/apps/sequence/cache_context.cpp index b16a3d4f3..96fb44608 100644 --- a/apps/sequence/cache_context.cpp +++ b/apps/sequence/cache_context.cpp @@ -22,7 +22,7 @@ const Expression CacheContext::expressionForSymbol(const SymbolAbstract & sym && (strcmp(symbol.name()+1, "(n)") == 0 || strcmp(symbol.name()+1, "(n+1)") == 0)) { Symbol s = const_cast(static_cast(symbol)); - return Float(m_values[nameIndexForSymbol(s)][rankIndexForSymbol(s)]); + return Float::Builder(m_values[nameIndexForSymbol(s)][rankIndexForSymbol(s)]); } return VariableContext::expressionForSymbol(symbol, clone); } diff --git a/apps/sequence/graph/curve_view_range.cpp b/apps/sequence/graph/curve_view_range.cpp index e9609669a..444422906 100644 --- a/apps/sequence/graph/curve_view_range.cpp +++ b/apps/sequence/graph/curve_view_range.cpp @@ -11,76 +11,82 @@ namespace Sequence { CurveViewRange::CurveViewRange(CurveViewCursor * cursor, InteractiveCurveViewRangeDelegate * delegate) : InteractiveCurveViewRange(cursor, delegate) { - m_xMin = -k_displayLeftMarginRatio*m_xMax; + m_xMin = -k_displayLeftMarginRatio * m_xMax; } void CurveViewRange::roundAbscissa() { - float xMin = m_xMin; - float xMax = m_xMax; - float newXMin = clipped(std::round((xMin+xMax)/2) - (float)Ion::Display::Width/2.0f, false); - float newXMax = clipped(std::round((xMin+xMax)/2) + (float)Ion::Display::Width/2.0f-1.0f, true); + int roundedXMean = std::round((m_xMin+m_xMax)/2); + float halfScreenWidth = ((float)Ion::Display::Width)/2.0f; + float newXMin = clipped(roundedXMean - halfScreenWidth, false); + float newXMax = clipped(roundedXMean + halfScreenWidth - 1.0f, true); if (std::isnan(newXMin) || std::isnan(newXMax)) { return; } m_xMin = newXMin; m_xMax = newXMax; - if (m_xMin < 0.0f) { - m_xMin = -k_displayLeftMarginRatio*(float)Ion::Display::Width; - m_xMax = m_xMin+(float)Ion::Display::Width; + float interestingXMin = m_delegate->interestingXMin(); + if (m_xMin < interestingXMin) { + m_xMin = interestingXMin - k_displayLeftMarginRatio * (float)Ion::Display::Width; + m_xMax = m_xMin + (float)Ion::Display::Width; } - m_xGridUnit = computeGridUnit(Axis::X, m_xMin, m_xMax); + m_xGridUnit = computeGridUnit(Axis::X, m_xMax - m_xMin); if (m_delegate) { m_delegate->didChangeRange(this); } } void CurveViewRange::normalize() { - float xMin = m_xMin; - float xMax = m_xMax; - float yMin = m_yMin; - float yMax = m_yMax; - float newXMin = clipped((xMin+xMax)/2 - 5.3f, false); - float newXMax = clipped((xMin+xMax)/2 + 5.3f, true); + float xMean = (m_xMin+m_xMax)/2; + float yMean = (m_yMin+m_yMax)/2; + + // Compute the X + float newXMin = clipped(xMean - NormalizedXHalfRange(), false); + float newXMax = clipped(xMean + NormalizedXHalfRange(), true); if (!std::isnan(newXMin) && !std::isnan(newXMax)) { m_xMin = newXMin; m_xMax = newXMax; - m_xGridUnit = computeGridUnit(Axis::X, m_xMin, m_xMax); + m_xGridUnit = computeGridUnit(Axis::X, m_xMax - m_xMin); } - if (m_xMin < 0.0f) { - m_xMin = -k_displayLeftMarginRatio*2.0f*5.3f; - m_xMax = m_xMin + 2.0f*5.3f; + float interestingXMin = m_delegate->interestingXMin(); + if (m_xMin < interestingXMin) { + m_xMin = interestingXMin -k_displayLeftMarginRatio*2.0f*NormalizedXHalfRange(); + m_xMax = m_xMin + 2.0f*NormalizedXHalfRange(); } + + // Compute the Y m_yAuto = false; - float newYMin = clipped((yMin+yMax)/2 - 3.1f, false); - float newYMax = clipped((yMin+yMax)/2 + 3.1f, true); + float newYMin = clipped(yMean - NormalizedYHalfRange(), false); + float newYMax = clipped(yMean + NormalizedYHalfRange(), true); if (!std::isnan(newYMin) && !std::isnan(newYMax)) { m_yMin = newYMin; m_yMax = newYMax; - m_yGridUnit = computeGridUnit(Axis::Y, m_yMin, m_yMax); + m_yGridUnit = computeGridUnit(Axis::Y, m_yMax - m_yMin); } } void CurveViewRange::setTrigonometric() { - m_xMin = -k_displayLeftMarginRatio*21.0f; - m_xMax = 21.0f; - if (Preferences::sharedPreferences()->angleUnit() == Preferences::AngleUnit::Degree) { - m_xMin = -k_displayLeftMarginRatio*1200; - m_xMax = 1200; - } - m_xGridUnit = computeGridUnit(Axis::X, m_xMin, m_xMax); + float interestingXMin = m_delegate->interestingXMin(); + float interestingXRange = Preferences::sharedPreferences()->angleUnit() == Preferences::AngleUnit::Degree ? 1200.0f : 21.0f; + m_xMin = interestingXMin - k_displayLeftMarginRatio * interestingXRange; + m_xMax = interestingXMin + interestingXRange; + m_xGridUnit = computeGridUnit(Axis::X, m_xMax - m_xMin); + m_yAuto = false; - m_yMin = -1.6f; - m_yMax = 1.6f; - m_yGridUnit = computeGridUnit(Axis::Y, m_yMin, m_yMax); + constexpr float y = 1.6f; + m_yMin = -y; + m_yMax = y; + m_yGridUnit = computeGridUnit(Axis::Y, m_yMax - m_yMin); } void CurveViewRange::setDefault() { if (m_delegate == nullptr) { return; } - m_xMax = m_delegate->interestingXRange(); - m_xMin = -k_displayLeftMarginRatio*m_xMax; - m_xGridUnit = computeGridUnit(Axis::X, m_xMin, m_xMax); + float interestingXMin = m_delegate->interestingXMin(); + float interestingXRange = m_delegate->interestingXHalfRange(); + m_xMin = interestingXMin - k_displayLeftMarginRatio * interestingXRange; + m_xMax = interestingXMin + interestingXRange; + m_xGridUnit = computeGridUnit(Axis::X, m_xMax - m_xMin); setYAuto(true); } diff --git a/apps/sequence/graph/curve_view_range.h b/apps/sequence/graph/curve_view_range.h index 480a49fc6..ac9316220 100644 --- a/apps/sequence/graph/curve_view_range.h +++ b/apps/sequence/graph/curve_view_range.h @@ -13,7 +13,7 @@ public: void setTrigonometric() override; void setDefault() override; private: - constexpr static float k_displayLeftMarginRatio = 0.05f; + constexpr static float k_displayLeftMarginRatio = 0.1f; }; } diff --git a/apps/sequence/graph/graph_controller.cpp b/apps/sequence/graph/graph_controller.cpp index d5686e5ca..92db25c1a 100644 --- a/apps/sequence/graph/graph_controller.cpp +++ b/apps/sequence/graph/graph_controller.cpp @@ -1,11 +1,15 @@ #include "graph_controller.h" #include +#include using namespace Shared; using namespace Poincare; namespace Sequence { +static inline int minInt(int x, int y) { return (x < y ? x : y); } +static inline int maxInt(int x, int y) { return (x > y ? x : y); } + GraphController::GraphController(Responder * parentResponder, InputEventHandlerDelegate * inputEventHandlerDelegate, SequenceStore * sequenceStore, CurveViewRange * graphRange, CurveViewCursor * cursor, int * indexFunctionSelectedByCursor, uint32_t * modelVersion, uint32_t * rangeVersion, Preferences::AngleUnit * angleUnitVersion, ButtonRowController * header) : FunctionGraphController(parentResponder, inputEventHandlerDelegate, header, graphRange, &m_view, cursor, indexFunctionSelectedByCursor, modelVersion, rangeVersion, angleUnitVersion), m_bannerView(), @@ -25,12 +29,32 @@ I18n::Message GraphController::emptyMessage() { return I18n::Message::NoActivatedSequence; } -TermSumController * GraphController::termSumController() { - return &m_termSumController; +float GraphController::interestingXMin() const { + int nmin = INT_MAX; + for (int i = 0; i < m_sequenceStore->numberOfModels(); i++) { + Sequence * s = m_sequenceStore->modelAtIndex(i); + if (s->isDefined() && s->isActive()) { + nmin = minInt(nmin, s->initialRank()); + } + } + assert(nmin < INT_MAX); + return nmin; } -BannerView * GraphController::bannerView() { - return &m_bannerView; +float GraphController::interestingXHalfRange() const { + float standardRange = Shared::FunctionGraphController::interestingXHalfRange(); + int nmin = INT_MAX; + int nmax = 0; + for (int i = 0; i < m_sequenceStore->numberOfModels(); i++) { + Sequence * s = m_sequenceStore->modelAtIndex(i); + if (s->isDefined() && s->isActive()) { + int firstInterestingIndex = s->initialRank(); + nmin = minInt(nmin, firstInterestingIndex); + nmax = maxInt(nmax, firstInterestingIndex + standardRange); + } + } + assert(nmax - nmin >= standardRange); + return nmax - nmin; } bool GraphController::handleEnter() { @@ -65,20 +89,4 @@ double GraphController::defaultCursorAbscissa() { return std::round(Shared::FunctionGraphController::defaultCursorAbscissa()); } -CurveViewRange * GraphController::interactiveCurveViewRange() { - return m_graphRange; -} - -SequenceStore * GraphController::functionStore() const { - return m_sequenceStore; -} - -GraphView * GraphController::functionGraphView() { - return &m_view; -} - -CurveParameterController * GraphController::curveParameterController() { - return &m_curveParameterController; -} - } diff --git a/apps/sequence/graph/graph_controller.h b/apps/sequence/graph/graph_controller.h index 7c6916c0d..b8579a776 100644 --- a/apps/sequence/graph/graph_controller.h +++ b/apps/sequence/graph/graph_controller.h @@ -12,23 +12,28 @@ namespace Sequence { -class GraphController : public Shared::FunctionGraphController { +class GraphController final : public Shared::FunctionGraphController { public: GraphController(Responder * parentResponder, InputEventHandlerDelegate * inputEventHandlerDelegate, SequenceStore * sequenceStore, CurveViewRange * graphRange, Shared::CurveViewCursor * cursor, int * indexFunctionSelectedByCursor, uint32_t * modelVersion, uint32_t * rangeVersion, Poincare::Preferences::AngleUnit * angleUnitVersion, ButtonRowController * header); I18n::Message emptyMessage() override; - TermSumController * termSumController(); + TermSumController * termSumController() { return &m_termSumController; } + // InteractiveCurveViewRangeDelegate + float interestingXMin() const override; + float interestingXHalfRange() const override; +protected: + int numberOfCurves() const override { return m_sequenceStore->numberOfModels(); } private: - BannerView * bannerView() override; + BannerView * bannerView() override { return &m_bannerView; } bool handleEnter() override; bool moveCursorHorizontally(int direction) override; double defaultCursorAbscissa() override; - CurveViewRange * interactiveCurveViewRange() override; - SequenceStore * functionStore() const override; - GraphView * functionGraphView() override; + CurveViewRange * interactiveCurveViewRange() override { return m_graphRange; } + SequenceStore * functionStore() const override { return m_sequenceStore; } + GraphView * functionGraphView() override { return &m_view; } View * cursorView() override { return &m_cursorView; } - CurveParameterController * curveParameterController() override; + CurveParameterController * curveParameterController() override { return &m_curveParameterController; } Shared::CursorView m_cursorView; BannerView m_bannerView; GraphView m_view; diff --git a/apps/sequence/graph/term_sum_controller.cpp b/apps/sequence/graph/term_sum_controller.cpp index 8a006eb24..35fcfb49f 100644 --- a/apps/sequence/graph/term_sum_controller.cpp +++ b/apps/sequence/graph/term_sum_controller.cpp @@ -50,10 +50,10 @@ double TermSumController::cursorNextStep(double x, int direction) { } Layout TermSumController::createFunctionLayout(const char * functionName) { - return HorizontalLayout( - CharLayout(functionName[0], KDFont::SmallFont), - VerticalOffsetLayout( - CharLayout('n', KDFont::SmallFont), + return HorizontalLayout::Builder( + CharLayout::Builder(functionName[0], KDFont::SmallFont), + VerticalOffsetLayout::Builder( + CharLayout::Builder('n', KDFont::SmallFont), VerticalOffsetLayoutNode::Type::Subscript ) ); diff --git a/apps/sequence/list/list_controller.cpp b/apps/sequence/list/list_controller.cpp index 83e3c88b9..0348a4c00 100644 --- a/apps/sequence/list/list_controller.cpp +++ b/apps/sequence/list/list_controller.cpp @@ -57,11 +57,12 @@ int ListController::numberOfExpressionRows() { }; KDCoordinate ListController::expressionRowHeight(int j) { + KDCoordinate defaultHeight = Metric::StoreRowHeight; if (m_sequenceStore->numberOfModels() < m_sequenceStore->maxNumberOfModels() && j == numberOfRows() - 1) { - return Metric::StoreRowHeight; + // Add sequence row + return defaultHeight; } Sequence * sequence = m_sequenceStore->modelAtIndex(modelIndexForRow(j)); - KDCoordinate defaultHeight = 2*k_expressionCellVerticalMargin + (sequence->type() == Sequence::Type::Explicit ? Metric::StoreRowHeight : k_emptySubRowHeight); Layout layout = sequence->layout(); if (sequenceDefinitionForRow(j) == 1) { layout = sequence->firstInitialConditionLayout(); @@ -191,7 +192,12 @@ HighlightCell * ListController::expressionCells(int index) { } void ListController::willDisplayTitleCellAtIndex(HighlightCell * cell, int j) { + assert(j>=0 && j < k_maxNumberOfRows); SequenceTitleCell * myCell = (SequenceTitleCell *)cell; + // Update the corresponding expression cell to get its baseline + willDisplayExpressionCellAtIndex(m_selectableTableView.cellAtLocation(1, j), j); + myCell->setBaseline(baseline(j)); + // Set the layout Sequence * sequence = m_sequenceStore->modelAtIndex(modelIndexForRow(j)); if (sequenceDefinitionForRow(j) == 0) { myCell->setLayout(sequence->definitionName()); @@ -202,6 +208,7 @@ void ListController::willDisplayTitleCellAtIndex(HighlightCell * cell, int j) { if (sequenceDefinitionForRow(j) == 2) { myCell->setLayout(sequence->secondInitialConditionName()); } + // Set the color KDColor nameColor = sequence->isActive() ? sequence->color() : Palette::GreyDark; myCell->setColor(nameColor); } @@ -298,4 +305,15 @@ void ListController::reinitExpression(Shared::ExpressionModel * model) { selectableTableView()->reloadData(); } +KDCoordinate ListController::baseline(int j) const { + //TODO copied from Graph::StorageListController, will be refactored when Sequence is a StorageApp + assert(j>=0 && j((const_cast(&m_selectableTableView))->cellAtLocation(1, j)); + Poincare::Layout layout = cell->layout(); + if (layout.isUninitialized()) { + return 0.5*const_cast(this)->rowHeight(j); + } + return 0.5*(const_cast(this)->rowHeight(j)-layout.layoutSize().height())+layout.baseline(); +} + } diff --git a/apps/sequence/list/list_controller.h b/apps/sequence/list/list_controller.h index 0b8453a19..969fa3d7e 100644 --- a/apps/sequence/list/list_controller.h +++ b/apps/sequence/list/list_controller.h @@ -44,7 +44,7 @@ private: void reinitExpression(Shared::ExpressionModel * model) override; void editExpression(Shared::ExpressionModel * model, Ion::Events::Event event) override; bool removeModelRow(Shared::ExpressionModel * model) override; - static constexpr KDCoordinate k_emptySubRowHeight = 30; + KDCoordinate baseline(int j) const; constexpr static int k_maxNumberOfRows = 3*MaxNumberOfSequences; SequenceStore * m_sequenceStore; SequenceTitleCell m_sequenceTitleCells[k_maxNumberOfRows]; diff --git a/apps/sequence/list/sequence_toolbox.cpp b/apps/sequence/list/sequence_toolbox.cpp index e5797e160..5dbcd3380 100644 --- a/apps/sequence/list/sequence_toolbox.cpp +++ b/apps/sequence/list/sequence_toolbox.cpp @@ -76,20 +76,20 @@ void SequenceToolbox::buildExtraCellsLayouts(const char * sequenceName, int recu const char * otherSequenceName = SequenceStore::k_sequenceNames[1-sequenceIndex]; for (int j = 0; j < recurrenceDepth; j++) { const char * indice = j == 0 ? "n" : "n+1"; - m_addedCellLayout[j] = HorizontalLayout( - CharLayout(sequenceName[0], KDFont::LargeFont), - VerticalOffsetLayout(LayoutHelper::String(indice, strlen(indice), KDFont::LargeFont), VerticalOffsetLayoutNode::Type::Subscript) + m_addedCellLayout[j] = HorizontalLayout::Builder( + CharLayout::Builder(sequenceName[0], KDFont::LargeFont), + VerticalOffsetLayout::Builder(LayoutHelper::String(indice, strlen(indice), KDFont::LargeFont), VerticalOffsetLayoutNode::Type::Subscript) ); - m_addedCellLayout[j+recurrenceDepth] = HorizontalLayout( - CharLayout(otherSequenceName[0], KDFont::LargeFont), - VerticalOffsetLayout(LayoutHelper::String(indice, strlen(indice), KDFont::LargeFont), VerticalOffsetLayoutNode::Type::Subscript) + m_addedCellLayout[j+recurrenceDepth] = HorizontalLayout::Builder( + CharLayout::Builder(otherSequenceName[0], KDFont::LargeFont), + VerticalOffsetLayout::Builder(LayoutHelper::String(indice, strlen(indice), KDFont::LargeFont), VerticalOffsetLayoutNode::Type::Subscript) ); } if (recurrenceDepth < 2) { const char * indice = recurrenceDepth == 0 ? "n" : (recurrenceDepth == 1 ? "n+1" : "n+2"); - m_addedCellLayout[2*recurrenceDepth] = HorizontalLayout( - CharLayout(otherSequenceName[0], KDFont::LargeFont), - VerticalOffsetLayout(LayoutHelper::String(indice, strlen(indice), KDFont::LargeFont), VerticalOffsetLayoutNode::Type::Subscript) + m_addedCellLayout[2*recurrenceDepth] = HorizontalLayout::Builder( + CharLayout::Builder(otherSequenceName[0], KDFont::LargeFont), + VerticalOffsetLayout::Builder(LayoutHelper::String(indice, strlen(indice), KDFont::LargeFont), VerticalOffsetLayoutNode::Type::Subscript) ); } } diff --git a/apps/sequence/list/type_parameter_controller.cpp b/apps/sequence/list/type_parameter_controller.cpp index c168c7435..71bace67e 100644 --- a/apps/sequence/list/type_parameter_controller.cpp +++ b/apps/sequence/list/type_parameter_controller.cpp @@ -23,7 +23,7 @@ TypeParameterController::TypeParameterController(Responder * parentResponder, Se m_listController(list) { m_selectableTableView.setMargins(topMargin, rightMargin, bottomMargin, leftMargin); - m_selectableTableView.setShowsIndicators(false); + m_selectableTableView.setDecoratorType(ScrollView::Decorator::Type::None); } const char * TypeParameterController::title() { @@ -115,9 +115,9 @@ void TypeParameterController::willDisplayCellAtLocation(HighlightCell * cell, in font = KDFont::SmallFont; } const char * subscripts[3] = {"n", "n+1", "n+2"}; - m_layouts[j] = HorizontalLayout( - CharLayout(nextName[0], font), - VerticalOffsetLayout(LayoutHelper::String(subscripts[j], strlen(subscripts[j]), font), VerticalOffsetLayoutNode::Type::Subscript) + m_layouts[j] = HorizontalLayout::Builder( + CharLayout::Builder(nextName[0], font), + VerticalOffsetLayout::Builder(LayoutHelper::String(subscripts[j], strlen(subscripts[j]), font), VerticalOffsetLayoutNode::Type::Subscript) ); ExpressionTableCellWithPointer * myCell = (ExpressionTableCellWithPointer *)cell; myCell->setLayout(m_layouts[j]); diff --git a/apps/sequence/sequence.cpp b/apps/sequence/sequence.cpp index d7bedbfef..d413343b0 100644 --- a/apps/sequence/sequence.cpp +++ b/apps/sequence/sequence.cpp @@ -146,9 +146,9 @@ int Sequence::numberOfElements() { Poincare::Layout Sequence::nameLayout() { if (m_nameLayout.isUninitialized()) { - m_nameLayout = HorizontalLayout( - CharLayout(name()[0], KDFont::SmallFont), - VerticalOffsetLayout(CharLayout('n', KDFont::SmallFont), VerticalOffsetLayoutNode::Type::Subscript) + m_nameLayout = HorizontalLayout::Builder( + CharLayout::Builder(name()[0], KDFont::SmallFont), + VerticalOffsetLayout::Builder(CharLayout::Builder('n', KDFont::SmallFont), VerticalOffsetLayoutNode::Type::Subscript) ); } return m_nameLayout; @@ -157,22 +157,18 @@ Poincare::Layout Sequence::nameLayout() { Poincare::Layout Sequence::definitionName() { if (m_definitionName.isUninitialized()) { if (m_type == Type::Explicit) { - m_definitionName = HorizontalLayout( - CharLayout(name()[0], KDFont::LargeFont), - VerticalOffsetLayout(LayoutHelper::String("n", 1, KDFont::LargeFont), VerticalOffsetLayoutNode::Type::Subscript) - ); - } - if (m_type == Type::SingleRecurrence) { - m_definitionName = HorizontalLayout( - CharLayout(name()[0], KDFont::LargeFont), - VerticalOffsetLayout(LayoutHelper::String("n+1", 3, KDFont::LargeFont), VerticalOffsetLayoutNode::Type::Subscript) - ); - } - if (m_type == Type::DoubleRecurrence) { - m_definitionName = HorizontalLayout( - CharLayout(name()[0], KDFont::LargeFont), - VerticalOffsetLayout(LayoutHelper::String("n+2", 3, KDFont::LargeFont), VerticalOffsetLayoutNode::Type::Subscript) - ); + m_definitionName = HorizontalLayout::Builder( + CharLayout::Builder(name()[0], k_layoutFont), + VerticalOffsetLayout::Builder(LayoutHelper::String("n", 1, k_layoutFont), VerticalOffsetLayoutNode::Type::Subscript)); + } else if (m_type == Type::SingleRecurrence) { + m_definitionName = HorizontalLayout::Builder( + CharLayout::Builder(name()[0], k_layoutFont), + VerticalOffsetLayout::Builder(LayoutHelper::String("n+1", 3, k_layoutFont), VerticalOffsetLayoutNode::Type::Subscript)); + } else { + assert(m_type == Type::DoubleRecurrence); + m_definitionName = HorizontalLayout::Builder( + CharLayout::Builder(name()[0], k_layoutFont), + VerticalOffsetLayout::Builder(LayoutHelper::String("n+2", 3, k_layoutFont), VerticalOffsetLayoutNode::Type::Subscript)); } } return m_definitionName; @@ -185,11 +181,10 @@ Poincare::Layout Sequence::firstInitialConditionName() { && (m_type == Type::SingleRecurrence || m_type == Type::DoubleRecurrence)) { - Layout indexLayout = LayoutHelper::String(buffer, strlen(buffer), KDFont::LargeFont); - m_firstInitialConditionName = HorizontalLayout( - CharLayout(name()[0], KDFont::LargeFont), - VerticalOffsetLayout(indexLayout, VerticalOffsetLayoutNode::Type::Subscript) - ); + Layout indexLayout = LayoutHelper::String(buffer, strlen(buffer), k_layoutFont); + m_firstInitialConditionName = HorizontalLayout::Builder( + CharLayout::Builder(name()[0], k_layoutFont), + VerticalOffsetLayout::Builder(indexLayout, VerticalOffsetLayoutNode::Type::Subscript)); } return m_firstInitialConditionName; } @@ -199,11 +194,10 @@ Poincare::Layout Sequence::secondInitialConditionName() { Integer(m_initialRank+1).serialize(buffer, k_initialRankNumberOfDigits+1); if (m_secondInitialConditionName.isUninitialized()) { if (m_type == Type::DoubleRecurrence) { - Layout indexLayout = LayoutHelper::String(buffer, strlen(buffer), KDFont::LargeFont); - m_secondInitialConditionName = HorizontalLayout( - CharLayout(name()[0], KDFont::LargeFont), - VerticalOffsetLayout(indexLayout, VerticalOffsetLayoutNode::Type::Subscript) - ); + Layout indexLayout = LayoutHelper::String(buffer, strlen(buffer), k_layoutFont); + m_secondInitialConditionName = HorizontalLayout::Builder( + CharLayout::Builder(name()[0], k_layoutFont), + VerticalOffsetLayout::Builder(indexLayout, VerticalOffsetLayoutNode::Type::Subscript)); } } return m_secondInitialConditionName; @@ -253,17 +247,16 @@ T Sequence::approximateToNextRank(int n, SequenceContext * sqctx) const { T vn = sqctx->valueOfSequenceAtPreviousRank(1, 0); T vnm1 = sqctx->valueOfSequenceAtPreviousRank(1, 1); T vnm2 = sqctx->valueOfSequenceAtPreviousRank(1, 2); - Poincare::Symbol vnSymbol("v(n)", 4); - Poincare::Symbol vn1Symbol("v(n+1)", 6); - Poincare::Symbol unSymbol("u(n)", 4); - Poincare::Symbol un1Symbol("u(n+1)", 6); - Preferences * preferences = Poincare::Preferences::sharedPreferences(); + Poincare::Symbol vnSymbol = Symbol::Builder("v(n)", 4); + Poincare::Symbol vn1Symbol = Symbol::Builder("v(n+1)", 6); + Poincare::Symbol unSymbol = Symbol::Builder("u(n)", 4); + Poincare::Symbol un1Symbol = Symbol::Builder("u(n+1)", 6); switch (m_type) { case Type::Explicit: { ctx.setValueForSymbol(un, unSymbol); ctx.setValueForSymbol(vn, vnSymbol); - return expression(sqctx).approximateWithValueForSymbol(symbol(), (T)n, ctx, preferences->angleUnit()); + return PoincareHelpers::ApproximateWithValueForSymbol(expression(sqctx), symbol(), (T)n, ctx); } case Type::SingleRecurrence: { @@ -274,7 +267,7 @@ T Sequence::approximateToNextRank(int n, SequenceContext * sqctx) const { ctx.setValueForSymbol(unm1, unSymbol); ctx.setValueForSymbol(vn, vn1Symbol); ctx.setValueForSymbol(vnm1, vnSymbol); - return expression(sqctx).approximateWithValueForSymbol(symbol(), (T)(n-1), ctx, preferences->angleUnit()); + return PoincareHelpers::ApproximateWithValueForSymbol(expression(sqctx), symbol(), (T)(n-1), ctx); } default: { @@ -288,7 +281,7 @@ T Sequence::approximateToNextRank(int n, SequenceContext * sqctx) const { ctx.setValueForSymbol(unm2, unSymbol); ctx.setValueForSymbol(vnm1, vn1Symbol); ctx.setValueForSymbol(vnm2, vnSymbol); - return expression(sqctx).approximateWithValueForSymbol(symbol(), (T)(n-2), ctx, preferences->angleUnit()); + return PoincareHelpers::ApproximateWithValueForSymbol(expression(sqctx), symbol(), (T)(n-2), ctx); } } } diff --git a/apps/sequence/sequence.h b/apps/sequence/sequence.h index 54209258b..bd9deb493 100644 --- a/apps/sequence/sequence.h +++ b/apps/sequence/sequence.h @@ -51,6 +51,7 @@ public: void tidy() override; constexpr static int k_initialRankNumberOfDigits = 3; // m_initialRank is capped by 999 private: + constexpr static const KDFont * k_layoutFont = KDFont::LargeFont; constexpr static double k_maxNumberOfTermsInSum = 100000.0; constexpr static size_t k_dataLengthInBytes = (3*TextField::maxBufferSize()+3)*sizeof(char)+sizeof(int)+1; static_assert((k_dataLengthInBytes & 0x3) == 0, "The sequence data size is not a multiple of 4 bytes (cannot compute crc)"); // Assert that dataLengthInBytes is a multiple of 4 diff --git a/apps/sequence/sequence_title_cell.cpp b/apps/sequence/sequence_title_cell.cpp index 0bb1d9bb9..e2357959d 100644 --- a/apps/sequence/sequence_title_cell.cpp +++ b/apps/sequence/sequence_title_cell.cpp @@ -7,9 +7,21 @@ using namespace Poincare; namespace Sequence { SequenceTitleCell::SequenceTitleCell() : - Shared::FunctionTitleCell(), - m_titleTextView(0.5f, 0.5f) + Shared::FunctionTitleCell(Orientation::VerticalIndicator), + m_titleTextView(k_verticalOrientationHorizontalAlignment, k_horizontalOrientationAlignment) { + m_titleTextView.setRightMargin(3); +} + +void SequenceTitleCell::setOrientation(Orientation orientation) { + if (orientation == Orientation::VerticalIndicator) { + /* We do not care here about the vertical alignment, it will be set properly + * in layoutSubviews */ + m_titleTextView.setAlignment(k_verticalOrientationHorizontalAlignment, k_verticalOrientationHorizontalAlignment); + } else { + m_titleTextView.setAlignment(k_horizontalOrientationAlignment, k_horizontalOrientationAlignment); + } + FunctionTitleCell::setOrientation(orientation); } void SequenceTitleCell::setLayout(Poincare::Layout layout) { @@ -41,11 +53,16 @@ View * SequenceTitleCell::subviewAtIndex(int index) { } void SequenceTitleCell::layoutSubviews() { - KDRect textFrame(0, k_colorIndicatorThickness, bounds().width(), bounds().height() - k_colorIndicatorThickness); - if (m_orientation == Orientation::VerticalIndicator){ - textFrame = KDRect(k_colorIndicatorThickness, 0, bounds().width() - k_colorIndicatorThickness-k_separatorThickness, bounds().height()-k_separatorThickness); + if (m_orientation == Orientation::VerticalIndicator) { + m_titleTextView.setAlignment(k_verticalOrientationHorizontalAlignment, verticalAlignment()); } - m_titleTextView.setFrame(textFrame); + m_titleTextView.setFrame(subviewFrame()); +} + +float SequenceTitleCell::verticalAlignmentGivenExpressionBaselineAndRowHeight(KDCoordinate expressionBaseline, KDCoordinate rowHeight) const { + assert(m_orientation == Orientation::VerticalIndicator); + Layout l = layout(); + return ((float)(expressionBaseline - l.baseline()))/((float)rowHeight-l.layoutSize().height()); } } diff --git a/apps/sequence/sequence_title_cell.h b/apps/sequence/sequence_title_cell.h index ae719a297..1f6b92c67 100644 --- a/apps/sequence/sequence_title_cell.h +++ b/apps/sequence/sequence_title_cell.h @@ -13,6 +13,7 @@ public: void setEven(bool even) override; void setHighlighted(bool highlight) override; void setColor(KDColor color) override; + void setOrientation(Orientation orientation) override; const KDFont * font() const override { return Poincare::CharLayoutNode::k_defaultFont; } @@ -20,9 +21,12 @@ public: return m_titleTextView.layout(); } private: + static constexpr float k_horizontalOrientationAlignment = 0.5f; + static constexpr float k_verticalOrientationHorizontalAlignment = 0.9f; int numberOfSubviews() const override; View * subviewAtIndex(int index) override; void layoutSubviews() override; + float verticalAlignmentGivenExpressionBaselineAndRowHeight(KDCoordinate expressionBaseline, KDCoordinate rowHeight) const override; EvenOddExpressionCell m_titleTextView; }; diff --git a/apps/settings/Makefile b/apps/settings/Makefile index d19995855..7061f6951 100644 --- a/apps/settings/Makefile +++ b/apps/settings/Makefile @@ -1,17 +1,17 @@ apps += Settings::App app_headers += apps/settings/app.h -app_objs += $(addprefix apps/settings/,\ - app.o\ - main_controller.o\ - settings_message_tree.o\ - sub_menu/about_controller.o\ - sub_menu/display_mode_controller.o\ - sub_menu/exam_mode_controller.o\ - sub_menu/generic_sub_controller.o\ - sub_menu/language_controller.o\ - sub_menu/message_table_cell_with_editable_text_with_separator.o\ - sub_menu/preferences_controller.o\ +app_src += $(addprefix apps/settings/,\ + app.cpp \ + main_controller.cpp \ + settings_message_tree.cpp \ + sub_menu/about_controller.cpp \ + sub_menu/display_mode_controller.cpp \ + sub_menu/exam_mode_controller.cpp \ + sub_menu/generic_sub_controller.cpp \ + sub_menu/language_controller.cpp \ + sub_menu/message_table_cell_with_editable_text_with_separator.cpp \ + sub_menu/preferences_controller.cpp \ ) i18n_files += $(addprefix apps/settings/,\ @@ -22,4 +22,4 @@ i18n_files += $(addprefix apps/settings/,\ base.pt.i18n\ ) -app_images += apps/settings/settings_icon.png +$(eval $(call depends_on_image,apps/settings/app.cpp,apps/settings/settings_icon.png)) diff --git a/apps/settings/app.cpp b/apps/settings/app.cpp index 891f2552d..d096c3d90 100644 --- a/apps/settings/app.cpp +++ b/apps/settings/app.cpp @@ -1,6 +1,6 @@ #include "app.h" #include "settings_icon.h" -#include "../i18n.h" +#include namespace Settings { diff --git a/apps/settings/base.de.i18n b/apps/settings/base.de.i18n index 9ddd2b4f5..b0a434359 100644 --- a/apps/settings/base.de.i18n +++ b/apps/settings/base.de.i18n @@ -9,12 +9,13 @@ ComplexFormat = "Komplex" ExamMode = "Testmodus" ActivateExamMode = "Start Testmodus" ExamModeActive = "Testmodus: aktiv" -About = "UEber" +About = "Ueber" Degres = "Grad " Radian = "Bogenmass " Decimal = "Dezimal " Scientific = "Wissenschaftlich " SignificantFigures = "Signifikante Stellen " +Real = "Reel " Cartesian = "Algebraische " Polar = "Polar " Brightness = "Helligkeit" diff --git a/apps/settings/base.en.i18n b/apps/settings/base.en.i18n index f79175be7..3b706c0e7 100644 --- a/apps/settings/base.en.i18n +++ b/apps/settings/base.en.i18n @@ -15,6 +15,7 @@ Radian = "Radians " Decimal = "Decimal " Scientific = "Scientific " SignificantFigures = "Significant figures " +Real = "Real " Cartesian = "Cartesian " Polar = "Polar " Brightness = "Brightness" diff --git a/apps/settings/base.es.i18n b/apps/settings/base.es.i18n index 94555dce2..68d1ca4b5 100644 --- a/apps/settings/base.es.i18n +++ b/apps/settings/base.es.i18n @@ -15,6 +15,7 @@ Radian = "Radianes " Decimal = "Decimal " Scientific = "Cientifico " SignificantFigures = "Cifras significativas " +Real = "Real " Cartesian = "Binómica " Polar = "Polar " Brightness = "Brillo" diff --git a/apps/settings/base.fr.i18n b/apps/settings/base.fr.i18n index d79d419ca..c2d22b74a 100644 --- a/apps/settings/base.fr.i18n +++ b/apps/settings/base.fr.i18n @@ -15,6 +15,7 @@ Radian = "Radians " Decimal = "Decimal " Scientific = "Scientifique " SignificantFigures = "Chiffres significatifs " +Real = "Réel " Cartesian = "Algébrique " Polar = "Exponentielle " Brightness = "Luminosite" diff --git a/apps/settings/base.pt.i18n b/apps/settings/base.pt.i18n index b4f6c45aa..86f277900 100644 --- a/apps/settings/base.pt.i18n +++ b/apps/settings/base.pt.i18n @@ -15,6 +15,7 @@ Radian = "Radianos " Decimal = "Decimal " Scientific = "Cientifico " SignificantFigures = "Algarismo significativo " +Real = "Real " Cartesian = "Cartesiana " Polar = "Polar " Brightness = "Brilho" diff --git a/apps/settings/main_controller.cpp b/apps/settings/main_controller.cpp index 0476e917a..df748f77e 100644 --- a/apps/settings/main_controller.cpp +++ b/apps/settings/main_controller.cpp @@ -1,6 +1,6 @@ #include "main_controller.h" #include "../global_preferences.h" -#include "../i18n.h" +#include #include using namespace Poincare; @@ -10,7 +10,7 @@ namespace Settings { const SettingsMessageTree angleChildren[2] = {SettingsMessageTree(I18n::Message::Degres), SettingsMessageTree(I18n::Message::Radian)}; const SettingsMessageTree editionModeChildren[2] = {SettingsMessageTree(I18n::Message::Edition2D), SettingsMessageTree(I18n::Message::EditionLinear)}; const SettingsMessageTree floatDisplayModeChildren[3] = {SettingsMessageTree(I18n::Message::Decimal), SettingsMessageTree(I18n::Message::Scientific), SettingsMessageTree(I18n::Message::SignificantFigures)}; -const SettingsMessageTree complexFormatChildren[2] = {SettingsMessageTree(I18n::Message::Cartesian), SettingsMessageTree(I18n::Message::Polar)}; +const SettingsMessageTree complexFormatChildren[3] = {SettingsMessageTree(I18n::Message::Real), SettingsMessageTree(I18n::Message::Cartesian), SettingsMessageTree(I18n::Message::Polar)}; const SettingsMessageTree examChildren[1] = {SettingsMessageTree(I18n::Message::ActivateExamMode)}; const SettingsMessageTree aboutChildren[3] = {SettingsMessageTree(I18n::Message::SoftwareVersion), SettingsMessageTree(I18n::Message::SerialNumber), SettingsMessageTree(I18n::Message::FccId)}; @@ -22,7 +22,7 @@ const SettingsMessageTree menu[8] = {SettingsMessageTree(I18n::Message::AngleUnit, angleChildren, 2), SettingsMessageTree(I18n::Message::DisplayMode, floatDisplayModeChildren, 3), SettingsMessageTree(I18n::Message::EditionMode, editionModeChildren, 2), - SettingsMessageTree(I18n::Message::ComplexFormat, complexFormatChildren, 2), + SettingsMessageTree(I18n::Message::ComplexFormat, complexFormatChildren, 3), SettingsMessageTree(I18n::Message::Brightness), SettingsMessageTree(I18n::Message::Language), SettingsMessageTree(I18n::Message::ExamMode, examChildren, 1), diff --git a/apps/settings/sub_menu/preferences_controller.cpp b/apps/settings/sub_menu/preferences_controller.cpp index eadb33627..bb96f02cf 100644 --- a/apps/settings/sub_menu/preferences_controller.cpp +++ b/apps/settings/sub_menu/preferences_controller.cpp @@ -60,7 +60,7 @@ Layout layoutForPreferences(I18n::Message message) { case I18n::Message::Radian: { const char pi[] = {Ion::Charset::SmallPi}; - return FractionLayout( + return FractionLayout::Builder( LayoutHelper::String(pi, sizeof(pi), KDFont::SmallFont), LayoutHelper::String("2", 1, KDFont::SmallFont) ); @@ -75,13 +75,17 @@ Layout layoutForPreferences(I18n::Message message) { } // Edition mode case I18n::Message::Edition2D: - return HorizontalLayout( + return HorizontalLayout::Builder( LayoutHelper::String("1+", 2, KDFont::SmallFont), - FractionLayout(LayoutHelper::String("2", 1, KDFont::SmallFont), LayoutHelper::String("3", 1, KDFont::SmallFont)) + FractionLayout::Builder(LayoutHelper::String("2", 1, KDFont::SmallFont), LayoutHelper::String("3", 1, KDFont::SmallFont)) ); case I18n::Message::EditionLinear: return LayoutHelper::String("1+2/3", 5, KDFont::SmallFont); // Complex format + case I18n::Message::Real: + { + return CharLayout::Builder('x', KDFont::SmallFont); + } case I18n::Message::Cartesian: { const char text[] = {'a','+', Ion::Charset::IComplex, 'b'}; @@ -91,9 +95,9 @@ Layout layoutForPreferences(I18n::Message message) { { const char base[] = {'r', Ion::Charset::Exponential}; const char superscript[] = {Ion::Charset::IComplex, Ion::Charset::SmallTheta}; - return HorizontalLayout( + return HorizontalLayout::Builder( LayoutHelper::String(base, sizeof(base), KDFont::SmallFont), - VerticalOffsetLayout(LayoutHelper::String(superscript, sizeof(superscript), KDFont::SmallFont), VerticalOffsetLayoutNode::Type::Superscript) + VerticalOffsetLayout::Builder(LayoutHelper::String(superscript, sizeof(superscript), KDFont::SmallFont), VerticalOffsetLayoutNode::Type::Superscript) ); } default: diff --git a/apps/settings/sub_menu/preferences_controller.h b/apps/settings/sub_menu/preferences_controller.h index 2ed186d58..5648feccb 100644 --- a/apps/settings/sub_menu/preferences_controller.h +++ b/apps/settings/sub_menu/preferences_controller.h @@ -15,7 +15,7 @@ public: void willDisplayCellForIndex(HighlightCell * cell, int index) override; KDCoordinate rowHeight(int j) override; protected: - constexpr static int k_totalNumberOfCell = 2; + constexpr static int k_totalNumberOfCell = 3; private: void setPreferenceWithValueIndex(I18n::Message message, int valueIndex); int valueIndexForPreference(I18n::Message message); diff --git a/apps/shared.de.i18n b/apps/shared.de.i18n index c395cc81f..26fed1661 100644 --- a/apps/shared.de.i18n +++ b/apps/shared.de.i18n @@ -32,6 +32,7 @@ Language = "Sprache" LowBattery = "Batterie erschoepft" Mean = "Mittelwert" Move = " Verschieben: " +NameCannotStartWithNumber = "Ein Name darf nicht mit einer Zahl beginnen" NameTaken = "Dieser Name ist bereits vergeben" NameTooLong = "Der Name ist zu lang" Next = "Naechste" diff --git a/apps/shared.en.i18n b/apps/shared.en.i18n index ce0555eb7..8cff681b6 100644 --- a/apps/shared.en.i18n +++ b/apps/shared.en.i18n @@ -32,6 +32,7 @@ Language = "Language" LowBattery = "Low battery" Mean = "Mean" Move = " Move: " +NameCannotStartWithNumber = "A name cannot start with a number" NameTaken = "This name has already been taken" NameTooLong = "This name is too long" Next = "Next" diff --git a/apps/shared.es.i18n b/apps/shared.es.i18n index ff3874dfe..697790e12 100644 --- a/apps/shared.es.i18n +++ b/apps/shared.es.i18n @@ -32,6 +32,7 @@ Language = "Idioma" LowBattery = "Bateria baja" Mean = "Media" Move = " Mover : " +NameCannotStartWithNumber = "Un nombre no puede empezar con un número" NameTaken = "Este nombre ya está en uso" NameTooLong = "Este nombre es demasiado largo" Next = "Siguiente" diff --git a/apps/shared.fr.i18n b/apps/shared.fr.i18n index fb485d58e..df664c364 100644 --- a/apps/shared.fr.i18n +++ b/apps/shared.fr.i18n @@ -32,6 +32,7 @@ Language = "Langue" LowBattery = "Batterie faible" Mean = "Moyenne" Move = " Deplacer : " +NameCannotStartWithNumber = "Un nom ne peut pas commencer par un chiffre" NameTaken = "Ce nom est déjà utilisé" NameTooLong = "Ce nom est trop long" Next = "Suivant" diff --git a/apps/shared.pt.i18n b/apps/shared.pt.i18n index bb68a1858..9e12ec57a 100644 --- a/apps/shared.pt.i18n +++ b/apps/shared.pt.i18n @@ -32,6 +32,7 @@ Language = "Idioma" LowBattery = "Bateria fraca" Mean = "Media" Move = " Mover : " +NameCannotStartWithNumber = "Um nome não pode começar com um número" NameTaken = "Este nome é já usado" NameTooLong = "Este nome é muito longo" Next = "Seguinte" diff --git a/apps/shared/Makefile b/apps/shared/Makefile index db1d6dd7f..93e7e4d52 100644 --- a/apps/shared/Makefile +++ b/apps/shared/Makefile @@ -1,88 +1,88 @@ -app_objs += $(addprefix apps/shared/,\ - banner_view.o\ - buffer_function_title_cell.o\ - buffer_text_view_with_text_field.o\ - button_with_separator.o\ - cursor_view.o\ - curve_view.o\ - curve_view_cursor.o\ - curve_view_range.o\ - double_pair_store.o\ - editable_cell_table_view_controller.o\ - expression_field_delegate_app.o\ - expression_model.o\ - expression_model_list_controller.o\ - expression_model_store.o\ - float_parameter_controller.o\ - function.o\ - function_app.o\ - function_banner_delegate.o\ - function_curve_parameter_controller.o\ - function_expression_cell.o\ - function_go_to_parameter_controller.o\ - function_graph_view.o\ - function_graph_controller.o\ - function_list_controller.o\ - function_store.o\ - function_title_cell.o\ - global_context.o\ - go_to_parameter_controller.o\ - hideable_even_odd_cell.o\ - hideable_even_odd_editable_text_cell.o\ - initialisation_parameter_controller.o\ - input_event_handler_delegate_app.o\ - interactive_curve_view_controller.o\ - interactive_curve_view_range.o\ - interactive_curve_view_range_delegate.o\ - interval.o\ - interval_parameter_controller.o\ - language_controller.o\ - layout_field_delegate.o\ - list_parameter_controller.o\ - margin_even_odd_message_text_cell.o\ - memoized_curve_view_range.o\ - message_view.o\ - ok_view.o\ - parameter_text_field_delegate.o\ - range_parameter_controller.o\ - regular_table_view_data_source.o\ - round_cursor_view.o\ - scrollable_exact_approximate_expressions_cell.o\ - scrollable_exact_approximate_expressions_view.o\ - separator_even_odd_buffer_text_cell.o\ - simple_interactive_curve_view_controller.o\ - storage_cartesian_function.o\ - storage_expression_model.o\ - storage_expression_model_store.o\ - storage_expression_model_list_controller.o\ - storage_function.o\ - storage_function_app.o\ - storage_function_banner_delegate.o\ - storage_function_curve_parameter_controller.o\ - storage_function_go_to_parameter_controller.o\ - storage_function_graph_controller.o\ - storage_function_graph_view.o\ - storage_function_list_controller.o\ - storage_function_store.o\ - storage_list_parameter_controller.o\ - storage_sum_graph_controller.o\ - storage_values_function_parameter_controller.o\ - storage_values_controller.o\ - store_cell.o\ - store_context.o\ - store_controller.o\ - store_parameter_controller.o\ - store_selectable_table_view.o\ - store_title_cell.o\ - sum_graph_controller.o\ - tab_table_controller.o\ - text_field_delegate.o\ - text_field_delegate_app.o\ - text_field_with_extension.o\ - toolbox_helpers.o\ - values_function_parameter_controller.o\ - values_parameter_controller.o\ - values_controller.o\ - vertical_cursor_view.o\ - zoom_parameter_controller.o\ +app_src += $(addprefix apps/shared/,\ + banner_view.cpp \ + buffer_function_title_cell.cpp \ + buffer_text_view_with_text_field.cpp \ + button_with_separator.cpp \ + cursor_view.cpp \ + curve_view.cpp \ + curve_view_cursor.cpp \ + curve_view_range.cpp \ + double_pair_store.cpp \ + editable_cell_table_view_controller.cpp \ + expression_field_delegate_app.cpp \ + expression_model.cpp \ + expression_model_list_controller.cpp \ + expression_model_store.cpp \ + float_parameter_controller.cpp \ + function.cpp \ + function_app.cpp \ + function_banner_delegate.cpp \ + function_curve_parameter_controller.cpp \ + function_expression_cell.cpp \ + function_go_to_parameter_controller.cpp \ + function_graph_controller.cpp \ + function_graph_view.cpp \ + function_list_controller.cpp \ + function_store.cpp \ + function_title_cell.cpp \ + global_context.cpp \ + go_to_parameter_controller.cpp \ + hideable_even_odd_cell.cpp \ + hideable_even_odd_editable_text_cell.cpp \ + initialisation_parameter_controller.cpp \ + input_event_handler_delegate_app.cpp \ + interactive_curve_view_controller.cpp \ + interactive_curve_view_range.cpp \ + interactive_curve_view_range_delegate.cpp \ + interval.cpp \ + interval_parameter_controller.cpp \ + language_controller.cpp \ + layout_field_delegate.cpp \ + list_parameter_controller.cpp \ + margin_even_odd_message_text_cell.cpp \ + memoized_curve_view_range.cpp \ + message_view.cpp \ + ok_view.cpp \ + parameter_text_field_delegate.cpp \ + range_parameter_controller.cpp \ + regular_table_view_data_source.cpp \ + round_cursor_view.cpp \ + scrollable_exact_approximate_expressions_cell.cpp \ + scrollable_exact_approximate_expressions_view.cpp \ + separator_even_odd_buffer_text_cell.cpp \ + simple_interactive_curve_view_controller.cpp \ + storage_cartesian_function.cpp \ + storage_expression_model.cpp \ + storage_expression_model_list_controller.cpp \ + storage_expression_model_store.cpp \ + storage_function.cpp \ + storage_function_app.cpp \ + storage_function_banner_delegate.cpp \ + storage_function_curve_parameter_controller.cpp \ + storage_function_go_to_parameter_controller.cpp \ + storage_function_graph_controller.cpp \ + storage_function_graph_view.cpp \ + storage_function_list_controller.cpp \ + storage_function_store.cpp \ + storage_list_parameter_controller.cpp \ + storage_sum_graph_controller.cpp \ + storage_values_controller.cpp \ + storage_values_function_parameter_controller.cpp \ + store_cell.cpp \ + store_context.cpp \ + store_controller.cpp \ + store_parameter_controller.cpp \ + store_selectable_table_view.cpp \ + store_title_cell.cpp \ + sum_graph_controller.cpp \ + tab_table_controller.cpp \ + text_field_delegate.cpp \ + text_field_delegate_app.cpp \ + text_field_with_extension.cpp \ + toolbox_helpers.cpp \ + values_controller.cpp \ + values_function_parameter_controller.cpp \ + values_parameter_controller.cpp \ + vertical_cursor_view.cpp \ + zoom_parameter_controller.cpp \ ) diff --git a/apps/shared/curve_view.cpp b/apps/shared/curve_view.cpp index 367c57763..d60a29130 100644 --- a/apps/shared/curve_view.cpp +++ b/apps/shared/curve_view.cpp @@ -1,16 +1,17 @@ #include "curve_view.h" #include "../constant.h" +#include #include #include #include #include -#include - using namespace Poincare; namespace Shared { +static inline int minInt(int x, int y) { return (x < y ? x : y); } + CurveView::CurveView(CurveViewRange * curveViewRange, CurveViewCursor * curveViewCursor, BannerView * bannerView, View * cursorView, View * okView, bool displayBanner) : View(), @@ -91,6 +92,29 @@ float CurveView::samplingRatio() const { return 1.1f; } +void CurveView::drawGridLines(KDContext * ctx, KDRect rect, Axis axis, float step, KDColor boldColor, KDColor lightColor) const { + Axis otherAxis = (axis == Axis::Horizontal) ? Axis::Vertical : Axis::Horizontal; + /* We translate the pixel coordinates into floats, adding/subtracting 1 to + * account for conversion errors. */ + float otherAxisMin = pixelToFloat(otherAxis, otherAxis == Axis::Horizontal ? rect.left() - 1 : rect.bottom() + 1); + float otherAxisMax = pixelToFloat(otherAxis, otherAxis == Axis::Horizontal ? rect.right() + 1 : rect.top() - 1); + float start = step * ((int)(min(otherAxis)/step)); + float boldStart = 2*step * ((int)(min(otherAxis)/(2*step))); + bool drawBold = std::fabs(start - boldStart) < FLT_EPSILON; + + for (float x = start; x < max(otherAxis); x+= step) { + /* When |start| >> step, start + step = start. In that case, quit the + * infinite loop. */ + if (x == x-step || x == x+step) { + return; + } + if (otherAxisMin <= x && x <= otherAxisMax) { + drawLine(ctx, rect, axis, x, drawBold ? boldColor : lightColor); + } + drawBold = !drawBold; + } +} + float CurveView::min(Axis axis) const { assert(axis == Axis::Horizontal || axis == Axis::Vertical); return (axis == Axis::Horizontal ? m_curveViewRange->xMin(): m_curveViewRange->yMin()); @@ -110,9 +134,18 @@ KDCoordinate CurveView::pixelLength(Axis axis) const { return (axis == Axis::Horizontal ? m_frame.width() : m_frame.height()); } +int CurveView::numberOfLabels(Axis axis) const { + float labelStep = 2.0f * gridUnit(axis); + float minLabel = std::ceil(min(axis)/labelStep); + float maxLabel = std::floor(max(axis)/labelStep); + return maxLabel - minLabel + 1; +} + float CurveView::pixelToFloat(Axis axis, KDCoordinate p) const { - KDCoordinate pixels = axis == Axis::Horizontal ? p : pixelLength(axis)-p; - return min(axis) + pixels*((max(axis)-min(axis))/pixelLength(axis)); + float pixelLen = pixelLength(axis); + float minA = min(axis); + KDCoordinate pixels = axis == Axis::Horizontal ? p : pixelLen - p; + return minA + pixels*(max(axis)-minA)/pixelLen; } float CurveView::floatToPixel(Axis axis, float f) const { @@ -132,51 +165,186 @@ float CurveView::floatToPixel(Axis axis, float f) const { void CurveView::computeLabels(Axis axis) { float step = gridUnit(axis); - int labelsCount = numberOfLabels(axis); - for (int index = 0; index < labelsCount; index++) { - float labelValue = 2.0f*step*(std::ceil(min(axis)/(2.0f*step)))+index*2.0f*step; + int axisLabelsCount = numberOfLabels(axis); + for (int i = 0; i < axisLabelsCount; i++) { + float labelValue = labelValueAtIndex(axis, i); + /* Label cannot hold more than k_labelBufferMaxSize characters to prevent + * them from overprinting one another.*/ + int labelMaxSize = k_labelBufferMaxSize; + if (axis == Axis::Horizontal) { + float pixelsPerLabel = ((float)Ion::Display::Width)/((float)axisLabelsCount) - k_labelMargin; + labelMaxSize = minInt(k_labelBufferMaxSize, pixelsPerLabel/k_font->glyphSize().width()); + } + if (labelValue < step && labelValue > -step) { + // Make sure the 0 value is really written 0 labelValue = 0.0f; } + /* Label cannot hold more than k_labelBufferSize characters to prevent them - * from overprinting one another.*/ - PrintFloat::convertFloatToText(labelValue, label(axis, index), k_labelBufferSize, - Constant::ShortNumberOfSignificantDigits, Preferences::PrintFloatMode::Decimal); + * from overprinting one another. */ + + char * labelBuffer = label(axis, i); + PrintFloat::convertFloatToText( + labelValue, + labelBuffer, + labelMaxSize, + k_numberSignificantDigits, + Preferences::PrintFloatMode::Decimal, + axis == Axis::Vertical); + + if (axis == Axis::Horizontal) { + if (labelBuffer[0] == 0) { + /* Some labels are too big and may overlap their neighbours. We write the + * extrema labels only. */ + computeHorizontalExtremaLabels(); + return; + } + if (i > 0 && strcmp(labelBuffer, label(axis, i-1)) == 0) { + /* We need to increase the number if significant digits, otherwise some + * labels are rounded to the same value. */ + computeHorizontalExtremaLabels(true); + return; + } + } } } -void CurveView::drawLabels(KDContext * ctx, KDRect rect, Axis axis, bool shiftOrigin, bool graduationOnly, bool fixCoordinate, KDCoordinate fixedCoordinate) const { - float step = gridUnit(axis); - float start = 2.0f*step*(std::ceil(min(axis)/(2.0f*step))); - float end = max(axis); +enum class FloatingPosition : uint8_t { + None, + Min, + Max +}; + +void CurveView::simpleDrawBothAxesLabels(KDContext * ctx, KDRect rect) const { + drawLabels(ctx, rect, Axis::Vertical, true); + drawLabels(ctx, rect, Axis::Horizontal, true); +} + +void CurveView::drawLabels(KDContext * ctx, KDRect rect, Axis axis, bool shiftOrigin, bool graduationOnly, bool fixCoordinate, KDCoordinate fixedCoordinate, KDColor backgroundColor) const { + int numberLabels = numberOfLabels(axis); + if (numberLabels <= 1) { + return; + } + float verticalCoordinate = fixCoordinate ? fixedCoordinate : std::round(floatToPixel(Axis::Vertical, 0.0f)); float horizontalCoordinate = fixCoordinate ? fixedCoordinate : std::round(floatToPixel(Axis::Horizontal, 0.0f)); - int i = 0; - for (float x = start; x < end; x += 2.0f*step) { - /* When |start| >> step, start + step = start. In that case, quit the - * infinite loop. */ - if (x == x-step || x == x+step) { - return; + + int viewHeight = bounds().height() - (bannerIsVisible() ? m_bannerView->minimalSizeForOptimalDisplay().height() : 0); + + /* If the axis is not visible, draw floating labels on the edge of the screen. + * The X axis floating status is needed when drawing both axes labels. */ + FloatingPosition floatingHorizontalLabels = FloatingPosition::None; + if (verticalCoordinate > viewHeight - k_font->glyphSize().height() - k_labelMargin) { + floatingHorizontalLabels = FloatingPosition::Max; + } else if (max(Axis::Vertical) < 0.0f) { + floatingHorizontalLabels = FloatingPosition::Min; + } + + FloatingPosition floatingLabels = FloatingPosition::None; + if (axis == Axis::Horizontal) { + floatingLabels = floatingHorizontalLabels; + } else { + if (horizontalCoordinate < k_labelMargin + k_font->glyphSize().width() * 3) { // We want do display at least 3 characters left of the Y axis + floatingLabels = FloatingPosition::Min; + } else if (max(Axis::Horizontal) < 0.0f) { + floatingLabels = FloatingPosition::Max; } - KDRect graduation(std::round(floatToPixel(Axis::Horizontal, x)), verticalCoordinate -(k_labelGraduationLength-2)/2, 1, k_labelGraduationLength); - if (axis == Axis::Vertical) { - graduation = KDRect(horizontalCoordinate-(k_labelGraduationLength-2)/2, std::round(floatToPixel(Axis::Vertical, x)), k_labelGraduationLength, 1); + } + + /* There might be less labels than graduations, if the extrema labels are too + * close to the screen edge to write them. We must thus draw the graduations + * separately from the labels. */ + + float labelStep = 2.0f * gridUnit(axis); + int minLabelPixelPosition = std::round(floatToPixel(axis, labelStep * std::ceil(min(axis)/labelStep))); + int maxLabelPixelPosition = std::round(floatToPixel(axis, labelStep * std::floor(max(axis)/labelStep))); + + // Draw the graduations + + int minDrawnLabel = 0; + int maxDrawnLabel = numberLabels; + if (axis == Axis::Vertical) { + /* Do not draw an extremal vertical label if it collides with the horizontal + * labels */ + int horizontalLabelsMargin = k_font->glyphSize().height() * 2; + if (floatingHorizontalLabels == FloatingPosition::Min + && maxLabelPixelPosition < horizontalLabelsMargin) { + maxDrawnLabel--; + } else if (floatingHorizontalLabels == FloatingPosition::Max + && minLabelPixelPosition > viewHeight - horizontalLabelsMargin) + { + minDrawnLabel++; } - if (!graduationOnly) { - KDSize textSize = KDFont::SmallFont->stringSize(label(axis, i)); - KDPoint origin(std::round(floatToPixel(Axis::Horizontal, x)) - textSize.width()/2, verticalCoordinate + k_labelMargin); - if (axis == Axis::Vertical) { - origin = KDPoint(horizontalCoordinate + k_labelMargin, std::round(floatToPixel(Axis::Vertical, x)) - textSize.height()/2); + } + + if (floatingLabels == FloatingPosition::None) { + for (int i = minDrawnLabel; i < maxDrawnLabel; i++) { + int labelPosition = minLabelPixelPosition + (((float)i)/((float)numberLabels-1)) * (maxLabelPixelPosition - minLabelPixelPosition); + KDRect graduation = axis == Axis::Horizontal ? + KDRect( + labelPosition, + verticalCoordinate -(k_labelGraduationLength-2)/2, + 1, + k_labelGraduationLength) : + KDRect( + horizontalCoordinate-(k_labelGraduationLength-2)/2, + labelPosition, + k_labelGraduationLength, + 1); + ctx->fillRect(graduation, KDColorBlack); + } + } + + if (graduationOnly) { + return; + } + + // Draw the labels + for (int i = minDrawnLabel; i < maxDrawnLabel; i++) { + int labelPosition = minLabelPixelPosition + (((float)i)/((float)numberLabels-1)) * (maxLabelPixelPosition - minLabelPixelPosition); + char * labelI = label(axis, i); + KDSize textSize = k_font->stringSize(labelI); + float xPosition = 0.0f; + float yPosition = 0.0f; + + bool positioned = false; + if (strcmp(labelI, "0") == 0) { + if (floatingLabels != FloatingPosition::None) { + // Do not draw the zero, it is symbolized by the other axis + continue; } - if (-step < x && x < step && shiftOrigin) { - origin = KDPoint(horizontalCoordinate + k_labelMargin, verticalCoordinate + k_labelMargin); - } - if (rect.intersects(KDRect(origin, KDFont::SmallFont->stringSize(label(axis, i))))) { - ctx->drawString(label(axis, i), origin, KDFont::SmallFont, KDColorBlack); + if (shiftOrigin && floatingLabels == FloatingPosition::None) { + xPosition = horizontalCoordinate - k_labelMargin - textSize.width(); + yPosition = verticalCoordinate + k_labelMargin; + positioned = true; } } - ctx->fillRect(graduation, KDColorBlack); - i++; + if (!positioned) { + if (axis == Axis::Horizontal) { + xPosition = labelPosition - textSize.width()/2; + if (floatingLabels == FloatingPosition::None) { + yPosition = verticalCoordinate + k_labelMargin; + } else if (floatingLabels == FloatingPosition::Min) { + yPosition = k_labelMargin; + } else { + yPosition = viewHeight - k_font->glyphSize().height() - k_labelMargin; + } + } else { + yPosition = labelPosition - textSize.height()/2; + if (floatingLabels == FloatingPosition::None) { + xPosition = horizontalCoordinate - k_labelMargin - textSize.width(); + } else if (floatingLabels == FloatingPosition::Min) { + xPosition = k_labelMargin; + } else { + xPosition = Ion::Display::Width - textSize.width() - k_labelMargin; + } + } + } + KDPoint origin = KDPoint(xPosition, yPosition); + if (rect.intersects(KDRect(origin, textSize))) { + ctx->drawString(labelI, origin, k_font, KDColorBlack, backgroundColor); + } } } @@ -261,33 +429,19 @@ void CurveView::drawDot(KDContext * ctx, KDRect rect, float x, float y, KDColor } } -void CurveView::drawGridLines(KDContext * ctx, KDRect rect, Axis axis, float step, KDColor color) const { - float rectMin = pixelToFloat(Axis::Horizontal, rect.left()); - float rectMax = pixelToFloat(Axis::Horizontal, rect.right()); - if (axis == Axis::Vertical) { - rectMax = pixelToFloat(Axis::Vertical, rect.top()); - rectMin = pixelToFloat(Axis::Vertical, rect.bottom()); - } - float start = step*((int)(min(axis)/step)); - Axis otherAxis = (axis == Axis::Horizontal) ? Axis::Vertical : Axis::Horizontal; - for (float x =start; x < max(axis); x += step) { - /* When |start| >> step, start + step = start. In that case, quit the - * infinite loop. */ - if (x == x-step || x == x+step) { - return; - } - if (rectMin <= x && x <= rectMax) { - drawLine(ctx, rect, otherAxis, x, color); - } - } -} - void CurveView::drawGrid(KDContext * ctx, KDRect rect) const { - drawGridLines(ctx, rect, Axis::Horizontal, m_curveViewRange->xGridUnit(), Palette::GreyWhite); - drawGridLines(ctx, rect, Axis::Vertical, m_curveViewRange->yGridUnit(), Palette::GreyWhite); + KDColor boldColor = Palette::GreyMiddle; + KDColor lightColor = Palette::GreyWhite; + drawGridLines(ctx, rect, Axis::Vertical, m_curveViewRange->xGridUnit(), boldColor, lightColor); + drawGridLines(ctx, rect, Axis::Horizontal, m_curveViewRange->yGridUnit(), boldColor, lightColor); } -void CurveView::drawAxes(KDContext * ctx, KDRect rect, Axis axis) const { +void CurveView::drawAxes(KDContext * ctx, KDRect rect) const { + drawAxis(ctx, rect, Axis::Vertical); + drawAxis(ctx, rect, Axis::Horizontal); +} + +void CurveView::drawAxis(KDContext * ctx, KDRect rect, Axis axis) const { drawLine(ctx, rect, axis, 0.0f, KDColorBlack, 1); } @@ -428,14 +582,6 @@ void CurveView::drawHistogram(KDContext * ctx, KDRect rect, EvaluateModelWithPar } } -int CurveView::numberOfLabels(Axis axis) const { - Axis otherAxis = axis == Axis::Horizontal ? Axis::Vertical : Axis::Horizontal; - if (min(otherAxis) > 0.0f || max(otherAxis) < 0.0f) { - return 0; - } - return std::ceil((max(axis) - min(axis))/(2*gridUnit(axis))); -} - void CurveView::jointDots(KDContext * ctx, KDRect rect, EvaluateModelWithParameter evaluation, void * model, void * context, float x, float y, float u, float v, KDColor color, int maxNumberOfRecursion) const { float pyf = floatToPixel(Axis::Vertical, y); float pvf = floatToPixel(Axis::Vertical, v); @@ -453,7 +599,7 @@ void CurveView::jointDots(KDContext * ctx, KDRect rect, EvaluateModelWithParamet if (std::isinf(pvf)) { pvf = pvf > 0 ? pixelLength(Axis::Vertical)+stampSize : -stampSize; } - if (pyf - (float)circleDiameter/2.0f < pvf && pvf < pyf + (float)circleDiameter/2.0f) { + if (pyf - ((float)circleDiameter)/2.0f < pvf && pvf < pyf + ((float)circleDiameter)/2.0f) { // the dots are already joined return; } @@ -493,11 +639,15 @@ void CurveView::straightJoinDots(KDContext * ctx, KDRect rect, float pxf, float void CurveView::stampAtLocation(KDContext * ctx, KDRect rect, float pxf, float pyf, KDColor color) const { // We avoid drawing when no part of the stamp is visible - if (pyf < -stampSize || pyf > pixelLength(Axis::Vertical)+stampSize) { + if (pyf < -stampSize - FLT_EPSILON || pyf > pixelLength(Axis::Vertical)+stampSize + FLT_EPSILON) { return; } - KDCoordinate px = pxf; - KDCoordinate py = pyf; + /* When converting floats to KDCoordinate, we need to add -1 if the float is + * negative, otherwise all floats in ]-1.0;1.0[ are converted to 0 and there + * is a blob for x = 0. Try for instance f(x)=cos(x), the blob is at the + * intersection of the curve with the left of the screen. */ + KDCoordinate px = pxf + (pxf >= 0 ? 0 : -1); + KDCoordinate py = pyf + (pyf >= 0 ? 0 : -1); KDRect stampRect(px-(circleDiameter-2)/2, py-(circleDiameter-2)/2, stampSize, stampSize); if (!rect.intersects(stampRect)) { return; @@ -547,7 +697,7 @@ KDRect CurveView::cursorFrame() { KDRect CurveView::bannerFrame() { KDRect bannerFrame = KDRectZero; - if (m_bannerView && m_mainViewSelected) { + if (bannerIsVisible()) { KDCoordinate bannerHeight = m_bannerView->minimalSizeForOptimalDisplay().height(); bannerFrame = KDRect(0, bounds().height()- bannerHeight, bounds().width(), bannerHeight); } @@ -580,15 +730,64 @@ View * CurveView::subviewAtIndex(int index) { if (m_okView != nullptr) { return m_okView; } else { - if (m_bannerView != nullptr) { - return m_bannerView; + if (m_cursorView != nullptr) { + return m_cursorView; } } } - if (index == 1 && m_bannerView != nullptr && m_okView != nullptr) { - return m_bannerView; + if (index == 1 && m_cursorView != nullptr && m_okView != nullptr) { + return m_cursorView; } - return m_cursorView; + return m_bannerView; +} + +void CurveView::computeHorizontalExtremaLabels(bool increaseNumberOfSignificantDigits) { + Axis axis = Axis::Horizontal; + int axisLabelsCount = numberOfLabels(axis); + float minA = min(axis); + + /* We want to draw the extrema labels (0 and numberOfLabels -1), but if they + * might not be fully visible, draw the labels 1 and numberOfLabels - 2. */ + bool skipExtremaLabels = + (axisLabelsCount >= 4) + && ((labelValueAtIndex(axis, 0) - minA)/(max(axis) - minA) < k_labelsHorizontalMarginRatio+FLT_EPSILON); + int firstLabel = skipExtremaLabels ? 1 : 0; + int lastLabel = axisLabelsCount - (skipExtremaLabels ? 2 : 1); + + assert(firstLabel != lastLabel); + + // All labels but the extrema are empty + for (int i = 0; i < firstLabel; i++) { + label(axis, i)[0] = 0; + } + for (int i = firstLabel + 1; i < lastLabel; i++) { + label(axis, i)[0] = 0; + } + for (int i = lastLabel + 1; i < axisLabelsCount; i++) { + label(axis, i)[0] = 0; + } + + int minMax[] = {firstLabel, lastLabel}; + for (int i : minMax) { + // Compute the minimal and maximal label + PrintFloat::convertFloatToText( + labelValueAtIndex(axis, i), + label(axis, i), + k_labelBufferMaxSize, + increaseNumberOfSignificantDigits ? k_bigNumberSignificantDigits : k_numberSignificantDigits, + Preferences::PrintFloatMode::Decimal, + false); + } +} + +float CurveView::labelValueAtIndex(Axis axis, int i) const { + assert(i >= 0 && i < numberOfLabels(axis)); + float labelStep = 2.0f * gridUnit(axis); + return labelStep*(std::ceil(min(axis)/labelStep)+i); +} + +bool CurveView::bannerIsVisible() const { + return m_bannerView && m_mainViewSelected; } } diff --git a/apps/shared/curve_view.h b/apps/shared/curve_view.h index 8b741a902..1d745383c 100644 --- a/apps/shared/curve_view.h +++ b/apps/shared/curve_view.h @@ -6,11 +6,15 @@ #include "curve_view_range.h" #include "curve_view_cursor.h" #include "banner_view.h" +#include namespace Shared { class CurveView : public View { public: + /* We want a 3 characters margin before the first label tick, so that most + * labels appear completely. This gives 3*charWidth/320 = 3*7/320= 0.066 */ + static constexpr float k_labelsHorizontalMarginRatio = 0.066f; typedef float (*EvaluateModelWithParameter)(float t, void * model, void * context); enum class Axis { Horizontal = 0, @@ -34,6 +38,7 @@ public: void setForceOkDisplay(bool force) { m_forceOkDisplay = force; } float resolution() const; protected: + CurveViewRange * curveViewRange() { return m_curveViewRange; } void setCurveViewRange(CurveViewRange * curveViewRange); // Drawing methods virtual float samplingRatio() const; @@ -41,14 +46,9 @@ protected: constexpr static KDCoordinate k_okVerticalMargin = 23; constexpr static KDCoordinate k_okHorizontalMargin = 10; constexpr static KDCoordinate k_labelGraduationLength = 6; - /* The labels are bounds by ±1E8 and ±1E-8 which in worse case can be written - * in 6 characters. - * To avoid overlapping labels, k_labelBufferSize should verify: - * k_labelBufferSize = Ion::Display::Width / ((CurveViewRange::k_maxNumberOfXGridUnits/2)*KDFont::SmallFont->glyphWidth) - * = 320/((18/2)*7) ~ 5. - * We take 6 creating small overlap in worse case but preventing from truncating - * labels (ie, "-1E-"). */ - constexpr static int k_labelBufferSize = 6; + constexpr static int k_numberSignificantDigits = 6; + constexpr static int k_bigNumberSignificantDigits = Constant::LargeNumberOfSignificantDigits; + constexpr static int k_labelBufferMaxSize = 1 + k_bigNumberSignificantDigits + 3 + 3 + 1; // '-' + significant digits + '.' + "E-" + 3 digits + null-terminating char constexpr static int k_maxNumberOfXLabels = CurveViewRange::k_maxNumberOfXGridUnits; constexpr static int k_maxNumberOfYLabels = CurveViewRange::k_maxNumberOfYGridUnits; constexpr static int k_externRectMargin = 2; @@ -60,17 +60,20 @@ protected: float coordinate, float lowerBound, float upperBound, KDColor color, KDCoordinate thickness = 1) const; void drawDot(KDContext * ctx, KDRect rect, float x, float y, KDColor color, bool oversize = false) const; - void drawGridLines(KDContext * ctx, KDRect rect, Axis axis, float step, KDColor color) const; void drawGrid(KDContext * ctx, KDRect rect) const; - void drawAxes(KDContext * ctx, KDRect rect, Axis axis) const; + void drawAxes(KDContext * ctx, KDRect rect) const; + void drawAxis(KDContext * ctx, KDRect rect, Axis axis) const; void drawCurve(KDContext * ctx, KDRect rect, EvaluateModelWithParameter evaluation, void * model, void * context, KDColor color, bool colorUnderCurve = false, float colorLowerBound = 0.0f, float colorUpperBound = 0.0f, bool continuously = false) const; void drawHistogram(KDContext * ctx, KDRect rect, EvaluateModelWithParameter evaluation, void * model, void * context, float firstBarAbscissa, float barWidth, bool fillBar, KDColor defaultColor, KDColor highlightColor, float highlightLowerBound = INFINITY, float highlightUpperBound = -INFINITY) const; void computeLabels(Axis axis); - void drawLabels(KDContext * ctx, KDRect rect, Axis axis, bool shiftOrigin, bool graduationOnly = false, bool fixCoordinate = false, KDCoordinate fixedCoordinate = 0) const; + void simpleDrawBothAxesLabels(KDContext * ctx, KDRect rect) const; + void drawLabels(KDContext * ctx, KDRect rect, Axis axis, bool shiftOrigin, bool graduationOnly = false, bool fixCoordinate = false, KDCoordinate fixedCoordinate = 0, KDColor backgroundColor = KDColorWhite) const; View * m_bannerView; CurveViewCursor * m_curveViewCursor; private: + static constexpr const KDFont * k_font = KDFont::SmallFont; + void drawGridLines(KDContext * ctx, KDRect rect, Axis axis, float step, KDColor boldColor, KDColor lightColor) const; /* The window bounds are deduced from the model bounds but also take into account a margin (computed with k_marginFactor) */ float min(Axis axis) const; @@ -96,6 +99,9 @@ private: View * subviewAtIndex(int index) override; /* m_curveViewRange has to be non null but the cursor model, the banner and * cursor views may be nullptr if not needed. */ + void computeHorizontalExtremaLabels(bool increaseNumberOfSignificantDigits = false); + float labelValueAtIndex(Axis axis, int i) const; + bool bannerIsVisible() const; CurveViewRange * m_curveViewRange; View * m_cursorView; View * m_okView; diff --git a/apps/shared/curve_view_range.cpp b/apps/shared/curve_view_range.cpp index 06c16ad46..8673ce7db 100644 --- a/apps/shared/curve_view_range.cpp +++ b/apps/shared/curve_view_range.cpp @@ -1,4 +1,5 @@ #include "curve_view_range.h" +#include "curve_view.h" #include #include #include @@ -18,25 +19,107 @@ float CurveViewRange::yGridUnit() { return 0.0f; } -float CurveViewRange::computeGridUnit(Axis axis, float min, float max) { +float CurveViewRange::computeGridUnit(Axis axis, float range) { int a = 0; int b = 0; - float d = max - min; - float maxNumberOfUnits = k_maxNumberOfXGridUnits; - float minNumberOfUnits = k_minNumberOfXGridUnits; - if (axis == Axis::Y) { - maxNumberOfUnits = k_maxNumberOfYGridUnits; - minNumberOfUnits = k_minNumberOfYGridUnits; - } - float units[3] = {k_smallGridUnitMantissa, k_mediumGridUnitMantissa, k_largeGridUnitMantissa}; - for (int k = 0; k < 3; k++) { - float unit = units[k]; - if (std::floor(std::log10(d/(unit*maxNumberOfUnits))) != std::floor(std::log10(d/(unit*minNumberOfUnits)))) { - b = std::floor(std::log10(d/(unit*minNumberOfUnits))); - a = unit; + float maxNumberOfUnits = (axis == Axis::X) ? k_maxNumberOfXGridUnits : k_maxNumberOfYGridUnits; + float minNumberOfUnits = (axis == Axis::X) ? k_minNumberOfXGridUnits : k_minNumberOfYGridUnits; + constexpr int unitsCount = 3; + float units[unitsCount] = {k_smallGridUnitMantissa, k_mediumGridUnitMantissa, k_largeGridUnitMantissa}; + for (int k = 0; k < unitsCount; k++) { + float currentA = units[k]; + int b1 = std::floor(std::log10(range/(currentA*maxNumberOfUnits))); + int b2 = std::floor(std::log10(range/(currentA*minNumberOfUnits))); + if (b1 != b2) { + b = b2; + a = currentA; } } return a*std::pow(10.0f,b); + + /* Proof of the algorithm: + * + * We want to find gridUnit = a*10^b, with a in {1; 2; 5} and b an integer + * We want: minNumberOfUnits <= range/gridUnit < maxNumberOfUnits + * + * A solution thus needs to verify: + * + * minNumberOfUnits/range <= 1/(a*10^b) < maxNumberOfUnits/range + * => range/minNumberOfUnits >= a*10^b > range/maxNumberOfUnits + * => range/(a*minNumberOfUnits) >= 10^b > range/(a*maxNumberOfUnits) + * => log10(range/(a*minNumberOfUnits)) >= b > log10(range/(a*maxNumberOfUnits)) + * => (1) log10(range/(a*maxNumberOfUnits)) < b <= log10(range/(a*minNumberOfUnits)) + * And, because b must be an integer, + * => floor(log10(range/(a*maxNumberOfUnits))) != floor(log10(range/(a*minNumberOfUnits))) + * The solution is then b = floor(log10(range/(a*minNumberOfUnits))) + * + * Is there always at least one solution ? + * + * (1) also gives: + * E1 = log10(range)-log10(a)-log10(maxNumberOfUnits) < b <= log10(range)-log10(a)-log10(maxNumberOfUnits) = E2 + * + * Let's compute E2-E1: + * E2-E1 = log10(maxNumberOfUnits) - log10(minNumberOfUnits) + * For minNumberOfUnits=7 and maxNumberOfUnits=18, E2-E1 = 0.41... + * For minNumberOfUnits=5 and maxNumberOfUnits=13, E2-E1 = 0.41... + * + * Let's compute the union of the [E1;E2] for a in {1; 2; 5}: + * [E1;E2 for a=1] U [E1;E2 for a=2] U [E1;E2 for a=5] + * = [e1;e2] U [e1-log10(2); e2-log10(2)] U [e1-log10(5); e2-log10(5)] + * = [e1;e2] U [e1-0.3; e2-0.3] U [e1-0.7; e2-0.7] + * = [e1-0.7; e2-0.7] U [e1-0.3; e2-0.3] U [e1;e2] + * = [e1-0.7; e2] because e2-0.7 > e1-0.3 as e2-e1 > 0.7-0.3 and e2-e1 = E2-E1 = 0.41... + * and e2-0.3 > e1 as e2-e1 > 0.3 + * + * The union of the [E1;E2] for a in {1; 2; 5} is an interval of size + * e2-e1+0.7 = 1.1 > 1. + * We will thus always have at least one a in {1; 2; 5} for which E1 and E2 + * are on each side of an integer. + * + * Let's make a drawing. + * + * n n+1 n+2 n+3 n+4 n+5 + * |.........|.........|.........|.........|.........|... + * E1^---^E2 + * 0.41 + * * + * To have a solution, we need E1 and E2 to be on each side of an integer. + * + * -------------------------------------------------------------------------------------------- + * -------------------------------------------------------------------------------------------- + * + * If e1 - floor(e1) > 1-(e2-e1) = 0.58..., a=1 is a solution + * + * n n+0.2 n+0.4 n+0.6 n+0.8 n+1 + * .........||....|....|....|....|....|....|....|....|....|....||... a=1 + * e1^--------------------^e2 + * + * -------------------------------------------------------------------------------------------- + * -------------------------------------------------------------------------------------------- + * + * If log10(5)-(e2-e1) = 0.29... < e1 - floor(e1) < log10(5) = 0.69..., a=5 is a solution + * + * n n+0.2 n+0.4 n+0.6 n+0.8 n+1 + * .........||....|....|....|....|....|....|....|....|....|....||... + * e1^--------------------^e2 + * + * n n+0.2 n+0.4 n+0.6 n+0.8 n+1 + * .........||....|....|....|....|....|....|....|....|....|....||... a=5 + * E1^--------------------^E2 <- shift by log10(5) = 0.7 + * + * -------------------------------------------------------------------------------------------- + * -------------------------------------------------------------------------------------------- + * + * If e1 - floor(e1) < log10(2) = 0.3..., a=2 is a solution + * n n+0.2 n+0.4 n+0.6 n+0.8 n+1 + * .........||....|....|....|....|....|....|....|....|....|....||... + * e1^--------------------^e2 + * + * n n+0.2 n+0.4 n+0.6 n+0.8 n+1 + * .........||....|....|....|....|....|....|....|....|....|....||... a=2 + * E1^--------------------^E2 <- shift by log10(2) = 0.3 + * + * */ } } diff --git a/apps/shared/curve_view_range.h b/apps/shared/curve_view_range.h index f7ad4d42a..f72b0eb6b 100644 --- a/apps/shared/curve_view_range.h +++ b/apps/shared/curve_view_range.h @@ -19,7 +19,7 @@ public: virtual float yMax() = 0; virtual float xGridUnit() = 0; virtual float yGridUnit(); - float computeGridUnit(Axis axis, float min, float max); + float computeGridUnit(Axis axis, float range); constexpr static float k_maxNumberOfXGridUnits = 18.0f; constexpr static float k_maxNumberOfYGridUnits = 13.0f; private: diff --git a/apps/shared/expression_field_delegate_app.cpp b/apps/shared/expression_field_delegate_app.cpp index 1549e8a30..95dd683bf 100644 --- a/apps/shared/expression_field_delegate_app.cpp +++ b/apps/shared/expression_field_delegate_app.cpp @@ -1,6 +1,6 @@ #include "expression_field_delegate_app.h" #include -#include "../i18n.h" +#include #include "../apps_container.h" using namespace Poincare; @@ -23,8 +23,14 @@ bool ExpressionFieldDelegateApp::layoutFieldDidReceiveEvent(LayoutField * layout layoutField->app()->displayWarning(I18n::Message::SyntaxError); return true; } - char buffer[TextField::maxBufferSize()]; - int bufferSize = TextField::maxBufferSize(); + /* An acceptable layout has to be parsable and serialized in a fixed-size + * buffer. We check all that here. */ + /* Step 1: Simple layout serialisation. Resulting texts can be parsed but + * not displayed, like: + * - 2a + * - log_{2}(x) */ + constexpr int bufferSize = TextField::maxBufferSize(); + char buffer[bufferSize]; int length = layoutField->layout().serializeForParsing(buffer, bufferSize); if (length >= bufferSize-1) { /* If the buffer is totally full, it is VERY likely that writeTextInBuffer @@ -32,7 +38,24 @@ bool ExpressionFieldDelegateApp::layoutFieldDidReceiveEvent(LayoutField * layout displayWarning(I18n::Message::SyntaxError); return true; } - if (!isAcceptableText(buffer)) { + // Step 2: Parsing + Poincare::Expression e = Poincare::Expression::Parse(buffer); + if (e.isUninitialized()) { + // Unparsable expression + displayWarning(I18n::Message::SyntaxError); + return true; + } + /* Step 3: Expression serialization. Tesulting texts are parseable and + * displayable, like: + * - 2*a + * - log(x,2) */ + length = e.serialize(buffer, bufferSize, Poincare::Preferences::sharedPreferences()->displayMode()); + if (length >= bufferSize-1) { + // Same comment as before + displayWarning(I18n::Message::SyntaxError); + return true; + } + if (!isAcceptableExpression(e)) { displayWarning(I18n::Message::SyntaxError); return true; } diff --git a/apps/shared/expression_model_list_controller.cpp b/apps/shared/expression_model_list_controller.cpp index 327a5ac3b..02a52ec4f 100644 --- a/apps/shared/expression_model_list_controller.cpp +++ b/apps/shared/expression_model_list_controller.cpp @@ -45,6 +45,7 @@ bool ExpressionModelListController::handleEventOnExpression(Ion::Events::Event e if (event == Ion::Events::OK || event == Ion::Events::EXE) { if (isAddEmptyRow(selectedRow())) { addEmptyModel(); + selectableTableView()->reloadCellAtLocation(selectedColumn(), selectedRow()); return true; } ExpressionModel * model = modelStore()->modelAtIndex(modelIndexForRow(selectedRow())); @@ -55,10 +56,12 @@ bool ExpressionModelListController::handleEventOnExpression(Ion::Events::Event e ExpressionModel * model = modelStore()->modelAtIndex(modelIndexForRow(selectedRow())); if (model->shouldBeClearedBeforeRemove()) { reinitExpression(model); + selectableTableView()->reloadCellAtLocation(selectedColumn(), selectedRow()); } else { if (removeModelRow(model)) { int newSelectedRow = selectedRow() >= numberOfExpressionRows() ? numberOfExpressionRows()-1 : selectedRow(); selectCellAtLocation(selectedColumn(), newSelectedRow); + selectableTableView()->reloadCellAtLocation(selectedColumn(), selectedRow()); selectableTableView()->reloadData(); } } @@ -84,7 +87,6 @@ void ExpressionModelListController::reinitExpression(ExpressionModel * model) { selectableTableView()->reloadData(); } - void ExpressionModelListController::editExpression(ExpressionModel * model, Ion::Events::Event event) { char * initialText = nullptr; char initialTextContent[TextField::maxBufferSize()]; diff --git a/apps/shared/expression_model_list_controller.h b/apps/shared/expression_model_list_controller.h index 1b67dc22b..f8f910670 100644 --- a/apps/shared/expression_model_list_controller.h +++ b/apps/shared/expression_model_list_controller.h @@ -3,7 +3,7 @@ #include #include "expression_model_store.h" -#include "../i18n.h" +#include namespace Shared { diff --git a/apps/shared/function.cpp b/apps/shared/function.cpp index efa4cc6e5..3fff69cd8 100644 --- a/apps/shared/function.cpp +++ b/apps/shared/function.cpp @@ -1,4 +1,5 @@ #include "function.h" +#include "poincare_helpers.h" #include #include #include @@ -41,7 +42,7 @@ void Function::setActive(bool active) { template T Function::templatedApproximateAtAbscissa(T x, Poincare::Context * context) const { - return expression(context).approximateWithValueForSymbol(symbol(), x, *context, Preferences::sharedPreferences()->angleUnit()); + return PoincareHelpers::ApproximateWithValueForSymbol(expression(context), symbol(), x, *context); } } diff --git a/apps/shared/function_expression_cell.cpp b/apps/shared/function_expression_cell.cpp index 8b7df31a5..950ccc2a3 100644 --- a/apps/shared/function_expression_cell.cpp +++ b/apps/shared/function_expression_cell.cpp @@ -2,11 +2,6 @@ namespace Shared { -FunctionExpressionCell::FunctionExpressionCell() : - EvenOddExpressionCell() -{ -} - KDSize FunctionExpressionCell::minimalSizeForOptimalDisplay() const { KDSize expressionSize = m_expressionView.minimalSizeForOptimalDisplay(); return KDSize(m_leftMargin + expressionSize.width() + m_rightMargin, expressionSize.height()+k_separatorThickness); diff --git a/apps/shared/function_expression_cell.h b/apps/shared/function_expression_cell.h index 203e3cb82..b35b418f4 100644 --- a/apps/shared/function_expression_cell.h +++ b/apps/shared/function_expression_cell.h @@ -7,7 +7,7 @@ namespace Shared { class FunctionExpressionCell : public EvenOddExpressionCell { public: - FunctionExpressionCell(); + FunctionExpressionCell() : EvenOddExpressionCell() {} KDSize minimalSizeForOptimalDisplay() const override; void drawRect(KDContext * ctx, KDRect rect) const override; void layoutSubviews() override; diff --git a/apps/shared/function_graph_controller.cpp b/apps/shared/function_graph_controller.cpp index 9549a7c51..11d3b6a18 100644 --- a/apps/shared/function_graph_controller.cpp +++ b/apps/shared/function_graph_controller.cpp @@ -119,7 +119,9 @@ void FunctionGraphController::initCursorParameters() { m_cursor->moveTo(x, y); functionIndex = (std::isnan(y) || std::isinf(y)) ? 0 : functionIndex - 1; selectFunctionWithCursor(functionIndex); - interactiveCurveViewRange()->panToMakePointVisible(x, y, k_displayTopMarginRatio, k_cursorRightMarginRatio, k_displayBottomMarginRatio, k_cursorLeftMarginRatio); + if (interactiveCurveViewRange()->yAuto()) { + interactiveCurveViewRange()->panToMakePointVisible(x, y, k_displayTopMarginRatio, k_cursorRightMarginRatio, k_displayBottomMarginRatio, k_cursorLeftMarginRatio); + } } bool FunctionGraphController::moveCursorVertically(int direction) { diff --git a/apps/shared/function_graph_view.cpp b/apps/shared/function_graph_view.cpp index 78592d635..a7a65bad7 100644 --- a/apps/shared/function_graph_view.cpp +++ b/apps/shared/function_graph_view.cpp @@ -22,10 +22,8 @@ FunctionGraphView::FunctionGraphView(InteractiveCurveViewRange * graphRange, void FunctionGraphView::drawRect(KDContext * ctx, KDRect rect) const { ctx->fillRect(rect, KDColorWhite); drawGrid(ctx, rect); - drawAxes(ctx, rect, Axis::Horizontal); - drawAxes(ctx, rect, Axis::Vertical); - drawLabels(ctx, rect, Axis::Horizontal, true); - drawLabels(ctx, rect, Axis::Vertical, true); + drawAxes(ctx, rect); + simpleDrawBothAxesLabels(ctx, rect); } void FunctionGraphView::setContext(Context * context) { diff --git a/apps/shared/function_graph_view.h b/apps/shared/function_graph_view.h index ebd780bd5..34de2e4d2 100644 --- a/apps/shared/function_graph_view.h +++ b/apps/shared/function_graph_view.h @@ -27,8 +27,8 @@ protected: bool m_shouldColorHighlighted; private: char * label(Axis axis, int index) const override; - char m_xLabels[k_maxNumberOfXLabels][k_labelBufferSize]; - char m_yLabels[k_maxNumberOfYLabels][k_labelBufferSize]; + char m_xLabels[k_maxNumberOfXLabels][k_labelBufferMaxSize]; + char m_yLabels[k_maxNumberOfYLabels][k_labelBufferMaxSize]; Poincare::Context * m_context; }; diff --git a/apps/shared/function_list_controller.cpp b/apps/shared/function_list_controller.cpp index cc295e35d..bc0790dee 100644 --- a/apps/shared/function_list_controller.cpp +++ b/apps/shared/function_list_controller.cpp @@ -24,7 +24,6 @@ FunctionListController::FunctionListController(Responder * parentResponder, Func { m_selectableTableView.setMargins(0); m_selectableTableView.setVerticalCellOverlap(0); - m_selectableTableView.setShowsIndicators(false); } int FunctionListController::numberOfColumns() { @@ -36,7 +35,7 @@ KDCoordinate FunctionListController::columnWidth(int i) { case 0: return k_functionNameWidth; case 1: - return selectableTableView()->bounds().width()-k_functionNameWidth; + return selectableTableView()->bounds().width() - k_functionNameWidth; default: assert(false); return 0; diff --git a/apps/shared/function_list_controller.h b/apps/shared/function_list_controller.h index 4e5f659f3..dc74a4058 100644 --- a/apps/shared/function_list_controller.h +++ b/apps/shared/function_list_controller.h @@ -6,7 +6,7 @@ #include "function_app.h" #include "list_parameter_controller.h" #include "expression_model_list_controller.h" -#include "../i18n.h" +#include namespace Shared { @@ -47,6 +47,7 @@ protected: StackViewController * stackController() const; void configureFunction(Function * function); FunctionStore * m_functionStore; + SelectableTableView m_selectableTableView; private: static constexpr KDCoordinate k_functionNameWidth = 65; TabViewController * tabController() const; @@ -60,7 +61,6 @@ private: virtual HighlightCell * titleCells(int index) = 0; virtual HighlightCell * expressionCells(int index) = 0; virtual void willDisplayTitleCellAtIndex(HighlightCell * cell, int j) = 0; - SelectableTableView m_selectableTableView; EvenOddCell m_emptyCell; Button m_plotButton; Button m_valuesButton; diff --git a/apps/shared/function_title_cell.cpp b/apps/shared/function_title_cell.cpp index a4917d237..b32a7b4ea 100644 --- a/apps/shared/function_title_cell.cpp +++ b/apps/shared/function_title_cell.cpp @@ -3,11 +3,8 @@ namespace Shared { -FunctionTitleCell::FunctionTitleCell(Orientation orientation) : - EvenOddCell(), - m_orientation(orientation) -{ -} +static inline float min(float x, float y) { return (xy ? x : y); } void FunctionTitleCell::setOrientation(Orientation orientation) { m_orientation = orientation; @@ -19,17 +16,46 @@ void FunctionTitleCell::setColor(KDColor color) { reloadCell(); } +void FunctionTitleCell::setBaseline(KDCoordinate baseline) { + if (m_baseline != baseline) { + m_baseline = baseline; + reloadCell(); + } +} + void FunctionTitleCell::drawRect(KDContext * ctx, KDRect rect) const { if (m_orientation == Orientation::VerticalIndicator){ - ctx->fillRect(KDRect(0, 0, k_colorIndicatorThickness, bounds().height()), m_functionColor); - // Color the vertical separator - ctx->fillRect(KDRect(bounds().width()-k_separatorThickness, 0, k_separatorThickness, bounds().height()), Palette::GreyBright); KDColor separatorColor = m_even ? Palette::WallScreen : KDColorWhite; - // Color the horizontal separator - ctx->fillRect(KDRect(k_colorIndicatorThickness, bounds().height()-k_separatorThickness, bounds().width()-k_colorIndicatorThickness-k_separatorThickness, k_separatorThickness), separatorColor); + KDColor backgroundColor = m_even ? KDColorWhite : Palette::WallScreen; + // Draw the color indicator + ctx->fillRect(KDRect(0, 0, k_colorIndicatorThickness, bounds().height()), m_functionColor); + // Draw the horizontal separator + ctx->fillRect(KDRect(k_colorIndicatorThickness, bounds().height()-k_separatorThickness, bounds().width()-k_colorIndicatorThickness, k_separatorThickness), separatorColor); + // Draw some background + ctx->fillRect(KDRect(bounds().width() - k_equalWidthWithMargins, 0, k_equalWidthWithMargins, bounds().height()-k_separatorThickness), backgroundColor); + // Draw '=' + KDPoint p = KDPoint(bounds().width() - k_equalWidthWithMargins, m_baseline - font()->glyphSize().height()/2 - 1); // -1 is visually needed + ctx->drawString("=", p, font(), m_functionColor, backgroundColor); } else { + // Draw the color indicator ctx->fillRect(KDRect(0, 0, bounds().width(), k_colorIndicatorThickness), m_functionColor); } } +KDRect FunctionTitleCell::subviewFrame() const { + if (m_orientation == Orientation::VerticalIndicator) { + return KDRect(k_colorIndicatorThickness, 0, bounds().width() - k_colorIndicatorThickness - k_equalWidthWithMargins, bounds().height()-k_separatorThickness); + } + return KDRect(0, k_colorIndicatorThickness, bounds().width(), bounds().height()-k_colorIndicatorThickness); +} + +float FunctionTitleCell::verticalAlignment() const { + assert(m_orientation == Orientation::VerticalIndicator); + return max( + 0.0f, + min( + 1.0f, + m_baseline < 0 ? 0.5f : verticalAlignmentGivenExpressionBaselineAndRowHeight(m_baseline, subviewFrame().height()))); +} + } diff --git a/apps/shared/function_title_cell.h b/apps/shared/function_title_cell.h index 604d44fbc..45fc820d6 100644 --- a/apps/shared/function_title_cell.h +++ b/apps/shared/function_title_cell.h @@ -11,16 +11,27 @@ public: HorizontalIndicator, VerticalIndicator }; - FunctionTitleCell(Orientation orientation = Orientation::VerticalIndicator); - void setOrientation(Orientation orientation); + FunctionTitleCell(Orientation orientation = Orientation::VerticalIndicator) : + EvenOddCell(), + m_orientation(orientation), + m_baseline(-1), + m_functionColor(KDColorBlack) + {} + virtual void setOrientation(Orientation orientation); virtual void setColor(KDColor color); void drawRect(KDContext * ctx, KDRect rect) const override; + void setBaseline(KDCoordinate baseline); virtual const KDFont * font() const = 0; protected: constexpr static KDCoordinate k_separatorThickness = 1; constexpr static KDCoordinate k_colorIndicatorThickness = 2; + KDRect subviewFrame() const; + float verticalAlignment() const; Orientation m_orientation; + KDCoordinate m_baseline; private: + constexpr static KDCoordinate k_equalWidthWithMargins = 10; // Ad hoc value + virtual float verticalAlignmentGivenExpressionBaselineAndRowHeight(KDCoordinate expressionBaseline, KDCoordinate rowHeight) const { assert(false); return 0; } KDColor m_functionColor; }; diff --git a/apps/shared/global_context.cpp b/apps/shared/global_context.cpp index 83e4da400..336945f11 100644 --- a/apps/shared/global_context.cpp +++ b/apps/shared/global_context.cpp @@ -71,7 +71,7 @@ void GlobalContext::setExpressionForSymbol(const Expression & expression, const Ion::Storage::Record record = SymbolAbstractRecordWithBaseName(symbol.name()); Expression e = ExpressionFromRecord(record); if (e.isUninitialized()) { - e = Undefined(); + e = Undefined::Builder(); } Expression finalExpression = expression.clone().replaceSymbolWithExpression(symbol, e); diff --git a/apps/shared/initialisation_parameter_controller.cpp b/apps/shared/initialisation_parameter_controller.cpp index 1cd9c1ca6..9c7cbe4fe 100644 --- a/apps/shared/initialisation_parameter_controller.cpp +++ b/apps/shared/initialisation_parameter_controller.cpp @@ -4,30 +4,21 @@ namespace Shared { -InitialisationParameterController::InitialisationParameterController(Responder * parentResponder, InteractiveCurveViewRange * graphRange) : - ViewController(parentResponder), - m_selectableTableView(this, this, this), - m_graphRange(graphRange) -{ +View * InitialisationParameterController::view() { + return &m_selectableTableView; } const char * InitialisationParameterController::title() { return I18n::translate(I18n::Message::Initialization); } -View * InitialisationParameterController::view() { - return &m_selectableTableView; -} - -void InitialisationParameterController::didBecomeFirstResponder() { - m_selectableTableView.selectCellAtLocation(0, 0); - app()->setFirstResponder(&m_selectableTableView); -} - bool InitialisationParameterController::handleEvent(Ion::Events::Event event) { if (event == Ion::Events::OK || event == Ion::Events::EXE) { - RangeMethodPointer rangeMethods[k_totalNumberOfCells] = {&InteractiveCurveViewRange::setTrigonometric, - &InteractiveCurveViewRange::roundAbscissa, &InteractiveCurveViewRange::normalize, &InteractiveCurveViewRange::setDefault}; + RangeMethodPointer rangeMethods[k_totalNumberOfCells] = { + &InteractiveCurveViewRange::setTrigonometric, + &InteractiveCurveViewRange::roundAbscissa, + &InteractiveCurveViewRange::normalize, + &InteractiveCurveViewRange::setDefault}; (m_graphRange->*rangeMethods[selectedRow()])(); StackViewController * stack = (StackViewController *)parentResponder(); stack->pop(); @@ -36,10 +27,18 @@ if (event == Ion::Events::OK || event == Ion::Events::EXE) { return false; } +void InitialisationParameterController::didBecomeFirstResponder() { + m_selectableTableView.selectCellAtLocation(0, 0); + app()->setFirstResponder(&m_selectableTableView); +} + int InitialisationParameterController::numberOfRows() { return k_totalNumberOfCells; -}; +} +KDCoordinate InitialisationParameterController::cellHeight() { + return Metric::ParameterCellHeight; +} HighlightCell * InitialisationParameterController::reusableCell(int index) { assert(index >= 0); @@ -51,14 +50,13 @@ int InitialisationParameterController::reusableCellCount() { return k_totalNumberOfCells; } -KDCoordinate InitialisationParameterController::cellHeight() { - return Metric::ParameterCellHeight; -} - void InitialisationParameterController::willDisplayCellForIndex(HighlightCell * cell, int index) { - MessageTableCell * myCell = (MessageTableCell *)cell; - I18n::Message titles[4] = {I18n::Message::Trigonometric, I18n::Message::RoundAbscissa, I18n::Message::Orthonormal, I18n::Message::DefaultSetting}; - myCell->setMessage(titles[index]); + I18n::Message titles[4] = { + I18n::Message::Trigonometric, + I18n::Message::RoundAbscissa, + I18n::Message::Orthonormal, + I18n::Message::DefaultSetting}; + ((MessageTableCell *)cell)->setMessage(titles[index]); } } diff --git a/apps/shared/initialisation_parameter_controller.h b/apps/shared/initialisation_parameter_controller.h index 46375df75..83bb27adf 100644 --- a/apps/shared/initialisation_parameter_controller.h +++ b/apps/shared/initialisation_parameter_controller.h @@ -3,13 +3,17 @@ #include #include "interactive_curve_view_range.h" -#include "../i18n.h" +#include namespace Shared { class InitialisationParameterController : public ViewController, public SimpleListViewDataSource, public SelectableTableViewDataSource { public: - InitialisationParameterController(Responder * parentResponder, Shared::InteractiveCurveViewRange * graphRange); + InitialisationParameterController(Responder * parentResponder, Shared::InteractiveCurveViewRange * graphRange) : + ViewController(parentResponder), + m_selectableTableView(this, this, this), + m_graphRange(graphRange) + {} View * view() override; const char * title() override; bool handleEvent(Ion::Events::Event event) override; diff --git a/apps/shared/interactive_curve_view_controller.cpp b/apps/shared/interactive_curve_view_controller.cpp index 73b36bf4e..d9250f266 100644 --- a/apps/shared/interactive_curve_view_controller.cpp +++ b/apps/shared/interactive_curve_view_controller.cpp @@ -37,7 +37,34 @@ InteractiveCurveViewController::InteractiveCurveViewController(Responder * paren } float InteractiveCurveViewController::addMargin(float x, float range, bool isMin) { + /* We are adding margins. Let's name: + * - The current range: rangeBefore + * - The next range: rangeAfter + * - The bottom margin ratio with which we will evaluate if a point is too + * low on the screen: bottomRatioAfter + * - The bottom margin ratio with which we will evaluate if a point is too + * high on the screen: topRatioAfter + * - The ratios we need to use to create the margins: bottomRatioBefore and + * topRatioBefore + * + * We want to add margins so that: + * bottomRatioAfter*rangeAfter == bottomRatioBefore * rangeBefore + * topRatioAfter*rangeAfter == topRatioBefore * rangeBefore + * Knowing that: + * rangeAfter = (1+bottomRatioBefore+topRatioBefore)*rangeBefore + * + * We thus have: + * bottomRatioBefore = bottomRatioAfter / (1-bottomRatioAfter-topRatioAfter) + * topRatioBefore = topRatioAfter / (1-bottomRatioAfter-topRatioAfter) + * + * If we just used bottomRatioBefore = bottomRatioAfter and + * topRatioBefore = topRatioAfter, we would create too small margins and the + * controller might need to pan right after a Y auto calibration. */ + + assert(displayBottomMarginRatio()+displayTopMarginRatio() < 1); // Assertion so that the formula is correct + float ratioDenominator = 1-displayBottomMarginRatio()-displayTopMarginRatio(); float ratio = isMin ? -displayBottomMarginRatio() : displayTopMarginRatio(); + ratio = ratio / ratioDenominator; return x+ratio*range; } @@ -115,8 +142,11 @@ Responder * InteractiveCurveViewController::defaultController() { void InteractiveCurveViewController::viewWillAppear() { uint32_t newModelVersion = modelVersion(); if (*m_modelVersion != newModelVersion) { + if (*m_modelVersion == 0 || numberOfCurves() == 1) { + initRangeParameters(); + } *m_modelVersion = newModelVersion; - initRangeParameters(); + didChangeRange(interactiveCurveViewRange()); /* Warning: init cursor parameter before reloading banner view. Indeed, * reloading banner view needs an updated cursor to load the right data. */ initCursorParameters(); diff --git a/apps/shared/interactive_curve_view_controller.h b/apps/shared/interactive_curve_view_controller.h index 27079c950..e455a6457 100644 --- a/apps/shared/interactive_curve_view_controller.h +++ b/apps/shared/interactive_curve_view_controller.h @@ -50,7 +50,7 @@ protected: virtual bool closestCurveIndexIsSuitable(int newIndex, int currentIndex) const { assert(false); return false; } virtual double yValue(int curveIndex, double x, Poincare::Context * context) const { assert(false); return 0; } virtual bool suitableYValue(double y) const { return true; } - virtual int numberOfCurves() const { assert(false); return 0; } + virtual int numberOfCurves() const = 0; OkView m_okView; private: diff --git a/apps/shared/interactive_curve_view_range.cpp b/apps/shared/interactive_curve_view_range.cpp index 9c151d54a..41eee5e59 100644 --- a/apps/shared/interactive_curve_view_range.cpp +++ b/apps/shared/interactive_curve_view_range.cpp @@ -1,6 +1,7 @@ #include "interactive_curve_view_range.h" #include #include +#include #include #include #include @@ -9,21 +10,8 @@ using namespace Poincare; namespace Shared { -InteractiveCurveViewRange::InteractiveCurveViewRange(CurveViewCursor * cursor, InteractiveCurveViewRangeDelegate * delegate) : - MemoizedCurveViewRange(), - m_yAuto(true), - m_delegate(delegate), - m_cursor(cursor) -{ -} - -void InteractiveCurveViewRange::setDelegate(InteractiveCurveViewRangeDelegate * delegate) { - m_delegate = delegate; -} - -void InteractiveCurveViewRange::setCursor(CurveViewCursor * cursor) { - m_cursor = cursor; -} +static inline float min(float x, float y) { return (xy ? x : y); } uint32_t InteractiveCurveViewRange::rangeChecksum() { float data[5] = {m_xMin, m_xMax, m_yMin, m_yMax, m_yAuto ? 1.0f : 0.0f}; @@ -32,8 +20,9 @@ uint32_t InteractiveCurveViewRange::rangeChecksum() { return Ion::crc32((uint32_t *)data, dataLengthInBytes/sizeof(uint32_t)); } -bool InteractiveCurveViewRange::yAuto() { - return m_yAuto; +void InteractiveCurveViewRange::setYAuto(bool yAuto) { + m_yAuto = yAuto; + notifyRangeChange(); } void InteractiveCurveViewRange::setXMin(float xMin) { @@ -43,9 +32,7 @@ void InteractiveCurveViewRange::setXMin(float xMin) { newXMin = m_xMax - k_minFloat; MemoizedCurveViewRange::setXMin(clipped(newXMin, false)); } - if (m_delegate) { - m_delegate->didChangeRange(this); - } + notifyRangeChange(); } void InteractiveCurveViewRange::setXMax(float xMax) { @@ -55,9 +42,7 @@ void InteractiveCurveViewRange::setXMax(float xMax) { newXMax = m_xMin + k_minFloat; MemoizedCurveViewRange::setXMax(clipped(newXMax, true)); } - if (m_delegate) { - m_delegate->didChangeRange(this); - } + notifyRangeChange(); } void InteractiveCurveViewRange::setYMin(float yMin) { @@ -78,13 +63,6 @@ void InteractiveCurveViewRange::setYMax(float yMax) { } } -void InteractiveCurveViewRange::setYAuto(bool yAuto) { - m_yAuto = yAuto; - if (m_delegate) { - m_delegate->didChangeRange(this); - } -} - void InteractiveCurveViewRange::zoom(float ratio, float x, float y) { float xMin = m_xMin; float xMax = m_xMax; @@ -101,7 +79,7 @@ void InteractiveCurveViewRange::zoom(float ratio, float x, float y) { m_xMax = newXMax; MemoizedCurveViewRange::setXMin(newXMin); } - m_yAuto = false; + setYAuto(false); float newYMin = clipped(centerY*(1.0f-ratio)+ratio*yMin, false); float newYMax = clipped(centerY*(1.0f-ratio)+ratio*yMax, true); if (!std::isnan(newYMin) && !std::isnan(newYMax)) { @@ -111,7 +89,7 @@ void InteractiveCurveViewRange::zoom(float ratio, float x, float y) { } void InteractiveCurveViewRange::panWithVector(float x, float y) { - m_yAuto = false; + setYAuto(false); if (clipped(m_xMin + x, false) != m_xMin + x || clipped(m_xMax + x, true) != m_xMax + x || clipped(m_yMin + y, false) != m_yMin + y || clipped(m_yMax + y, true) != m_yMax + y || std::isnan(clipped(m_xMin + x, false)) || std::isnan(clipped(m_xMax + x, true)) || std::isnan(clipped(m_yMin + y, false)) || std::isnan(clipped(m_yMax + y, true))) { return; } @@ -122,6 +100,7 @@ void InteractiveCurveViewRange::panWithVector(float x, float y) { } void InteractiveCurveViewRange::roundAbscissa() { + // Set x range float xMin = m_xMin; float xMax = m_xMax; float newXMin = clipped(std::round((xMin+xMax)/2) - (float)Ion::Display::Width/2.0f, false); @@ -131,25 +110,28 @@ void InteractiveCurveViewRange::roundAbscissa() { } m_xMax = newXMax; MemoizedCurveViewRange::setXMin(newXMin); - if (m_delegate) { - m_delegate->didChangeRange(this); - } + // Set y range + notifyRangeChange(); } void InteractiveCurveViewRange::normalize() { + /* We center the ranges on the current range center, and put each axis so that + * 1cm = 2 units. */ float xMin = m_xMin; float xMax = m_xMax; float yMin = m_yMin; float yMax = m_yMax; - float newXMin = clipped((xMin+xMax)/2 - 5.3f, false); - float newXMax = clipped((xMin+xMax)/2 + 5.3f, true); + // Set x range + float newXMin = clipped((xMin+xMax)/2 - NormalizedXHalfRange(), false); + float newXMax = clipped((xMin+xMax)/2 + NormalizedXHalfRange(), true); if (!std::isnan(newXMin) && !std::isnan(newXMax)) { m_xMax = newXMax; MemoizedCurveViewRange::setXMin(newXMin); } - m_yAuto = false; - float newYMin = clipped((yMin+yMax)/2 - 3.1f, false); - float newYMax = clipped((yMin+yMax)/2 + 3.1f, true); + // Set y range + setYAuto(false); + float newYMin = clipped((yMin+yMax)/2 - NormalizedYHalfRange(), false); + float newYMax = clipped((yMin+yMax)/2 + NormalizedYHalfRange(), true); if (!std::isnan(newYMin) && !std::isnan(newYMax)) { m_yMax = newYMax; MemoizedCurveViewRange::setYMin(newYMin); @@ -157,22 +139,22 @@ void InteractiveCurveViewRange::normalize() { } void InteractiveCurveViewRange::setTrigonometric() { - m_xMax = 10.5f; - MemoizedCurveViewRange::setXMin(-10.5f); - if (Preferences::sharedPreferences()->angleUnit() == Preferences::AngleUnit::Degree) { - m_xMax = 600.0f; - MemoizedCurveViewRange::setXMin(-600.0f); - } - m_yAuto = false; - m_yMax = 1.6f; - MemoizedCurveViewRange::setYMin(-1.6f); + // Set x range + float x = (Preferences::sharedPreferences()->angleUnit() == Preferences::AngleUnit::Degree) ? 600.0f : 10.5f; + m_xMax = x; + MemoizedCurveViewRange::setXMin(-x); + // Set y range + setYAuto(false); + float y = 1.6f; + m_yMax = y; + MemoizedCurveViewRange::setYMin(-y); } void InteractiveCurveViewRange::setDefault() { if (m_delegate == nullptr) { return; } - m_xMax = m_delegate->interestingXRange(); + m_xMax = m_delegate->interestingXHalfRange(); MemoizedCurveViewRange::setXMin(-m_xMax); setYAuto(true); } @@ -189,7 +171,7 @@ void InteractiveCurveViewRange::centerAxisAround(Axis axis, float position) { m_xMax = clipped(position + range/2.0f, true); MemoizedCurveViewRange::setXMin(clipped(position - range/2.0f, false)); } else { - m_yAuto = false; + setYAuto(false); float range = m_yMax - m_yMin; if (std::fabs(position/range) > k_maxRatioPositionRange) { range = std::pow(10.0f, std::floor(std::log10(std::fabs(position)))-1.0f); @@ -202,27 +184,27 @@ void InteractiveCurveViewRange::centerAxisAround(Axis axis, float position) { void InteractiveCurveViewRange::panToMakePointVisible(float x, float y, float topMarginRatio, float rightMarginRatio, float bottomMarginRation, float leftMarginRation) { float xRange = m_xMax - m_xMin; float yRange = m_yMax - m_yMin; - if (x < m_xMin + leftMarginRation*xRange && !std::isinf(x) && !std::isnan(x)) { + if (x < m_xMin + leftMarginRation*xRange - FLT_EPSILON && !std::isinf(x) && !std::isnan(x)) { float newXMin = clipped(x - leftMarginRation*xRange, false); m_xMax = clipped(newXMin + xRange, true); MemoizedCurveViewRange::setXMin(newXMin); - m_yAuto = false; + setYAuto(false); } - if (x > m_xMax - rightMarginRatio*xRange && !std::isinf(x) && !std::isnan(x)) { + if (x > m_xMax - rightMarginRatio*xRange + FLT_EPSILON && !std::isinf(x) && !std::isnan(x)) { m_xMax = clipped(x + rightMarginRatio*xRange, true); MemoizedCurveViewRange::setXMin(clipped(m_xMax - xRange, false)); - m_yAuto = false; + setYAuto(false); } - if (y < m_yMin + bottomMarginRation*yRange && !std::isinf(y) && !std::isnan(y)) { + if (y < m_yMin + bottomMarginRation*yRange - FLT_EPSILON && !std::isinf(y) && !std::isnan(y)) { float newYMin = clipped(y - bottomMarginRation*yRange, false); m_yMax = clipped(newYMin + yRange, true); MemoizedCurveViewRange::setYMin(newYMin); - m_yAuto = false; + setYAuto(false); } - if (y > m_yMax - topMarginRatio*yRange && !std::isinf(y) && !std::isnan(y)) { + if (y > m_yMax - topMarginRatio*yRange + FLT_EPSILON && !std::isinf(y) && !std::isnan(y)) { m_yMax = clipped(y + topMarginRatio*yRange, true); MemoizedCurveViewRange::setYMin(clipped(m_yMax - yRange, false)); - m_yAuto = false; + setYAuto(false); } } @@ -233,11 +215,15 @@ bool InteractiveCurveViewRange::isCursorVisible(float topMarginRatio, float righ } float InteractiveCurveViewRange::clipped(float x, bool isMax) { - float max = isMax ? k_upperMaxFloat : k_lowerMaxFloat; - float min = isMax ? -k_lowerMaxFloat : -k_upperMaxFloat; - float clippedX = x > max ? max : x; - clippedX = clippedX < min ? min : clippedX; - return clippedX; + float maxF = isMax ? k_upperMaxFloat : k_lowerMaxFloat; + float minF = isMax ? -k_lowerMaxFloat : -k_upperMaxFloat; + return max(minF, min(x, maxF)); +} + +void InteractiveCurveViewRange::notifyRangeChange() { + if (m_delegate) { + m_delegate->didChangeRange(this); + } } } diff --git a/apps/shared/interactive_curve_view_range.h b/apps/shared/interactive_curve_view_range.h index afe7b6c21..6ea5cb4ef 100644 --- a/apps/shared/interactive_curve_view_range.h +++ b/apps/shared/interactive_curve_view_range.h @@ -5,23 +5,33 @@ #include "memoized_curve_view_range.h" #include "curve_view_cursor.h" #include "interactive_curve_view_range_delegate.h" +#include +#include namespace Shared { class InteractiveCurveViewRange : public MemoizedCurveViewRange { public: - InteractiveCurveViewRange(CurveViewCursor * cursor, InteractiveCurveViewRangeDelegate * delegate = nullptr); - void setDelegate(InteractiveCurveViewRangeDelegate * delegate); - void setCursor(CurveViewCursor * cursor); + constexpr static float k_minFloat = 1E-4f; + InteractiveCurveViewRange(CurveViewCursor * cursor, InteractiveCurveViewRangeDelegate * delegate = nullptr) : + MemoizedCurveViewRange(), + m_yAuto(true), + m_delegate(delegate), + m_cursor(cursor) + {} + + void setDelegate(InteractiveCurveViewRangeDelegate * delegate) { m_delegate = delegate; } + void setCursor(CurveViewCursor * cursor) { m_cursor = cursor; } uint32_t rangeChecksum() override; - //CurveViewWindow - bool yAuto(); + bool yAuto() const { return m_yAuto; } + void setYAuto(bool yAuto); + + // CurveViewWindow void setXMin(float f) override; void setXMax(float f) override; void setYMin(float f) override; void setYMax(float f) override; - void setYAuto(bool yAuto); // Window void zoom(float ratio, float x, float y); @@ -35,16 +45,32 @@ public: bool isCursorVisible(float topMarginRatio, float rightMarginRatio, float bottomMarginRation, float leftMarginRation); protected: bool m_yAuto; + /* In normalized settings, we put each axis so that 1cm = 2 units. For now, + * the screen has size 43.2mm * 57.6mm. + * We want: + * 2*NormalizedXHalfRange -> 57.6mm + * 2*1 -> 10.0mm + * So NormalizedXHalfRange = 5.76 + * We want: + * 2*NormalizedYHalfRange -> 43.2mm * 170/240 + * 2*1 -> 10.0mm + * So NormalizedYHalfRange = 3.06 */ + constexpr static float NormalizedXHalfRange() { return 5.76f; } + constexpr static float NormalizedYHalfRange() { return 3.06f; } static float clipped(float f, bool isMax); InteractiveCurveViewRangeDelegate * m_delegate; private: - constexpr static float k_minFloat = 1E-8f; constexpr static float k_upperMaxFloat = 1E+8f; constexpr static float k_lowerMaxFloat = 9E+7f; constexpr static float k_maxRatioPositionRange = 1E5f; + void notifyRangeChange(); CurveViewCursor * m_cursor; }; +static_assert(InteractiveCurveViewRange::k_minFloat >= FLT_EPSILON, "InteractiveCurveViewRange's minimal float range is lower than float precision, it might draw uglily curves such as cos(x)^2+sin(x)^2"); +static_assert(Ion::Display::WidthInTenthOfMillimeter == 576, "Use the new screen width to compute Shared::InteractiveCurveViewRange::NormalizedXHalfRange"); +static_assert(Ion::Display::HeightInTenthOfMillimeter == 432, "Use the new screen height to compute Shared::InteractiveCurveViewRange::NormalizedYHalfRange"); + typedef void (InteractiveCurveViewRange::*ParameterSetterPointer)(float); typedef float (InteractiveCurveViewRange::*ParameterGetterPointer)(); typedef void (InteractiveCurveViewRange::*RangeMethodPointer)(); diff --git a/apps/shared/interactive_curve_view_range_delegate.cpp b/apps/shared/interactive_curve_view_range_delegate.cpp index 017ed64bd..271b7237c 100644 --- a/apps/shared/interactive_curve_view_range_delegate.cpp +++ b/apps/shared/interactive_curve_view_range_delegate.cpp @@ -5,7 +5,6 @@ namespace Shared { - bool InteractiveCurveViewRangeDelegate::didChangeRange(InteractiveCurveViewRange * interactiveCurveViewRange) { if (!interactiveCurveViewRange->yAuto()) { return false; @@ -21,7 +20,7 @@ bool InteractiveCurveViewRangeDelegate::didChangeRange(InteractiveCurveViewRange return false; } if (min == max) { - float step = max != 0.0f ? interactiveCurveViewRange->computeGridUnit(CurveViewRange::Axis::Y, 0.0f, max) : 1.0f; + float step = max != 0.0f ? interactiveCurveViewRange->computeGridUnit(CurveViewRange::Axis::Y, max) : 1.0f; min = min - step; max = max + step; } @@ -30,14 +29,18 @@ bool InteractiveCurveViewRangeDelegate::didChangeRange(InteractiveCurveViewRange max = 1.0f; } if (min == FLT_MAX) { - float step = max != 0.0f ? interactiveCurveViewRange->computeGridUnit(CurveViewRange::Axis::Y, 0.0f, std::fabs(max)) : 1.0f; + float step = max != 0.0f ? interactiveCurveViewRange->computeGridUnit(CurveViewRange::Axis::Y, std::fabs(max)) : 1.0f; min = max-step; } - if (max == -FLT_MAX) { - float step = min != 0.0f ? interactiveCurveViewRange->computeGridUnit(CurveViewRange::Axis::Y, 0.0f, std::fabs(min)) : 1.0f; + if (max == -FLT_MAX) { + float step = min != 0.0f ? interactiveCurveViewRange->computeGridUnit(CurveViewRange::Axis::Y, std::fabs(min)) : 1.0f; max = min+step; } range = max - min; + if (range < InteractiveCurveViewRange::k_minFloat) { + max += InteractiveCurveViewRange::k_minFloat; + min -= InteractiveCurveViewRange::k_minFloat; + } interactiveCurveViewRange->setYMin(addMargin(min, range, true)); interactiveCurveViewRange->setYMax(addMargin(max, range, false)); if (std::isinf(interactiveCurveViewRange->xMin())) { @@ -49,8 +52,4 @@ bool InteractiveCurveViewRangeDelegate::didChangeRange(InteractiveCurveViewRange return true; } -float InteractiveCurveViewRangeDelegate::interestingXRange() { - return 10.0f; -} - } diff --git a/apps/shared/interactive_curve_view_range_delegate.h b/apps/shared/interactive_curve_view_range_delegate.h index 841e8f43c..b7193ebd1 100644 --- a/apps/shared/interactive_curve_view_range_delegate.h +++ b/apps/shared/interactive_curve_view_range_delegate.h @@ -8,7 +8,8 @@ class InteractiveCurveViewRange; class InteractiveCurveViewRangeDelegate { public: bool didChangeRange(InteractiveCurveViewRange * interactiveCurveViewRange); - virtual float interestingXRange(); + virtual float interestingXMin() const { return -interestingXHalfRange(); } + virtual float interestingXHalfRange() const { return 10.0f; } protected: struct Range { float min; diff --git a/apps/shared/language_controller.cpp b/apps/shared/language_controller.cpp index d277a28f7..682806643 100644 --- a/apps/shared/language_controller.cpp +++ b/apps/shared/language_controller.cpp @@ -1,7 +1,7 @@ #include "language_controller.h" #include "../global_preferences.h" #include "../apps_container.h" -#include "../i18n.h" +#include namespace Shared { diff --git a/apps/shared/language_controller.h b/apps/shared/language_controller.h index 5dda27fac..746e68e1c 100644 --- a/apps/shared/language_controller.h +++ b/apps/shared/language_controller.h @@ -2,7 +2,7 @@ #define SHARED_LANGUAGE_CONTROLLER_H #include -#include "../i18n.h" +#include namespace Shared { diff --git a/apps/shared/list_parameter_controller.h b/apps/shared/list_parameter_controller.h index 8266e5496..4bde78f7f 100644 --- a/apps/shared/list_parameter_controller.h +++ b/apps/shared/list_parameter_controller.h @@ -4,7 +4,7 @@ #include #include "function.h" #include "function_store.h" -#include "../i18n.h" +#include namespace Shared { diff --git a/apps/shared/memoized_curve_view_range.cpp b/apps/shared/memoized_curve_view_range.cpp index 7e80aef5a..56f464154 100644 --- a/apps/shared/memoized_curve_view_range.cpp +++ b/apps/shared/memoized_curve_view_range.cpp @@ -47,7 +47,7 @@ void MemoizedCurveViewRange::setXMin(float xMin) { if (m_xMin >= m_xMax) { m_xMax = xMin + std::pow(10.0f, std::floor(std::log10(std::fabs(xMin)))-1.0f); } - m_xGridUnit = computeGridUnit(Axis::X, m_xMin, m_xMax); + m_xGridUnit = computeGridUnit(Axis::X, m_xMax - m_xMin); } void MemoizedCurveViewRange::setXMax(float xMax) { @@ -58,7 +58,7 @@ void MemoizedCurveViewRange::setXMax(float xMax) { if (m_xMin >= m_xMax) { m_xMin = xMax - std::pow(10.0f, std::floor(std::log10(std::fabs(xMax)))-1.0f); } - m_xGridUnit = computeGridUnit(Axis::X, m_xMin, m_xMax); + m_xGridUnit = computeGridUnit(Axis::X, m_xMax - m_xMin); } void MemoizedCurveViewRange::setYMin(float yMin) { @@ -69,7 +69,7 @@ void MemoizedCurveViewRange::setYMin(float yMin) { if (m_yMin >= m_yMax) { m_yMax = yMin + std::pow(10.0f, std::floor(std::log10(std::fabs(yMin)))-1.0f); } - m_yGridUnit = computeGridUnit(Axis::Y, m_yMin, m_yMax); + m_yGridUnit = computeGridUnit(Axis::Y, m_yMax - m_yMin); } void MemoizedCurveViewRange::setYMax(float yMax) { @@ -81,7 +81,7 @@ void MemoizedCurveViewRange::setYMax(float yMax) { m_yMin = yMax - + std::pow(10.0f, std::floor(std::log10(std::fabs(yMax)))-1.0f); } - m_yGridUnit = computeGridUnit(Axis::Y, m_yMin, m_yMax); + m_yGridUnit = computeGridUnit(Axis::Y, m_yMax - m_yMin); } } diff --git a/apps/shared/poincare_helpers.h b/apps/shared/poincare_helpers.h index a9a2b9ef6..e401f58f5 100644 --- a/apps/shared/poincare_helpers.h +++ b/apps/shared/poincare_helpers.h @@ -10,7 +10,8 @@ namespace Shared { namespace PoincareHelpers { inline Poincare::Layout CreateLayout(const Poincare::Expression e) { - return e.createLayout(Poincare::Preferences::sharedPreferences()->displayMode(), Poincare::Preferences::sharedPreferences()->numberOfSignificantDigits()); + Poincare::Preferences * preferences = Poincare::Preferences::sharedPreferences(); + return e.createLayout(preferences->displayMode(), preferences->numberOfSignificantDigits()); } template @@ -24,25 +25,73 @@ inline int Serialize(const Poincare::Expression e, char * buffer, int bufferSize template inline Poincare::Expression Approximate(const Poincare::Expression e, Poincare::Context & context) { - return e.approximate(context, Poincare::Preferences::sharedPreferences()->angleUnit(), Poincare::Preferences::sharedPreferences()->complexFormat()); + Poincare::Preferences * preferences = Poincare::Preferences::sharedPreferences(); + Poincare::Preferences::ComplexFormat complexFormat = Poincare::Expression::UpdatedComplexFormatWithExpressionInput(preferences->complexFormat(), e, context); + return e.approximate(context, complexFormat, preferences->angleUnit()); } template inline T ApproximateToScalar(const Poincare::Expression e, Poincare::Context & context) { - return e.approximateToScalar(context, Poincare::Preferences::sharedPreferences()->angleUnit()); + Poincare::Preferences * preferences = Poincare::Preferences::sharedPreferences(); + Poincare::Preferences::ComplexFormat complexFormat = Poincare::Expression::UpdatedComplexFormatWithExpressionInput(preferences->complexFormat(), e, context); + return e.approximateToScalar(context, complexFormat, preferences->angleUnit()); +} + +template +inline T ApproximateWithValueForSymbol(const Poincare::Expression e, const char * symbol, T x, Poincare::Context & context) { + Poincare::Preferences * preferences = Poincare::Preferences::sharedPreferences(); + Poincare::Preferences::ComplexFormat complexFormat = Poincare::Expression::UpdatedComplexFormatWithExpressionInput(preferences->complexFormat(), e, context); + return e.approximateWithValueForSymbol(symbol, x, context, complexFormat, preferences->angleUnit()); } template inline T ApproximateToScalar(const char * text, Poincare::Context & context) { - return Poincare::Expression::approximateToScalar(text, context, Poincare::Preferences::sharedPreferences()->angleUnit()); + Poincare::Preferences * preferences = Poincare::Preferences::sharedPreferences(); + Poincare::Preferences::ComplexFormat complexFormat = Poincare::Expression::UpdatedComplexFormatWithTextInput(preferences->complexFormat(), text); + return Poincare::Expression::ApproximateToScalar(text, context, complexFormat, preferences->angleUnit()); } inline Poincare::Expression ParseAndSimplify(const char * text, Poincare::Context & context) { - return Poincare::Expression::ParseAndSimplify(text, context, Poincare::Preferences::sharedPreferences()->angleUnit()); + Poincare::Preferences * preferences = Poincare::Preferences::sharedPreferences(); + Poincare::Preferences::ComplexFormat complexFormat = Poincare::Expression::UpdatedComplexFormatWithTextInput(preferences->complexFormat(), text); + return Poincare::Expression::ParseAndSimplify(text, context, complexFormat, preferences->angleUnit()); } inline void Simplify(Poincare::Expression * e, Poincare::Context & context) { - *e = e->simplify(context, Poincare::Preferences::sharedPreferences()->angleUnit()); + Poincare::Preferences * preferences = Poincare::Preferences::sharedPreferences(); + Poincare::Preferences::ComplexFormat complexFormat = Poincare::Expression::UpdatedComplexFormatWithExpressionInput(preferences->complexFormat(), *e, context); + *e = e->simplify(context, complexFormat, preferences->angleUnit()); +} + +inline void ParseAndSimplifyAndApproximate(const char * text, Poincare::Expression * simplifiedExpression, Poincare::Expression * approximateExpression, Poincare::Context & context) { + Poincare::Preferences * preferences = Poincare::Preferences::sharedPreferences(); + Poincare::Preferences::ComplexFormat complexFormat = Poincare::Expression::UpdatedComplexFormatWithTextInput(preferences->complexFormat(), text); + Poincare::Expression::ParseAndSimplifyAndApproximate(text, simplifiedExpression, approximateExpression, context, complexFormat, preferences->angleUnit()); +} + +inline typename Poincare::Expression::Coordinate2D NextMinimum(const Poincare::Expression e, const char * symbol, double start, double step, double max, Poincare::Context & context) { + Poincare::Preferences * preferences = Poincare::Preferences::sharedPreferences(); + Poincare::Preferences::ComplexFormat complexFormat = Poincare::Expression::UpdatedComplexFormatWithExpressionInput(preferences->complexFormat(), e, context); + return e.nextMinimum(symbol, start, step, max, context, complexFormat, preferences->angleUnit()); +} + +inline typename Poincare::Expression::Coordinate2D NextMaximum(const Poincare::Expression e, const char * symbol, double start, double step, double max, Poincare::Context & context) { + Poincare::Preferences * preferences = Poincare::Preferences::sharedPreferences(); + Poincare::Preferences::ComplexFormat complexFormat = Poincare::Expression::UpdatedComplexFormatWithExpressionInput(preferences->complexFormat(), e, context); + return e.nextMaximum(symbol, start, step, max, context, complexFormat, preferences->angleUnit()); +} + +inline double NextRoot(const Poincare::Expression e, const char * symbol, double start, double step, double max, Poincare::Context & context) { + Poincare::Preferences * preferences = Poincare::Preferences::sharedPreferences(); + Poincare::Preferences::ComplexFormat complexFormat = Poincare::Expression::UpdatedComplexFormatWithExpressionInput(preferences->complexFormat(), e, context); + return e.nextRoot(symbol, start, step, max, context, complexFormat, preferences->angleUnit()); +} + +inline typename Poincare::Expression::Coordinate2D NextIntersection(const Poincare::Expression e, const char * symbol, double start, double step, double max, Poincare::Context & context, const Poincare::Expression expression) { + Poincare::Preferences * preferences = Poincare::Preferences::sharedPreferences(); + Poincare::Preferences::ComplexFormat complexFormat = Poincare::Expression::UpdatedComplexFormatWithExpressionInput(preferences->complexFormat(), e, context); + complexFormat = Poincare::Expression::UpdatedComplexFormatWithExpressionInput(complexFormat, expression, context); + return e.nextIntersection(symbol, start, step, max, context, complexFormat, preferences->angleUnit(), expression); } } @@ -50,4 +99,3 @@ inline void Simplify(Poincare::Expression * e, Poincare::Context & context) { } #endif - diff --git a/apps/shared/scrollable_exact_approximate_expressions_cell.cpp b/apps/shared/scrollable_exact_approximate_expressions_cell.cpp index b590b3381..e9c456b75 100644 --- a/apps/shared/scrollable_exact_approximate_expressions_cell.cpp +++ b/apps/shared/scrollable_exact_approximate_expressions_cell.cpp @@ -17,6 +17,7 @@ void ScrollableExactApproximateExpressionsCell::setHighlighted(bool highlight) { void ScrollableExactApproximateExpressionsCell::setEven(bool even) { EvenOddCell::setEven(even); + m_view.setBackgroundColor(backgroundColor()); m_view.evenOddCell()->setEven(even); } @@ -41,7 +42,7 @@ View * ScrollableExactApproximateExpressionsCell::subviewAtIndex(int index) { } void ScrollableExactApproximateExpressionsCell::layoutSubviews() { - m_view.setFrame(KDRect(k_margin,k_margin, bounds().width()-2*k_margin, bounds().height()-2*k_margin)); + m_view.setFrame(bounds()); } } diff --git a/apps/shared/scrollable_exact_approximate_expressions_cell.h b/apps/shared/scrollable_exact_approximate_expressions_cell.h index f74f77091..3d8a8209f 100644 --- a/apps/shared/scrollable_exact_approximate_expressions_cell.h +++ b/apps/shared/scrollable_exact_approximate_expressions_cell.h @@ -24,7 +24,6 @@ public: } Poincare::Layout layout() const override { return m_view.layout(); } void didBecomeFirstResponder() override; - constexpr static KDCoordinate k_margin = 5; private: int numberOfSubviews() const override; View * subviewAtIndex(int index) override; diff --git a/apps/shared/scrollable_exact_approximate_expressions_view.cpp b/apps/shared/scrollable_exact_approximate_expressions_view.cpp index 1a26b9709..fc86fcfed 100644 --- a/apps/shared/scrollable_exact_approximate_expressions_view.cpp +++ b/apps/shared/scrollable_exact_approximate_expressions_view.cpp @@ -1,5 +1,5 @@ #include "scrollable_exact_approximate_expressions_view.h" -#include "../i18n.h" +#include #include using namespace Poincare; @@ -53,7 +53,7 @@ KDSize ScrollableExactApproximateExpressionsView::ContentCell::minimalSizeForOpt KDCoordinate rightBaseline = m_rightExpressionView.layout().baseline(); KDCoordinate height = max(leftBaseline, rightBaseline) + max(leftExpressionSize.height()-leftBaseline, rightExpressionSize.height()-rightBaseline); KDSize approximateSignSize = m_approximateSign.minimalSizeForOptimalDisplay(); - return KDSize(leftExpressionSize.width()+approximateSignSize.width()+rightExpressionSize.width()+2*k_digitHorizontalMargin, height); + return KDSize(leftExpressionSize.width()+approximateSignSize.width()+rightExpressionSize.width()+2*Metric::CommonLargeMargin, height); } void ScrollableExactApproximateExpressionsView::ContentCell::setSelectedSubviewPosition(ScrollableExactApproximateExpressionsView::SubviewPosition subviewPosition) { @@ -94,14 +94,21 @@ void ScrollableExactApproximateExpressionsView::ContentCell::layoutSubviews() { KDSize leftExpressionSize = m_leftExpressionView.minimalSizeForOptimalDisplay(); KDSize approximateSignSize = m_approximateSign.minimalSizeForOptimalDisplay(); m_leftExpressionView.setFrame(KDRect(0, baseline-leftBaseline, leftExpressionSize)); - m_rightExpressionView.setFrame(KDRect(2*k_digitHorizontalMargin+leftExpressionSize.width()+approximateSignSize.width(), baseline-rightBaseline, rightExpressionSize)); - m_approximateSign.setFrame(KDRect(k_digitHorizontalMargin+leftExpressionSize.width(), baseline-approximateSignSize.height()/2, approximateSignSize)); + m_rightExpressionView.setFrame(KDRect(2*Metric::CommonLargeMargin+leftExpressionSize.width()+approximateSignSize.width(), baseline-rightBaseline, rightExpressionSize)); + m_approximateSign.setFrame(KDRect(Metric::CommonLargeMargin+leftExpressionSize.width(), baseline-approximateSignSize.height()/2, approximateSignSize)); } ScrollableExactApproximateExpressionsView::ScrollableExactApproximateExpressionsView(Responder * parentResponder) : ScrollableView(parentResponder, &m_contentCell, this), m_contentCell() { + setDecoratorType(ScrollView::Decorator::Type::Arrows); + setMargins( + Metric::CommonSmallMargin, + Metric::CommonLargeMargin, + Metric::CommonSmallMargin, + Metric::CommonLargeMargin + ); } void ScrollableExactApproximateExpressionsView::setLayouts(Poincare::Layout rightLayout, Poincare::Layout leftLayout) { @@ -138,8 +145,4 @@ bool ScrollableExactApproximateExpressionsView::handleEvent(Ion::Events::Event e return ScrollableView::handleEvent(event); } -KDSize ScrollableExactApproximateExpressionsView::minimalSizeForOptimalDisplay() const { - return m_contentCell.minimalSizeForOptimalDisplay(); -} - } diff --git a/apps/shared/scrollable_exact_approximate_expressions_view.h b/apps/shared/scrollable_exact_approximate_expressions_view.h index f14011fd5..38a35357e 100644 --- a/apps/shared/scrollable_exact_approximate_expressions_view.h +++ b/apps/shared/scrollable_exact_approximate_expressions_view.h @@ -25,7 +25,6 @@ public: } void didBecomeFirstResponder() override; bool handleEvent(Ion::Events::Event event) override; - KDSize minimalSizeForOptimalDisplay() const override; Poincare::Layout layout() const { return m_contentCell.layout(); } @@ -55,12 +54,11 @@ private: Poincare::Layout layout() const override; private: View * subviewAtIndex(int index) override; - constexpr static KDCoordinate k_digitHorizontalMargin = 10; ExpressionView m_rightExpressionView; MessageTextView m_approximateSign; ExpressionView m_leftExpressionView; SubviewPosition m_selectedSubviewPosition; -}; + }; ContentCell m_contentCell; }; diff --git a/apps/shared/storage_cartesian_function.cpp b/apps/shared/storage_cartesian_function.cpp index 33a785ca3..d07eb935d 100644 --- a/apps/shared/storage_cartesian_function.cpp +++ b/apps/shared/storage_cartesian_function.cpp @@ -85,7 +85,7 @@ void StorageCartesianFunction::setDisplayDerivative(bool display) { } double StorageCartesianFunction::approximateDerivative(double x, Poincare::Context * context) const { - Poincare::Derivative derivative = Poincare::Derivative::Builder(expressionReduced(context).clone(), Symbol(Symbol::SpecialSymbols::UnknownX), Poincare::Float(x)); // derivative takes ownership of Poincare::Float(x) and the clone of expression + Poincare::Derivative derivative = Poincare::Derivative::Builder(expressionReduced(context).clone(), Symbol::Builder(Symbol::SpecialSymbols::UnknownX), Poincare::Float::Builder(x)); // derivative takes ownership of Poincare::Float::Builder(x) and the clone of expression /* TODO: when we approximate derivative, we might want to simplify the * derivative here. However, we might want to do it once for all x (to avoid * lagging in the derivative table. */ @@ -94,7 +94,7 @@ double StorageCartesianFunction::approximateDerivative(double x, Poincare::Conte double StorageCartesianFunction::sumBetweenBounds(double start, double end, Poincare::Context * context) const { // TODO: this does not work yet because integral does not understand UnknownX - Poincare::Integral integral = Poincare::Integral::Builder(expressionReduced(context).clone(), Symbol(Symbol::SpecialSymbols::UnknownX), Poincare::Float(start), Poincare::Float(end)); // Integral takes ownership of args + Poincare::Integral integral = Poincare::Integral::Builder(expressionReduced(context).clone(), Symbol::Builder(Symbol::SpecialSymbols::UnknownX), Poincare::Float::Builder(start), Poincare::Float::Builder(end)); // Integral takes ownership of args /* TODO: when we approximate integral, we might want to simplify the integral * here. However, we might want to do it once for all x (to avoid lagging in * the derivative table. */ @@ -103,22 +103,22 @@ double StorageCartesianFunction::sumBetweenBounds(double start, double end, Poin Expression::Coordinate2D StorageCartesianFunction::nextMinimumFrom(double start, double step, double max, Context * context) const { const char unknownX[2] = {Poincare::Symbol::UnknownX, 0}; - return expressionReduced(context).nextMinimum(unknownX, start, step, max, *context, Preferences::sharedPreferences()->angleUnit()); + return PoincareHelpers::NextMinimum(expressionReduced(context), unknownX, start, step, max, *context); } Expression::Coordinate2D StorageCartesianFunction::nextMaximumFrom(double start, double step, double max, Context * context) const { const char unknownX[2] = {Poincare::Symbol::UnknownX, 0}; - return expressionReduced(context).nextMaximum(unknownX, start, step, max, *context, Preferences::sharedPreferences()->angleUnit()); + return PoincareHelpers::NextMaximum(expressionReduced(context), unknownX, start, step, max, *context); } double StorageCartesianFunction::nextRootFrom(double start, double step, double max, Context * context) const { const char unknownX[2] = {Poincare::Symbol::UnknownX, 0}; - return expressionReduced(context).nextRoot(unknownX, start, step, max, *context, Preferences::sharedPreferences()->angleUnit()); + return PoincareHelpers::NextRoot(expressionReduced(context), unknownX, start, step, max, *context); } Expression::Coordinate2D StorageCartesianFunction::nextIntersectionFrom(double start, double step, double max, Poincare::Context * context, Expression e) const { const char unknownX[2] = {Poincare::Symbol::UnknownX, 0}; - return expressionReduced(context).nextIntersection(unknownX, start, step, max, *context, Preferences::sharedPreferences()->angleUnit(), e); + return PoincareHelpers::NextIntersection(expressionReduced(context), unknownX, start, step, max, *context, e); } StorageCartesianFunction::CartesianFunctionRecordData * StorageCartesianFunction::recordData() const { diff --git a/apps/shared/storage_expression_model.cpp b/apps/shared/storage_expression_model.cpp index 234875015..bfb274c7b 100644 --- a/apps/shared/storage_expression_model.cpp +++ b/apps/shared/storage_expression_model.cpp @@ -60,7 +60,7 @@ Layout StorageExpressionModel::layout() { if (m_layout.isUninitialized()) { m_layout = PoincareHelpers::CreateLayout(expressionClone()); if (m_layout.isUninitialized()) { - m_layout = HorizontalLayout(); + m_layout = HorizontalLayout::Builder(); } } return m_layout; @@ -87,7 +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()) { - expressionToStore = expressionToStore.replaceUnknown(Symbol('x')); //TODO Beware of non x unknowns! (for instance whe sequences are in the storage) + expressionToStore = expressionToStore.replaceUnknown(Symbol::Builder('x')); //TODO Beware of non x unknowns! (for instance whe sequences are in the storage) } } return setExpressionContent(expressionToStore); diff --git a/apps/shared/storage_expression_model_list_controller.cpp b/apps/shared/storage_expression_model_list_controller.cpp index d11522dd1..1569b3881 100644 --- a/apps/shared/storage_expression_model_list_controller.cpp +++ b/apps/shared/storage_expression_model_list_controller.cpp @@ -15,20 +15,18 @@ StorageExpressionModelListController::StorageExpressionModelListController(Respo } void StorageExpressionModelListController::tableSelectionDidChange(int previousSelectedRow) { - constexpr int currentSelectedMemoizedIndex = k_memoizedCellHeightsCount/2 + 1; // Needs k_memoizedCellHeightsCount to be odd, which is static asserted in the header file + constexpr int currentSelectedMemoizedIndex = k_memoizedCellsCount/2 + 1; // Needs k_memoizedCellsCount to be odd, which is static asserted in the header file int currentSelectedRow = selectedRow(); // The previously selected cell's height might have changed. - m_memoizedCellHeight[currentSelectedMemoizedIndex] = k_resetedMemoizedValue; + resetMemoizationForIndex(currentSelectedMemoizedIndex); // Update m_cumulatedHeightForSelectedIndex if we scrolled one cell up/down if (currentSelectedRow == previousSelectedRow + 1) { /* We selected the cell under the previous cell. Shift the memoized cell * heights. */ - for (int i = 0; i < k_memoizedCellHeightsCount - 1; i++) { - m_memoizedCellHeight[i] = m_memoizedCellHeight[i+1]; - } - m_memoizedCellHeight[k_memoizedCellHeightsCount-1] = k_resetedMemoizedValue; + shiftMemoization(true); + resetMemoizationForIndex(k_memoizedCellsCount-1); // Update m_cumulatedHeightForSelectedIndex if (previousSelectedRow >= 0) { m_cumulatedHeightForSelectedIndex+= memoizedRowHeight(previousSelectedRow); @@ -39,10 +37,8 @@ void StorageExpressionModelListController::tableSelectionDidChange(int previousS } else if (currentSelectedRow == previousSelectedRow - 1) { /* We selected the cell above the previous cell. Shift the memoized cell * heights. */ - for (int i = k_memoizedCellHeightsCount - 1; i > 0; i--) { - m_memoizedCellHeight[i] = m_memoizedCellHeight[i-1]; - } - m_memoizedCellHeight[0] = k_resetedMemoizedValue; + shiftMemoization(false); + resetMemoizationForIndex(0); // Update m_cumulatedHeightForSelectedIndex if (currentSelectedRow >= 0) { m_cumulatedHeightForSelectedIndex-= memoizedRowHeight(currentSelectedRow); @@ -59,7 +55,7 @@ KDCoordinate StorageExpressionModelListController::memoizedRowHeight(int j) { return 0; } int currentSelectedRow = selectedRow(); - constexpr int halfMemoizationCount = k_memoizedCellHeightsCount/2; + constexpr int halfMemoizationCount = k_memoizedCellsCount/2; if (j >= currentSelectedRow - halfMemoizationCount && j <= currentSelectedRow + halfMemoizationCount) { int memoizedIndex = j - (currentSelectedRow - halfMemoizationCount); if (m_memoizedCellHeight[memoizedIndex] == k_resetedMemoizedValue) { @@ -75,7 +71,7 @@ KDCoordinate StorageExpressionModelListController::memoizedCumulatedHeightFromIn return 0; } int currentSelectedRow = selectedRow(); - constexpr int halfMemoizationCount = k_memoizedCellHeightsCount/2; + constexpr int halfMemoizationCount = k_memoizedCellsCount/2; /* If j is not easily computable from the memoized values, compute it the hard * way. */ if (j < currentSelectedRow - halfMemoizationCount || j > currentSelectedRow + halfMemoizationCount) { @@ -176,6 +172,7 @@ void StorageExpressionModelListController::addEmptyModel() { void StorageExpressionModelListController::reinitExpression(ExpiringPointer model) { model->setContent(""); + resetMemoization(); selectableTableView()->reloadData(); } @@ -201,6 +198,7 @@ void StorageExpressionModelListController::editExpression(Ion::Events::Event eve StorageExpressionModelListController * myController = static_cast(context); InputViewController * myInputViewController = (InputViewController *)sender; const char * textBody = myInputViewController->textBody(); + myController->resetMemoization(); return myController->editSelectedRecordWithText(textBody); }, [](void * context, void * sender){ @@ -210,7 +208,7 @@ void StorageExpressionModelListController::editExpression(Ion::Events::Event eve bool StorageExpressionModelListController::editSelectedRecordWithText(const char * text) { Ion::Storage::Record record = modelStore()->recordAtIndex(modelIndexForRow(selectedRow())); - ExpiringPointer model = modelStore()->modelForRecord(record); + ExpiringPointer model = modelStore()->modelForRecord(record); return (model->setContent(text) == Ion::Storage::Record::ErrorStatus::None); } @@ -224,10 +222,27 @@ bool StorageExpressionModelListController::isAddEmptyRow(int j) { return j == modelStore()->numberOfModels(); } +void StorageExpressionModelListController::resetMemoizationForIndex(int index) { + assert(index >= 0 && index < k_memoizedCellsCount); + m_memoizedCellHeight[index] = k_resetedMemoizedValue; +} + +void StorageExpressionModelListController::shiftMemoization(bool newCellIsUnder) { + if (newCellIsUnder) { + for (int i = 0; i < k_memoizedCellsCount - 1; i++) { + m_memoizedCellHeight[i] = m_memoizedCellHeight[i+1]; + } + } else { + for (int i = k_memoizedCellsCount - 1; i > 0; i--) { + m_memoizedCellHeight[i] = m_memoizedCellHeight[i-1]; + } + } +} + void StorageExpressionModelListController::resetMemoization() { m_cumulatedHeightForSelectedIndex = k_resetedMemoizedValue; - for (int i = 0; i < k_memoizedCellHeightsCount; i++) { - m_memoizedCellHeight[i] = k_resetedMemoizedValue; + for (int i = 0; i < k_memoizedCellsCount; i++) { + resetMemoizationForIndex(i); } } diff --git a/apps/shared/storage_expression_model_list_controller.h b/apps/shared/storage_expression_model_list_controller.h index 1d5b2961b..d2bdfb43d 100644 --- a/apps/shared/storage_expression_model_list_controller.h +++ b/apps/shared/storage_expression_model_list_controller.h @@ -3,7 +3,7 @@ #include #include "storage_expression_model_store.h" -#include "../i18n.h" +#include namespace Shared { @@ -23,7 +23,7 @@ protected: // Responder bool handleEventOnExpression(Ion::Events::Event event); virtual void addEmptyModel(); - virtual void didChangeModelsList() {} + virtual void didChangeModelsList() { resetMemoization(); } virtual void reinitExpression(ExpiringPointer model); virtual void editExpression(Ion::Events::Event event); virtual bool editSelectedRecordWithText(const char * text); @@ -37,7 +37,7 @@ protected: EvenOddMessageTextCell m_addNewModel; protected: // Memoization - static constexpr int k_memoizedCellHeightsCount = 7; + static constexpr int k_memoizedCellsCount = 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 @@ -48,18 +48,20 @@ protected: * (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."); + static_assert(StorageExpressionModelListController::k_memoizedCellsCount % 2 == 1, "StorageExpressionModelListController::k_memoizedCellsCount 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; + * k_memoizedCellsCount needs to be odd to compute things such as: + * constexpr int halfMemoizationCount = k_memoizedCellsCount/2; * if (j < selectedRow - halfMemoizationCount * || j > selectedRow + halfMemoizationCount) { ... } */ + virtual void resetMemoizationForIndex(int index); + virtual void shiftMemoization(bool newCellIsUnder); private: // Memoization static constexpr int k_resetedMemoizedValue = -1; void resetMemoization(); virtual KDCoordinate notMemoizedCumulatedHeightFromIndex(int j) = 0; - KDCoordinate m_memoizedCellHeight[k_memoizedCellHeightsCount]; + KDCoordinate m_memoizedCellHeight[k_memoizedCellsCount]; KDCoordinate m_cumulatedHeightForSelectedIndex; }; diff --git a/apps/shared/storage_function.cpp b/apps/shared/storage_function.cpp index afb572634..02fa70fd4 100644 --- a/apps/shared/storage_function.cpp +++ b/apps/shared/storage_function.cpp @@ -1,4 +1,5 @@ #include "storage_function.h" +#include "poincare_helpers.h" #include #include "poincare/src/parsing/parser.h" #include @@ -76,7 +77,7 @@ T StorageFunction::templatedApproximateAtAbscissa(T x, Poincare::Context * conte return NAN; } const char unknownX[2] = {Poincare::Symbol::UnknownX, 0}; - return expressionReduced(context).approximateWithValueForSymbol(unknownX, x, *context, Preferences::sharedPreferences()->angleUnit()); + return PoincareHelpers::ApproximateWithValueForSymbol(expressionReduced(context), unknownX, x, *context); } StorageFunction::FunctionRecordData * StorageFunction::recordData() const { diff --git a/apps/shared/storage_function_graph_controller.cpp b/apps/shared/storage_function_graph_controller.cpp index 9d8c7efac..bc34ce043 100644 --- a/apps/shared/storage_function_graph_controller.cpp +++ b/apps/shared/storage_function_graph_controller.cpp @@ -136,7 +136,9 @@ void StorageFunctionGraphController::initCursorParameters() { m_cursor->moveTo(x, y); functionIndex = (std::isnan(y) || std::isinf(y)) ? 0 : functionIndex - 1; selectFunctionWithCursor(functionIndex); - interactiveCurveViewRange()->panToMakePointVisible(x, y, displayTopMarginRatio(), k_cursorRightMarginRatio, displayBottomMarginRatio(), k_cursorLeftMarginRatio); + if (interactiveCurveViewRange()->yAuto()) { + interactiveCurveViewRange()->panToMakePointVisible(x, y, displayTopMarginRatio(), k_cursorRightMarginRatio, displayBottomMarginRatio(), k_cursorLeftMarginRatio); + } } bool StorageFunctionGraphController::moveCursorVertically(int direction) { diff --git a/apps/shared/storage_function_graph_view.cpp b/apps/shared/storage_function_graph_view.cpp index 4c517b4d2..91e0fe8cd 100644 --- a/apps/shared/storage_function_graph_view.cpp +++ b/apps/shared/storage_function_graph_view.cpp @@ -22,10 +22,8 @@ StorageFunctionGraphView::StorageFunctionGraphView(InteractiveCurveViewRange * g void StorageFunctionGraphView::drawRect(KDContext * ctx, KDRect rect) const { ctx->fillRect(rect, KDColorWhite); drawGrid(ctx, rect); - drawAxes(ctx, rect, Axis::Horizontal); - drawAxes(ctx, rect, Axis::Vertical); - drawLabels(ctx, rect, Axis::Horizontal, true); - drawLabels(ctx, rect, Axis::Vertical, true); + drawAxes(ctx, rect); + simpleDrawBothAxesLabels(ctx, rect); } void StorageFunctionGraphView::setContext(Context * context) { diff --git a/apps/shared/storage_function_graph_view.h b/apps/shared/storage_function_graph_view.h index 40cc3e24e..97b5f6990 100644 --- a/apps/shared/storage_function_graph_view.h +++ b/apps/shared/storage_function_graph_view.h @@ -27,8 +27,8 @@ protected: bool m_shouldColorHighlighted; private: char * label(Axis axis, int index) const override; - char m_xLabels[k_maxNumberOfXLabels][k_labelBufferSize]; - char m_yLabels[k_maxNumberOfYLabels][k_labelBufferSize]; + char m_xLabels[k_maxNumberOfXLabels][k_labelBufferMaxSize]; + char m_yLabels[k_maxNumberOfYLabels][k_labelBufferMaxSize]; Poincare::Context * m_context; }; diff --git a/apps/shared/storage_function_list_controller.cpp b/apps/shared/storage_function_list_controller.cpp index d15d10f8f..da9c3ef31 100644 --- a/apps/shared/storage_function_list_controller.cpp +++ b/apps/shared/storage_function_list_controller.cpp @@ -25,9 +25,14 @@ StorageFunctionListController::StorageFunctionListController(Responder * parentR }, this), KDFont::SmallFont, Palette::PurpleBright), m_titlesColumnWidth(k_minTitleColumnWidth) { + /* m_memoizedCellBaseline is not initialized by the call to + * resetMemoizationForIndex in StorageExpressionModelListController's + * constructor, because it is a virtual method in a constructor. */ + for (int i = 0; i < k_memoizedCellsCount; i++) { + m_memoizedCellBaseline[i] = -1; + } m_selectableTableView.setMargins(0); m_selectableTableView.setVerticalCellOverlap(0); - m_selectableTableView.setShowsIndicators(false); } /* TableViewDataSource */ @@ -99,7 +104,7 @@ int StorageFunctionListController::indexFromCumulatedHeight(KDCoordinate offsetY KDCoordinate currentCumulatedHeight = cumulatedHeightFromIndex(currentSelectedRow); if (offsetY > currentCumulatedHeight) { - int iMax = min(k_memoizedCellHeightsCount/2 + 1, rowsCount - currentSelectedRow); + int iMax = min(k_memoizedCellsCount/2 + 1, rowsCount - currentSelectedRow); for (int i = 0; i < iMax; i++) { currentCumulatedHeight+= rowHeight(currentSelectedRow + i); if (offsetY <= currentCumulatedHeight) { @@ -107,7 +112,7 @@ int StorageFunctionListController::indexFromCumulatedHeight(KDCoordinate offsetY } } } else { - int iMax = min(k_memoizedCellHeightsCount/2, currentSelectedRow); + int iMax = min(k_memoizedCellsCount/2, currentSelectedRow); for (int i = 1; i <= iMax; i++) { currentCumulatedHeight-= rowHeight(currentSelectedRow-i); if (offsetY > currentCumulatedHeight) { @@ -274,9 +279,13 @@ void StorageFunctionListController::configureFunction(Ion::Storage::Record recor stack->push(parameterController()); } -void StorageFunctionListController::computeTitlesColumnWidth() { +void StorageFunctionListController::computeTitlesColumnWidth(bool forceMax) { + if (forceMax) { + m_titlesColumnWidth = nameWidth(StorageFunction::k_maxNameWithArgumentSize - 1)+k_functionTitleSumOfMargins; + return; + } KDCoordinate maxTitleWidth = maxFunctionNameWidth()+k_functionTitleSumOfMargins; - m_titlesColumnWidth = maxTitleWidth < k_minTitleColumnWidth ? k_minTitleColumnWidth : maxTitleWidth; + m_titlesColumnWidth = max(maxTitleWidth, k_minTitleColumnWidth); } TabViewController * StorageFunctionListController::tabController() const { @@ -303,10 +312,11 @@ KDCoordinate StorageFunctionListController::maxFunctionNameWidth() { assert(dotPosition != nullptr); maxNameLength = max(maxNameLength, dotPosition-functionName); } - return (maxNameLength + StorageFunction::k_parenthesedArgumentLength) * titleCells(0)->font()->glyphSize().width(); + return nameWidth(maxNameLength + StorageFunction::k_parenthesedArgumentLength); } void StorageFunctionListController::didChangeModelsList() { + StorageExpressionModelListController::didChangeModelsList(); computeTitlesColumnWidth(); } @@ -314,4 +324,44 @@ KDCoordinate StorageFunctionListController::notMemoizedCumulatedHeightFromIndex( return TableViewDataSource::cumulatedHeightFromIndex(j); } +KDCoordinate StorageFunctionListController::baseline(int j) { + if (j < 0) { + return -1; + } + int currentSelectedRow = selectedRow(); + constexpr int halfMemoizationCount = k_memoizedCellsCount/2; + if (j >= currentSelectedRow - halfMemoizationCount && j <= currentSelectedRow + halfMemoizationCount) { + int memoizedIndex = j - (currentSelectedRow - halfMemoizationCount); + if (m_memoizedCellBaseline[memoizedIndex] < 0) { + m_memoizedCellBaseline[memoizedIndex] = privateBaseline(j); + } + return m_memoizedCellBaseline[memoizedIndex]; + } + return privateBaseline(j); +} + +void StorageFunctionListController::resetMemoizationForIndex(int index) { + assert(index >= 0 && index < k_memoizedCellsCount); + m_memoizedCellBaseline[index] = -1; + StorageExpressionModelListController::resetMemoizationForIndex(index); +} + +void StorageFunctionListController::shiftMemoization(bool newCellIsUnder) { + if (newCellIsUnder) { + for (int i = 0; i < k_memoizedCellsCount - 1; i++) { + m_memoizedCellBaseline[i] = m_memoizedCellBaseline[i+1]; + } + } else { + for (int i = k_memoizedCellsCount - 1; i > 0; i--) { + m_memoizedCellBaseline[i] = m_memoizedCellBaseline[i-1]; + } + } + StorageExpressionModelListController::shiftMemoization(newCellIsUnder); +} + +KDCoordinate StorageFunctionListController::nameWidth(int nameLength) const { + assert(nameLength >= 0); + return nameLength * const_cast(this)->titleCells(0)->font()->glyphSize().width(); +} + } diff --git a/apps/shared/storage_function_list_controller.h b/apps/shared/storage_function_list_controller.h index c8e21fb30..dfc7bcfe7 100644 --- a/apps/shared/storage_function_list_controller.h +++ b/apps/shared/storage_function_list_controller.h @@ -7,7 +7,7 @@ #include "function_title_cell.h" #include "storage_list_parameter_controller.h" #include "storage_expression_model_list_controller.h" -#include "../i18n.h" +#include namespace Shared { @@ -53,12 +53,15 @@ public: protected: StackViewController * stackController() const; void configureFunction(Ion::Storage::Record record); - void computeTitlesColumnWidth(); + void computeTitlesColumnWidth(bool forceMax = false); StorageFunctionStore * modelStore() override; + KDCoordinate baseline(int j); + void resetMemoizationForIndex(int index) override; + void shiftMemoization(bool newCellIsUnder) override; SelectableTableView m_selectableTableView; private: static constexpr KDCoordinate k_minTitleColumnWidth = 65; - static constexpr KDCoordinate k_functionTitleSumOfMargins = 2*Metric::HistoryHorizontalMargin; + static constexpr KDCoordinate k_functionTitleSumOfMargins = 25; TabViewController * tabController() const; InputViewController * inputController() override; KDCoordinate maxFunctionNameWidth(); @@ -69,10 +72,13 @@ private: virtual FunctionTitleCell * titleCells(int index) = 0; virtual HighlightCell * expressionCells(int index) = 0; virtual void willDisplayTitleCellAtIndex(HighlightCell * cell, int j) = 0; + virtual KDCoordinate privateBaseline(int j) const = 0; + KDCoordinate nameWidth(int nameLength) const; EvenOddCell m_emptyCell; Button m_plotButton; Button m_valuesButton; KDCoordinate m_titlesColumnWidth; + KDCoordinate m_memoizedCellBaseline[k_memoizedCellsCount]; }; } diff --git a/apps/shared/storage_list_parameter_controller.h b/apps/shared/storage_list_parameter_controller.h index 46aeb8921..35b56ffdd 100644 --- a/apps/shared/storage_list_parameter_controller.h +++ b/apps/shared/storage_list_parameter_controller.h @@ -3,7 +3,7 @@ #include #include "storage_function_store.h" -#include "../i18n.h" +#include namespace Shared { diff --git a/apps/shared/storage_sum_graph_controller.cpp b/apps/shared/storage_sum_graph_controller.cpp index 6d2997718..bc164fb5a 100644 --- a/apps/shared/storage_sum_graph_controller.cpp +++ b/apps/shared/storage_sum_graph_controller.cpp @@ -240,10 +240,10 @@ void StorageSumGraphController::LegendView::setSumSymbol(Step step, double start } else if (step == Step::SecondParameter) { char buffer[PrintFloat::bufferSizeForFloatsWithPrecision(Constant::MediumNumberOfSignificantDigits)]; PrintFloat::convertFloatToText(start, buffer, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::MediumNumberOfSignificantDigits), Constant::MediumNumberOfSignificantDigits, Preferences::PrintFloatMode::Decimal); - m_sumLayout = CondensedSumLayout( + m_sumLayout = CondensedSumLayout::Builder( LayoutHelper::String(sigma, sizeof(sigma)), LayoutHelper::String(buffer, strlen(buffer), k_font), - EmptyLayout(EmptyLayoutNode::Color::Yellow, false, k_font, false)); + EmptyLayout::Builder(EmptyLayoutNode::Color::Yellow, false, k_font, false)); } else { m_sumLayout = LayoutHelper::String(sigma, sizeof(sigma)); char buffer[2+PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits)]; @@ -251,13 +251,13 @@ void StorageSumGraphController::LegendView::setSumSymbol(Step step, double start Layout start = LayoutHelper::String(buffer, strlen(buffer), KDFont::SmallFont); PrintFloat::convertFloatToText(end, buffer, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits, Preferences::PrintFloatMode::Decimal); Layout end = LayoutHelper::String(buffer, strlen(buffer), k_font); - m_sumLayout = CondensedSumLayout( + m_sumLayout = CondensedSumLayout::Builder( LayoutHelper::String(sigma, sizeof(sigma)), start, end); strlcpy(buffer, "= ", 3); PoincareHelpers::ConvertFloatToText(result, buffer+2, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits); - m_sumLayout = HorizontalLayout( + m_sumLayout = HorizontalLayout::Builder( m_sumLayout, functionLayout, LayoutHelper::String(buffer, strlen(buffer), k_font)); diff --git a/apps/shared/storage_values_controller.cpp b/apps/shared/storage_values_controller.cpp index 4a7558ee7..d8946031d 100644 --- a/apps/shared/storage_values_controller.cpp +++ b/apps/shared/storage_values_controller.cpp @@ -32,7 +32,6 @@ StorageValuesController::StorageValuesController(Responder * parentResponder, In m_selectableTableView.setBottomMargin(k_bottomMargin); m_selectableTableView.setLeftMargin(k_leftMargin); m_selectableTableView.setBackgroundColor(Palette::WallScreenDark); - m_selectableTableView.setIndicatorThickness(13); m_abscissaTitleCell.setMessageFont(k_font); for (int i = 0; i < k_maxNumberOfAbscissaCells; i++) { m_abscissaCells[i].setParentResponder(&m_selectableTableView); diff --git a/apps/shared/storage_values_controller.h b/apps/shared/storage_values_controller.h index 1e9fb5629..fa345d8e6 100644 --- a/apps/shared/storage_values_controller.h +++ b/apps/shared/storage_values_controller.h @@ -9,7 +9,7 @@ #include "values_parameter_controller.h" #include "storage_values_function_parameter_controller.h" #include "interval_parameter_controller.h" -#include "../i18n.h" +#include namespace Shared { diff --git a/apps/shared/storage_values_function_parameter_controller.h b/apps/shared/storage_values_function_parameter_controller.h index d41e51073..6d2a4b376 100644 --- a/apps/shared/storage_values_function_parameter_controller.h +++ b/apps/shared/storage_values_function_parameter_controller.h @@ -3,7 +3,7 @@ #include #include "storage_function.h" -#include "../i18n.h" +#include namespace Shared { diff --git a/apps/shared/store_parameter_controller.h b/apps/shared/store_parameter_controller.h index 602ffd7aa..fa3212778 100644 --- a/apps/shared/store_parameter_controller.h +++ b/apps/shared/store_parameter_controller.h @@ -3,7 +3,7 @@ #include #include "double_pair_store.h" -#include "../i18n.h" +#include namespace Shared { diff --git a/apps/shared/sum_graph_controller.cpp b/apps/shared/sum_graph_controller.cpp index 09b299b4b..ee03dbc2b 100644 --- a/apps/shared/sum_graph_controller.cpp +++ b/apps/shared/sum_graph_controller.cpp @@ -239,10 +239,10 @@ void SumGraphController::LegendView::setSumSymbol(Step step, double start, doubl } else if (step == Step::SecondParameter) { char buffer[PrintFloat::bufferSizeForFloatsWithPrecision(Constant::MediumNumberOfSignificantDigits)]; PrintFloat::convertFloatToText(start, buffer, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::MediumNumberOfSignificantDigits), Constant::MediumNumberOfSignificantDigits, Preferences::PrintFloatMode::Decimal); - m_sumLayout = CondensedSumLayout( + m_sumLayout = CondensedSumLayout::Builder( LayoutHelper::String(sigma, sizeof(sigma)), LayoutHelper::String(buffer, strlen(buffer), KDFont::SmallFont), - EmptyLayout(EmptyLayoutNode::Color::Yellow, false, KDFont::SmallFont, false)); + EmptyLayout::Builder(EmptyLayoutNode::Color::Yellow, false, KDFont::SmallFont, false)); } else { m_sumLayout = LayoutHelper::String(sigma, sizeof(sigma)); constexpr size_t bufferSize = 2+PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits); @@ -251,13 +251,13 @@ void SumGraphController::LegendView::setSumSymbol(Step step, double start, doubl Layout start = LayoutHelper::String(buffer, strlen(buffer), KDFont::SmallFont); PrintFloat::convertFloatToText(end, buffer, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits, Preferences::PrintFloatMode::Decimal); Layout end = LayoutHelper::String(buffer, strlen(buffer), KDFont::SmallFont); - m_sumLayout = CondensedSumLayout( + m_sumLayout = CondensedSumLayout::Builder( LayoutHelper::String(sigma, sizeof(sigma)), start, end); strlcpy(buffer, "= ", bufferSize); PoincareHelpers::ConvertFloatToText(result, buffer+2, PrintFloat::bufferSizeForFloatsWithPrecision(Constant::LargeNumberOfSignificantDigits), Constant::LargeNumberOfSignificantDigits); - m_sumLayout = HorizontalLayout( + m_sumLayout = HorizontalLayout::Builder( m_sumLayout, functionLayout, LayoutHelper::String(buffer, strlen(buffer), KDFont::SmallFont)); diff --git a/apps/shared/text_field_delegate_app.h b/apps/shared/text_field_delegate_app.h index 3bad9e1ac..20e6eb0e8 100644 --- a/apps/shared/text_field_delegate_app.h +++ b/apps/shared/text_field_delegate_app.h @@ -4,7 +4,7 @@ #include #include #include "input_event_handler_delegate_app.h" -#include "../i18n.h" +#include class AppsContainer; diff --git a/apps/shared/values_controller.cpp b/apps/shared/values_controller.cpp index 5a57c738a..5d8091cb9 100644 --- a/apps/shared/values_controller.cpp +++ b/apps/shared/values_controller.cpp @@ -32,7 +32,6 @@ ValuesController::ValuesController(Responder * parentResponder, InputEventHandle m_selectableTableView.setBottomMargin(k_bottomMargin); m_selectableTableView.setLeftMargin(k_leftMargin); m_selectableTableView.setBackgroundColor(Palette::WallScreenDark); - m_selectableTableView.setIndicatorThickness(13); m_abscissaTitleCell.setMessageFont(KDFont::SmallFont); for (int i = 0; i < k_maxNumberOfAbscissaCells; i++) { m_abscissaCells[i].setParentResponder(&m_selectableTableView); diff --git a/apps/shared/values_controller.h b/apps/shared/values_controller.h index 525c6210c..2eca47217 100644 --- a/apps/shared/values_controller.h +++ b/apps/shared/values_controller.h @@ -9,7 +9,7 @@ #include "values_parameter_controller.h" #include "values_function_parameter_controller.h" #include "interval_parameter_controller.h" -#include "../i18n.h" +#include namespace Shared { diff --git a/apps/shared/values_function_parameter_controller.h b/apps/shared/values_function_parameter_controller.h index 7fd5f017f..b6c303d8a 100644 --- a/apps/shared/values_function_parameter_controller.h +++ b/apps/shared/values_function_parameter_controller.h @@ -3,7 +3,7 @@ #include #include "function.h" -#include "../i18n.h" +#include namespace Shared { diff --git a/apps/shared/zoom_parameter_controller.cpp b/apps/shared/zoom_parameter_controller.cpp index 92a9aa179..d47e81251 100644 --- a/apps/shared/zoom_parameter_controller.cpp +++ b/apps/shared/zoom_parameter_controller.cpp @@ -60,12 +60,33 @@ bool ZoomParameterController::handleEvent(Ion::Events::Event event) { void ZoomParameterController::viewWillAppear() { m_contentView.curveView()->setOkView(nullptr); + /* We need to change the curve range to keep the same visual aspect of the + * view. */ + adaptCurveRange(true); +} + +void ZoomParameterController::viewDidDisappear() { + // Restore the curve range + adaptCurveRange(false); } void ZoomParameterController::didBecomeFirstResponder() { m_contentView.layoutSubviews(); } +void ZoomParameterController::adaptCurveRange(bool viewWillAppear) { + float currentYMin = m_interactiveRange->yMin(); + float currentRange = m_interactiveRange->yMax() - m_interactiveRange->yMin(); + float newYMin = 0; + if (viewWillAppear) { + newYMin = currentYMin + ((float)ContentView::k_legendHeight)/((float)k_standardViewHeight)*currentRange; + } else { + newYMin = m_interactiveRange->yMax() - currentRange*((float)k_standardViewHeight)/(((float)k_standardViewHeight)-((float)ContentView::k_legendHeight)); + } + m_interactiveRange->setYMin(newYMin); + m_contentView.curveView()->reload(); +} + /* Content View */ ZoomParameterController::ContentView::ContentView(CurveView * curveView) : @@ -86,6 +107,7 @@ View * ZoomParameterController::ContentView::subviewAtIndex(int index) { } void ZoomParameterController::ContentView::layoutSubviews() { + assert(bounds().height() == ZoomParameterController::k_standardViewHeight); m_curveView->setFrame(KDRect(0, 0, bounds().width(), bounds().height() - k_legendHeight)); m_legendView.setFrame(KDRect(0, bounds().height() - k_legendHeight, bounds().width(), k_legendHeight)); } diff --git a/apps/shared/zoom_parameter_controller.h b/apps/shared/zoom_parameter_controller.h index 6ff2f7b19..13751a1b2 100644 --- a/apps/shared/zoom_parameter_controller.h +++ b/apps/shared/zoom_parameter_controller.h @@ -4,7 +4,7 @@ #include #include "interactive_curve_view_range.h" #include "curve_view.h" -#include "../i18n.h" +#include namespace Shared { @@ -15,10 +15,13 @@ public: View * view() override; bool handleEvent(Ion::Events::Event event) override; void viewWillAppear() override; + void viewDidDisappear() override; void didBecomeFirstResponder() override; private: + constexpr static KDCoordinate k_standardViewHeight = 175; class ContentView : public View { public: + constexpr static KDCoordinate k_legendHeight = 30; ContentView(CurveView * curveView); void layoutSubviews() override; CurveView * curveView(); @@ -41,8 +44,8 @@ private: View * subviewAtIndex(int index) override; CurveView * m_curveView; LegendView m_legendView; - constexpr static KDCoordinate k_legendHeight = 30; }; + void adaptCurveRange(bool viewWillAppear); ContentView m_contentView; InteractiveCurveViewRange * m_interactiveRange; }; diff --git a/apps/shift_alpha_lock_view.h b/apps/shift_alpha_lock_view.h index f96dcfedc..b3b0bd05d 100644 --- a/apps/shift_alpha_lock_view.h +++ b/apps/shift_alpha_lock_view.h @@ -3,7 +3,7 @@ #include #include "lock_view.h" -#include "i18n.h" +#include class ShiftAlphaLockView : public View { public: diff --git a/apps/solver/Makefile b/apps/solver/Makefile index a15ab0b36..a87822ad6 100644 --- a/apps/solver/Makefile +++ b/apps/solver/Makefile @@ -1,15 +1,15 @@ apps += Solver::App app_headers += apps/solver/app.h -app_objs += $(addprefix apps/solver/,\ - app.o\ - equation_models_parameter_controller.o\ - equation.o\ - equation_list_view.o\ - equation_store.o\ - interval_controller.o\ - list_controller.o\ - solutions_controller.o\ +app_src += $(addprefix apps/solver/,\ + app.cpp \ + equation_models_parameter_controller.cpp \ + equation.cpp \ + equation_list_view.cpp \ + equation_store.cpp \ + interval_controller.cpp \ + list_controller.cpp \ + solutions_controller.cpp \ ) i18n_files += $(addprefix apps/solver/,\ @@ -24,4 +24,4 @@ tests += $(addprefix apps/solver/test/,\ equation_store.cpp\ ) -app_images += apps/solver/solver_icon.png +$(eval $(call depends_on_image,apps/solver/app.cpp,apps/solver/solver_icon.png)) diff --git a/apps/solver/app.cpp b/apps/solver/app.cpp index f00b7abe0..a27ad587e 100644 --- a/apps/solver/app.cpp +++ b/apps/solver/app.cpp @@ -1,5 +1,5 @@ #include "app.h" -#include "../i18n.h" +#include #include "solver_icon.h" using namespace Shared; diff --git a/apps/solver/base.de.i18n b/apps/solver/base.de.i18n index 7e5b250b1..b5977d8b6 100644 --- a/apps/solver/base.de.i18n +++ b/apps/solver/base.de.i18n @@ -6,6 +6,7 @@ ResolveSystem = "Loesen des Gleichungssystems" UseEquationModel = "Verwenden Sie ein Gleichungsmodell" RequireEquation = "Die Eingabe muss eine Gleichung sein" UndefinedEquation = "Nicht definierte Gleichung" +UnrealEquation = "Nicht reelle Gleichung" TooManyVariables = "Es gibt zu viele Unbekannte" NonLinearSystem = "Das System ist nicht linear" Solution = "Loesung" @@ -20,3 +21,5 @@ ApproximateSolutionIntervalInstruction0= "Geben Sie das Intervall fuer die Suche ApproximateSolutionIntervalInstruction1= "nach einer ungefaehren Loesung ein" OnlyFirstSolutionsDisplayed0 = "Es werden nur die ersten" OnlyFirstSolutionsDisplayed1 = "zehn Loesungen angezeigt." +PolynomeHasNoRealSolution0 = "Das Polynom hat" +PolynomeHasNoRealSolution1 = "keine reelle Nullstelle" diff --git a/apps/solver/base.en.i18n b/apps/solver/base.en.i18n index 0aaa4efe4..ebbb853e6 100644 --- a/apps/solver/base.en.i18n +++ b/apps/solver/base.en.i18n @@ -5,6 +5,7 @@ ResolveEquation = "Solve the equation" ResolveSystem = "Solve the system" UseEquationModel = "Use an equation template" RequireEquation = "The input must be an equation" +UnrealEquation = "Unreal equation" UndefinedEquation = "Undefined equation" TooManyVariables = "There are too many unknowns" NonLinearSystem = "The system is not linear" @@ -20,3 +21,5 @@ ApproximateSolutionIntervalInstruction0= "Enter the interval to search" ApproximateSolutionIntervalInstruction1= "for an approximate solution" OnlyFirstSolutionsDisplayed0 = "Only the first 10 solutions" OnlyFirstSolutionsDisplayed1 = "are displayed" +PolynomeHasNoRealSolution0 = "The polynomial has no" +PolynomeHasNoRealSolution1 = "real root" diff --git a/apps/solver/base.es.i18n b/apps/solver/base.es.i18n index 7611eb06f..7702ad8ec 100644 --- a/apps/solver/base.es.i18n +++ b/apps/solver/base.es.i18n @@ -5,7 +5,8 @@ ResolveEquation = "Resolver la ecuación" ResolveSystem = "Resolver el sistema" UseEquationModel = "Usar un modelo de ecuación" RequireEquation = "La entrada debe ser una ecuación" -UndefinedEquation = "Ecuación indefinida" +UnrealEquation = "Una ecuación no es real" +UndefinedEquation = "Una ecuación es indefinida" TooManyVariables = "Hay demasiadas incógnitas" NonLinearSystem = "El sistema no es lineal" Solution = "Solucion" @@ -20,3 +21,5 @@ ApproximateSolutionIntervalInstruction0= "Introduzca el intervalo para" ApproximateSolutionIntervalInstruction1= "buscar una solución aproximada" OnlyFirstSolutionsDisplayed0 = "Sólo se muestran las" OnlyFirstSolutionsDisplayed1 = "10 primeras soluciones" +PolynomeHasNoRealSolution0 = "El polinomio no tiene" +PolynomeHasNoRealSolution1 = "ninguna raíz real" diff --git a/apps/solver/base.fr.i18n b/apps/solver/base.fr.i18n index f8d408d11..def88cd86 100644 --- a/apps/solver/base.fr.i18n +++ b/apps/solver/base.fr.i18n @@ -6,6 +6,7 @@ ResolveSystem = "Résoudre le système" UseEquationModel = "Utiliser un modèle d'équation" RequireEquation = "L'entrée doit être une équation" UndefinedEquation = "Une equation est indéfinie" +UnrealEquation = "Une equation n'est pas réelle" TooManyVariables = "Le nombre d'inconnues est trop grand" NonLinearSystem = "Le système n'est pas linéaire" Solution = "Solution" @@ -20,3 +21,5 @@ ApproximateSolutionIntervalInstruction0= "Entrez l'intervalle dans lequel" ApproximateSolutionIntervalInstruction1= "rechercher une solution approchée" OnlyFirstSolutionsDisplayed0 = "Seulement les 10 premières" OnlyFirstSolutionsDisplayed1 = "solutions sont affichées" +PolynomeHasNoRealSolution0 = "Le polynôme n'admet pas" +PolynomeHasNoRealSolution1 = "de racine réelle" diff --git a/apps/solver/base.pt.i18n b/apps/solver/base.pt.i18n index 96a97debf..29c643aff 100644 --- a/apps/solver/base.pt.i18n +++ b/apps/solver/base.pt.i18n @@ -5,7 +5,8 @@ ResolveEquation = "Resolver a equação" ResolveSystem = "Resolver o sistema" UseEquationModel = "Usar um modelo de equação" RequireEquation = "A entrada deve ser uma equação" -UndefinedEquation = "Equação indefinida" +UndefinedEquation = "Uma equação é indefinida" +UnrealEquation = "Uma equação não é real" TooManyVariables = "Existem muitas incógnitas" NonLinearSystem = "O sistema não é linear" Solution = "Solução" @@ -20,3 +21,5 @@ ApproximateSolutionIntervalInstruction0= "Digite o intervalo para procurar" ApproximateSolutionIntervalInstruction1= "uma solução aproximada" OnlyFirstSolutionsDisplayed0 = "Somente as 10 primeiras" OnlyFirstSolutionsDisplayed1 = "soluções são exibidas" +PolynomeHasNoRealSolution0 = "O polinômio não tem" +PolynomeHasNoRealSolution1 = "nenhuma raiz real" diff --git a/apps/solver/equation.cpp b/apps/solver/equation.cpp index 3565c86c8..15c9e1578 100644 --- a/apps/solver/equation.cpp +++ b/apps/solver/equation.cpp @@ -2,6 +2,7 @@ #include #include +#include #include using namespace Poincare; @@ -28,21 +29,30 @@ void Equation::tidy() { Expression Equation::standardForm(Context * context) const { if (m_standardForm.isUninitialized()) { const Expression e = expression(context); + if (e.type() == ExpressionNode::Type::Unreal) { + m_standardForm = Unreal::Builder(); + return m_standardForm; + } if (e.recursivelyMatches([](const Expression e, Context & context, bool replaceSymbols) { return e.type() == ExpressionNode::Type::Undefined || e.type() == ExpressionNode::Type::Infinity || Expression::IsMatrix(e, context, replaceSymbols); }, *context, true)) { - m_standardForm = Undefined(); + m_standardForm = Undefined::Builder(); return m_standardForm; } if (e.type() == ExpressionNode::Type::Equal) { - m_standardForm = static_cast(e).standardEquation(*context, Preferences::sharedPreferences()->angleUnit()); + Preferences * preferences = Preferences::sharedPreferences(); + m_standardForm = static_cast(e).standardEquation(*context, Expression::UpdatedComplexFormatWithTextInput(preferences->complexFormat(), text()), preferences->angleUnit()); } else { assert(e.type() == ExpressionNode::Type::Rational && static_cast(e).isOne()); // The equality was reduced which means the equality was always true. - m_standardForm = Rational(0); + m_standardForm = Rational::Builder(0); } } return m_standardForm; } +bool Equation::containsIComplex() const { + return strchr(text(), Ion::Charset::IComplex) != nullptr; +} + void Equation::tidyStandardForm() { // Free the pool of the m_standardForm m_standardForm = Expression(); diff --git a/apps/solver/equation.h b/apps/solver/equation.h index 3a079e836..de5688753 100644 --- a/apps/solver/equation.h +++ b/apps/solver/equation.h @@ -14,6 +14,7 @@ public: return false; } Poincare::Expression standardForm(Poincare::Context * context) const; + bool containsIComplex() const; private: void tidyStandardForm(); mutable Poincare::Expression m_standardForm; diff --git a/apps/solver/equation_list_view.cpp b/apps/solver/equation_list_view.cpp index ae19c0dde..78d2e9e0a 100644 --- a/apps/solver/equation_list_view.cpp +++ b/apps/solver/equation_list_view.cpp @@ -14,10 +14,10 @@ EquationListView::EquationListView(Responder * parentResponder, TableViewDataSou { m_listView.setMargins(0); m_listView.setVerticalCellOverlap(0); - m_listView.setShowsIndicators(false); + m_listView.setDecoratorType(ScrollView::Decorator::Type::None); selectionDataSource->setScrollViewDelegate(this); m_scrollBraceView.setMargins(k_margin, k_margin, k_margin, k_margin); - m_scrollBraceView.setShowsIndicators(false); + m_scrollBraceView.setDecoratorType(ScrollView::Decorator::Type::None); m_scrollBraceView.setBackgroundColor(KDColorWhite); } diff --git a/apps/solver/equation_models_parameter_controller.cpp b/apps/solver/equation_models_parameter_controller.cpp index 0a73cfb1d..d1ab2622c 100644 --- a/apps/solver/equation_models_parameter_controller.cpp +++ b/apps/solver/equation_models_parameter_controller.cpp @@ -3,7 +3,7 @@ #include "../constant.h" #include #include -#include "../i18n.h" +#include using namespace Poincare; @@ -20,7 +20,7 @@ EquationModelsParameterController::EquationModelsParameterController(Responder * m_listController(listController) { m_selectableTableView.setMargins(0); - m_selectableTableView.setShowsIndicators(false); + m_selectableTableView.setDecoratorType(ScrollView::Decorator::Type::None); for (int i = 0; i < k_numberOfExpressionCells; i++) { Poincare::Expression e = Expression::Parse(k_models[i+1]); m_layouts[i] = e.createLayout(Poincare::Preferences::PrintFloatMode::Decimal, Constant::ShortNumberOfSignificantDigits); diff --git a/apps/solver/equation_store.cpp b/apps/solver/equation_store.cpp index 1dea69d30..b81de3552 100644 --- a/apps/solver/equation_store.cpp +++ b/apps/solver/equation_store.cpp @@ -2,6 +2,7 @@ #include "../shared/poincare_helpers.h" #include +#include #include #include #include @@ -12,6 +13,7 @@ #include #include #include +#include using namespace Poincare; using namespace Shared; @@ -41,7 +43,7 @@ void EquationStore::tidy() { } Poincare::Layout EquationStore::exactSolutionLayoutAtIndex(int i, bool exactLayout) { - assert(m_type != Type::Monovariable && i >= 0 && (i < m_numberOfSolutions || (i == m_numberOfSolutions && m_type == Type::PolynomialMonovariable))); + assert(m_type != Type::Monovariable && i >= 0 && (i < m_numberOfSolutions)); if (exactLayout) { return m_exactSolutionExactLayouts[i]; } else { @@ -76,7 +78,7 @@ bool EquationStore::haveMoreApproximationSolutions(Context * context) { return false; } double step = (m_intervalApproximateSolutions[1]-m_intervalApproximateSolutions[0])*k_precision; - return !std::isnan(definedModelAtIndex(0)->standardForm(context).nextRoot(m_variables[0], m_approximateSolutions[m_numberOfSolutions-1], step, m_intervalApproximateSolutions[1], *context, Preferences::sharedPreferences()->angleUnit())); + return !std::isnan(PoincareHelpers::NextRoot(definedModelAtIndex(0)->standardForm(context), m_variables[0], m_approximateSolutions[m_numberOfSolutions-1], step, m_intervalApproximateSolutions[1], *context)); } void EquationStore::approximateSolve(Poincare::Context * context) { @@ -86,7 +88,7 @@ void EquationStore::approximateSolve(Poincare::Context * context) { double start = m_intervalApproximateSolutions[0]; double step = (m_intervalApproximateSolutions[1]-m_intervalApproximateSolutions[0])*k_precision; for (int i = 0; i < k_maxNumberOfApproximateSolutions; i++) { - m_approximateSolutions[i] = definedModelAtIndex(0)->standardForm(context).nextRoot(m_variables[0], start, step, m_intervalApproximateSolutions[1], *context, Preferences::sharedPreferences()->angleUnit()); + m_approximateSolutions[i] = PoincareHelpers::NextRoot(definedModelAtIndex(0)->standardForm(context), m_variables[0], start, step, m_intervalApproximateSolutions[1], *context); if (std::isnan(m_approximateSolutions[i])) { break; } else { @@ -99,7 +101,7 @@ void EquationStore::approximateSolve(Poincare::Context * context) { EquationStore::Error EquationStore::exactSolve(Poincare::Context * context) { tidySolution(); - /* 0- Get unknown variables */ + // Step 0. Get unknown variables m_variables[0][0] = 0; int numberOfVariables = 0; for (int i = 0; i < numberOfDefinedModels(); i++) { @@ -107,6 +109,9 @@ EquationStore::Error EquationStore::exactSolve(Poincare::Context * context) { if (e.isUninitialized() || e.type() == ExpressionNode::Type::Undefined) { return Error::EquationUndefined; } + if (e.type() == ExpressionNode::Type::Unreal) { + return Error::EquationUnreal; + } numberOfVariables = e.getVariables(*context, [](const char * symbol) { return true; }, (char *)m_variables, Poincare::SymbolAbstract::k_maxNameSize); if (numberOfVariables == -1) { return Error::TooManyVariables; @@ -116,15 +121,16 @@ EquationStore::Error EquationStore::exactSolve(Poincare::Context * context) { assert(numberOfVariables >= 0); } - /* 1- Linear System? */ + // Step 1. Linear System? + /* Create matrix coefficients and vector constants as: - * coefficients*(x y z ...) = constants */ + * coefficients * (x y z ...) = constants */ Expression coefficients[k_maxNumberOfEquations][Expression::k_maxNumberOfVariables]; Expression constants[k_maxNumberOfEquations]; bool isLinear = true; // Invalid the linear system if one equation is non-linear Preferences * preferences = Preferences::sharedPreferences(); for (int i = 0; i < numberOfDefinedModels(); i++) { - isLinear = isLinear && definedModelAtIndex(i)->standardForm(context).getLinearCoefficients((char *)m_variables, Poincare::SymbolAbstract::k_maxNameSize, coefficients[i], &constants[i], *context, preferences->angleUnit()); + isLinear = isLinear && definedModelAtIndex(i)->standardForm(context).getLinearCoefficients((char *)m_variables, Poincare::SymbolAbstract::k_maxNameSize, coefficients[i], &constants[i], *context, updatedComplexFormat(), preferences->angleUnit()); if (!isLinear) { // TODO: should we clean pool allocated memory if the system is not linear #if 0 @@ -143,60 +149,69 @@ EquationStore::Error EquationStore::exactSolve(Poincare::Context * context) { } } - /* Initialize result */ + // Initialize result Expression exactSolutions[k_maxNumberOfExactSolutions]; + Expression exactSolutionsApproximations[k_maxNumberOfExactSolutions]; EquationStore::Error error; if (isLinear) { m_type = Type::LinearSystem; - error = resolveLinearSystem(exactSolutions, coefficients, constants, context); + error = resolveLinearSystem(exactSolutions, exactSolutionsApproximations, coefficients, constants, context); } else { - /* 2- Polynomial & Monovariable? */ + // Step 2. Polynomial & Monovariable? assert(numberOfVariables == 1 && numberOfDefinedModels() == 1); Expression polynomialCoefficients[Expression::k_maxNumberOfPolynomialCoefficients]; - int degree = definedModelAtIndex(0)->standardForm(context).getPolynomialReducedCoefficients(m_variables[0], polynomialCoefficients, *context, preferences->angleUnit()); + int degree = definedModelAtIndex(0)->standardForm(context).getPolynomialReducedCoefficients(m_variables[0], polynomialCoefficients, *context, updatedComplexFormat(), preferences->angleUnit()); if (degree == 2) { - /* Polynomial degree <= 2*/ + // Polynomial degree <= 2 m_type = Type::PolynomialMonovariable; - error = oneDimensialPolynomialSolve(exactSolutions, polynomialCoefficients, degree, context); + error = oneDimensialPolynomialSolve(exactSolutions, exactSolutionsApproximations, polynomialCoefficients, degree, context); } else { - /* 3- Monovariable non-polynomial or polynomial with degree > 2 */ + // Step 3. Monovariable non-polynomial or polynomial with degree > 2 m_type = Type::Monovariable; m_intervalApproximateSolutions[0] = -10; m_intervalApproximateSolutions[1] = 10; return Error::RequireApproximateSolution; } } - /* Turn the results in layouts */ - for (int i = 0; i < k_maxNumberOfExactSolutions; i++) { + // Create the results' layouts + int solutionIndex = 0; + int initialNumberOfSolutions = m_numberOfSolutions <= k_maxNumberOfExactSolutions ? m_numberOfSolutions : -1; + // We iterate through the solutions and the potential delta + for (int i = 0; i < initialNumberOfSolutions; i++) { if (!exactSolutions[i].isUninitialized()) { - m_exactSolutionExactLayouts[i] = PoincareHelpers::CreateLayout(exactSolutions[i]); - Expression approximate = PoincareHelpers::Approximate(exactSolutions[i], *context); - m_exactSolutionApproximateLayouts[i] = PoincareHelpers::CreateLayout(approximate); - /* Check for identity between exact and approximate layouts */ + assert(!exactSolutionsApproximations[i].isUninitialized()); + if (exactSolutionsApproximations[i].type() == ExpressionNode::Type::Unreal) { + // Discard unreal solutions. + m_numberOfSolutions--; + continue; + } + m_exactSolutionExactLayouts[solutionIndex] = PoincareHelpers::CreateLayout(exactSolutions[i]); + m_exactSolutionApproximateLayouts[solutionIndex] = PoincareHelpers::CreateLayout(exactSolutionsApproximations[i]); + // Check for identity between exact and approximate layouts char exactBuffer[Shared::ExpressionModel::k_expressionBufferSize]; char approximateBuffer[Shared::ExpressionModel::k_expressionBufferSize]; - m_exactSolutionExactLayouts[i].serializeForParsing(exactBuffer, Shared::ExpressionModel::k_expressionBufferSize); - m_exactSolutionApproximateLayouts[i].serializeForParsing(approximateBuffer, Shared::ExpressionModel::k_expressionBufferSize); - m_exactSolutionIdentity[i] = strcmp(exactBuffer, approximateBuffer) == 0; - /* Check for equality between exact and approximate layouts */ - if (!m_exactSolutionIdentity[i]) { + m_exactSolutionExactLayouts[solutionIndex].serializeForParsing(exactBuffer, Shared::ExpressionModel::k_expressionBufferSize); + m_exactSolutionApproximateLayouts[solutionIndex].serializeForParsing(approximateBuffer, Shared::ExpressionModel::k_expressionBufferSize); + m_exactSolutionIdentity[solutionIndex] = strcmp(exactBuffer, approximateBuffer) == 0; + if (!m_exactSolutionIdentity[solutionIndex]) { char buffer[Shared::ExpressionModel::k_expressionBufferSize]; - m_exactSolutionEquality[i] = exactSolutions[i].isEqualToItsApproximationLayout(approximate, buffer, Shared::ExpressionModel::k_expressionBufferSize, preferences->angleUnit(), preferences->displayMode(), preferences->numberOfSignificantDigits(), *context); + m_exactSolutionEquality[solutionIndex] = exactSolutions[i].isEqualToItsApproximationLayout(exactSolutionsApproximations[i], buffer, Shared::ExpressionModel::k_expressionBufferSize, preferences->complexFormat(), preferences->angleUnit(), preferences->displayMode(), preferences->numberOfSignificantDigits(), *context); } + solutionIndex++; } } return error; } -EquationStore::Error EquationStore::resolveLinearSystem(Expression exactSolutions[k_maxNumberOfExactSolutions], Expression coefficients[k_maxNumberOfEquations][Expression::k_maxNumberOfVariables], Expression constants[k_maxNumberOfEquations], Context * context) { +EquationStore::Error EquationStore::resolveLinearSystem(Expression exactSolutions[k_maxNumberOfExactSolutions], Expression exactSolutionsApproximations[k_maxNumberOfExactSolutions], Expression coefficients[k_maxNumberOfEquations][Expression::k_maxNumberOfVariables], Expression constants[k_maxNumberOfEquations], Context * context) { Preferences::AngleUnit angleUnit = Preferences::sharedPreferences()->angleUnit(); // n unknown variables int n = 0; while (m_variables[n][0] != 0) { n++; } int m = numberOfDefinedModels(); // m equations /* Create the matrix (A | b) for the equation Ax=b */ - Matrix Ab; + Matrix Ab = Matrix::Builder(); for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { Ab.addChildAtIndexInPlace(coefficients[i][j], Ab.numberOfChildren(), Ab.numberOfChildren()); @@ -206,7 +221,7 @@ EquationStore::Error EquationStore::resolveLinearSystem(Expression exactSolution Ab.setDimensions(m, n+1); // Compute the rank of (A | b) - int rankAb = Ab.rank(*context, angleUnit, true); + int rankAb = Ab.rank(*context, updatedComplexFormat(), angleUnit, true); // Initialize the number of solutions m_numberOfSolutions = INT_MAX; @@ -231,33 +246,36 @@ EquationStore::Error EquationStore::resolveLinearSystem(Expression exactSolution m_numberOfSolutions = n; for (int i = 0; i < m_numberOfSolutions; i++) { exactSolutions[i] = Ab.matrixChild(i,n); - PoincareHelpers::Simplify(&exactSolutions[i], *context); + exactSolutions[i].simplifyAndApproximate(&exactSolutions[i], &exactSolutionsApproximations[i], *context, updatedComplexFormat(), Poincare::Preferences::sharedPreferences()->angleUnit()); } } } return Error::NoError; } -EquationStore::Error EquationStore::oneDimensialPolynomialSolve(Expression exactSolutions[k_maxNumberOfExactSolutions], Expression coefficients[Expression::k_maxNumberOfPolynomialCoefficients], int degree, Context * context) { +EquationStore::Error EquationStore::oneDimensialPolynomialSolve(Expression exactSolutions[k_maxNumberOfExactSolutions], Expression exactSolutionsApproximations[k_maxNumberOfExactSolutions], Expression coefficients[Expression::k_maxNumberOfPolynomialCoefficients], int degree, Context * context) { /* Equation ax^2+bx+c = 0 */ assert(degree == 2); // Compute delta = b*b-4ac - Expression delta = Subtraction(Power(coefficients[1].clone(), Rational(2)), Multiplication(Rational(4), coefficients[0].clone(), coefficients[2].clone())); - PoincareHelpers::Simplify(&delta, *context); + Expression delta = Subtraction::Builder(Power::Builder(coefficients[1].clone(), Rational::Builder(2)), Multiplication::Builder(Rational::Builder(4), coefficients[0].clone(), coefficients[2].clone())); + delta = delta.simplify(*context, updatedComplexFormat(), Poincare::Preferences::sharedPreferences()->angleUnit()); + if (delta.isUninitialized()) { + delta = Poincare::Undefined::Builder(); + } if (delta.isRationalZero()) { // if delta = 0, x0=x1= -b/(2a) - exactSolutions[0] = Division(Opposite(coefficients[1]), Multiplication(Rational(2), coefficients[2])); - m_numberOfSolutions = 1; + exactSolutions[0] = Division::Builder(Opposite::Builder(coefficients[1]), Multiplication::Builder(Rational::Builder(2), coefficients[2])); + m_numberOfSolutions = 2; } else { // x0 = (-b-sqrt(delta))/(2a) - exactSolutions[0] = Division(Subtraction(Opposite(coefficients[1].clone()), SquareRoot::Builder(delta.clone())), Multiplication(Rational(2), coefficients[2].clone())); + exactSolutions[0] = Division::Builder(Subtraction::Builder(Opposite::Builder(coefficients[1].clone()), SquareRoot::Builder(delta.clone())), Multiplication::Builder(Rational::Builder(2), coefficients[2].clone())); // x1 = (-b+sqrt(delta))/(2a) - exactSolutions[1] = Division(Addition(Opposite(coefficients[1]), SquareRoot::Builder(delta.clone())), Multiplication(Rational(2), coefficients[2])); - m_numberOfSolutions = 2; + exactSolutions[1] = Division::Builder(Addition::Builder(Opposite::Builder(coefficients[1]), SquareRoot::Builder(delta.clone())), Multiplication::Builder(Rational::Builder(2), coefficients[2])); + m_numberOfSolutions = 3; } - exactSolutions[m_numberOfSolutions] = delta; + exactSolutions[m_numberOfSolutions-1] = delta; for (int i = 0; i < m_numberOfSolutions; i++) { - PoincareHelpers::Simplify(&exactSolutions[i], *context); + exactSolutions[i].simplifyAndApproximate(&exactSolutions[i], &exactSolutionsApproximations[i], *context, updatedComplexFormat(), Poincare::Preferences::sharedPreferences()->angleUnit()); } return Error::NoError; #if 0 @@ -267,57 +285,57 @@ EquationStore::Error EquationStore::oneDimensialPolynomialSolve(Expression exact Expression * c = coefficients[1]; Expression * d = coefficients[0]; // Delta = b^2*c^2+18abcd-27a^2*d^2-4ac^3-4db^3 - Expression * mult0Operands[2] = {new Power(b->clone(), new Rational(2), false), new Power(c->clone(), new Rational(2), false)}; - Expression * mult1Operands[5] = {new Rational(18), a->clone(), b->clone(), c->clone(), d->clone()}; - Expression * mult2Operands[3] = {new Rational(-27), new Power(a->clone(), new Rational(2), false), new Power(d->clone(), new Rational(2), false)}; - Expression * mult3Operands[3] = {new Rational(-4), a->clone(), new Power(c->clone(), new Rational(3), false)}; - Expression * mult4Operands[3] = {new Rational(-4), d->clone(), new Power(b->clone(), new Rational(3), false)}; - Expression * add0Operands[5] = {new Multiplication(mult0Operands, 2, false), new Multiplication(mult1Operands, 5, false), new Multiplication(mult2Operands, 3, false), new Multiplication(mult3Operands, 3, false), new Multiplication(mult4Operands, 3, false)}; + Expression * mult0Operands[2] = {new Power::Builder(b->clone(), new Rational::Builder(2), false), new Power::Builder(c->clone(), new Rational::Builder(2), false)}; + Expression * mult1Operands[5] = {new Rational::Builder(18), a->clone(), b->clone(), c->clone(), d->clone()}; + Expression * mult2Operands[3] = {new Rational::Builder(-27), new Power::Builder(a->clone(), new Rational::Builder(2), false), new Power::Builder(d->clone(), new Rational::Builder(2), false)}; + Expression * mult3Operands[3] = {new Rational::Builder(-4), a->clone(), new Power::Builder(c->clone(), new Rational::Builder(3), false)}; + Expression * mult4Operands[3] = {new Rational::Builder(-4), d->clone(), new Power::Builder(b->clone(), new Rational::Builder(3), false)}; + Expression * add0Operands[5] = {new Multiplication::Builder(mult0Operands, 2, false), new Multiplication::Builder(mult1Operands, 5, false), new Multiplication::Builder(mult2Operands, 3, false), new Multiplication::Builder(mult3Operands, 3, false), new Multiplication::Builder(mult4Operands, 3, false)}; Expression * delta = new Addition(add0Operands, 5, false); PoincareHelpers::Simplify(&delta, *context); // Delta0 = b^2-3ac - Expression * mult5Operands[3] = {new Rational(3), a->clone(), c->clone()}; - Expression * delta0 = new Subtraction(new Power(b->clone(), new Rational(2), false), new Multiplication(mult5Operands, 3, false), false); + Expression * mult5Operands[3] = {new Rational::Builder(3), a->clone(), c->clone()}; + Expression * delta0 = new Subtraction::Builder(new Power::Builder(b->clone(), new Rational::Builder(2), false), new Multiplication::Builder(mult5Operands, 3, false), false); Reduce(&delta0, *context); if (delta->isRationalZero()) { if (delta0->isRationalZero()) { // delta0 = 0 && delta = 0 --> x0 = -b/(3a) delete delta0; - m_exactSolutions[0] = new Opposite(new Division(b, new Multiplication(new Rational(3), a, false), false), false); + m_exactSolutions[0] = new Opposite::Builder(new Division::Builder(b, new Multiplication::Builder(new Rational::Builder(3), a, false), false), false); m_numberOfSolutions = 1; delete c; delete d; } else { // delta = 0 --> x0 = (9ad-bc)/(2delta0) // --> x1 = (4abc-9a^2d-b^3)/(a*delta0) - Expression * mult6Operands[3] = {new Rational(9), a, d}; - m_exactSolutions[0] = new Division(new Subtraction(new Multiplication(mult6Operands, 3, false), new Multiplication(b, c, false), false), new Multiplication(new Rational(2), delta0, false), false); - Expression * mult7Operands[4] = {new Rational(4), a->clone(), b->clone(), c->clone()}; - Expression * mult8Operands[3] = {new Rational(-9), new Power(a->clone(), new Rational(2), false), d->clone()}; - Expression * add1Operands[3] = {new Multiplication(mult7Operands, 4, false), new Multiplication(mult8Operands,3, false), new Opposite(new Power(b->clone(), new Rational(3), false), false)}; - m_exactSolutions[1] = new Division(new Addition(add1Operands, 3, false), new Multiplication(a->clone(), delta0, false), false); + Expression * mult6Operands[3] = {new Rational::Builder(9), a, d}; + m_exactSolutions[0] = new Division::Builder(new Subtraction::Builder(new Multiplication::Builder(mult6Operands, 3, false), new Multiplication::Builder(b, c, false), false), new Multiplication::Builder(new Rational::Builder(2), delta0, false), false); + Expression * mult7Operands[4] = {new Rational::Builder(4), a->clone(), b->clone(), c->clone()}; + Expression * mult8Operands[3] = {new Rational::Builder(-9), new Power::Builder(a->clone(), new Rational::Builder(2), false), d->clone()}; + Expression * add1Operands[3] = {new Multiplication::Builder(mult7Operands, 4, false), new Multiplication::Builder(mult8Operands,3, false), new Opposite::Builder(new Power::Builder(b->clone(), new Rational::Builder(3), false), false)}; + m_exactSolutions[1] = new Division::Builder(new Addition(add1Operands, 3, false), new Multiplication::Builder(a->clone(), delta0, false), false); m_numberOfSolutions = 2; } } else { // delta1 = 2b^3-9abc+27a^2*d - Expression * mult9Operands[4] = {new Rational(-9), a, b, c}; - Expression * mult10Operands[3] = {new Rational(27), new Power(a->clone(), new Rational(2), false), d}; - Expression * add2Operands[3] = {new Multiplication(new Rational(2), new Power(b->clone(), new Rational(3), false), false), new Multiplication(mult9Operands, 4, false), new Multiplication(mult10Operands, 3, false)}; + Expression * mult9Operands[4] = {new Rational::Builder(-9), a, b, c}; + Expression * mult10Operands[3] = {new Rational::Builder(27), new Power::Builder(a->clone(), new Rational::Builder(2), false), d}; + Expression * add2Operands[3] = {new Multiplication::Builder(new Rational::Builder(2), new Power::Builder(b->clone(), new Rational::Builder(3), false), false), new Multiplication::Builder(mult9Operands, 4, false), new Multiplication::Builder(mult10Operands, 3, false)}; Expression * delta1 = new Addition(add2Operands, 3, false); // C = Root((delta1+sqrt(-27a^2*delta))/2, 3) - Expression * mult11Operands[3] = {new Rational(-27), new Power(a->clone(), new Rational(2), false), (*delta)->clone()}; - Expression * c = new Power(new Division(new Addition(delta1, new SquareRoot(new Multiplication(mult11Operands, 3, false), false), false), new Rational(2), false), new Rational(1,3), false); - Expression * unary3roots[2] = {new Addition(new Rational(-1,2), new Division(new Multiplication(new SquareRoot(new Rational(3), false), new Constant(Ion::Charset::IComplex), false), new Rational(2), false), false), new Subtraction(new Rational(-1,2), new Division(new Multiplication(new SquareRoot(new Rational(3), false), new Constant(Ion::Charset::IComplex), false), new Rational(2), false), false)}; + Expression * mult11Operands[3] = {new Rational::Builder(-27), new Power::Builder(a->clone(), new Rational::Builder(2), false), (*delta)->clone()}; + Expression * c = new Power::Builder(new Division::Builder(new Addition(delta1, new SquareRoot(new Multiplication::Builder(mult11Operands, 3, false), false), false), new Rational::Builder(2), false), new Rational::Builder(1,3), false); + Expression * unary3roots[2] = {new Addition(new Rational::Builder(-1,2), new Division::Builder(new Multiplication::Builder(new SquareRoot(new Rational::Builder(3), false), new Constant::Builder(Ion::Charset::IComplex), false), new Rational::Builder(2), false), false), new Subtraction::Builder(new Rational::Builder(-1,2), new Division::Builder(new Multiplication::Builder(new SquareRoot(new Rational::Builder(3), false), new Constant::Builder(Ion::Charset::IComplex), false), new Rational::Builder(2), false), false)}; // x_k = -1/(3a)*(b+C*z+delta0/(zC)) with z = unary cube root for (int k = 0; k < 3; k++) { Expression * ccopy = c; Expression * delta0copy = delta0; if (k < 2) { - ccopy = new Multiplication(c->clone(), unary3roots[k], false); + ccopy = new Multiplication::Builder(c->clone(), unary3roots[k], false); delta0copy = delta0->clone(); } - Expression * add3Operands[3] = {b->clone(), ccopy, new Division(delta0copy, ccopy->clone(), false)}; - m_exactSolutions[k] = new Multiplication(new Division(new Rational(-1), new Multiplication(new Rational(3), a->clone(), false), false), new Addition(add3Operands, 3, false), false); + Expression * add3Operands[3] = {b->clone(), ccopy, new Division::Builder(delta0copy, ccopy->clone(), false)}; + m_exactSolutions[k] = new Multiplication::Builder(new Division::Builder(new Rational::Builder(-1), new Multiplication::Builder(new Rational::Builder(3), a->clone(), false), false), new Addition(add3Operands, 3, false), false); } m_numberOfSolutions = 3; } @@ -333,4 +351,21 @@ void EquationStore::tidySolution() { } } +Preferences::ComplexFormat EquationStore::updatedComplexFormat() { + Preferences::ComplexFormat complexFormat = Preferences::sharedPreferences()->complexFormat(); + if (complexFormat == Preferences::ComplexFormat::Real && isExplictlyComplex()) { + return Preferences::ComplexFormat::Cartesian; + } + return complexFormat; +} + +bool EquationStore::isExplictlyComplex() { + for (int i = 0; i < numberOfDefinedModels(); i++) { + if (definedModelAtIndex(i)->containsIComplex()) { + return true; + } + } + return false; +} + } diff --git a/apps/solver/equation_store.h b/apps/solver/equation_store.h index b57ab0086..510c6dbdf 100644 --- a/apps/solver/equation_store.h +++ b/apps/solver/equation_store.h @@ -18,9 +18,10 @@ public: enum class Error : int16_t { NoError = 0, EquationUndefined = -1, - TooManyVariables = -2, - NonLinearSystem = -3, - RequireApproximateSolution = -4, + EquationUnreal = -2, + TooManyVariables = -3, + NonLinearSystem = -4, + RequireApproximateSolution = -5, }; /* EquationStore */ EquationStore(); @@ -79,9 +80,11 @@ private: return emptyModel(); } void setModelAtIndex(Shared::ExpressionModel * f, int i) override; - Error resolveLinearSystem(Poincare::Expression solutions[k_maxNumberOfExactSolutions], Poincare::Expression coefficients[k_maxNumberOfEquations][Poincare::Expression::k_maxNumberOfVariables], Poincare::Expression constants[k_maxNumberOfEquations], Poincare::Context * context); - Error oneDimensialPolynomialSolve(Poincare::Expression solutions[k_maxNumberOfExactSolutions], Poincare::Expression polynomialCoefficients[Poincare::Expression::k_maxNumberOfPolynomialCoefficients], int degree, Poincare::Context * context); + Error resolveLinearSystem(Poincare::Expression solutions[k_maxNumberOfExactSolutions], Poincare::Expression solutionApproximations[k_maxNumberOfExactSolutions], Poincare::Expression coefficients[k_maxNumberOfEquations][Poincare::Expression::k_maxNumberOfVariables], Poincare::Expression constants[k_maxNumberOfEquations], Poincare::Context * context); + Error oneDimensialPolynomialSolve(Poincare::Expression solutions[k_maxNumberOfExactSolutions], Poincare::Expression solutionApproximations[k_maxNumberOfExactSolutions], Poincare::Expression polynomialCoefficients[Poincare::Expression::k_maxNumberOfPolynomialCoefficients], int degree, Poincare::Context * context); void tidySolution(); + bool isExplictlyComplex(); + Poincare::Preferences::ComplexFormat updatedComplexFormat(); Equation m_equations[k_maxNumberOfEquations]; Type m_type; diff --git a/apps/solver/interval_controller.cpp b/apps/solver/interval_controller.cpp index 16514c0c9..19d061602 100644 --- a/apps/solver/interval_controller.cpp +++ b/apps/solver/interval_controller.cpp @@ -1,6 +1,6 @@ #include "interval_controller.h" #include "app.h" -#include "../i18n.h" +#include #include #include diff --git a/apps/solver/list_controller.cpp b/apps/solver/list_controller.cpp index fdbef2483..d46095f21 100644 --- a/apps/solver/list_controller.cpp +++ b/apps/solver/list_controller.cpp @@ -182,6 +182,9 @@ void ListController::resolveEquations() { case EquationStore::Error::EquationUndefined: app()->displayWarning(I18n::Message::UndefinedEquation); return; + case EquationStore::Error::EquationUnreal: + app()->displayWarning(I18n::Message::UnrealEquation); + return; case EquationStore::Error::TooManyVariables: app()->displayWarning(I18n::Message::TooManyVariables); return; diff --git a/apps/solver/list_controller.h b/apps/solver/list_controller.h index 9355e9f37..14ca87f87 100644 --- a/apps/solver/list_controller.h +++ b/apps/solver/list_controller.h @@ -8,7 +8,7 @@ #include "equation_store.h" #include "equation_list_view.h" #include "equation_models_parameter_controller.h" -#include "../i18n.h" +#include namespace Solver { diff --git a/apps/solver/solutions_controller.cpp b/apps/solver/solutions_controller.cpp index bc28a6d47..f9b97405b 100644 --- a/apps/solver/solutions_controller.cpp +++ b/apps/solver/solutions_controller.cpp @@ -16,8 +16,8 @@ using namespace Shared; namespace Solver { SolutionsController::ContentView::ContentView(SolutionsController * controller) : - m_warningMessageView0(KDFont::SmallFont, I18n::Message::OnlyFirstSolutionsDisplayed0, 0.5f, 0.5f, KDColorBlack, Palette::WallScreenDark), - m_warningMessageView1(KDFont::SmallFont, I18n::Message::OnlyFirstSolutionsDisplayed1, 0.5f, 0.5f, KDColorBlack, Palette::WallScreenDark), + m_warningMessageView0(KDFont::SmallFont, I18n::Message::Default, 0.5f, 0.5f, KDColorBlack, Palette::WallScreenDark), + m_warningMessageView1(KDFont::SmallFont, I18n::Message::Default, 0.5f, 0.5f, KDColorBlack, Palette::WallScreenDark), m_selectableTableView(controller), m_displayWarningMoreSolutions(false) { @@ -31,11 +31,15 @@ void SolutionsController::ContentView::drawRect(KDContext * ctx, KDRect rect) co } } -void SolutionsController::ContentView::setWarningMoreSolutions(bool warning) { +void SolutionsController::ContentView::setWarning(bool warning) { m_displayWarningMoreSolutions = warning; m_selectableTableView.setTopMargin(m_displayWarningMoreSolutions ? 0 : Metric::CommonTopMargin); layoutSubviews(); - markRectAsDirty(bounds()); +} + +void SolutionsController::ContentView::setWarningMessages(I18n::Message message0, I18n::Message message1) { + m_warningMessageView0.setMessage(message0); + m_warningMessageView1.setMessage(message1); } int SolutionsController::ContentView::numberOfSubviews() const { @@ -71,7 +75,7 @@ SolutionsController::SolutionsController(Responder * parentResponder, EquationSt m_delta2Layout(), m_contentView(this) { - m_delta2Layout = HorizontalLayout(VerticalOffsetLayout(CharLayout('2', KDFont::SmallFont), VerticalOffsetLayoutNode::Type::Superscript), LayoutHelper::String("-4ac", 4, KDFont::SmallFont)); + m_delta2Layout = HorizontalLayout::Builder(VerticalOffsetLayout::Builder(CharLayout::Builder('2', KDFont::SmallFont), VerticalOffsetLayoutNode::Type::Superscript), LayoutHelper::String("-4ac", 4, KDFont::SmallFont)); char deltaB[] = {Ion::Charset::CapitalDelta, '=', 'b'}; static_cast(m_delta2Layout).addOrMergeChildAtIndex(LayoutHelper::String(deltaB, 3, KDFont::SmallFont), 0, false); for (int i = 0; i < EquationStore::k_maxNumberOfExactSolutions; i++) { @@ -100,7 +104,16 @@ View * SolutionsController::view() { void SolutionsController::viewWillAppear() { ViewController::viewWillAppear(); App * solverApp = static_cast(app()); - m_contentView.setWarningMoreSolutions(m_equationStore->haveMoreApproximationSolutions(solverApp->localContext())); + bool requireWarning = false; + if (m_equationStore->type() == EquationStore::Type::Monovariable) { + m_contentView.setWarningMessages(I18n::Message::OnlyFirstSolutionsDisplayed0, I18n::Message::OnlyFirstSolutionsDisplayed1); + requireWarning = m_equationStore->haveMoreApproximationSolutions(solverApp->localContext()); + } else if (m_equationStore->type() == EquationStore::Type::PolynomialMonovariable && m_equationStore->numberOfSolutions() == 1) { + assert(Preferences::sharedPreferences()->complexFormat() == Preferences::ComplexFormat::Real); + m_contentView.setWarningMessages(I18n::Message::PolynomeHasNoRealSolution0, I18n::Message::PolynomeHasNoRealSolution1); + requireWarning = true; + } + m_contentView.setWarning(requireWarning); m_contentView.selectableTableView()->reloadData(); if (selectedRow() < 0) { selectCellAtLocation(0, 0); @@ -136,9 +149,6 @@ Responder * SolutionsController::defaultController() { /* TableViewDataSource */ int SolutionsController::numberOfRows() { - if (m_equationStore->type() == EquationStore::Type::PolynomialMonovariable) { - return m_equationStore->numberOfSolutions() + 1; // add the delta row - } return m_equationStore->numberOfSolutions(); } @@ -149,7 +159,7 @@ int SolutionsController::numberOfColumns() { void SolutionsController::willDisplayCellAtLocation(HighlightCell * cell, int i, int j) { if (i == 0) { // Name of the variable or discriminant - if (m_equationStore->type() == EquationStore::Type::PolynomialMonovariable && j == m_equationStore->numberOfSolutions()) { + if (m_equationStore->type() == EquationStore::Type::PolynomialMonovariable && j == m_equationStore->numberOfSolutions()-1) { // Discriminant EvenOddExpressionCell * deltaCell = static_cast(cell); deltaCell->setLayout(m_delta2Layout); @@ -165,9 +175,9 @@ void SolutionsController::willDisplayCellAtLocation(HighlightCell * cell, int i, break; default: /* The system has one variable but might have many solutions: the cell - * text is variableX, with X the row index. For instance, x0, x1,...*/ + * text is variableX, with X the row index + 1 (e.g. x1, x2,...) */ int length = strlcpy(bufferSymbol, m_equationStore->variableAtIndex(0), Poincare::SymbolAbstract::k_maxNameSize); - bufferSymbol[length++] = j+'0'; + bufferSymbol[length++] = j+'1'; bufferSymbol[length] = 0; break; } @@ -210,7 +220,7 @@ KDCoordinate SolutionsController::rowHeight(int j) { KDCoordinate exactLayoutHeight = exactLayout.layoutSize().height(); KDCoordinate approximateLayoutHeight = approximateLayout.layoutSize().height(); KDCoordinate layoutHeight = max(exactLayout.baseline(), approximateLayout.baseline()) + max(exactLayoutHeight-exactLayout.baseline(), approximateLayoutHeight-approximateLayout.baseline()); - return layoutHeight+ScrollableExactApproximateExpressionsCell::k_margin*2; + return layoutHeight + 2 * Metric::CommonSmallMargin; } KDCoordinate SolutionsController::cumulatedWidthFromIndex(int i) { @@ -265,7 +275,7 @@ int SolutionsController::reusableCellCount(int type) { int SolutionsController::typeAtLocation(int i, int j) { if (i == 0) { - if (m_equationStore->type() == EquationStore::Type::PolynomialMonovariable && j == m_equationStore->numberOfSolutions()) { + if (m_equationStore->type() == EquationStore::Type::PolynomialMonovariable && j == m_equationStore->numberOfSolutions()-1) { return 1; } return 0; diff --git a/apps/solver/solutions_controller.h b/apps/solver/solutions_controller.h index a136bb675..172faf761 100644 --- a/apps/solver/solutions_controller.h +++ b/apps/solver/solutions_controller.h @@ -4,7 +4,7 @@ #include #include "equation_store.h" #include "../shared/scrollable_exact_approximate_expressions_cell.h" -#include "../i18n.h" +#include namespace Solver { @@ -38,7 +38,8 @@ private: public: ContentView(SolutionsController * controller); void drawRect(KDContext * ctx, KDRect rect) const override; - void setWarningMoreSolutions(bool warning); + void setWarning(bool warning); + void setWarningMessages(I18n::Message message0, I18n::Message message1); SelectableTableView * selectableTableView() { return &m_selectableTableView; } diff --git a/apps/solver/test/equation_store.cpp b/apps/solver/test/equation_store.cpp index 55c5d00f8..fd2e3e9c5 100644 --- a/apps/solver/test/equation_store.cpp +++ b/apps/solver/test/equation_store.cpp @@ -39,8 +39,7 @@ void assert_equation_system_exact_solve_to(const char * equations[], EquationSto } else { quiz_assert(strcmp(equationStore.variableAtIndex(0), variables[0]) == 0); } - int n = type == EquationStore::Type::PolynomialMonovariable ? numberOfSolutions+1 : numberOfSolutions; // Check Delta for PolynomialMonovariable - for (int i = 0; i < n; i++) { + for (int i = 0; i < numberOfSolutions; i++) { equationStore.exactSolutionLayoutAtIndex(i, true).serializeForParsing(buffer, 200); translate_in_ASCII_chars(buffer); quiz_assert(strcmp(buffer, solutions[i]) == 0); @@ -105,28 +104,28 @@ QUIZ_CASE(equation_solve) { // 3x^2-4x+4=2 const char * equations8[] = {"3*x^2-4x+4=2", 0}; - const char * solutions8[] = {"(2-R(2)*I)/(3)","(2+R(2)*I)/(3)", "-8"}; - assert_equation_system_exact_solve_to(equations8, EquationStore::Error::NoError, EquationStore::Type::PolynomialMonovariable, (const char **)variablesx, solutions8, 2); + const char * solutions8[] = {"(2)/(3)-(R(2))/(3)*I","(2)/(3)+(R(2))/(3)*I", "-8"}; + assert_equation_system_exact_solve_to(equations8, EquationStore::Error::NoError, EquationStore::Type::PolynomialMonovariable, (const char **)variablesx, solutions8, 3); // 2*x^2-4*x+4=3 const char * equations9[] = {"2*x^2-4*x+4=3", 0}; - const char * solutions9[] = {"(2-R(2))/(2)","(2+R(2))/(2)", "8"}; - assert_equation_system_exact_solve_to(equations9, EquationStore::Error::NoError, EquationStore::Type::PolynomialMonovariable, (const char **)variablesx, solutions9, 2); + const char * solutions9[] = {"(-R(2)+2)/(2)","(R(2)+2)/(2)", "8"}; + assert_equation_system_exact_solve_to(equations9, EquationStore::Error::NoError, EquationStore::Type::PolynomialMonovariable, (const char **)variablesx, solutions9, 3); // 2*x^2-4*x+2=0 const char * equations10[] = {"2*x^2-4*x+2=0", 0}; const char * solutions10[] = {"1", "0"}; - assert_equation_system_exact_solve_to(equations10, EquationStore::Error::NoError, EquationStore::Type::PolynomialMonovariable, (const char **)variablesx, solutions10, 1); + assert_equation_system_exact_solve_to(equations10, EquationStore::Error::NoError, EquationStore::Type::PolynomialMonovariable, (const char **)variablesx, solutions10, 2); // x^2+x+1=3*x^2+pi*x-R(5) const char * equations11[] = {"x^2+x+1=3*x^2+P*x-R(5)", 0}; - const char * solutions11[] = {"(1-P+R(9+8*R(5)-2*P+P$2#))/(4)", "(1-P-R(9+8*R(5)-2*P+P$2#))/(4)", "9+8*R(5)-2*P+P$2#"}; - assert_equation_system_exact_solve_to(equations11, EquationStore::Error::NoError, EquationStore::Type::PolynomialMonovariable, (const char **)variablesx, solutions11, 2); + const char * solutions11[] = {"(R(P$2#-2*P+8*R(5)+9)-P+1)/(4)", "(-R(P$2#-2*P+8*R(5)+9)-P+1)/(4)", "P$2#-2*P+8*R(5)+9"}; + assert_equation_system_exact_solve_to(equations11, EquationStore::Error::NoError, EquationStore::Type::PolynomialMonovariable, (const char **)variablesx, solutions11, 3); // TODO // x^3 - 4x^2 + 6x - 24 = 0 //const char * equations10[] = {"2*x^2-4*x+4=3", 0}; - //assert_equation_system_exact_solve_to(equations10, EquationStore::Error::NoError, EquationStore::Type::PolynomialMonovariable, {"x", ""}, {"4", "I*R(6)", "-I*R(6)", "-11616"}, 3); + //assert_equation_system_exact_solve_to(equations10, EquationStore::Error::NoError, EquationStore::Type::PolynomialMonovariable, {"x", ""}, {"4", "I*R(6)", "-I*R(6)", "-11616"}, 4); //x^3+x^2+1=0 // x^3-3x-2=0 @@ -144,7 +143,7 @@ QUIZ_CASE(equation_solve) { const char * variablesxyz[] = {"x", "y", "z", ""}; const char * equations14[] = {"x+y=0", "3x+y+z=-5", "4z-P=0", 0}; - const char * solutions14[] = {"(-20-P)/(8)", "(20+P)/(8)", "(P)/(4)"}; + const char * solutions14[] = {"(-P-20)/(8)", "(P+20)/(8)", "(P)/(4)"}; assert_equation_system_exact_solve_to(equations14, EquationStore::Error::NoError, EquationStore::Type::LinearSystem, (const char **)variablesxyz, solutions14, 3); // Monovariable non-polynomial equation @@ -169,4 +168,66 @@ QUIZ_CASE(equation_solve) { assert_equation_system_exact_solve_to(equations19, EquationStore::Error::NoError, EquationStore::Type::LinearSystem, (const char **)variablesBig1Big2, solutions19, 2); } +QUIZ_CASE(equation_solve_complex_format) { + Poincare::Preferences::sharedPreferences()->setComplexFormat(Poincare::Preferences::ComplexFormat::Real); + const char * variablesx[] = {"x", ""}; + // x+I = 0 --> x = -I + const char * equations0[] = {"x+I=0", 0}; + const char * solutions0[] = {"-I"}; + assert_equation_system_exact_solve_to(equations0, EquationStore::Error::NoError, EquationStore::Type::LinearSystem, (const char **)variablesx, solutions0, 1); + + // x+R(-1) = 0 --> Not defined in R + const char * equations1[] = {"x+R(-1)=0", 0}; + assert_equation_system_exact_solve_to(equations1, EquationStore::Error::EquationUnreal, EquationStore::Type::LinearSystem, (const char **)variablesx, nullptr, 0); + + // x^2+x+1=0 --> No solution in R + const char * equations2[] = {"x^2+x+1=0", 0}; + const char * delta2[] = {"-3"}; + assert_equation_system_exact_solve_to(equations2, EquationStore::Error::NoError, EquationStore::Type::PolynomialMonovariable, (const char **)variablesx, delta2, 1); + + // x^2-R(-1)=0 --> Not defined in R + const char * equations3[] = {"x^2-R(-1)=0", 0}; + assert_equation_system_exact_solve_to(equations3, EquationStore::Error::EquationUnreal, EquationStore::Type::PolynomialMonovariable, (const char **)variablesx, nullptr, 0); + + // x+R(-1)*R(-1) = 0 --> Not defined in R + const char * equations4[] = {"x+R(-1)*R(-1)=0", 0}; + assert_equation_system_exact_solve_to(equations4, EquationStore::Error::EquationUnreal, EquationStore::Type::LinearSystem, (const char **)variablesx, nullptr, 0); + + Poincare::Preferences::sharedPreferences()->setComplexFormat(Poincare::Preferences::ComplexFormat::Cartesian); + // x+I = 0 --> x = -I + assert_equation_system_exact_solve_to(equations0, EquationStore::Error::NoError, EquationStore::Type::LinearSystem, (const char **)variablesx, solutions0, 1); + + // x+R(-1) = 0 --> x = -I + assert_equation_system_exact_solve_to(equations1, EquationStore::Error::NoError, EquationStore::Type::LinearSystem, (const char **)variablesx, solutions0, 1); + + // x^2+x+1=0 + const char * solutions2[] = {"-(1)/(2)-(R(3))/(2)*I","-(1)/(2)+(R(3))/(2)*I", "-3"}; + assert_equation_system_exact_solve_to(equations2, EquationStore::Error::NoError, EquationStore::Type::PolynomialMonovariable, (const char **)variablesx, solutions2, 3); + + // x^2-R(-1)=0 + const char * solutions3[] = {"-(R(2))/(2)-(R(2))/(2)*I", "(R(2))/(2)+(R(2))/(2)*I","4*I"}; + assert_equation_system_exact_solve_to(equations3, EquationStore::Error::NoError, EquationStore::Type::PolynomialMonovariable, (const char **)variablesx, solutions3, 3); + + // x+R(-1)*R(-1) = 0 + const char * solutions4[] = {"1"}; + assert_equation_system_exact_solve_to(equations4, EquationStore::Error::NoError, EquationStore::Type::LinearSystem, (const char **)variablesx, solutions4, 1); + + Poincare::Preferences::sharedPreferences()->setComplexFormat(Poincare::Preferences::ComplexFormat::Polar); + // x+I = 0 --> x = e^(-pi/2*i) + const char * solutions0Polar[] = {"X$-(P)/(2)*I#"}; + assert_equation_system_exact_solve_to(equations0, EquationStore::Error::NoError, EquationStore::Type::LinearSystem, (const char **)variablesx, solutions0Polar, 1); + + // x+R(-1) = 0 --> x = e^(-pi/2*i) + assert_equation_system_exact_solve_to(equations1, EquationStore::Error::NoError, EquationStore::Type::LinearSystem, (const char **)variablesx, solutions0Polar, 1); + + // x^2+x+1=0 + const char * solutions2Polar[] = {"X$-(2*P)/(3)*I#","X$(2*P)/(3)*I#", "3*X$P*I#"}; + assert_equation_system_exact_solve_to(equations2, EquationStore::Error::NoError, EquationStore::Type::PolynomialMonovariable, (const char **)variablesx, solutions2Polar, 3); + + // x^2-R(-1)=0 + const char * solutions3Polar[] = {"X$-(3*P)/(4)*I#", "X$(P)/(4)*I#", "4*X$(P)/(2)*I#"}; + assert_equation_system_exact_solve_to(equations3, EquationStore::Error::NoError, EquationStore::Type::PolynomialMonovariable, (const char **)variablesx, solutions3Polar, 3); + +} + } diff --git a/apps/statistics/Makefile b/apps/statistics/Makefile index 424fb48c0..8e5fa5b7d 100644 --- a/apps/statistics/Makefile +++ b/apps/statistics/Makefile @@ -1,26 +1,26 @@ apps += Statistics::App app_headers += apps/statistics/app.h -app_objs += $(addprefix apps/statistics/,\ - app.o\ - box_axis_view.o\ - box_banner_view.o\ - box_controller.o\ - box_range.o\ - box_view.o\ - calculation_controller.o\ - calculation_selectable_table_view.o\ - histogram_banner_view.o\ - histogram_controller.o\ - histogram_parameter_controller.o\ - histogram_view.o\ - multiple_boxes_view.o\ - multiple_data_view.o\ - multiple_data_view_controller.o\ - multiple_histograms_view.o\ - statistics_context.o\ - store.o\ - store_controller.o\ +app_src += $(addprefix apps/statistics/,\ + app.cpp \ + box_axis_view.cpp \ + box_banner_view.cpp \ + box_controller.cpp \ + box_range.cpp \ + box_view.cpp \ + calculation_controller.cpp \ + calculation_selectable_table_view.cpp \ + histogram_banner_view.cpp \ + histogram_controller.cpp \ + histogram_parameter_controller.cpp \ + histogram_view.cpp \ + multiple_boxes_view.cpp \ + multiple_data_view.cpp \ + multiple_data_view_controller.cpp \ + multiple_histograms_view.cpp \ + statistics_context.cpp \ + store.cpp \ + store_controller.cpp \ ) i18n_files += $(addprefix apps/statistics/,\ @@ -35,4 +35,4 @@ tests += $(addprefix apps/statistics/test/,\ store.cpp\ ) -app_images += apps/statistics/stat_icon.png +$(eval $(call depends_on_image,apps/statistics/app.cpp,apps/statistics/stat_icon.png)) diff --git a/apps/statistics/app.cpp b/apps/statistics/app.cpp index 64c935553..787e51ace 100644 --- a/apps/statistics/app.cpp +++ b/apps/statistics/app.cpp @@ -1,6 +1,6 @@ #include "app.h" #include "stat_icon.h" -#include "../i18n.h" +#include using namespace Shared; diff --git a/apps/statistics/box_axis_view.h b/apps/statistics/box_axis_view.h index b22e0fc69..2fbfe0df8 100644 --- a/apps/statistics/box_axis_view.h +++ b/apps/statistics/box_axis_view.h @@ -20,7 +20,7 @@ public: private: constexpr static KDCoordinate k_axisMargin = 3; char * label(Axis axis, int index) const override; - char m_labels[k_maxNumberOfXLabels][k_labelBufferSize]; + char m_labels[k_maxNumberOfXLabels][k_labelBufferMaxSize]; BoxRange m_boxRange; }; diff --git a/apps/statistics/box_banner_view.h b/apps/statistics/box_banner_view.h index 9192e8275..a53060061 100644 --- a/apps/statistics/box_banner_view.h +++ b/apps/statistics/box_banner_view.h @@ -3,7 +3,7 @@ #include #include "../shared/banner_view.h" -#include "../i18n.h" +#include namespace Statistics { diff --git a/apps/statistics/box_range.cpp b/apps/statistics/box_range.cpp index f8a9c674d..1307acaf1 100644 --- a/apps/statistics/box_range.cpp +++ b/apps/statistics/box_range.cpp @@ -22,7 +22,7 @@ float BoxRange::xMax() { } float BoxRange::xGridUnit() { - return computeGridUnit(Axis::X, xMin(), xMax()); + return computeGridUnit(Axis::X, xMax() - xMin()); } } diff --git a/apps/statistics/histogram_banner_view.h b/apps/statistics/histogram_banner_view.h index dcb3df8f3..161c78a37 100644 --- a/apps/statistics/histogram_banner_view.h +++ b/apps/statistics/histogram_banner_view.h @@ -3,7 +3,7 @@ #include #include "../shared/banner_view.h" -#include "../i18n.h" +#include namespace Statistics { diff --git a/apps/statistics/histogram_controller.cpp b/apps/statistics/histogram_controller.cpp index 4414513ac..b4e1a22e9 100644 --- a/apps/statistics/histogram_controller.cpp +++ b/apps/statistics/histogram_controller.cpp @@ -250,7 +250,7 @@ void HistogramController::initBarParameters() { } maxValue = minValue >= maxValue ? minValue + std::pow(10.0f, std::floor(std::log10(std::fabs(minValue)))-1.0f) : maxValue; m_store->setFirstDrawnBarAbscissa(minValue); - float barWidth = m_store->computeGridUnit(CurveViewRange::Axis::X, minValue, maxValue); + float barWidth = m_store->computeGridUnit(CurveViewRange::Axis::X, maxValue - minValue); if (barWidth <= 0.0f) { barWidth = 1.0f; } diff --git a/apps/statistics/histogram_view.cpp b/apps/statistics/histogram_view.cpp index c50972cbc..94b681aa2 100644 --- a/apps/statistics/histogram_view.cpp +++ b/apps/statistics/histogram_view.cpp @@ -41,7 +41,7 @@ void HistogramView::reloadSelectedBar() { void HistogramView::drawRect(KDContext * ctx, KDRect rect) const { m_controller->setCurrentDrawnSeries(m_series); ctx->fillRect(rect, KDColorWhite); - drawAxes(ctx, rect, Axis::Horizontal); + drawAxis(ctx, rect, Axis::Horizontal); drawLabels(ctx, rect, Axis::Horizontal, false, !m_displayLabels); /* We memoize the total size to avoid recomputing it in double precision at * every call to EvaluateHistogramAtAbscissa() */ diff --git a/apps/statistics/histogram_view.h b/apps/statistics/histogram_view.h index 1af1dad1f..32117ffde 100644 --- a/apps/statistics/histogram_view.h +++ b/apps/statistics/histogram_view.h @@ -24,7 +24,7 @@ private: char * label(Axis axis, int index) const override; HistogramController * m_controller; Store * m_store; - char m_labels[k_maxNumberOfXLabels][k_labelBufferSize]; + char m_labels[k_maxNumberOfXLabels][k_labelBufferMaxSize]; static float EvaluateHistogramAtAbscissa(float abscissa, void * model, void * context); float m_highlightedBarStart; float m_highlightedBarEnd; diff --git a/apps/statistics/multiple_data_view_controller.cpp b/apps/statistics/multiple_data_view_controller.cpp index c9687b55c..73ed94fe9 100644 --- a/apps/statistics/multiple_data_view_controller.cpp +++ b/apps/statistics/multiple_data_view_controller.cpp @@ -1,5 +1,5 @@ #include "multiple_data_view_controller.h" -#include "../i18n.h" +#include #include using namespace Shared; diff --git a/apps/statistics/statistics_context.cpp b/apps/statistics/statistics_context.cpp index 1f5260ee9..23830d431 100644 --- a/apps/statistics/statistics_context.cpp +++ b/apps/statistics/statistics_context.cpp @@ -22,7 +22,7 @@ const Expression StatisticsContext::expressionForSymbol(const SymbolAbstract & s assert(m_seriesPairIndex >= 0); assert(m_seriesPairIndex < m_store->numberOfPairsOfSeries(series)); - return Float(m_store->get(series, storeI, m_seriesPairIndex)); + return Float::Builder(m_store->get(series, storeI, m_seriesPairIndex)); } else { return m_parentContext->expressionForSymbol(symbol, clone); } diff --git a/apps/title_bar_view.h b/apps/title_bar_view.h index 264be8a96..b5335dc76 100644 --- a/apps/title_bar_view.h +++ b/apps/title_bar_view.h @@ -4,7 +4,7 @@ #include #include "battery_view.h" #include "shift_alpha_lock_view.h" -#include "i18n.h" +#include class TitleBarView : public View { public: diff --git a/apps/usb/Makefile b/apps/usb/Makefile index 0bb37aa5f..45c6e0d8d 100644 --- a/apps/usb/Makefile +++ b/apps/usb/Makefile @@ -1,6 +1,6 @@ -app_objs += $(addprefix apps/usb/,\ - app.o\ - usb_connected_controller.o\ +app_src += $(addprefix apps/usb/,\ + app.cpp \ + usb_connected_controller.cpp \ ) i18n_files += $(addprefix apps/usb/,\ diff --git a/apps/usb/usb_connected_controller.cpp b/apps/usb/usb_connected_controller.cpp index a03835dc6..189c060bd 100644 --- a/apps/usb/usb_connected_controller.cpp +++ b/apps/usb/usb_connected_controller.cpp @@ -1,5 +1,5 @@ #include "usb_connected_controller.h" -#include "../i18n.h" +#include namespace USB { diff --git a/apps/variable_box_controller.cpp b/apps/variable_box_controller.cpp index a9d8ac9a3..adc6f61bd 100644 --- a/apps/variable_box_controller.cpp +++ b/apps/variable_box_controller.cpp @@ -59,8 +59,7 @@ bool VariableBoxController::handleEvent(Ion::Events::Event event) { if (event == Ion::Events::Backspace && m_currentPage != Page::RootMenu && m_lockPageDelete != m_currentPage && !isDisplayingEmptyController()) { int rowIndex = selectedRow(); m_selectableTableView.deselectTable(); - Storage::Record record = recordAtIndex(rowIndex); - record.destroy(); + destroyRecordAtRowIndex(rowIndex); int newSelectedRow = rowIndex >= numberOfRows() ? numberOfRows()-1 : rowIndex; selectCellAtLocation(selectedColumn(), newSelectedRow); m_selectableTableView.reloadData(); @@ -221,16 +220,18 @@ I18n::Message VariableBoxController::nodeLabelAtIndex(int index) { Layout VariableBoxController::expressionLayoutForRecord(Storage::Record record, int index) { assert(m_currentPage != Page::RootMenu); + assert(index >= 0); if (index >= m_firstMemoizedLayoutIndex+k_maxNumberOfDisplayedRows || index < m_firstMemoizedLayoutIndex) { // Change range of layout memoization int deltaIndex = index >= m_firstMemoizedLayoutIndex + k_maxNumberOfDisplayedRows ? index - k_maxNumberOfDisplayedRows + 1 - m_firstMemoizedLayoutIndex : index - m_firstMemoizedLayoutIndex; - for (int i = 0; i < k_maxNumberOfDisplayedRows-1; i++) { + for (int i = 0; i < k_maxNumberOfDisplayedRows; i++) { int j = deltaIndex + i; - m_layouts[i] = j >= 0 && j < k_maxNumberOfDisplayedRows ? m_layouts[j] : Layout(); + m_layouts[i] = (j >= m_firstMemoizedLayoutIndex && j < k_maxNumberOfDisplayedRows) ? m_layouts[j] : Layout(); } m_firstMemoizedLayoutIndex += deltaIndex; + assert(m_firstMemoizedLayoutIndex >= 0); } - assert(index-m_firstMemoizedLayoutIndex < k_maxNumberOfDisplayedRows); + assert(index >= m_firstMemoizedLayoutIndex && index < m_firstMemoizedLayoutIndex + k_maxNumberOfDisplayedRows); if (m_layouts[index-m_firstMemoizedLayoutIndex].isUninitialized()) { m_layouts[index-m_firstMemoizedLayoutIndex] = GlobalContext::ExpressionFromRecord(record).createLayout(Poincare::Preferences::sharedPreferences()->displayMode(), Constant::ShortNumberOfSignificantDigits); } @@ -250,7 +251,7 @@ Storage::Record VariableBoxController::recordAtIndex(int rowIndex) { bool VariableBoxController::displayEmptyController() { assert(!isDisplayingEmptyController()); - /* If the content is empty, we push above an empty controller. */ + // If the content is empty, we push above an empty controller. if (numberOfRows() == 0) { m_emptyViewController.setType((VariableBoxEmptyController::Type)m_currentPage); push(&m_emptyViewController); @@ -265,3 +266,14 @@ void VariableBoxController::resetMemoization() { } m_firstMemoizedLayoutIndex = 0; } + +void VariableBoxController::destroyRecordAtRowIndex(int rowIndex) { + // Destroy the record + recordAtIndex(rowIndex).destroy(); + // Shift the memoization + assert(rowIndex >= m_firstMemoizedLayoutIndex && rowIndex < m_firstMemoizedLayoutIndex + k_maxNumberOfDisplayedRows); + for (int i = rowIndex - m_firstMemoizedLayoutIndex; i < k_maxNumberOfDisplayedRows - 1; i++) { + m_layouts[i] = m_layouts[i+1]; + } + m_layouts[k_maxNumberOfDisplayedRows - 1] = Layout(); +} diff --git a/apps/variable_box_controller.h b/apps/variable_box_controller.h index 80fa9ede8..13da1ea3c 100644 --- a/apps/variable_box_controller.h +++ b/apps/variable_box_controller.h @@ -6,7 +6,7 @@ #include #include "shared/global_context.h" #include "variable_box_empty_controller.h" -#include "i18n.h" +#include class VariableBoxController : public NestedMenuController { public: @@ -51,6 +51,7 @@ private: bool displayEmptyController(); bool isDisplayingEmptyController() { return StackViewController::depth() == 2; } void resetMemoization(); + void destroyRecordAtRowIndex(int rowIndex); Page m_currentPage; Page m_lockPageDelete; ExpressionTableCellWithExpression m_leafCells[k_maxNumberOfDisplayedRows]; diff --git a/apps/variable_box_empty_controller.cpp b/apps/variable_box_empty_controller.cpp index 741b025ae..0c810bdd8 100644 --- a/apps/variable_box_empty_controller.cpp +++ b/apps/variable_box_empty_controller.cpp @@ -1,7 +1,7 @@ #include "variable_box_empty_controller.h" #include #include "graph/storage_cartesian_function_store.h" -#include "i18n.h" +#include #include using namespace Poincare; diff --git a/apps/variables.de.i18n b/apps/variables.de.i18n index ffe0c55c7..60817027e 100644 --- a/apps/variables.de.i18n +++ b/apps/variables.de.i18n @@ -1,10 +1,10 @@ Variables = "Variablen" -Expressions = "Expressions" -Functions = "Funktions" +Expressions = "Ausdruecke" +Functions = "Funktionen" EmptyExpressionBox0 = "Sie haben keine Variable definiert." EmptyFunctionBox0 = "Sie haben keine Funktion definiert." EmptyExpressionBox1 = "Um eine Variable zu definieren:" EmptyFunctionBox1 = "Um eine Funktion zu definieren:" -EmptyExpressionBox2 = "Erlaubte Zeichen im namen:" -EmptyFunctionBox2 = "Erlaubte Zeichen im namen:" +EmptyExpressionBox2 = "Erlaubte Zeichen im Namen:" +EmptyFunctionBox2 = "Erlaubte Zeichen im Namen:" EnableCharacters = "A..Z, a..z, 0..9 und _" diff --git a/build/targets.emscripten.mak b/build/targets.emscripten.mak deleted file mode 100644 index 6d651eabb..000000000 --- a/build/targets.emscripten.mak +++ /dev/null @@ -1,14 +0,0 @@ -epsilon.packed.js: LDFLAGS += --memory-init-file 0 -epsilon.packed.js: $(objs) $(app_objs) $(app_image_objs) - -simulator.zip: epsilon.packed.js - @rm -rf $(basename $@) - @mkdir $(basename $@) - @cp epsilon.packed.js $(basename $@)/epsilon.js - @cp ion/src/emscripten/background.jpg $(basename $@)/ - @cp ion/src/emscripten/simulator.html $(basename $@)/ - @echo "ZIP $@" - @zip -r -9 $@ $(basename $@) > /dev/null - @rm -rf $(basename $@) - -products += $(addprefix epsilon,.js .js.mem) epsilon.packed.js simulator.zip diff --git a/build/toolchain.mingw.mak b/build/toolchain.mingw.mak deleted file mode 100644 index 50004be72..000000000 --- a/build/toolchain.mingw.mak +++ /dev/null @@ -1,7 +0,0 @@ -CC = gcc -CXX = g++ -LD = g++ -EXE = exe - -SFLAGS += -D_USE_MATH_DEFINES -LDFLAGS += -static -mwindows diff --git a/escher/Makefile b/escher/Makefile index 3c96dcd46..91799fdf4 100644 --- a/escher/Makefile +++ b/escher/Makefile @@ -1,102 +1,118 @@ SFLAGS += -Iescher/include -objs += $(addprefix escher/src/,\ - alternate_empty_view_controller.o\ - app.o\ - bank_view_controller.o\ - buffer_text_view.o\ - button.o\ - button_row_controller.o\ - chevron_view.o\ - clipboard.o\ - container.o\ - editable_text_cell.o\ - ellipsis_view.o\ - expression_field.o\ - even_odd_cell.o\ - even_odd_cell_with_ellipsis.o\ - even_odd_buffer_text_cell.o\ - even_odd_editable_text_cell.o\ - even_odd_expression_cell.o\ - even_odd_message_text_cell.o\ - expression_table_cell.o\ - expression_table_cell_with_pointer.o\ - expression_table_cell_with_expression.o\ - expression_view.o\ - highlight_cell.o\ - gauge_view.o\ - image_view.o\ - input_event_handler.o\ - invocation.o\ - input_view_controller.o\ - key_view.o\ - layout_field.o\ - list_view_data_source.o\ - message_table_cell.o\ - message_table_cell_with_buffer.o\ - message_table_cell_with_chevron.o\ - message_table_cell_with_chevron_and_message.o\ - message_table_cell_with_chevron_and_expression.o\ - message_table_cell_with_editable_text.o\ - message_table_cell_with_expression.o\ - message_table_cell_with_gauge.o\ - message_table_cell_with_message.o\ - message_table_cell_with_switch.o\ - message_text_view.o\ - message_tree.o\ - modal_view_controller.o\ - nested_menu_controller.o\ - palette.o\ - pointer_text_view.o\ - responder.o\ - run_loop.o\ - scroll_view.o\ - scroll_view_data_source.o\ - scroll_view_indicator.o\ - scrollable_view.o\ - selectable_table_view.o\ - selectable_table_view_data_source.o\ - selectable_table_view_delegate.o\ - simple_list_view_data_source.o\ - simple_table_view_data_source.o\ - solid_color_view.o\ - solid_text_area.o\ - stack_view.o\ - stack_view_controller.o\ - switch_view.o\ - tab_view.o\ - tab_view_cell.o\ - tab_view_controller.o\ - tab_view_data_source.o\ - table_cell.o\ - table_view.o\ - table_view_data_source.o\ - text_cursor_view.o\ - text_area.o\ - text_field.o\ - text_input.o\ - text_input_helpers.o\ - text_view.o\ - tiled_view.o\ - timer.o\ - toolbox.o\ - transparent_view.o\ - view.o\ - view_controller.o\ - warning_controller.o\ - window.o\ +src += $(addprefix escher/src/,\ + alternate_empty_view_controller.cpp \ + app.cpp \ + bank_view_controller.cpp \ + buffer_text_view.cpp \ + button.cpp \ + button_row_controller.cpp \ + chevron_view.cpp \ + clipboard.cpp \ + container.cpp \ + editable_text_cell.cpp \ + ellipsis_view.cpp \ + expression_field.cpp \ + even_odd_cell.cpp \ + even_odd_cell_with_ellipsis.cpp \ + even_odd_buffer_text_cell.cpp \ + even_odd_editable_text_cell.cpp \ + even_odd_expression_cell.cpp \ + even_odd_message_text_cell.cpp \ + expression_table_cell.cpp \ + expression_table_cell_with_pointer.cpp \ + expression_table_cell_with_expression.cpp \ + expression_view.cpp \ + highlight_cell.cpp \ + gauge_view.cpp \ + image_view.cpp \ + input_event_handler.cpp \ + invocation.cpp \ + input_view_controller.cpp \ + key_view.cpp \ + layout_field.cpp \ + list_view_data_source.cpp \ + message_table_cell.cpp \ + message_table_cell_with_buffer.cpp \ + message_table_cell_with_chevron.cpp \ + message_table_cell_with_chevron_and_message.cpp \ + message_table_cell_with_chevron_and_expression.cpp \ + message_table_cell_with_editable_text.cpp \ + message_table_cell_with_expression.cpp \ + message_table_cell_with_gauge.cpp \ + message_table_cell_with_message.cpp \ + message_table_cell_with_switch.cpp \ + message_text_view.cpp \ + message_tree.cpp \ + modal_view_controller.cpp \ + nested_menu_controller.cpp \ + palette.cpp \ + pointer_text_view.cpp \ + responder.cpp \ + run_loop.cpp \ + scroll_view.cpp \ + scroll_view_data_source.cpp \ + scroll_view_indicator.cpp \ + scrollable_view.cpp \ + selectable_table_view.cpp \ + selectable_table_view_data_source.cpp \ + selectable_table_view_delegate.cpp \ + simple_list_view_data_source.cpp \ + simple_table_view_data_source.cpp \ + solid_color_view.cpp \ + solid_text_area.cpp \ + stack_view.cpp \ + stack_view_controller.cpp \ + switch_view.cpp \ + tab_view.cpp \ + tab_view_cell.cpp \ + tab_view_controller.cpp \ + tab_view_data_source.cpp \ + table_cell.cpp \ + table_view.cpp \ + table_view_data_source.cpp \ + text_cursor_view.cpp \ + text_area.cpp \ + text_field.cpp \ + text_input.cpp \ + text_input_helpers.cpp \ + text_view.cpp \ + tiled_view.cpp \ + timer.cpp \ + toolbox.cpp \ + transparent_view.cpp \ + view.cpp \ + view_controller.cpp \ + warning_controller.cpp \ + window.cpp \ ) -INLINER := escher/image/inliner -$(INLINER): escher/image/inliner.c $(addprefix ion/src/external/lz4/, lz4.c lz4hc.c) - @echo "HOSTCC $@" - $(Q) $(HOSTCC) -std=c99 `libpng-config --cflags` $^ `libpng-config --ldflags` -o $@ +$(eval $(call rule_for, \ + HOSTCC, \ + escher/image/inliner, \ + escher/image/inliner.c $(addprefix ion/src/external/lz4/, lz4.c lz4hc.c), \ + $$(HOSTCC) -std=c99 `libpng-config --cflags` $$^ `libpng-config --ldflags` -o $$@ \ +)) -%.h %.cpp : %.png $(INLINER) - @echo "INLINER $@" - $(Q) $(INLINER) $< +INLINER := $(BUILD_DIR)/escher/image/inliner -INLINER_PRODUCTS = $(1:.png=.h) $(1:.png=.cpp) $(1:.png=.o) +.PRECIOUS: $(BUILD_DIR)/%.h $(BUILD_DIR)/%.cpp +$(eval $(call rule_for, \ + INLINER, \ + %.h %.cpp, \ + %.png $$(INLINER), \ + $$(INLINER) $$< $$(basename $$@).h $$(basename $$@).cpp \ +)) -products += $(INLINER) +# Mark a .cpp file as depending on a .png one +# This is called with $1 = code.cpp and $2 = image.png +# First, we mark code.o as requiring image.o. Rules will take care of inlining +# the PNG and building the inlined cpp file. Second, we add the directory +# corresponding to the one of code.cpp in the output dir as a header search +# path. Since $1 can be a list, we have to map with foreach. +define depends_on_image +$(call object_for,$(1)): $(call object_for,$(2)) +$(call object_for,$(1)): SFLAGS += $(foreach d,$(sort $(dir $(call object_for,$(1)))),-I$(d)) +src += $(2) +endef diff --git a/escher/image/inliner.c b/escher/image/inliner.c index 4f3a4290b..67d983feb 100644 --- a/escher/image/inliner.c +++ b/escher/image/inliner.c @@ -16,7 +16,7 @@ #include #include "../../ion/src/external/lz4/lz4hc.h" -#define ERROR_IF(cond, message) if (cond) { printf(message); return -1; }; +#define ERROR_IF(cond, message) if (cond) { printf(message "\n"); return -1; }; #define MAX_FILENAME_LENGTH 255 void generateHeaderFromImage(FILE * file, const char * guardian, const char * variable); @@ -29,7 +29,7 @@ void camelCaseNameFromSnakeCaseNames(const char * snakeCaseName, const char * up // TODO: truncate the app image dimensions to 55x56 pixels int main(int argc, char * argv[]) { - ERROR_IF(argc != 2, "Usage: inliner source.png"); + ERROR_IF(argc != 4, "Usage: inliner source.png output.h output.cpp"); const char * inputPath = argv[1]; FILE * inputFile = fopen(inputPath, "rb"); @@ -79,6 +79,7 @@ int main(int argc, char * argv[]) { snakeCaseNameToUpperSnakeName(lowerSnakeCaseName, upperSnakeCaseName, MAX_FILENAME_LENGTH); camelCaseNameFromSnakeCaseNames(lowerSnakeCaseName, upperSnakeCaseName, camelCaseName, MAX_FILENAME_LENGTH); + /* char headerPath[MAX_FILENAME_LENGTH]; size_t pathLength = strlen(inputPath); strcpy(headerPath, inputPath); @@ -92,6 +93,9 @@ int main(int argc, char * argv[]) { implementationPath[pathLength-3] = 'c'; implementationPath[pathLength-2] = 'p'; implementationPath[pathLength-1] = 'p'; + */ + char * headerPath = argv[2]; + char * implementationPath = argv[3]; FILE * header = fopen(headerPath, "w"); generateHeaderFromImage(header, upperSnakeCaseName, camelCaseName); diff --git a/escher/include/escher/alternate_empty_view_controller.h b/escher/include/escher/alternate_empty_view_controller.h index e0834ad75..1e16d58fc 100644 --- a/escher/include/escher/alternate_empty_view_controller.h +++ b/escher/include/escher/alternate_empty_view_controller.h @@ -12,6 +12,7 @@ public: const char * title() override; bool handleEvent(Ion::Events::Event event) override; void didBecomeFirstResponder() override; + void initView() override; void viewWillAppear() override; void viewDidDisappear() override; private: diff --git a/escher/include/escher/bank_view_controller.h b/escher/include/escher/bank_view_controller.h index 5da61235b..90f7270da 100644 --- a/escher/include/escher/bank_view_controller.h +++ b/escher/include/escher/bank_view_controller.h @@ -15,6 +15,7 @@ public: View * view() override { return &m_view; } void didEnterResponderChain(Responder * previousResponder) override; + void initView() override; void viewWillAppear() override; void viewDidDisappear() override; private: diff --git a/escher/include/escher/button_row_controller.h b/escher/include/escher/button_row_controller.h index 4f4faa66f..879c1b5d7 100644 --- a/escher/include/escher/button_row_controller.h +++ b/escher/include/escher/button_row_controller.h @@ -33,6 +33,7 @@ public: int selectedButton(); bool setSelectedButton(int selectedButton); void setMessageOfButtonAtIndex(I18n::Message message, int index); + void initView() override; void viewWillAppear() override; void viewDidDisappear() override; ViewController::DisplayParameter displayParameter() override { return DisplayParameter::DoNotShowOwnTitle; } diff --git a/escher/include/escher/container.h b/escher/include/escher/container.h index 8857c59bc..e4f33604e 100644 --- a/escher/include/escher/container.h +++ b/escher/include/escher/container.h @@ -27,7 +27,7 @@ public: virtual void run(); App * activeApp(); virtual bool dispatchEvent(Ion::Events::Event event) override; - virtual void switchTo(App::Snapshot * snapshot); + virtual bool switchTo(App::Snapshot * snapshot); protected: virtual Window * window() = 0; private: diff --git a/escher/include/escher/even_odd_expression_cell.h b/escher/include/escher/even_odd_expression_cell.h index 5978cfca8..5a63bfc36 100644 --- a/escher/include/escher/even_odd_expression_cell.h +++ b/escher/include/escher/even_odd_expression_cell.h @@ -17,6 +17,7 @@ public: void setAlignment(float horizontalAlignment, float verticalAlignment) { m_expressionView.setAlignment(horizontalAlignment, verticalAlignment); } void setLeftMargin(KDCoordinate margin); void setRightMargin(KDCoordinate margin); + KDPoint drawingOrigin() const { return m_expressionView.drawingOrigin(); } Poincare::Layout layout() const override { return m_expressionView.layout(); } void drawRect(KDContext * ctx, KDRect rect) const override; protected: diff --git a/escher/include/escher/layout_field.h b/escher/include/escher/layout_field.h index 343de5446..bb8a38927 100644 --- a/escher/include/escher/layout_field.h +++ b/escher/include/escher/layout_field.h @@ -43,12 +43,6 @@ public: return m_delegate->layoutFieldShouldFinishEditing(this, event); } - /* View */ - KDSize minimalSizeForOptimalDisplay() const override { - KDSize contentViewSize = m_contentView.minimalSizeForOptimalDisplay(); - return KDSize(contentViewSize.width(), contentViewSize.height()); - } - protected: void reload(KDSize previousSize); virtual bool privateHandleEvent(Ion::Events::Event event); diff --git a/escher/include/escher/metric.h b/escher/include/escher/metric.h index 5294e9e46..265b69d8a 100644 --- a/escher/include/escher/metric.h +++ b/escher/include/escher/metric.h @@ -9,7 +9,8 @@ public: constexpr static KDCoordinate CommonRightMargin = 20; constexpr static KDCoordinate CommonTopMargin = 15; constexpr static KDCoordinate CommonBottomMargin = 15; - constexpr static KDCoordinate HistoryHorizontalMargin = 10; + constexpr static KDCoordinate CommonLargeMargin = 10; + constexpr static KDCoordinate CommonSmallMargin = 5; constexpr static KDCoordinate TitleBarExternHorizontalMargin = 5; constexpr static KDCoordinate TitleBarHeight = 18; constexpr static KDCoordinate ParameterCellHeight = 35; diff --git a/escher/include/escher/modal_view_controller.h b/escher/include/escher/modal_view_controller.h index 198e473b3..1bce427ad 100644 --- a/escher/include/escher/modal_view_controller.h +++ b/escher/include/escher/modal_view_controller.h @@ -16,6 +16,7 @@ public: void reloadModalViewController(); void dismissModalViewController(); bool isDisplayingModal(); + void initView() override; void viewWillAppear() override; void viewDidDisappear() override; protected: diff --git a/escher/include/escher/pointer_text_view.h b/escher/include/escher/pointer_text_view.h index 432ee3147..bb2ec650d 100644 --- a/escher/include/escher/pointer_text_view.h +++ b/escher/include/escher/pointer_text_view.h @@ -10,7 +10,6 @@ public: KDColor textColor = KDColorBlack, KDColor backgroundColor = KDColorWhite); const char * text() const override { return m_text; } void setText(const char * text) override; - KDSize minimalSizeForOptimalDisplay() const override; private: const char * m_text; }; diff --git a/escher/include/escher/scroll_view.h b/escher/include/escher/scroll_view.h index 097913aa1..90555ff67 100644 --- a/escher/include/escher/scroll_view.h +++ b/escher/include/escher/scroll_view.h @@ -8,7 +8,8 @@ class ScrollView : public View { public: ScrollView(View * contentView, ScrollViewDataSource * dataSource); - void drawRect(KDContext * ctx, KDRect rect) const override; + ScrollView(ScrollView&& other); + KDSize minimalSizeForOptimalDisplay() const override; void setTopMargin(KDCoordinate m) { m_topMargin = m; } KDCoordinate topMargin() const { return m_topMargin; } @@ -23,32 +24,77 @@ public: setTopMargin(top); setRightMargin(right); setBottomMargin(bottom); setLeftMargin(left); } void setMargins(KDCoordinate m) { setMargins(m, m, m, m); } - void setCommonMargins(); - void setShowsIndicators(bool s) { m_showsIndicators = s; } - bool showsIndicators() const { return m_showsIndicators; } - void setColorsBackground(bool c) { m_colorsBackground = c; } - bool colorsBackground() const { return m_colorsBackground; } - virtual void setBackgroundColor(KDColor c) { m_backgroundColor = c; } + class Decorator { + public: + enum class Type { + None, + Bars, + Arrows + }; + /* We want (Decorator *)->~Decorator() to call ~BarDecorator() or ~ArrowDecorator() + * when required. */ + virtual ~Decorator() = default; + virtual int numberOfIndicators() const { return 0; } + virtual View * indicatorAtIndex(int index) { assert(false); return nullptr; } + virtual KDRect layoutIndicators(KDSize content, KDPoint offset, KDRect frame) { return frame; } + virtual void setBackgroundColor(KDColor c) {} + }; + + class BarDecorator : public Decorator { + public: + BarDecorator(); + int numberOfIndicators() const override { return 2; } + View * indicatorAtIndex(int index) override; + KDRect layoutIndicators(KDSize content, KDPoint offset, KDRect frame) override; + ScrollViewVerticalBar * verticalBar() { return &m_verticalBar; } + ScrollViewHorizontalBar * horizontalBar() { return &m_horizontalBar; } + private: + ScrollViewVerticalBar m_verticalBar; + ScrollViewHorizontalBar m_horizontalBar; + static constexpr KDCoordinate k_barsFrameBreadth = 13; + }; + + class ArrowDecorator : public Decorator { + public: + ArrowDecorator(); + int numberOfIndicators() const override { return 4; } + View * indicatorAtIndex(int index) override; + KDRect layoutIndicators(KDSize content, KDPoint offset, KDRect frame) override; + void setBackgroundColor(KDColor c) override; + private: + ScrollViewArrow m_topArrow; + ScrollViewArrow m_rightArrow; + ScrollViewArrow m_bottomArrow; + ScrollViewArrow m_leftArrow; + }; + + Decorator * decorator() { return m_decorators.activeDecorator(); } + void setDecoratorType(Decorator::Type t) { + m_decoratorType = t; + m_decorators.setActiveDecorator(t); + } + virtual void setBackgroundColor(KDColor c) { + m_backgroundColor = c; + decorator()->setBackgroundColor(m_backgroundColor); + } KDColor backgroundColor() const { return m_backgroundColor; } - ScrollViewIndicator * verticalScrollIndicator() { return &m_verticalScrollIndicator; } - ScrollViewIndicator * horizontalScrollIndicator() { return &m_horizontalScrollIndicator; } - void setIndicatorThickness(KDCoordinate t) { m_indicatorThickness = t; } - KDCoordinate indicatorThickness() const { return m_indicatorThickness; } - void setContentOffset(KDPoint offset, bool forceRelayout = false); KDPoint contentOffset() const { return m_dataSource->offset(); } void scrollToContentPoint(KDPoint p, bool allowOverscroll = false); void scrollToContentRect(KDRect rect, bool allowOverscroll = false); // Minimal scrolling to make this rect visible protected: - KDCoordinate maxContentWidthDisplayableWithoutScrolling(); - KDCoordinate maxContentHeightDisplayableWithoutScrolling(); + KDCoordinate maxContentWidthDisplayableWithoutScrolling() const { + return m_frame.width() - m_leftMargin - m_rightMargin; + } + KDCoordinate maxContentHeightDisplayableWithoutScrolling() const { + return m_frame.height() - m_topMargin - m_bottomMargin; + } KDRect visibleContentRect(); void layoutSubviews() override; - void updateScrollIndicator(); - KDSize contentSize(); + virtual KDSize contentSize() const { return m_contentView->minimalSizeForOptimalDisplay(); } #if ESCHER_VIEW_LOGGING virtual const char * className() const override; virtual void logAttributes(std::ostream &os) const override; @@ -56,20 +102,41 @@ protected: View * m_contentView; private: ScrollViewDataSource * m_dataSource; - int numberOfSubviews() const override; - View * subviewAtIndex(int index) override; + int numberOfSubviews() const override { return 1 + const_cast(this)->decorator()->numberOfIndicators(); } + View * subviewAtIndex(int index) override { return (index == 0) ? &m_innerView : decorator()->indicatorAtIndex(index); } + + class InnerView : public View { + public: + InnerView(ScrollView * scrollView) : View(), m_scrollView(scrollView) {} + void drawRect(KDContext * ctx, KDRect rect) const override; + private: + int numberOfSubviews() const override { return 1; } + View * subviewAtIndex(int index) override { + assert(index == 0); + return m_scrollView->m_contentView; + } + const ScrollView * m_scrollView; + }; - ScrollViewIndicator m_verticalScrollIndicator; - ScrollViewIndicator m_horizontalScrollIndicator; - bool hasVerticalIndicator() const; - bool hasHorizontalIndicator() const; KDCoordinate m_topMargin; KDCoordinate m_rightMargin; KDCoordinate m_bottomMargin; KDCoordinate m_leftMargin; - KDCoordinate m_indicatorThickness; - bool m_showsIndicators; - bool m_colorsBackground; + + InnerView m_innerView; + Decorator::Type m_decoratorType; + union Decorators { + public: + Decorators(); + ~Decorators(); + Decorator * activeDecorator() { return &m_none; } + void setActiveDecorator(Decorator::Type t); + private: + Decorator m_none; + BarDecorator m_bars; + ArrowDecorator m_arrows; + }; + Decorators m_decorators; KDColor m_backgroundColor; }; diff --git a/escher/include/escher/scroll_view_indicator.h b/escher/include/escher/scroll_view_indicator.h index fee919f2b..b91a64bb4 100644 --- a/escher/include/escher/scroll_view_indicator.h +++ b/escher/include/escher/scroll_view_indicator.h @@ -5,38 +5,60 @@ class ScrollViewIndicator : public View { public: - enum class Direction { - Horizontal, - Vertical - }; - ScrollViewIndicator(Direction direction); - void drawRect(KDContext * ctx, KDRect rect) const override; - - void setIndicatorColor(KDColor c) { m_indicatorColor = c; } - KDColor indicatorColor() const { return m_indicatorColor; } - void setBackgroundColor(KDColor c) { m_backgroundColor = c; } - KDColor backgroundColor() const { return m_backgroundColor; } + ScrollViewIndicator(); void setMargin(KDCoordinate m) { m_margin = m; } KDCoordinate margin() const { return m_margin; } - - float start() const; - void setStart(float start); - float end() const; - void setEnd(float end); - KDRect frame(); protected: #if ESCHER_VIEW_LOGGING virtual const char * className() const override; virtual void logAttributes(std::ostream &os) const override; #endif -private: - constexpr static KDCoordinate k_indicatorThickness = 4; - Direction m_direction; - float m_start; - float m_end; - KDColor m_indicatorColor; - KDColor m_backgroundColor; + KDColor m_color; KDCoordinate m_margin; }; +class ScrollViewBar : public ScrollViewIndicator { +public: + ScrollViewBar(); + bool update(KDCoordinate totalContentLength, KDCoordinate contentOffset, KDCoordinate visibleContentLength); +protected: + constexpr static KDCoordinate k_indicatorThickness = 4; + bool visible() const { return 0 < m_offset || m_visibleLength < 1; } + float m_offset; + float m_visibleLength; + KDColor m_trackColor; +}; + +class ScrollViewHorizontalBar : public ScrollViewBar { +public: + void drawRect(KDContext * ctx, KDRect rect) const override; +private: + KDCoordinate totalLength() const { return m_frame.width() - 2*m_margin; } +}; + +class ScrollViewVerticalBar : public ScrollViewBar { +public: + void drawRect(KDContext * ctx, KDRect rect) const override; +private: + KDCoordinate totalLength() const { return m_frame.height() - 2*m_margin; } +}; + +class ScrollViewArrow : public ScrollViewIndicator { +public: + enum Side : char { //FIXME + Top = 't', + Right = '>', + Bottom = 'b', + Left = '<' + }; + ScrollViewArrow(Side side); + bool update(bool visible); + void setBackgroundColor(KDColor c) { m_backgroundColor = c; } + void drawRect(KDContext * ctx, KDRect rect) const override; +private: + bool m_visible; + const char m_arrow; + KDColor m_backgroundColor; +}; + #endif diff --git a/escher/include/escher/scrollable_view.h b/escher/include/escher/scrollable_view.h index 4ceeca709..ba4aaea4e 100644 --- a/escher/include/escher/scrollable_view.h +++ b/escher/include/escher/scrollable_view.h @@ -11,7 +11,7 @@ public: bool handleEvent(Ion::Events::Event event) override; void reloadScroll(bool forceRelayout = false); protected: - void layoutSubviews() override; + KDSize contentSize() const override; KDPoint m_manualScrollingOffset; }; diff --git a/escher/include/escher/stack_view_controller.h b/escher/include/escher/stack_view_controller.h index f711f2748..7675072c4 100644 --- a/escher/include/escher/stack_view_controller.h +++ b/escher/include/escher/stack_view_controller.h @@ -21,6 +21,7 @@ public: const char * title() override; bool handleEvent(Ion::Events::Event event) override; void didBecomeFirstResponder() override; + void initView() override; void viewWillAppear() override; void viewDidDisappear() override; private: diff --git a/escher/include/escher/tab_view_controller.h b/escher/include/escher/tab_view_controller.h index 9e6328b03..f7bed6244 100644 --- a/escher/include/escher/tab_view_controller.h +++ b/escher/include/escher/tab_view_controller.h @@ -19,6 +19,7 @@ public: void didBecomeFirstResponder() override; void didEnterResponderChain(Responder * previousResponder) override; void willResignFirstResponder() override; + void initView() override; void viewWillAppear() override; void viewDidDisappear() override; private: diff --git a/escher/include/escher/table_view.h b/escher/include/escher/table_view.h index b67868cae..ca2db0d32 100644 --- a/escher/include/escher/table_view.h +++ b/escher/include/escher/table_view.h @@ -20,7 +20,6 @@ public: virtual void scrollToCell(int i, int j); HighlightCell * cellAtLocation(int i, int j); void reloadCellAtLocation(int i, int j); - KDSize minimalSizeForOptimalDisplay() const override; protected: #if ESCHER_VIEW_LOGGING const char * className() const override; @@ -38,12 +37,12 @@ protected: void scrollToCell(int i, int j) const; void reloadCellAtLocation(int i, int j); HighlightCell * cellAtLocation(int i, int j); - void resizeToFitContent(); TableViewDataSource * dataSource(); int rowsScrollingOffset() const; int columnsScrollingOffset() const; int numberOfDisplayableRows() const; int numberOfDisplayableColumns() const; + void layoutSubviews() override; protected: #if ESCHER_VIEW_LOGGING const char * className() const override; @@ -54,11 +53,10 @@ protected: int numberOfSubviews() const override; View * subviewAtIndex(int index) override; - void layoutSubviews() override; /* realCellWidth enables to handle list view for which * TableViewDataSource->cellWidht = 0 */ - KDCoordinate columnWidth(int x) const; + KDRect cellFrame(int i, int j) const; /* These two methods transform an index (of subview for instance) into * coordinates that refer to the data source entire table */ int absoluteColumnNumberFromSubviewIndex(int index) const; diff --git a/escher/include/escher/text_field.h b/escher/include/escher/text_field.h index f04e5772a..49b488747 100644 --- a/escher/include/escher/text_field.h +++ b/escher/include/escher/text_field.h @@ -20,7 +20,6 @@ public: void setText(const char * text); void setAlignment(float horizontalAlignment, float verticalAlignment); virtual void setEditing(bool isEditing, bool reinitDraftBuffer = true) override; - KDSize minimalSizeForOptimalDisplay() const override; char XNTChar(char defaultXNTChar) override; bool handleEventWithText(const char * text, bool indentation = false, bool forceCursorRightOfText = false) override; bool handleEvent(Ion::Events::Event event) override; diff --git a/escher/include/escher/view_controller.h b/escher/include/escher/view_controller.h index 316d7cae2..6084edc39 100644 --- a/escher/include/escher/view_controller.h +++ b/escher/include/escher/view_controller.h @@ -21,8 +21,12 @@ extern "C" { * - viewWillDisappear * - willExitResponderChain * - willResignFirstResponder - * Both methods are always called after setting a view and layouting it - * subviews. */ + * + * Both methods are always called after setting a view and laying its subwiews + * out. + * + * The method initView is called before setting a View (or often sets itself) + * and laying it out. */ #include #include @@ -43,6 +47,7 @@ public: ViewController(Responder * parentResponder); virtual const char * title(); virtual View * view() = 0; + virtual void initView() {} virtual void viewWillAppear(); virtual void viewDidDisappear(); virtual DisplayParameter displayParameter() { return DisplayParameter::Default; } diff --git a/escher/src/alternate_empty_view_controller.cpp b/escher/src/alternate_empty_view_controller.cpp index 5c23c1af9..43159799e 100644 --- a/escher/src/alternate_empty_view_controller.cpp +++ b/escher/src/alternate_empty_view_controller.cpp @@ -71,6 +71,10 @@ void AlternateEmptyViewController::didBecomeFirstResponder() { } } +void AlternateEmptyViewController::initView() { + m_contentView.mainViewController()->initView(); +} + void AlternateEmptyViewController::viewWillAppear() { m_contentView.layoutSubviews(); if (!m_contentView.alternateEmptyViewDelegate()->isEmpty()) { diff --git a/escher/src/app.cpp b/escher/src/app.cpp index 4a0b73b36..db8d49626 100644 --- a/escher/src/app.cpp +++ b/escher/src/app.cpp @@ -108,8 +108,9 @@ const Container * App::container() const { void App::didBecomeActive(Window * window) { View * view = m_modalViewController.view(); assert(m_modalViewController.app() == this); - m_modalViewController.viewWillAppear(); + m_modalViewController.initView(); window->setContentView(view); + m_modalViewController.viewWillAppear(); setFirstResponder(&m_modalViewController); } diff --git a/escher/src/bank_view_controller.cpp b/escher/src/bank_view_controller.cpp index 6e6146ad0..315088247 100644 --- a/escher/src/bank_view_controller.cpp +++ b/escher/src/bank_view_controller.cpp @@ -24,8 +24,14 @@ void BankViewController::didEnterResponderChain(Responder * previousResponder) { app()->setFirstResponder(activeViewController()); } -void BankViewController::viewWillAppear() { +void BankViewController::initView() { + for (int i = 0; i < numberOfChildren(); i++) { + childAtIndex(i)->initView(); + } m_view.setSubview(activeViewController()->view()); +} + +void BankViewController::viewWillAppear() { activeViewController()->viewWillAppear(); } diff --git a/escher/src/button_row_controller.cpp b/escher/src/button_row_controller.cpp index e225a5fae..941415889 100644 --- a/escher/src/button_row_controller.cpp +++ b/escher/src/button_row_controller.cpp @@ -211,6 +211,10 @@ bool ButtonRowController::handleEvent(Ion::Events::Event event) { return false; } +void ButtonRowController::initView() { + m_contentView.mainViewController()->initView(); +} + void ButtonRowController::viewWillAppear() { /* We need to layout subviews at first appearance because the number of * buttons might have changed between 2 appearences. */ diff --git a/escher/src/container.cpp b/escher/src/container.cpp index 136ffb2f4..fd92857a4 100644 --- a/escher/src/container.cpp +++ b/escher/src/container.cpp @@ -13,9 +13,14 @@ Container::~Container() { } } -void Container::switchTo(App::Snapshot * snapshot) { +bool Container::switchTo(App::Snapshot * snapshot) { if (m_activeApp && snapshot == m_activeApp->snapshot()) { - return; + return true; + } + if (m_activeApp && !m_activeApp->prepareForExit()) { + /* activeApp()->prepareForExit() returned false, which means that the app + * needs another event loop to prepare for being switched off. */ + return false; } if (m_activeApp) { m_activeApp->willBecomeInactive(); @@ -29,6 +34,7 @@ void Container::switchTo(App::Snapshot * snapshot) { m_activeApp->didBecomeActive(window()); window()->redraw(); } + return true; } App * Container::activeApp() { diff --git a/escher/src/expression_field.cpp b/escher/src/expression_field.cpp index 8d7e26287..2fa9ee575 100644 --- a/escher/src/expression_field.cpp +++ b/escher/src/expression_field.cpp @@ -13,11 +13,9 @@ ExpressionField::ExpressionField(Responder * parentResponder, char * textBuffer, // Initialize text field m_textField.setMargins(0, k_horizontalMargin, 0, k_horizontalMargin); m_textField.setBackgroundColor(KDColorWhite); - m_textField.setColorsBackground(true); // Initialize layout field m_layoutField.setMargins(k_verticalMargin, k_horizontalMargin, k_verticalMargin, k_horizontalMargin); m_layoutField.setBackgroundColor(KDColorWhite); - m_layoutField.setColorsBackground(true); } void ExpressionField::setEditing(bool isEditing, bool reinitDraftBuffer) { diff --git a/escher/src/expression_view.cpp b/escher/src/expression_view.cpp index 8203333b5..cb81588bf 100644 --- a/escher/src/expression_view.cpp +++ b/escher/src/expression_view.cpp @@ -51,7 +51,7 @@ KDSize ExpressionView::minimalSizeForOptimalDisplay() const { KDPoint ExpressionView::drawingOrigin() const { KDSize expressionSize = m_layout.layoutSize(); - return KDPoint(m_horizontalMargin + m_horizontalAlignment*(m_frame.width() - 2*m_horizontalMargin - expressionSize.width()), max(0, (m_frame.height() - expressionSize.height())/2)); + return KDPoint(m_horizontalMargin + m_horizontalAlignment*(m_frame.width() - 2*m_horizontalMargin - expressionSize.width()), max(0, m_verticalAlignment*(m_frame.height() - expressionSize.height()))); } KDPoint ExpressionView::absoluteDrawingOrigin() const { diff --git a/escher/src/layout_field.cpp b/escher/src/layout_field.cpp index 866875eb2..b31123263 100644 --- a/escher/src/layout_field.cpp +++ b/escher/src/layout_field.cpp @@ -25,7 +25,7 @@ void LayoutField::ContentView::setEditing(bool isEditing) { } void LayoutField::ContentView::clearLayout() { - HorizontalLayout h; + HorizontalLayout h = HorizontalLayout::Builder(); m_expressionView.setLayout(h); m_cursor.setLayout(h); } diff --git a/escher/src/modal_view_controller.cpp b/escher/src/modal_view_controller.cpp index cd3ab4bd3..316a85c26 100644 --- a/escher/src/modal_view_controller.cpp +++ b/escher/src/modal_view_controller.cpp @@ -118,6 +118,7 @@ void ModalViewController::displayModalViewController(ViewController * vc, float m_currentModalViewController = vc; vc->setParentResponder(this); m_previousResponder = app()->firstResponder(); + m_currentModalViewController->initView(); m_contentView.presentModalView(vc->view(), verticalAlignment, horizontalAlignment, topMargin, leftMargin, bottomMargin, rightMargin); m_currentModalViewController->viewWillAppear(); app()->setFirstResponder(vc); @@ -152,8 +153,12 @@ bool ModalViewController::handleEvent(Ion::Events::Event event) { return false; } -void ModalViewController::viewWillAppear() { +void ModalViewController::initView() { m_contentView.setMainView(m_regularViewController->view()); + m_regularViewController->initView(); +} + +void ModalViewController::viewWillAppear() { m_contentView.layoutSubviews(); if (m_contentView.isDisplayingModal()) { m_currentModalViewController->viewWillAppear(); diff --git a/escher/src/nested_menu_controller.cpp b/escher/src/nested_menu_controller.cpp index 1844e3673..e19708512 100644 --- a/escher/src/nested_menu_controller.cpp +++ b/escher/src/nested_menu_controller.cpp @@ -94,7 +94,7 @@ NestedMenuController::NestedMenuController(Responder * parentResponder, I18n::Me m_sender(nullptr) { m_selectableTableView.setMargins(0); - m_selectableTableView.setShowsIndicators(false); + m_selectableTableView.setDecoratorType(ScrollView::Decorator::Type::None); } bool NestedMenuController::handleEvent(Ion::Events::Event event) { diff --git a/escher/src/pointer_text_view.cpp b/escher/src/pointer_text_view.cpp index 892311ee5..59d239050 100644 --- a/escher/src/pointer_text_view.cpp +++ b/escher/src/pointer_text_view.cpp @@ -14,7 +14,3 @@ void PointerTextView::setText(const char * text) { markRectAsDirty(bounds()); } } - -KDSize PointerTextView::minimalSizeForOptimalDisplay() const { - return m_font->stringSize(text()); -} diff --git a/escher/src/scroll_view.cpp b/escher/src/scroll_view.cpp index 11fc00f64..75d07ec11 100644 --- a/escher/src/scroll_view.cpp +++ b/escher/src/scroll_view.cpp @@ -1,6 +1,7 @@ #include #include -#include + +#include extern "C" { #include @@ -10,72 +11,39 @@ ScrollView::ScrollView(View * contentView, ScrollViewDataSource * dataSource) : View(), m_contentView(contentView), m_dataSource(dataSource), - m_verticalScrollIndicator(ScrollViewIndicator::Direction::Vertical), - m_horizontalScrollIndicator(ScrollViewIndicator::Direction::Horizontal), m_topMargin(0), m_rightMargin(0), m_bottomMargin(0), m_leftMargin(0), - m_indicatorThickness(20), - m_showsIndicators(true), - m_colorsBackground(true), + m_innerView(this), + m_decorators(), m_backgroundColor(Palette::WallScreen) { assert(m_dataSource != nullptr); + setDecoratorType(Decorator::Type::Bars); } -void ScrollView::setCommonMargins() { - setTopMargin(Metric::CommonTopMargin); - setRightMargin(Metric::CommonRightMargin); - setBottomMargin(Metric::CommonBottomMargin); - setLeftMargin(Metric::CommonLeftMargin); +ScrollView::ScrollView(ScrollView&& other) : + m_contentView(other.m_contentView), + m_dataSource(other.m_dataSource), + m_topMargin(other.m_topMargin), + m_rightMargin(other.m_rightMargin), + m_bottomMargin(other.m_bottomMargin), + m_leftMargin(other.m_leftMargin), + m_innerView(this), + m_backgroundColor(other.m_backgroundColor) +{ + setDecoratorType(other.m_decoratorType); } -bool ScrollView::hasVerticalIndicator() const { - if (m_showsIndicators) { - return m_verticalScrollIndicator.end() < 1 || m_verticalScrollIndicator.start() > 0; - } - return false; +KDSize ScrollView::minimalSizeForOptimalDisplay() const { + KDSize contentSize = m_contentView->minimalSizeForOptimalDisplay(); + return KDSize( + contentSize.width() + m_leftMargin + m_rightMargin, + contentSize.height() + m_topMargin + m_bottomMargin + ); } -bool ScrollView::hasHorizontalIndicator() const { - if (m_showsIndicators) { - return m_horizontalScrollIndicator.end() < 1 || m_horizontalScrollIndicator.start() > 0; - } - return false; -} - -int ScrollView::numberOfSubviews() const { - return 1 + hasVerticalIndicator() + hasHorizontalIndicator(); -} - -View * ScrollView::subviewAtIndex(int index) { - switch (index) { - case 0: - return m_contentView; - case 1: - return hasHorizontalIndicator() ? &m_horizontalScrollIndicator : &m_verticalScrollIndicator; - case 2: - return &m_verticalScrollIndicator; - } - return nullptr; -} - -void ScrollView::drawRect(KDContext * ctx, KDRect rect) const { - if (!m_colorsBackground) { - return; - } - KDCoordinate height = bounds().height(); - KDCoordinate width = bounds().width(); - KDCoordinate offsetX = contentOffset().x(); - KDCoordinate offsetY = contentOffset().y(); - KDCoordinate contentHeight = m_contentView->bounds().height(); - KDCoordinate contentWidth = m_contentView->bounds().width(); - ctx->fillRect(KDRect(0, 0, width, m_topMargin-offsetY), m_backgroundColor); - ctx->fillRect(KDRect(0, contentHeight+m_topMargin-offsetY, width, height - contentHeight - m_topMargin + offsetY), m_backgroundColor); - ctx->fillRect(KDRect(0, 0, m_leftMargin-offsetX, height), m_backgroundColor); - ctx->fillRect(KDRect(contentWidth + m_leftMargin - offsetX, 0, width - contentWidth - m_leftMargin + offsetX, height), m_backgroundColor); -} void ScrollView::scrollToContentPoint(KDPoint p, bool allowOverscroll) { if (!allowOverscroll && !m_contentView->bounds().contains(p)) { @@ -101,15 +69,10 @@ void ScrollView::scrollToContentPoint(KDPoint p, bool allowOverscroll) { } /* Handle cases when the size of the view has decreased. */ - KDCoordinate contentOffsetX = contentOffset().x(); - KDCoordinate contentOffsetY = contentOffset().y(); - if (maxContentHeightDisplayableWithoutScrolling() > contentSize().height()-contentOffsetY) { - contentOffsetY = contentSize().height() > maxContentHeightDisplayableWithoutScrolling() ? contentSize().height()-maxContentHeightDisplayableWithoutScrolling() : 0; - } - if (maxContentWidthDisplayableWithoutScrolling() > contentSize().width()-contentOffsetX) { - contentOffsetX = contentSize().width() > maxContentWidthDisplayableWithoutScrolling() ? contentSize().width()-maxContentWidthDisplayableWithoutScrolling() : 0; - } - setContentOffset(KDPoint(contentOffsetX, contentOffsetY)); + setContentOffset(KDPoint( + min(contentOffset().x(), max(minimalSizeForOptimalDisplay().width() - bounds().width(), 0)), + min(contentOffset().y(), max(minimalSizeForOptimalDisplay().height() - bounds().height(), 0)) + )); } void ScrollView::scrollToContentRect(KDRect rect, bool allowOverscroll) { @@ -127,73 +90,11 @@ KDRect ScrollView::visibleContentRect() { } void ScrollView::layoutSubviews() { - // Layout contentView - // We're only re-positionning the contentView, not modifying its size. - KDPoint absoluteOffset = contentOffset().opposite().translatedBy(KDPoint(m_leftMargin, m_topMargin)); - KDRect contentFrame = KDRect(absoluteOffset, m_contentView->bounds().size()); + KDRect innerFrame = decorator()->layoutIndicators(minimalSizeForOptimalDisplay(), contentOffset(), bounds()); + m_innerView.setFrame(innerFrame); + KDPoint absoluteOffset = contentOffset().opposite().translatedBy(KDPoint(m_leftMargin - innerFrame.x(), m_topMargin - innerFrame.y())); + KDRect contentFrame = KDRect(absoluteOffset, contentSize()); m_contentView->setFrame(contentFrame); - - // We recompute the size of the scroll indicator - updateScrollIndicator(); - - // Layout indicators - /* If the two indicators are visible, we leave an empty rectangle in the right - * bottom corner. Otherwise, the only indicator uses all the height/width. */ - if (hasHorizontalIndicator() && hasVerticalIndicator()) { - KDRect verticalIndicatorFrame = KDRect( - m_frame.width() - m_indicatorThickness, 0, - m_indicatorThickness, m_frame.height() - m_indicatorThickness - ); - m_verticalScrollIndicator.setFrame(verticalIndicatorFrame); - KDRect horizontalIndicatorFrame = KDRect( - 0, m_frame.height() - m_indicatorThickness, - m_frame.width() - m_indicatorThickness, m_indicatorThickness - ); - m_horizontalScrollIndicator.setFrame(horizontalIndicatorFrame); - } else { - if (hasVerticalIndicator()) { - KDRect verticalIndicatorFrame = KDRect( - m_frame.width() - m_indicatorThickness, 0, - m_indicatorThickness, m_frame.height() - ); - m_verticalScrollIndicator.setFrame(verticalIndicatorFrame); - } - if (hasHorizontalIndicator()) { - KDRect horizontalIndicatorFrame = KDRect( - 0, m_frame.height() - m_indicatorThickness, - m_frame.width(), m_indicatorThickness - ); - m_horizontalScrollIndicator.setFrame(horizontalIndicatorFrame); - } - } -} - -void ScrollView::updateScrollIndicator() { - if (!m_showsIndicators) { - return; - } - float contentHeight = m_contentView->bounds().height()+m_topMargin+m_bottomMargin; - bool hadVerticalIndicator = hasVerticalIndicator(); - float verticalStart = contentOffset().y(); - float verticalEnd = contentOffset().y() + m_frame.height(); - m_verticalScrollIndicator.setStart(verticalStart/contentHeight); - m_verticalScrollIndicator.setEnd(verticalEnd/contentHeight); - if (hadVerticalIndicator && !hasVerticalIndicator()) { - markRectAsDirty(m_verticalScrollIndicator.frame()); - } - float contentWidth = m_contentView->bounds().width()+m_leftMargin+m_rightMargin; - bool hadHorizontalIndicator = hasHorizontalIndicator(); - float horizontalStart = contentOffset().x(); - float horizontalEnd = contentOffset().x() + m_frame.width(); - m_horizontalScrollIndicator.setStart(horizontalStart/contentWidth); - m_horizontalScrollIndicator.setEnd(horizontalEnd/contentWidth); - if (hadHorizontalIndicator && !hasHorizontalIndicator()) { - markRectAsDirty(m_horizontalScrollIndicator.frame()); - } -} - -KDSize ScrollView::contentSize() { - return m_contentView->minimalSizeForOptimalDisplay(); } void ScrollView::setContentOffset(KDPoint offset, bool forceRelayout) { @@ -202,12 +103,142 @@ void ScrollView::setContentOffset(KDPoint offset, bool forceRelayout) { } } -KDCoordinate ScrollView::maxContentWidthDisplayableWithoutScrolling() { - return m_frame.width() - m_leftMargin - m_rightMargin; +void ScrollView::InnerView::drawRect(KDContext * ctx, KDRect rect) const { + KDCoordinate height = bounds().height(); + KDCoordinate width = bounds().width(); + KDCoordinate offsetX = m_scrollView->contentOffset().x() + m_frame.x(); + KDCoordinate offsetY = m_scrollView->contentOffset().y() + m_frame.y(); + KDCoordinate contentHeight = m_scrollView->m_contentView->bounds().height(); + KDCoordinate contentWidth = m_scrollView->m_contentView->bounds().width(); + ctx->fillRect(KDRect(0, 0, width, m_scrollView->m_topMargin-offsetY), m_scrollView->m_backgroundColor); + ctx->fillRect(KDRect(0, contentHeight+m_scrollView->m_topMargin-offsetY, width, height - contentHeight - m_scrollView->m_topMargin + offsetY), m_scrollView->m_backgroundColor); + ctx->fillRect(KDRect(0, 0, m_scrollView->m_leftMargin-offsetX, height), m_scrollView->m_backgroundColor); + ctx->fillRect(KDRect(contentWidth + m_scrollView->m_leftMargin - offsetX, 0, width - contentWidth - m_scrollView->m_leftMargin + offsetX, height), m_scrollView->m_backgroundColor); } -KDCoordinate ScrollView::maxContentHeightDisplayableWithoutScrolling() { - return m_frame.height() - m_topMargin - m_bottomMargin; +ScrollView::BarDecorator::BarDecorator() : + m_verticalBar(), + m_horizontalBar() +{ +} + +View * ScrollView::BarDecorator::indicatorAtIndex(int index) { + switch(index) { + case 1: + return &m_verticalBar; + default: + assert(index == 2); + return &m_horizontalBar; + } +} + +KDRect ScrollView::BarDecorator::layoutIndicators(KDSize content, KDPoint offset, KDRect frame) { + KDCoordinate hBarFrameBreadth = k_barsFrameBreadth * m_horizontalBar.update( + content.width(), + offset.x(), + frame.width() + ); + KDCoordinate vBarFrameBreadth = k_barsFrameBreadth * m_verticalBar.update( + content.height(), + offset.y(), + frame.height() + ); + /* If the two indicators are visible, we leave an empty rectangle in the right + * bottom corner. Otherwise, the only indicator uses all the height/width. */ + m_verticalBar.setFrame(KDRect( + frame.width() - vBarFrameBreadth, 0, + vBarFrameBreadth, frame.height() - hBarFrameBreadth + )); + m_horizontalBar.setFrame(KDRect( + 0, frame.height() - hBarFrameBreadth, + frame.width() - vBarFrameBreadth, hBarFrameBreadth + )); + return frame; +} + +ScrollView::ArrowDecorator::ArrowDecorator() : + m_topArrow(ScrollViewArrow::Side::Top), + m_rightArrow(ScrollViewArrow::Side::Right), + m_bottomArrow(ScrollViewArrow::Side::Bottom), + m_leftArrow(ScrollViewArrow::Side::Left) +{ +} + +View * ScrollView::ArrowDecorator::indicatorAtIndex(int index) { + switch(index) { + case 1: + return &m_topArrow; + case 2: + return &m_rightArrow; + case 3: + return &m_bottomArrow; + default: + assert(index == 4); + return &m_leftArrow; + } +} + +KDRect ScrollView::ArrowDecorator::layoutIndicators(KDSize content, KDPoint offset, KDRect frame) { + KDSize arrowSize = KDFont::LargeFont->glyphSize(); + KDCoordinate topArrowFrameBreadth = arrowSize.height() * m_topArrow.update(0 < offset.y()); + KDCoordinate rightArrowFrameBreadth = arrowSize.width() * m_rightArrow.update(offset.x() + frame.width() < content.width()); + KDCoordinate bottomArrowFrameBreadth = arrowSize.height() * m_bottomArrow.update(offset.y() + frame.height() < content.height()); + KDCoordinate leftArrowFrameBreadth = arrowSize.width() * m_leftArrow.update(0 < offset.x()); + m_topArrow.setFrame(KDRect( + 0, 0, + frame.width(), topArrowFrameBreadth + )); + m_rightArrow.setFrame(KDRect( + frame.width() - rightArrowFrameBreadth, 0, + rightArrowFrameBreadth, frame.height() + )); + m_bottomArrow.setFrame(KDRect( + 0, frame.height() - bottomArrowFrameBreadth, + frame.width(), bottomArrowFrameBreadth + )); + m_leftArrow.setFrame(KDRect( + 0, 0, + leftArrowFrameBreadth, frame.height() + )); + return KDRect( + frame.x() + leftArrowFrameBreadth, + frame.y() + topArrowFrameBreadth, + frame.width() - leftArrowFrameBreadth - rightArrowFrameBreadth, + frame.height() - topArrowFrameBreadth - bottomArrowFrameBreadth + ); +} + +void ScrollView::ArrowDecorator::setBackgroundColor(KDColor c) { + for (int index = 1; index <= numberOfIndicators(); index++) { + static_cast(indicatorAtIndex(index))->setBackgroundColor(c); + } +} + +ScrollView::Decorators::Decorators() { + /* We need to initiate the Union at construction to avoid destructing an + * uninitialized object when changing the decorator type. */ + new (this) Decorator(); +} + +ScrollView::Decorators::~Decorators() { + activeDecorator()->~Decorator(); +} + +void ScrollView::Decorators::setActiveDecorator(Decorator::Type t) { + /* Decorator destructor is virtual so calling ~Decorator() on a Decorator + * pointer will call the appropriate destructor. */ + activeDecorator()->~Decorator(); + switch (t) { + case Decorator::Type::Bars: + new (&m_bars) BarDecorator(); + break; + case Decorator::Type::Arrows: + new (&m_arrows) ArrowDecorator(); + break; + default: + assert(t == Decorator::Type::None); + new (&m_none) Decorator(); + } } #if ESCHER_VIEW_LOGGING diff --git a/escher/src/scroll_view_indicator.cpp b/escher/src/scroll_view_indicator.cpp index e5be1f5c8..4e1eb3dec 100644 --- a/escher/src/scroll_view_indicator.cpp +++ b/escher/src/scroll_view_indicator.cpp @@ -3,68 +3,100 @@ extern "C" { #include } +#include -ScrollViewIndicator::ScrollViewIndicator(ScrollViewIndicator::Direction direction) : +ScrollViewIndicator::ScrollViewIndicator() : View(), - m_direction(direction), - m_start(0), - m_end(0), - m_indicatorColor(Palette::GreyDark), - m_backgroundColor(Palette::GreyMiddle), + m_color(Palette::GreyDark), m_margin(14) { } -void ScrollViewIndicator::drawRect(KDContext * ctx, KDRect rect) const { - KDRect frame = KDRectZero; - if (m_direction == Direction::Horizontal) { - frame = KDRect(m_margin, (m_frame.height() - k_indicatorThickness)/2, - m_frame.width() - 2*m_margin, k_indicatorThickness); - } else { - assert(m_direction == Direction::Vertical); - frame = KDRect((m_frame.width() - k_indicatorThickness)/2, m_margin, - k_indicatorThickness, m_frame.height() - 2*m_margin); - } - ctx->fillRect(frame, m_backgroundColor); - KDRect indicatorFrame = KDRectZero; - if (m_direction == Direction::Horizontal) { - KDCoordinate indicatorWidth = m_frame.width() - 2*m_margin; - indicatorFrame = KDRect(m_margin+m_start*indicatorWidth, (m_frame.height() - k_indicatorThickness)/2, - (m_end-m_start)*indicatorWidth, k_indicatorThickness); - } else { - assert(m_direction == Direction::Vertical); - KDCoordinate indicatorHeight = m_frame.height() - 2*m_margin; - indicatorFrame = KDRect((m_frame.width() - k_indicatorThickness)/2, m_margin+m_start*indicatorHeight, - k_indicatorThickness, (m_end-m_start)*indicatorHeight); - } - ctx->fillRect(indicatorFrame, m_indicatorColor); +ScrollViewBar::ScrollViewBar() : + ScrollViewIndicator(), + m_offset(0), + m_visibleLength(0), + m_trackColor(Palette::GreyMiddle) +{ } -float ScrollViewIndicator::start() const { - return m_start; -} - -void ScrollViewIndicator::setStart(float start) { - if (m_start != start) { - m_start = start; +bool ScrollViewBar::update(KDCoordinate totalContentLength, KDCoordinate contentOffset, KDCoordinate visibleContentLength) { + float offset = contentOffset; + float visibleLength = visibleContentLength; + offset = offset / totalContentLength; + visibleLength = visibleLength / totalContentLength; + if (m_offset != offset || m_visibleLength != visibleLength) { + m_offset = offset; + m_visibleLength = visibleLength; markRectAsDirty(bounds()); } + return visible(); } -float ScrollViewIndicator::end() const { - return m_end; +void ScrollViewHorizontalBar::drawRect(KDContext * ctx, KDRect rect) const { + if (!visible()) { + return; + } + ctx->fillRect( + KDRect( + m_margin, (m_frame.height() - k_indicatorThickness)/2, + totalLength(), k_indicatorThickness + ), + m_trackColor + ); + ctx->fillRect( + KDRect( + m_margin+m_offset*totalLength(), (m_frame.height() - k_indicatorThickness)/2, + std::ceil(m_visibleLength*totalLength()), k_indicatorThickness + ), + m_color + ); } -void ScrollViewIndicator::setEnd(float end) { - if (m_end != end) { - m_end = end; +void ScrollViewVerticalBar::drawRect(KDContext * ctx, KDRect rect) const { + if (!visible()) { + return; + } + ctx->fillRect( + KDRect( + (m_frame.width() - k_indicatorThickness)/2, m_margin, + k_indicatorThickness, totalLength() + ), + m_trackColor + ); + ctx->fillRect( + KDRect( + (m_frame.width() - k_indicatorThickness)/2, m_margin+m_offset*totalLength(), + k_indicatorThickness, std::ceil(m_visibleLength*totalLength()) + ), + m_color + ); +} + +ScrollViewArrow::ScrollViewArrow(Side side) : + m_visible(false), + m_arrow(side) +{ +} + +bool ScrollViewArrow::update(bool visible) { + if (m_visible != visible) { markRectAsDirty(bounds()); } + m_visible = visible; + return visible; } -KDRect ScrollViewIndicator::frame() { - return m_frame; +void ScrollViewArrow::drawRect(KDContext * ctx, KDRect rect) const { + ctx->fillRect(bounds(), m_backgroundColor); + KDSize arrowSize = KDFont::LargeFont->glyphSize(); + const KDPoint arrowAlign = KDPoint( + (m_arrow == Top || m_arrow == Bottom) * (m_frame.width() - arrowSize.width()) / 2, + (m_arrow == Left || m_arrow == Right) * (m_frame.height() - arrowSize.height()) / 2 + ); + ctx->drawString(&m_arrow, arrowAlign, KDFont::LargeFont, m_color, m_backgroundColor, m_visible); } + #if ESCHER_VIEW_LOGGING const char * ScrollViewIndicator::className() const { return "ScrollViewIndicator"; @@ -72,7 +104,7 @@ const char * ScrollViewIndicator::className() const { void ScrollViewIndicator::logAttributes(std::ostream &os) const { View::logAttributes(os); - os << " start=\"" << m_start << "\""; - os << " end=\"" << m_end << "\""; + os << " offset=\"" << m_offset << "\""; + os << " visibleLength=\"" << m_visibleLength << "\""; } #endif diff --git a/escher/src/scrollable_view.cpp b/escher/src/scrollable_view.cpp index bc9048715..f2781fa32 100644 --- a/escher/src/scrollable_view.cpp +++ b/escher/src/scrollable_view.cpp @@ -7,8 +7,7 @@ ScrollableView::ScrollableView(Responder * parentResponder, View * view, ScrollV ScrollView(view, dataSource), m_manualScrollingOffset(KDPointZero) { - setShowsIndicators(false); - setColorsBackground(false); + setDecoratorType(ScrollView::Decorator::Type::None); } bool ScrollableView::handleEvent(Ion::Events::Event event) { @@ -20,7 +19,7 @@ bool ScrollableView::handleEvent(Ion::Events::Event event) { } } if (event == Ion::Events::Right) { - KDCoordinate movementToEdge = m_contentView->minimalSizeForOptimalDisplay().width() - bounds().width() - m_manualScrollingOffset.x(); + KDCoordinate movementToEdge = minimalSizeForOptimalDisplay().width() - bounds().width() - m_manualScrollingOffset.x(); if (movementToEdge > 0) { translation = KDPoint(min(Metric::ScrollStep, movementToEdge), 0); } @@ -32,7 +31,7 @@ bool ScrollableView::handleEvent(Ion::Events::Event event) { } } if (event == Ion::Events::Down) { - KDCoordinate movementToEdge = m_contentView->minimalSizeForOptimalDisplay().height() - bounds().height() - m_manualScrollingOffset.y(); + KDCoordinate movementToEdge = minimalSizeForOptimalDisplay().height() - bounds().height() - m_manualScrollingOffset.y(); if (movementToEdge > 0) { translation = KDPoint(0, min(Metric::ScrollStep, movementToEdge)); } @@ -50,10 +49,9 @@ void ScrollableView::reloadScroll(bool forceReLayout) { setContentOffset(m_manualScrollingOffset, forceReLayout); } -void ScrollableView::layoutSubviews() { - KDSize viewSize = contentSize(); - KDCoordinate viewWidth = max(viewSize.width(), bounds().width() - leftMargin() - rightMargin()); - KDCoordinate viewHeight = max(viewSize.height(), bounds().height() - topMargin() - bottomMargin()); - m_contentView->setSize(KDSize(viewWidth, viewHeight)); - ScrollView::layoutSubviews(); +KDSize ScrollableView::contentSize() const { + KDSize viewSize = ScrollView::contentSize(); + KDCoordinate viewWidth = max(viewSize.width(), maxContentWidthDisplayableWithoutScrolling()); + KDCoordinate viewHeight = max(viewSize.height(), maxContentHeightDisplayableWithoutScrolling()); + return KDSize(viewWidth, viewHeight); } diff --git a/escher/src/selectable_table_view.cpp b/escher/src/selectable_table_view.cpp index 05c003d3b..9924a9cd7 100644 --- a/escher/src/selectable_table_view.cpp +++ b/escher/src/selectable_table_view.cpp @@ -1,5 +1,6 @@ #include #include +#include #include SelectableTableView::SelectableTableView(Responder * parentResponder, TableViewDataSource * dataSource, SelectableTableViewDataSource * selectionDataSource, SelectableTableViewDelegate * delegate) : @@ -8,8 +9,13 @@ SelectableTableView::SelectableTableView(Responder * parentResponder, TableViewD m_selectionDataSource(selectionDataSource), m_delegate(delegate) { - setCommonMargins(); assert(m_selectionDataSource != nullptr); + setMargins( + Metric::CommonTopMargin, + Metric::CommonRightMargin, + Metric::CommonBottomMargin, + Metric::CommonLeftMargin + ); } int SelectableTableView::selectedRow() { diff --git a/escher/src/stack_view_controller.cpp b/escher/src/stack_view_controller.cpp index fd5e2e74e..795a5ee3a 100644 --- a/escher/src/stack_view_controller.cpp +++ b/escher/src/stack_view_controller.cpp @@ -96,6 +96,7 @@ void StackViewController::push(ViewController * vc, KDColor textColor, KDColor b Frame frame = Frame(vc, textColor, backgroundColor, separatorColor); /* Add the frame to the model */ pushModel(frame); + frame.viewController()->initView(); if (!m_isVisible) { return; } @@ -156,6 +157,10 @@ View * StackViewController::view() { return &m_view; } +void StackViewController::initView() { + m_childrenFrame[0].viewController()->initView(); +} + void StackViewController::viewWillAppear() { /* Load the stack view */ for (int i = 0; i < m_numberOfChildren; i++) { diff --git a/escher/src/tab_view_controller.cpp b/escher/src/tab_view_controller.cpp index 91672bfe3..098663bc7 100644 --- a/escher/src/tab_view_controller.cpp +++ b/escher/src/tab_view_controller.cpp @@ -156,12 +156,14 @@ const char * TabViewController::tabName(uint8_t index) { return m_children[index]->title(); } -void TabViewController::viewWillAppear() { - if (m_view.m_tabView.numberOfTabs() != m_numberOfChildren) { - for (int i=0; iinitView(); } +} + +void TabViewController::viewWillAppear() { if (m_dataSource->activeTab() < 0) { m_dataSource->setActiveTab(0); } diff --git a/escher/src/table_view.cpp b/escher/src/table_view.cpp index 6aa1784cd..ed0ae9c7c 100644 --- a/escher/src/table_view.cpp +++ b/escher/src/table_view.cpp @@ -13,10 +13,6 @@ TableView::TableView(TableViewDataSource * dataSource, ScrollViewDataSource * sc { } -KDSize TableView::minimalSizeForOptimalDisplay() const { - return m_contentView.minimalSizeForOptimalDisplay(); -} - TableViewDataSource * TableView::dataSource() { return m_contentView.dataSource(); } @@ -38,11 +34,22 @@ const char * TableView::className() const { #endif void TableView::layoutSubviews() { - // We only have to layout our contentView. - // We will size it here, and ScrollView::layoutSubviews will position it. - - m_contentView.resizeToFitContent(); - + /* On the one hand, ScrollView::layoutSubviews() + * calls setFrame(...) over m_contentView, + * which typically calls layoutSubviews() over m_contentView. + * However, if the frame happens to be unchanged, + * setFrame(...) does not call layoutSubviews. + * On the other hand, calling only m_contentView.layoutSubviews() + * does not relayout ScrollView when the offset + * or the content's size changes. + * For those reasons, we call both of them explicitly. + * Besides, one must call layoutSubviews() over + * m_contentView first, in order to reload the table's data, + * otherwise the table's size might be miscomputed... + * FIXME: + * Finally, this solution is not optimal at all since + * layoutSubviews is called twice over m_contentView. */ + m_contentView.layoutSubviews(); ScrollView::layoutSubviews(); } @@ -69,17 +76,14 @@ TableViewDataSource * TableView::ContentView::dataSource() { return m_dataSource; } -KDCoordinate TableView::ContentView::columnWidth(int i) const { - int columnWidth = m_dataSource->columnWidth(i); +KDRect TableView::ContentView::cellFrame(int i, int j) const { + KDCoordinate columnWidth = m_dataSource->columnWidth(i); columnWidth = columnWidth ? columnWidth : m_tableView->maxContentWidthDisplayableWithoutScrolling(); - return columnWidth; -} - -void TableView::ContentView::resizeToFitContent() { - if (!(m_tableView->bounds() == KDRectZero)) { - layoutSubviews(); - setSize(KDSize(width(), height())); - } + return KDRect( + m_dataSource->cumulatedWidthFromIndex(i), m_dataSource->cumulatedHeightFromIndex(j), + columnWidth + m_horizontalCellOverlap, + m_dataSource->rowHeight(j) + m_verticalCellOverlap + ); } KDCoordinate TableView::ContentView::height() const { @@ -93,8 +97,7 @@ KDCoordinate TableView::ContentView::width() const { } void TableView::ContentView::scrollToCell(int x, int y) const { - KDRect cellRect = KDRect(m_dataSource->cumulatedWidthFromIndex(x), m_dataSource->cumulatedHeightFromIndex(y), columnWidth(x), m_dataSource->rowHeight(y)); - m_tableView->scrollToContentRect(cellRect, true); + m_tableView->scrollToContentRect(cellFrame(x, y), true); } void TableView::ContentView::reloadCellAtLocation(int i, int j) { @@ -175,14 +178,7 @@ void TableView::ContentView::layoutSubviews() { int i = absoluteColumnNumberFromSubviewIndex(index); int j = absoluteRowNumberFromSubviewIndex(index); m_dataSource->willDisplayCellAtLocation((HighlightCell *)cell, i, j); - - KDCoordinate rowHeight = m_dataSource->rowHeight(j); - KDCoordinate columnWidth = this->columnWidth(i); - KDCoordinate verticalOffset = m_dataSource->cumulatedHeightFromIndex(j); - KDCoordinate horizontalOffset = m_dataSource->cumulatedWidthFromIndex(i); - KDRect cellFrame(horizontalOffset, verticalOffset, - columnWidth+m_horizontalCellOverlap, rowHeight+m_verticalCellOverlap); - cell->setFrame(cellFrame); + cell->setFrame(cellFrame(i,j)); } } diff --git a/escher/src/text_field.cpp b/escher/src/text_field.cpp index bd0cb88b9..7f21bcfc6 100644 --- a/escher/src/text_field.cpp +++ b/escher/src/text_field.cpp @@ -312,10 +312,6 @@ bool TextField::privateHandleEvent(Ion::Events::Event event) { return false; } -KDSize TextField::minimalSizeForOptimalDisplay() const { - return m_contentView.minimalSizeForOptimalDisplay(); -} - char TextField::XNTChar(char defaultXNTChar) { static constexpr struct { const char *name; char xnt; } sFunctions[] = { { "diff", 'x' }, { "int", 'x' }, diff --git a/escher/src/text_view.cpp b/escher/src/text_view.cpp index 610816b33..b799a8fe0 100644 --- a/escher/src/text_view.cpp +++ b/escher/src/text_view.cpp @@ -33,6 +33,9 @@ void TextView::setFont(const KDFont * font) { } KDSize TextView::minimalSizeForOptimalDisplay() const { + if (text() == nullptr) { + return KDSize(0,0); + } return m_font->stringSize(text()); } diff --git a/ion/Makefile b/ion/Makefile index 611e42916..0bd749129 100644 --- a/ion/Makefile +++ b/ion/Makefile @@ -14,17 +14,17 @@ include ion/src/shared/tools/Makefile # char test[4]= "ab"; is valid and should initialize test to 'a','b',0,0). # Older versions of GCC are not conformant so we resort to an initializer list. initializer_list = $(shell echo $(1) | sed "s/\(.\)/'\1',/g")0 -ion/src/shared/platform_info.o: SFLAGS += -DPATCH_LEVEL="$(call initializer_list,$(PATCH_LEVEL))" -DEPSILON_VERSION="$(call initializer_list,$(EPSILON_VERSION))" +$(call object_for,ion/src/shared/platform_info.cpp): SFLAGS += -DPATCH_LEVEL="$(call initializer_list,$(PATCH_LEVEL))" -DEPSILON_VERSION="$(call initializer_list,$(EPSILON_VERSION))" -objs += $(addprefix ion/src/shared/, \ - crc32_padded.o\ - events.o \ - platform_info.o \ - storage.o \ - decompress.o \ +src += $(addprefix ion/src/shared/, \ + crc32_padded.cpp \ + decompress.cpp \ + events.cpp \ + platform_info.cpp \ + storage.cpp \ ) -objs += ion/src/external/lz4/lz4.o +src += ion/src/external/lz4/lz4.c tests += $(addprefix ion/test/,\ crc32.cpp\ diff --git a/ion/include/ion/display.h b/ion/include/ion/display.h index 4db471ba7..a43c830cc 100644 --- a/ion/include/ion/display.h +++ b/ion/include/ion/display.h @@ -25,6 +25,8 @@ void waitForVBlank(); constexpr int Width = 320; constexpr int Height = 240; +constexpr int WidthInTenthOfMillimeter = 576; +constexpr int HeightInTenthOfMillimeter = 432; } } diff --git a/ion/include/ion/events.h b/ion/include/ion/events.h index fc5270631..3ecbc5d2f 100644 --- a/ion/include/ion/events.h +++ b/ion/include/ion/events.h @@ -16,8 +16,9 @@ public: constexpr Event() : m_id(4*PageSize){} // Return Ion::Event::None by default constexpr Event(int i) : m_id(i){} // TODO: Assert here that i>=0 && i<255 -#if DEBUG + uint8_t id() const { return m_id; } +#if DEBUG const char * name() const; #endif Event(Keyboard::Key key, bool shift, bool alpha); diff --git a/ion/src/blackbox/Makefile b/ion/src/blackbox/Makefile index 931d9f7b2..6835cf65a 100644 --- a/ion/src/blackbox/Makefile +++ b/ion/src/blackbox/Makefile @@ -1,33 +1,33 @@ -objs += $(addprefix ion/src/blackbox/, \ - boot.o \ - ion.o \ - display.o \ - events.o \ +src += $(addprefix ion/src/blackbox/, \ + boot.cpp \ + ion.cpp \ + display.cpp \ + events.cpp \ ) -objs += $(addprefix ion/src/shared/, \ - console_line.o \ - console_stdio.o \ - crc32.o \ - crc32_padded.o \ - events.o \ - power.o \ - random.o \ - timing.o \ - dummy/backlight.o \ - dummy/battery.o \ - dummy/events_modifier.o \ - dummy/fcc_id.o \ - dummy/led.o \ - dummy/keyboard.o \ - dummy/serial_number.o \ - dummy/stack.o \ - dummy/usb.o \ +src += $(addprefix ion/src/shared/, \ + console_line.cpp \ + console_stdio.cpp \ + crc32.cpp \ + crc32_padded.cpp \ + events.cpp \ + power.cpp \ + random.cpp \ + timing.cpp \ + dummy/backlight.cpp \ + dummy/battery.cpp \ + dummy/events_modifier.cpp \ + dummy/fcc_id.cpp \ + dummy/led.cpp \ + dummy/keyboard.cpp \ + dummy/serial_number.cpp \ + dummy/stack.cpp \ + dummy/usb.cpp \ ) -ion/src/shared/log_printf.o: SFLAGS=-Iion/include -ion/src/shared/console_stdio.o: SFLAGS=-Iion/include -ion/src/shared/events_stdin.o: SFLAGS=-Iion/include +$(call object_for,ion/src/shared/log_printf.cpp): SFLAGS=-Iion/include +$(call object_for,ion/src/shared/console_stdio.cpp): SFLAGS=-Iion/include +$(call object_for,ion/src/shared/events_stdin.cpp): SFLAGS=-Iion/include SFLAGS += `libpng-config --cflags` LDFLAGS += `libpng-config --ldflags` diff --git a/ion/src/blackbox/compare.cpp b/ion/src/blackbox/compare.cpp index 791ccfd2a..b3c18aec5 100644 --- a/ion/src/blackbox/compare.cpp +++ b/ion/src/blackbox/compare.cpp @@ -13,7 +13,7 @@ * * To compare the versions on a given scenario: * make -j8 PLATFORM=blackbox compare - * ./compare path/to/scenario + * ./compare < path/to/scenario * To fuzz over scenarios that are in a folder named "tests": * make -j8 PLATFORM=blackbox TOOLCHAIN=afl compare_fuzz */ diff --git a/ion/src/device/Makefile b/ion/src/device/Makefile index bd55cada8..ab53af54c 100644 --- a/ion/src/device/Makefile +++ b/ion/src/device/Makefile @@ -1,22 +1,22 @@ -# Makefile belows should augment $(ion_device_objs) +# Makefile belows should augment $(ion_device_src) include ion/src/device/shared/Makefile include ion/src/device/bench/Makefile include ion/src/device/$(MODEL)/Makefile -ion/src/shared/platform_info.o: SFLAGS += -DHEADER_SECTION="__attribute__((section(\".header\")))" +$(call object_for,ion/src/shared/platform_info.cpp): SFLAGS += -DHEADER_SECTION="__attribute__((section(\".header\")))" -objs += $(addprefix ion/src/shared/, \ - console_line.o \ - crc32_padded.o\ - events_modifier.o \ +src += $(addprefix ion/src/shared/, \ + console_line.cpp \ + crc32_padded.cpp \ + events_modifier.cpp \ ) # If you need to profile execution, you can replace events_keyboard with # events_replay.o and dummy/events_modifier.o ION_DEVICE_SFLAGS = -Iion/src/device/$(MODEL) -Iion/src/device/shared -$(ion_device_objs): SFLAGS += $(ION_DEVICE_SFLAGS) -objs += $(ion_device_objs) +$(call object_for,$(ion_device_src) $(dfu_src) $(usb_src)): SFLAGS += $(ION_DEVICE_SFLAGS) +src += $(ion_device_src) # When using the register.h C++ file in production mode, we expect the compiler # to completely inline all bit manipulations. For some reason, if we build using @@ -26,10 +26,10 @@ objs += $(ion_device_objs) ifneq ($(DEBUG),1) ifneq ($(COMPILER),llvm) -ion/src/device/led.o: SFLAGS+=-O3 -ion/src/device/console.o: SFLAGS+=-O3 -ion/src/device/display.o: SFLAGS+=-O3 -ion/src/device/swd.o: SFLAGS+=-O3 +$(BUILD_DIR)/ion/src/device/led.o: SFLAGS+=-O3 +$(BUILD_DIR)/ion/src/device/console.o: SFLAGS+=-O3 +$(BUILD_DIR)/ion/src/device/display.o: SFLAGS+=-O3 +$(BUILD_DIR)/ion/src/device/swd.o: SFLAGS+=-O3 endif endif diff --git a/ion/src/device/bench/Makefile b/ion/src/device/bench/Makefile index 90b0c82b6..c882e1393 100644 --- a/ion/src/device/bench/Makefile +++ b/ion/src/device/bench/Makefile @@ -1,21 +1,21 @@ -objs += $(addprefix ion/src/device/bench/, \ - bench.o \ - command_handler.o \ - command_list.o \ +src += $(addprefix ion/src/device/bench/, \ + bench.cpp \ + command_handler.cpp \ + command_list.cpp \ ) -objs += $(addprefix ion/src/device/bench/command/, \ - command.o \ - adc.o \ - backlight.o \ - charge.o \ - display.o \ - exit.o \ - keyboard.o \ - led.o \ - mcu_serial.o \ - ping.o \ - print.o \ - suspend.o \ - vblank.o \ +src += $(addprefix ion/src/device/bench/command/, \ + adc.cpp \ + backlight.cpp \ + charge.cpp \ + command.cpp \ + display.cpp \ + exit.cpp \ + keyboard.cpp \ + led.cpp \ + mcu_serial.cpp \ + ping.cpp \ + print.cpp \ + suspend.cpp \ + vblank.cpp \ ) diff --git a/ion/src/device/n0100/Makefile b/ion/src/device/n0100/Makefile index b0bd9c3b1..931bd69d0 100644 --- a/ion/src/device/n0100/Makefile +++ b/ion/src/device/n0100/Makefile @@ -1,5 +1,5 @@ -ion_device_objs += $(addprefix ion/src/device/n0100/drivers/, \ - board.o \ +ion_device_src += $(addprefix ion/src/device/n0100/drivers/, \ + board.cpp \ ) LDSCRIPT ?= ion/src/device/n0100/flash.ld diff --git a/ion/src/device/n0101/Makefile b/ion/src/device/n0101/Makefile index 7bbdb9308..ec4576e4d 100644 --- a/ion/src/device/n0101/Makefile +++ b/ion/src/device/n0101/Makefile @@ -1,5 +1,5 @@ -ion_device_objs += $(addprefix ion/src/device/n0101/drivers/, \ - board.o \ +ion_device_src += $(addprefix ion/src/device/n0101/drivers/, \ + board.cpp \ ) LDSCRIPT ?= ion/src/device/n0101/flash.ld diff --git a/ion/src/device/shared/boot/Makefile b/ion/src/device/shared/boot/Makefile index 6dbc4b08c..7c702f7ab 100644 --- a/ion/src/device/shared/boot/Makefile +++ b/ion/src/device/shared/boot/Makefile @@ -1,4 +1,4 @@ -objs += $(addprefix ion/src/device/shared/boot/, \ - isr.o \ - rt0.o \ +src += $(addprefix ion/src/device/shared/boot/, \ + isr.c \ + rt0.cpp \ ) diff --git a/ion/src/device/shared/drivers/Makefile b/ion/src/device/shared/drivers/Makefile index 4cf8c19e0..473a72b96 100644 --- a/ion/src/device/shared/drivers/Makefile +++ b/ion/src/device/shared/drivers/Makefile @@ -1,22 +1,22 @@ -ion_device_objs += $(addprefix ion/src/device/shared/drivers/, \ - backlight.o \ - battery.o \ - base64.o \ - board.o \ - console.o \ - crc32.o \ - display.o \ - events.o \ - external_flash.o \ - flash.o \ - keyboard.o \ - led.o \ - power.o\ - random.o\ - reset.o \ - serial_number.o \ - swd.o \ - timing.o \ - usb.o \ - wakeup.o \ +ion_device_src += $(addprefix ion/src/device/shared/drivers/, \ + backlight.cpp \ + battery.cpp \ + base64.cpp \ + board.cpp \ + console.cpp \ + crc32.cpp \ + display.cpp \ + events.cpp \ + external_flash.cpp \ + flash.cpp \ + keyboard.cpp \ + led.cpp \ + power.cpp\ + random.cpp\ + reset.cpp \ + serial_number.cpp \ + swd.cpp \ + timing.cpp \ + usb.cpp \ + wakeup.cpp \ ) diff --git a/ion/src/device/shared/usb/Makefile b/ion/src/device/shared/usb/Makefile index 67f0ab958..fd26a27a7 100644 --- a/ion/src/device/shared/usb/Makefile +++ b/ion/src/device/shared/usb/Makefile @@ -1,73 +1,71 @@ -usb_objs += $(addprefix ion/src/device/shared/usb/, \ - calculator.o \ - dfu_interface.o\ +usb_src += $(addprefix ion/src/device/shared/usb/, \ + calculator.cpp \ + dfu_interface.cpp\ ) -usb_objs += $(addprefix ion/src/device/shared/usb/stack/, \ - device.o\ - endpoint0.o \ - interface.o\ - request_recipient.o\ - setup_packet.o\ - streamable.o\ +usb_src += $(addprefix ion/src/device/shared/usb/stack/, \ + device.cpp\ + endpoint0.cpp \ + interface.cpp\ + request_recipient.cpp\ + setup_packet.cpp\ + streamable.cpp\ ) -usb_objs += $(addprefix ion/src/device/shared/usb/stack/descriptor/, \ - bos_descriptor.o\ - configuration_descriptor.o \ - descriptor.o\ - device_descriptor.o\ - device_capability_descriptor.o\ - dfu_functional_descriptor.o\ - extended_compat_id_descriptor.o \ - interface_descriptor.o\ - language_id_string_descriptor.o \ - microsoft_os_string_descriptor.o\ - platform_device_capability_descriptor.o\ - string_descriptor.o\ - url_descriptor.o\ - webusb_platform_descriptor.o\ +usb_src += $(addprefix ion/src/device/shared/usb/stack/descriptor/, \ + bos_descriptor.cpp\ + configuration_descriptor.cpp \ + descriptor.cpp\ + device_descriptor.cpp\ + device_capability_descriptor.cpp\ + dfu_functional_descriptor.cpp\ + extended_compat_id_descriptor.cpp \ + interface_descriptor.cpp\ + language_id_string_descriptor.cpp \ + microsoft_os_string_descriptor.cpp\ + platform_device_capability_descriptor.cpp\ + string_descriptor.cpp\ + url_descriptor.cpp\ + webusb_platform_descriptor.cpp\ ) -$(usb_objs): SFLAGS += $(ION_DEVICE_SFLAGS) +$(usb_src): SFLAGS += $(ION_DEVICE_SFLAGS) EPSILON_USB_DFU_XIP ?= 0 ifeq ($(EPSILON_USB_DFU_XIP),1) -ion_device_objs += ion/src/device/shared/usb/dfu_xip.o -ion_device_objs += $(usb_objs) +ion_device_src += ion/src/device/shared/usb/dfu_xip.cpp +ion_device_src += $(usb_src) else -dfu_objs += liba/src/assert.o -dfu_objs += liba/src/strlen.o -dfu_objs += liba/src/strlcpy.o -dfu_objs += liba/src/memset.o -dfu_objs += liba/src/memcpy.o -dfu_objs += libaxx/src/cxxabi/pure_virtual.o -dfu_objs += ion/src/device/shared/usb/boot.o -dfu_objs += $(addprefix ion/src/device/shared/drivers/, \ - base64.o \ - external_flash.o \ - flash.o \ - keyboard.o \ - reset.o \ - serial_number.o \ - timing.o \ - usb.o \ +dfu_src += liba/src/assert.c +dfu_src += liba/src/strlen.c +dfu_src += liba/src/strlcpy.c +dfu_src += liba/src/memset.c +dfu_src += liba/src/memcpy.c +dfu_src += libaxx/src/cxxabi/pure_virtual.cpp +dfu_src += ion/src/device/shared/usb/boot.cpp +dfu_src += $(addprefix ion/src/device/shared/drivers/, \ + base64.cpp \ + external_flash.cpp \ + flash.cpp \ + keyboard.cpp \ + reset.cpp \ + serial_number.cpp \ + timing.cpp \ + usb.cpp \ ) -ion/src/device/shared/usb/dfu.elf: LDSCRIPT = ion/src/device/shared/usb/dfu.ld -ion/src/device/shared/usb/dfu.elf: $(usb_objs) $(dfu_objs) +$(BUILD_DIR)/ion/src/device/shared/usb/dfu.elf: LDSCRIPT = ion/src/device/shared/usb/dfu.ld +$(BUILD_DIR)/ion/src/device/shared/usb/dfu.elf: $(call object_for,$(usb_src) $(dfu_src)) -ion/src/device/shared/usb/dfu.o: ion/src/device/shared/usb/dfu.bin +$(BUILD_DIR)/ion/src/device/shared/usb/dfu.o: $(BUILD_DIR)/ion/src/device/shared/usb/dfu.bin @echo "OBJCOPY $@" - $(Q) $(OBJCOPY) -I binary -O elf32-littlearm -B arm --rename-section .data=.rodata --redefine-sym _binary_ion_src_device_shared_usb_dfu_bin_start=_dfu_bootloader_flash_start --redefine-sym _binary_ion_src_device_shared_usb_dfu_bin_end=_dfu_bootloader_flash_end $< $@ + $(Q) cd $(dir $<) ; $(OBJCOPY) -I binary -O elf32-littlearm -B arm --rename-section .data=.rodata --redefine-sym _binary_dfu_bin_start=_dfu_bootloader_flash_start --redefine-sym _binary_dfu_bin_end=_dfu_bootloader_flash_end $(notdir $<) $(notdir $@) -ion_device_objs += ion/src/device/shared/usb/dfu.o -ion_device_objs += ion/src/device/shared/usb/dfu_relocated.o - -products += $(usb_objs) $(addprefix ion/src/device/shared/usb/dfu, .elf .bin) +ion_device_src += ion/src/device/shared/usb/dfu.cpp +ion_device_src += ion/src/device/shared/usb/dfu_relocated.cpp endif diff --git a/ion/src/emscripten/Makefile b/ion/src/emscripten/Makefile index 69b2d5d1f..c697a08cd 100644 --- a/ion/src/emscripten/Makefile +++ b/ion/src/emscripten/Makefile @@ -1,22 +1,22 @@ -objs += $(addprefix ion/src/emscripten/, \ - display.o \ - events_keyboard.o \ - main.o \ +src += $(addprefix ion/src/emscripten/, \ + display.cpp \ + events_keyboard.cpp \ + main.cpp \ ) -objs += $(addprefix ion/src/shared/, \ - crc32.o \ - crc32_padded.o \ - events.o \ - events_modifier.o \ - power.o \ - random.o \ - timing.o \ - dummy/backlight.o \ - dummy/battery.o \ - dummy/fcc_id.o \ - dummy/led.o \ - dummy/serial_number.o \ - dummy/stack.o \ - dummy/usb.o \ +src += $(addprefix ion/src/shared/, \ + crc32.cpp \ + crc32_padded.cpp \ + events.cpp \ + events_modifier.cpp \ + power.cpp \ + random.cpp \ + timing.cpp \ + dummy/backlight.cpp \ + dummy/battery.cpp \ + dummy/fcc_id.cpp \ + dummy/led.cpp \ + dummy/serial_number.cpp \ + dummy/stack.cpp \ + dummy/usb.cpp \ ) diff --git a/ion/src/emscripten/main.cpp b/ion/src/emscripten/main.cpp index daee539d3..dac995e3c 100644 --- a/ion/src/emscripten/main.cpp +++ b/ion/src/emscripten/main.cpp @@ -16,6 +16,9 @@ int main(int argc, char * argv[]) { Ion::Display::Emscripten::init(); Ion::Events::Emscripten::init(); + // Set the seed for random using the current time + srand(emscripten_get_now()); + ion_main(argc, argv); return 0; } diff --git a/ion/src/f730/keyboard.cpp b/ion/src/f730/keyboard.cpp index 3fc9f9948..e148acbf7 100644 --- a/ion/src/f730/keyboard.cpp +++ b/ion/src/f730/keyboard.cpp @@ -53,8 +53,8 @@ State scan() { for (uint8_t i=0; igetBitRange(5,0); + // TODO: Assert pin numbers are sequentials and dynamically find 5 and 0 + uint8_t columns = Device::ColumnGPIO.IDR()->getBitRange(Device::numberOfColumns-1,0); /* The key is down if the input is brought low by the output. In other * words, we want to return true if the input is low (false). So we need to diff --git a/ion/src/f730/usb/Makefile b/ion/src/f730/usb/Makefile index d32478413..7d2b990a7 100644 --- a/ion/src/f730/usb/Makefile +++ b/ion/src/f730/usb/Makefile @@ -1,3 +1,4 @@ +<<<<<<< HEAD:ion/src/f730/usb/Makefile usb_objs += $(addprefix ion/src/f730/usb/, \ calculator.o \ dfu_interface.o\ @@ -27,12 +28,44 @@ usb_objs += $(addprefix ion/src/f730/usb/stack/descriptor/, \ string_descriptor.o\ url_descriptor.o\ webusb_platform_descriptor.o\ +======= +usb_src += $(addprefix ion/src/device/usb/, \ + calculator.cpp \ + dfu_interface.cpp \ +) + +usb_src += $(addprefix ion/src/device/usb/stack/, \ + device.cpp \ + endpoint0.cpp \ + interface.cpp \ + request_recipient.cpp \ + setup_packet.cpp \ + streamable.cpp \ +) + +usb_src += $(addprefix ion/src/device/usb/stack/descriptor/, \ + bos_descriptor.cpp \ + configuration_descriptor.cpp \ + descriptor.cpp \ + device_capability_descriptor.cpp \ + device_descriptor.cpp \ + dfu_functional_descriptor.cpp \ + extended_compat_id_descriptor.cpp \ + interface_descriptor.cpp \ + language_id_string_descriptor.cpp \ + microsoft_os_string_descriptor.cpp \ + platform_device_capability_descriptor.cpp \ + string_descriptor.cpp \ + url_descriptor.cpp \ + webusb_platform_descriptor.cpp \ +>>>>>>> caef2e8c6fa246205cc829d623f15a8dd5b1505b:ion/src/device/usb/Makefile ) EPSILON_USB_DFU_XIP ?= 0 ifeq ($(EPSILON_USB_DFU_XIP),1) +<<<<<<< HEAD:ion/src/f730/usb/Makefile objs += ion/src/f730/usb/dfu_xip.o objs += $(usb_objs) @@ -57,12 +90,49 @@ ion/src/f730/usb/dfu.elf: LDSCRIPT = ion/src/f730/usb/dfu.ld ion/src/f730/usb/dfu.elf: $(usb_objs) $(dfu_objs) ion/src/f730/usb/dfu.o: ion/src/f730/usb/dfu.bin - @echo "OBJCOPY $@" - $(Q) $(OBJCOPY) -I binary -O elf32-littlearm -B arm --rename-section .data=.rodata --redefine-sym _binary_ion_src_device_usb_dfu_bin_start=_dfu_bootloader_flash_start --redefine-sym _binary_ion_src_device_usb_dfu_bin_end=_dfu_bootloader_flash_end $< $@ +======= +src += ion/src/device/usb/dfu_xip.cpp +src += $(usb_src) +else + +dfu_src += liba/src/assert.cpp +dfu_src += liba/src/strlen.cpp +dfu_src += liba/src/strlcpy.cpp +dfu_src += liba/src/memset.cpp +dfu_src += liba/src/memcpy.cpp +dfu_src += libaxx/src/cxxabi/pure_virtual.cpp +dfu_src += ion/src/device/usb/boot.cpp +dfu_src += ion/src/device/keyboard.cpp +dfu_src += ion/src/device/device.cpp +dfu_src += ion/src/device/usb.cpp +dfu_src += ion/src/device/base64.cpp +dfu_src += ion/src/device/flash.cpp +dfu_src += ion/src/device/timing.cpp + +$(BUILD_DIR)/ion/src/device/usb/dfu.elf: LDSCRIPT = ion/src/device/usb/dfu.ld +$(BUILD_DIR)/ion/src/device/usb/dfu.elf: $(call object_for,$(usb_src)) $(call object_for,$(dfu_src)) + +# This command embeds a binary file into an object one. +# This allows us to embed standalone code (the dfu routines) into the final +# executable, and easily relocate it to RAM for execution. The objcopy command +# that turns binary data into an ELF object generates three symbols (start, size +# and end), but prefixes them with a mangled file path. To have consistent names +# we simply "cd" into the directory. This assumes input and output lives in the +# same directory. +$(BUILD_DIR)/ion/src/device/usb/dfu.o: $(BUILD_DIR)/ion/src/device/usb/dfu.bin +>>>>>>> caef2e8c6fa246205cc829d623f15a8dd5b1505b:ion/src/device/usb/Makefile + @echo "OBJCOPY $@" + $(Q) cd $(dir $<) ; $(OBJCOPY) -I binary -O elf32-littlearm -B arm --rename-section .data=.rodata --redefine-sym _binary_dfu_bin_start=_dfu_bootloader_flash_start --redefine-sym _binary_dfu_bin_end=_dfu_bootloader_flash_end $(notdir $<) $(notdir $@) + +<<<<<<< HEAD:ion/src/f730/usb/Makefile objs += ion/src/f730/usb/dfu.o objs += ion/src/f730/usb/dfu_relocated.o products += $(usb_objs) $(addprefix ion/src/f730/usb/dfu, .elf .bin) +======= +src += ion/src/device/usb/dfu.cpp +src += ion/src/device/usb/dfu_relocated.cpp +>>>>>>> caef2e8c6fa246205cc829d623f15a8dd5b1505b:ion/src/device/usb/Makefile endif diff --git a/ion/src/shared/storage.cpp b/ion/src/shared/storage.cpp index efa031ab9..b08ccc900 100644 --- a/ion/src/shared/storage.cpp +++ b/ion/src/shared/storage.cpp @@ -206,6 +206,7 @@ Storage::Record Storage::recordBaseNamedWithExtensions(const char * baseName, co if (strncmp(baseName, currentName, nameLength) == 0) { for (size_t i = 0; i < numberOfExtensions; i++) { if (strcmp(currentName+nameLength+1 /*+1 to pass the dot*/, extensions[i]) == 0) { + assert(*(currentName + nameLength) == '.'); return Record(currentName); } } @@ -359,10 +360,8 @@ const char * Storage::fullNameOfRecordStarting(char * start) const { const void * Storage::valueOfRecordStarting(char * start) const { char * currentChar = start+sizeof(record_size_t); - while (*currentChar != 0) { - currentChar++; - } - return currentChar+1; + size_t fullNameLength = strlen(currentChar); + return currentChar+fullNameLength+1; } size_t Storage::overrideSizeAtPosition(char * position, record_size_t size) { diff --git a/ion/src/shared/tools/Makefile b/ion/src/shared/tools/Makefile index 27b59a6ee..081339af1 100644 --- a/ion/src/shared/tools/Makefile +++ b/ion/src/shared/tools/Makefile @@ -1,5 +1,3 @@ -ion/src/shared/tools/event_%: ion/src/shared/tools/event_%.cpp ion/src/shared/events.cpp +$(BUILD_DIR)/ion/src/shared/tools/event_%: ion/src/shared/tools/event_%.cpp ion/src/shared/events.cpp @echo "HOSTCXX $@" @$(HOSTCXX) -std=c++11 -Iion/include -DDEBUG=1 $^ -o $@ - -products += $(addprefix ion/src/shared/tools/, event_filter event_generator event_parser event_printer) diff --git a/ion/src/simulator/Makefile b/ion/src/simulator/Makefile index dc4a9d250..341258950 100644 --- a/ion/src/simulator/Makefile +++ b/ion/src/simulator/Makefile @@ -1,34 +1,28 @@ -objs += $(addprefix ion/src/simulator/, \ - init.o\ +src += $(addprefix ion/src/simulator/, \ + init.cpp \ ) -objs += $(addprefix ion/src/simulator/boot/, main.o) -objs += $(addprefix ion/src/simulator/display/, fltklcd.o) -objs += $(addprefix ion/src/simulator/keyboard/, fltkkbd.o) +src += $(addprefix ion/src/simulator/boot/, main.cpp) +src += $(addprefix ion/src/simulator/display/, fltklcd.cpp) +src += $(addprefix ion/src/simulator/keyboard/, fltkkbd.cpp) -objs += $(addprefix ion/src/shared/, \ - crc32.o \ - crc32_padded.o \ - console_line.o \ - console_stdio.o \ - events_modifier.o \ - power.o \ - random.o \ - timing.o \ - dummy/backlight.o \ - dummy/battery.o \ - dummy/fcc_id.o \ - dummy/led.o \ - dummy/serial_number.o \ - dummy/stack.o \ - dummy/usb.o \ +src += $(addprefix ion/src/shared/, \ + crc32.cpp \ + crc32_padded.cpp \ + console_line.cpp \ + console_stdio.cpp \ + events_modifier.cpp \ + power.cpp \ + random.cpp \ + timing.cpp \ + dummy/backlight.cpp \ + dummy/battery.cpp \ + dummy/fcc_id.cpp \ + dummy/led.cpp \ + dummy/serial_number.cpp \ + dummy/stack.cpp \ + dummy/usb.cpp \ ) SFLAGS += `fltk-config --cflags` LDFLAGS += `fltk-config --ldflags` - -.PHONY: %_memory_map -%_memory_map: - @echo "========== MEMORY MAP =========" - @echo "No memory map on simulator." - @echo "===============================" diff --git a/ion/src/simulator/init.cpp b/ion/src/simulator/init.cpp index 437ef5be6..b8bf9693d 100644 --- a/ion/src/simulator/init.cpp +++ b/ion/src/simulator/init.cpp @@ -76,9 +76,8 @@ void Ion::Display::waitForVBlank() { Ion::Keyboard::State Ion::Keyboard::scan() { Ion::Keyboard::State result = 0; - for (int i=0; ikey_down((Ion::Keyboard::Key)(Ion::Keyboard::NumberOfKeys-1-i)); - } return result; } diff --git a/ion/src/simulator/keyboard/fltkkbd.cpp b/ion/src/simulator/keyboard/fltkkbd.cpp index 8b55d0b42..fa95d5215 100644 --- a/ion/src/simulator/keyboard/fltkkbd.cpp +++ b/ion/src/simulator/keyboard/fltkkbd.cpp @@ -22,19 +22,15 @@ static const char* kCharForKey[Ion::Keyboard::NumberOfKeys] = { static const int kShortcutForKey[Ion::Keyboard::NumberOfKeys] = { FL_Left, FL_Up, FL_Down, FL_Right, FL_Enter, FL_Escape, FL_Home, 0, 0, 0, 0, 0, - 0, 0, 'x', 0, 0, FL_BackSpace, - 0, 0, 0, 'i', ',', 0, + 0, 0, 0, 0, 0, FL_BackSpace, + 0, 0, 0, 0, ',', 0, 0, 0, 0, 0, 0, 0, - '7', '8', '9', '(',')', 0, - '4', '5', '6', '*', '/', 0, - '1', '2', '3', '+', '-', 0, - '0', '.', 0, 0, FL_KP_Enter, 0 + FL_SHIFT | '7', FL_SHIFT | '8', FL_SHIFT | '9', '(',')', 0, + FL_SHIFT | '4', FL_SHIFT | '5', FL_SHIFT | '6', '*', '/', 0, + FL_SHIFT | '1', FL_SHIFT | '2', FL_SHIFT | '3', '+', '-', 0, + FL_SHIFT | '0', '.', 0, 0, FL_KP_Enter, 0 }; -static bool shouldRepeatKey(Ion::Keyboard::Key k) { - return k <= Ion::Keyboard::Key::A4 || k == Ion::Keyboard::Key::A6; -} - static void keyHandler(Fl_Widget *, long key) { if (currentEvent == Ion::Events::None) { currentEvent = Ion::Events::Event((Ion::Keyboard::Key)key, @@ -45,16 +41,27 @@ static void keyHandler(Fl_Widget *, long key) { } FltkKbd::FltkKbd(int x, int y, int w, int h) : Fl_Group(x, y, w, h) { - assert(KeyboardRows*KeyboardColumns == Ion::Keyboard::NumberOfKeys); - int key_width = w/KeyboardColumns; - int key_height = h/KeyboardRows; - for (int k=0; klabelfont(FL_SYMBOL); @@ -69,9 +76,117 @@ FltkKbd::FltkKbd(int x, int y, int w, int h) : Fl_Group(x, y, w, h) { } m_buttons[k]->clear_visible_focus(); } - end(); +} + +static void alphabetEventHandler(Fl_Widget *, long c) { + static Ion::Events::Event lowerEvents[FltkKbd::sNumberOfLettersInAlphabet] = { + Ion::Events::LowerA, Ion::Events::LowerB, Ion::Events::LowerC, Ion::Events::LowerD, Ion::Events::LowerE, + Ion::Events::LowerF, Ion::Events::LowerG, Ion::Events::LowerH, Ion::Events::LowerI, Ion::Events::LowerJ, + Ion::Events::LowerK, Ion::Events::LowerL, Ion::Events::LowerM, Ion::Events::LowerN, Ion::Events::LowerO, + Ion::Events::LowerP, Ion::Events::LowerQ, Ion::Events::LowerR, Ion::Events::LowerS, Ion::Events::LowerT, + Ion::Events::LowerU, Ion::Events::LowerV, Ion::Events::LowerW, Ion::Events::LowerX, Ion::Events::LowerY, + Ion::Events::LowerZ}; + static Ion::Events::Event upperEvents[FltkKbd::sNumberOfLettersInAlphabet] = { + Ion::Events::UpperA, Ion::Events::UpperB, Ion::Events::UpperC, Ion::Events::UpperD, Ion::Events::UpperE, + Ion::Events::UpperF, Ion::Events::UpperG, Ion::Events::UpperH, Ion::Events::UpperI, Ion::Events::UpperJ, + Ion::Events::UpperK, Ion::Events::UpperL, Ion::Events::UpperM, Ion::Events::UpperN, Ion::Events::UpperO, + Ion::Events::UpperP, Ion::Events::UpperQ, Ion::Events::UpperR, Ion::Events::UpperS, Ion::Events::UpperT, + Ion::Events::UpperU, Ion::Events::UpperV, Ion::Events::UpperW, Ion::Events::UpperX, Ion::Events::UpperY, + Ion::Events::UpperZ}; + if (currentEvent == Ion::Events::None) { + if (c >= 'a' && c <= 'z') { + currentEvent = lowerEvents[c - 'a']; + } else if (c >= 'A' && c <= 'Z') { + currentEvent = upperEvents[c - 'A']; + } + } +} + +void FltkKbd::initAlphabetShortcuts() { + // Get keyboard letters event + for (int k = 0; k < sNumberOfLettersInAlphabet; k++) { + char l = 'a' + k; + m_alphabetShortcuts[k] = new Fl_Button(0, 0, 0, 0, ""); + m_alphabetShortcuts[k]->callback(alphabetEventHandler, l); + m_alphabetShortcuts[k]->shortcut(l); + m_alphabetShortcuts[k]->clear_visible_focus(); + } + for (int k = 0; k < sNumberOfLettersInAlphabet; k++) { + char l = 'A' + k; + int index = sNumberOfLettersInAlphabet + k; + m_alphabetShortcuts[index] = new Fl_Button(0, 0, 0, 0, ""); + m_alphabetShortcuts[index]->callback(alphabetEventHandler, l); + m_alphabetShortcuts[index]->shortcut(FL_SHIFT | ('a' + k)); + m_alphabetShortcuts[index]->clear_visible_focus(); + } +} + +static void otherEventHandler(Fl_Widget *, long c) { + if (currentEvent == Ion::Events::None) { + currentEvent = Ion::Events::Event(c); + } +} + +void FltkKbd::initOtherShortcuts() { + // Get other events + for (int k = 0; k < sNumberOfOtherShortcuts; k++) { + m_otherShortcuts[k] = new Fl_Button(0, 0, 0, 0, ""); + } + int index = 0; + m_otherShortcuts[index]->callback(otherEventHandler, Ion::Events::Imaginary.id()); + m_otherShortcuts[index++]->shortcut(FL_ALT | 'i'); + m_otherShortcuts[index]->callback(otherEventHandler, Ion::Events::Comma.id()); + m_otherShortcuts[index++]->shortcut(','); + m_otherShortcuts[index]->callback(otherEventHandler, Ion::Events::Power.id()); + m_otherShortcuts[index++]->shortcut('^'); + m_otherShortcuts[index]->callback(otherEventHandler, Ion::Events::ShiftLeft.id()); + m_otherShortcuts[index++]->shortcut(FL_SHIFT | FL_Left); + m_otherShortcuts[index]->callback(otherEventHandler, Ion::Events::ShiftRight.id()); + m_otherShortcuts[index++]->shortcut(FL_SHIFT | FL_Right); + m_otherShortcuts[index]->callback(otherEventHandler, Ion::Events::Cut.id()); + m_otherShortcuts[index++]->shortcut(FL_META | 'x'); + m_otherShortcuts[index]->callback(otherEventHandler, Ion::Events::Copy.id()); + m_otherShortcuts[index++]->shortcut(FL_META | 'c'); + m_otherShortcuts[index]->callback(otherEventHandler, Ion::Events::Paste.id()); + m_otherShortcuts[index++]->shortcut(FL_META | 'v'); + m_otherShortcuts[index]->callback(otherEventHandler, Ion::Events::Clear.id()); + m_otherShortcuts[index++]->shortcut(FL_META | FL_BackSpace); + // TODO not working +#if 0 + m_otherShortcuts[index]->callback(otherEventHandler, Ion::Events::LeftBracket.id()); + m_otherShortcuts[index++]->shortcut(FL_SHIFT | FL_ALT | '('); + m_otherShortcuts[index]->callback(otherEventHandler, Ion::Events::RightBracket.id()); + m_otherShortcuts[index++]->shortcut(FL_SHIFT | FL_ALT | ')'); + m_otherShortcuts[index]->callback(otherEventHandler, Ion::Events::LeftBrace.id()); + m_otherShortcuts[index++]->shortcut(FL_ALT | '('); + m_otherShortcuts[index]->callback(otherEventHandler, Ion::Events::RightBrace.id()); +#endif + m_otherShortcuts[index++]->shortcut(FL_ALT | ')'); + m_otherShortcuts[index]->callback(otherEventHandler, Ion::Events::Underscore.id()); + m_otherShortcuts[index++]->shortcut('_'); + m_otherShortcuts[index]->callback(otherEventHandler, Ion::Events::Equal.id()); + m_otherShortcuts[index++]->shortcut('='); + m_otherShortcuts[index]->callback(otherEventHandler, Ion::Events::Lower.id()); + m_otherShortcuts[index++]->shortcut('<'); + m_otherShortcuts[index]->callback(otherEventHandler, Ion::Events::Greater.id()); + m_otherShortcuts[index++]->shortcut('>'); + m_otherShortcuts[index]->callback(otherEventHandler, Ion::Events::Colon.id()); + m_otherShortcuts[index++]->shortcut(':'); + m_otherShortcuts[index]->callback(otherEventHandler, Ion::Events::SemiColon.id()); + m_otherShortcuts[index++]->shortcut(';'); + m_otherShortcuts[index]->callback(otherEventHandler, Ion::Events::DoubleQuotes.id()); + m_otherShortcuts[index++]->shortcut('"'); + m_otherShortcuts[index]->callback(otherEventHandler, Ion::Events::Question.id()); + m_otherShortcuts[index++]->shortcut('?'); + m_otherShortcuts[index]->callback(otherEventHandler, Ion::Events::Exclamation.id()); + m_otherShortcuts[index++]->shortcut('!'); + assert(index == sNumberOfOtherShortcuts); + for (int k = 0; k < sNumberOfOtherShortcuts; k++) { + m_otherShortcuts[k]->clear_visible_focus(); + } } bool FltkKbd::key_down(Ion::Keyboard::Key key) { + assert((int)key >= 0 && (int)key < Ion::Keyboard::NumberOfKeys); return m_buttons[(int)key]->value(); } diff --git a/ion/src/simulator/keyboard/fltkkbd.h b/ion/src/simulator/keyboard/fltkkbd.h index 3ff1673f4..482896b1e 100644 --- a/ion/src/simulator/keyboard/fltkkbd.h +++ b/ion/src/simulator/keyboard/fltkkbd.h @@ -7,10 +7,17 @@ class FltkKbd : public Fl_Group { public: + constexpr static int sNumberOfLettersInAlphabet = 26; + constexpr static int sNumberOfOtherShortcuts = 19; // TODO 3 are not working, when fixed put 22 FltkKbd(int x, int y, int w, int h); bool key_down(Ion::Keyboard::Key key); private: + void initButtons(int x, int y, int w, int h); + void initAlphabetShortcuts(); + void initOtherShortcuts(); Fl_Button * m_buttons[Ion::Keyboard::NumberOfKeys]; + Fl_Button * m_alphabetShortcuts[2 * sNumberOfLettersInAlphabet]; + Fl_Button * m_otherShortcuts[sNumberOfOtherShortcuts]; }; #endif diff --git a/kandinsky/Makefile b/kandinsky/Makefile index 5c23b1b64..9705de6fe 100644 --- a/kandinsky/Makefile +++ b/kandinsky/Makefile @@ -1,19 +1,23 @@ SFLAGS += -Ikandinsky/include -objs += $(addprefix kandinsky/src/,\ - color.o\ - context.o\ - context_line.o\ - context_pixel.o\ - context_rect.o\ - context_text.o\ - font.o\ - font_large.o\ - font_small.o\ - framebuffer.o\ - framebuffer_context.o\ - ion_context.o\ - point.o\ - rect.o\ + +src += $(addprefix kandinsky/src/,\ + color.cpp \ + context.cpp \ + context_line.cpp \ + context_pixel.cpp \ + context_rect.cpp \ + context_text.cpp \ + font.cpp \ + framebuffer.cpp \ + framebuffer_context.cpp \ + ion_context.cpp \ + point.cpp \ + rect.cpp \ +) + +src += $(addprefix kandinsky/fonts/, \ + LargeSourcePixel.ttf \ + SmallSourcePixel.ttf \ ) tests += $(addprefix kandinsky/test/,\ @@ -21,14 +25,6 @@ tests += $(addprefix kandinsky/test/,\ rect.cpp\ ) -FREETYPE_PATH := /usr/local/Cellar/freetype/2.6.3 -# LIBPNG_PATH is optional. If LIBPNG_PATH is not defined, rasterizer will be -# built w/o PNG support and simply won't output an image of the rasterization -#LIBPNG_PATH := /usr/local/Cellar/libpng/1.6.21 - -small_font_files = $(addprefix kandinsky/src/, font_small.cpp) -large_font_files = $(addprefix kandinsky/src/, font_large.cpp) - RASTERIZER_CFLAGS := -std=c99 `pkg-config freetype2 --cflags` RASTERIZER_LDFLAGS := `pkg-config freetype2 --libs` @@ -38,20 +34,25 @@ ifdef LIBPNG_PATH RASTERIZER_CFLAGS += -I$(LIBPNG_PATH)/include -DGENERATE_PNG=1 -L$(LIBPNG_PATH)/lib -lpng endif -# Even though raster will generate both .c and .h files, we don't declare it as -# such to make. If we did, "make -jN" with N>1 may call "raster" twice. +$(eval $(call rule_for, \ + HOSTCC, \ + kandinsky/fonts/rasterizer, \ + kandinsky/fonts/rasterizer.c kandinsky/fonts/unicode_for_symbol.c $(addprefix ion/src/external/lz4/, lz4.c lz4hc.c), \ + $$(HOSTCC) $$(RASTERIZER_CFLAGS) $$^ $$(RASTERIZER_LDFLAGS) -o $$@ \ +)) -kandinsky/src/font_small.cpp: kandinsky/fonts/rasterizer - @echo "RASTER $(small_font_files)" - $(Q) $< kandinsky/fonts/SmallSourcePixel.ttf 12 12 SmallFont $(small_font_files) +RASTERIZER := $(BUILD_DIR)/kandinsky/fonts/rasterizer -kandinsky/src/font_large.cpp: kandinsky/fonts/rasterizer - @echo "RASTER $(large_font_files)" - $(Q) $< kandinsky/fonts/LargeSourcePixel.ttf 16 16 LargeFont $(large_font_files) - -kandinsky/fonts/rasterizer: kandinsky/fonts/rasterizer.c kandinsky/fonts/unicode_for_symbol.c $(addprefix ion/src/external/lz4/, lz4.c lz4hc.c) - @echo "HOSTCC $@" - $(Q) $(HOSTCC) $(RASTERIZER_CFLAGS) $^ $(RASTERIZER_LDFLAGS) -o $@ - -products += $(small_font_files) $(large_font_files) kandinsky/fonts/rasterizer +$(eval $(call rule_for, \ + RASTER, \ + kandinsky/fonts/SmallSourcePixel.cpp, \ + kandinsky/fonts/SmallSourcePixel.ttf $$(RASTERIZER), \ + $$(RASTERIZER) $$< 12 12 SmallFont $$@ \ +)) +$(eval $(call rule_for, \ + RASTER, \ + kandinsky/fonts/LargeSourcePixel.cpp, \ + kandinsky/fonts/LargeSourcePixel.ttf $$(RASTERIZER), \ + $$(RASTERIZER) $$< 16 16 LargeFont $$@ \ +)) diff --git a/liba/Makefile b/liba/Makefile index da54983c1..96bf3f3bc 100644 --- a/liba/Makefile +++ b/liba/Makefile @@ -1,126 +1,125 @@ SFLAGS += -Iliba/include -liba/src/external/sqlite/mem5.o: CFLAGS += -w - -objs += $(addprefix liba/src/, \ - armv7m/setjmp.o \ - armv7m/longjmp.o \ - assert.o \ - bzero.o \ - ctype.o \ - errno.o \ - fpclassify.o \ - fpclassifyf.o \ - ieee754.o \ - malloc.o \ - memcmp.o \ - memcpy.o \ - memmove.o \ - memset.o \ - nearbyint.o \ - nearbyintf.o \ - strcmp.o \ - strchr.o \ - strlcpy.o \ - strlen.o \ - external/sqlite/mem5.o \ +src += $(addprefix liba/src/, \ + armv7m/setjmp.s \ + armv7m/longjmp.s \ + assert.c \ + bzero.c \ + ctype.c \ + errno.c \ + fpclassify.c \ + fpclassifyf.c \ + ieee754.c \ + malloc.c \ + memcmp.c \ + memcpy.c \ + memmove.c \ + memset.c \ + nearbyint.c \ + nearbyintf.c \ + strcmp.c \ + strchr.c \ + strlcpy.c \ + strlen.c \ + external/sqlite/mem5.c \ ) -objs += $(addprefix liba/src/external/openbsd/, \ - b_exp__D.o \ - b_log__D.o \ - b_tgamma.o \ - e_acosf.o \ - e_acoshf.o \ - e_asinf.o \ - e_atanhf.o \ - e_atan2f.o \ - e_coshf.o \ - e_expf.o \ - e_fmodf.o \ - e_hypotf.o \ - e_lgammaf_r.o \ - e_log10f.o \ - e_log2.o \ - e_logf.o \ - e_powf.o \ - e_rem_pio2f.o \ - e_scalb.o \ - e_sinhf.o \ - e_sqrtf.o \ - k_cosf.o \ - k_rem_pio2f.o \ - k_sinf.o \ - k_tanf.o \ +src += $(addprefix liba/src/external/openbsd/, \ + b_exp__D.c \ + b_log__D.c \ + b_tgamma.c \ + e_acosf.c \ + e_acoshf.c \ + e_asinf.c \ + e_atanhf.c \ + e_atan2f.c \ + e_coshf.c \ + e_expf.c \ + e_fmodf.c \ + e_hypotf.c \ + e_lgammaf_r.c \ + e_log10f.c \ + e_log2.c \ + e_logf.c \ + e_powf.c \ + e_rem_pio2f.c \ + e_scalb.c \ + e_sinhf.c \ + e_sqrtf.c \ + k_cosf.c \ + k_rem_pio2f.c \ + k_sinf.c \ + k_tanf.c \ s_asinhf.o\ - s_atanf.o \ - s_ceilf.o \ - s_copysignf.o \ - s_cosf.o \ - s_erf.o \ + s_atanf.c \ + s_ceilf.c \ + s_copysignf.c \ + s_cosf.c \ + s_erf.c \ s_expm1f.o\ - s_fabsf.o \ - s_fmaxf.o \ - s_floorf.o \ - s_frexpf.o \ - s_frexp.o \ - s_log1pf.o \ - s_logb.o \ - s_modf.o \ - s_modff.o \ - s_rint.o \ - s_roundf.o \ - s_scalbnf.o \ - s_signgam.o \ - s_sinf.o \ - s_tanf.o \ - s_tanhf.o \ - s_trunc.o \ - s_truncf.o \ - w_lgammaf.o \ + s_fabsf.c \ + s_fmaxf.c \ + s_floorf.c \ + s_frexpf.c \ + s_frexp.c \ + s_log1pf.c \ + s_logb.c \ + s_modf.c \ + s_modff.c \ + s_rint.c \ + s_roundf.c \ + s_scalbnf.c \ + s_signgam.c \ + s_sinf.c \ + s_tanf.c \ + s_tanhf.c \ + s_trunc.c \ + s_truncf.c \ + w_lgammaf.c \ ) -objs += $(addprefix liba/src/external/openbsd/, \ - e_acos.o \ - e_acosh.o \ - e_asin.o \ - e_atanh.o \ - e_atan2.o \ - e_cosh.o \ - e_exp.o \ - e_fmod.o \ - e_hypot.o \ - e_lgamma_r.o \ - e_log.o \ - e_log10.o \ - e_pow.o \ - e_rem_pio2.o \ - e_sinh.o \ - e_sqrt.o \ - k_cos.o \ - k_rem_pio2.o \ - k_sin.o \ - k_tan.o \ - s_asinh.o \ - s_atan.o \ - s_ceil.o \ - s_copysign.o \ - s_cos.o \ - s_expm1.o \ - s_fabs.o \ - s_fmax.o \ - s_floor.o \ - s_log1p.o \ - s_round.o \ - s_scalbn.o \ - s_sin.o \ - s_tan.o \ - s_tanh.o \ - w_lgamma.o \ +src += $(addprefix liba/src/external/openbsd/, \ + e_acos.c \ + e_acosh.c \ + e_asin.c \ + e_atanh.c \ + e_atan2.c \ + e_cosh.c \ + e_exp.c \ + e_fmod.c \ + e_hypot.c \ + e_lgamma_r.c \ + e_log.c \ + e_log10.c \ + e_pow.c \ + e_rem_pio2.c \ + e_sinh.c \ + e_sqrt.c \ + k_cos.c \ + k_rem_pio2.c \ + k_sin.c \ + k_tan.c \ + s_asinh.c \ + s_atan.c \ + s_ceil.c \ + s_copysign.c \ + s_cos.c \ + s_expm1.c \ + s_fabs.c \ + s_fmax.c \ + s_floor.c \ + s_log1p.c \ + s_round.c \ + s_scalbn.c \ + s_sin.c \ + s_tan.c \ + s_tanh.c \ + w_lgamma.c \ ) -liba/src/external/openbsd/%.o: SFLAGS := -Iliba/src/external/openbsd/include $(SFLAGS) -liba/src/external/openbsd/%.o: CFLAGS += -w +$(call object_for,liba/src/external/sqlite/mem5.c): CFLAGS += -w +$(call object_for,liba/src/external/openbsd/%.c): SFLAGS := -Iliba/src/external/openbsd/include $(SFLAGS) +$(call object_for,liba/src/external/openbsd/%.c): CFLAGS += -w tests += $(addprefix liba/test/, \ aeabi.c \ @@ -136,6 +135,6 @@ tests += $(addprefix liba/test/, \ # The use of aeabi-rt could be made conditional to an AEABI target. # In practice we're always using liba on such a target. -objs += $(addprefix liba/src/aeabi-rt/, \ - atexit.o \ +src += $(addprefix liba/src/aeabi-rt/, \ + atexit.c \ ) diff --git a/liba/Makefile.bridge b/liba/Makefile.bridge index f9157402a..70e190f58 100644 --- a/liba/Makefile.bridge +++ b/liba/Makefile.bridge @@ -1,3 +1,3 @@ SFLAGS += -Iliba/include/bridge -objs += liba/src/bridge.o +src += liba/src/bridge.c diff --git a/libaxx/Makefile b/libaxx/Makefile index f2e6702f5..fd64e42ea 100644 --- a/libaxx/Makefile +++ b/libaxx/Makefile @@ -1,6 +1,5 @@ SFLAGS += -Ilibaxx/include -objs += $(addprefix libaxx/src/, new.o) -objs += $(addprefix libaxx/src/cxxabi/, atexit.o pure_virtual.o) -objs += $(addprefix libaxx/include/external/libcxx/, complex.o) - +src += $(addprefix libaxx/src/, new.cpp) +src += $(addprefix libaxx/src/cxxabi/, atexit.cpp pure_virtual.cpp) +src += $(addprefix libaxx/include/external/libcxx/, complex.cpp) diff --git a/poincare/Makefile b/poincare/Makefile index 4f5baca15..4a41680f9 100644 --- a/poincare/Makefile +++ b/poincare/Makefile @@ -1,137 +1,138 @@ SFLAGS += -Ipoincare/include -#include poincare/src/simplify/Makefile -#include poincare/src/simplification/Makefile -objs += $(addprefix poincare/src/,\ - binomial_coefficient_layout.o\ - bracket_layout.o\ - bracket_pair_layout.o\ - ceiling_layout.o\ - char_layout.o\ - condensed_sum_layout.o\ - conjugate_layout.o\ - empty_layout.o\ - fraction_layout.o\ - grid_layout.o\ - horizontal_layout.o\ - integral_layout.o\ - layout_cursor.o\ - layout.o\ - layout_node.o\ - left_parenthesis_layout.o\ - left_square_bracket_layout.o\ - matrix_layout.o\ - nth_root_layout.o\ - parenthesis_layout.o\ - product_layout.o\ - right_parenthesis_layout.o\ - right_square_bracket_layout.o\ - sequence_layout.o\ - sum_layout.o\ - vertical_offset_layout.o\ +src += $(addprefix poincare/src/,\ + binomial_coefficient_layout.cpp \ + bracket_layout.cpp \ + bracket_pair_layout.cpp \ + char_layout.cpp \ + condensed_sum_layout.cpp \ + conjugate_layout.cpp \ + empty_layout.cpp \ + fraction_layout.cpp \ + grid_layout.cpp \ + horizontal_layout.cpp \ + integral_layout.cpp \ + layout_cursor.cpp \ + layout.cpp \ + layout_node.cpp \ + left_parenthesis_layout.cpp \ + left_square_bracket_layout.cpp \ + matrix_layout.cpp \ + nth_root_layout.cpp \ + parenthesis_layout.cpp \ + product_layout.cpp \ + right_parenthesis_layout.cpp \ + right_square_bracket_layout.cpp \ + sequence_layout.cpp \ + sum_layout.cpp \ + vertical_offset_layout.cpp \ ) -objs += $(addprefix poincare/src/,\ - init.o\ - exception_checkpoint.o\ - helpers.o\ +src += $(addprefix poincare/src/,\ + init.cpp \ + exception_checkpoint.cpp \ + helpers.cpp \ ) -objs += $(addprefix poincare/src/,\ - absolute_value.o\ - addition.o\ - approximation_helper.o\ - arc_cosine.o\ - arc_sine.o\ - arc_tangent.o\ - arithmetic.o\ - binomial_coefficient.o\ - ceiling.o\ - complex.o\ - complex_argument.o\ - confidence_interval.o\ - conjugate.o\ - constant.o\ - cosine.o\ - decimal.o\ - derivative.o\ - determinant.o\ - division.o\ - division_quotient.o\ - division_remainder.o\ - empty_expression.o\ - equal.o\ - evaluation.o\ - expression.o\ - expression_node.o\ - factor.o\ - factorial.o\ - float.o\ - floor.o\ - frac_part.o\ - function.o\ - great_common_divisor.o\ - hyperbolic_arc_cosine.o\ - hyperbolic_arc_sine.o\ - hyperbolic_arc_tangent.o\ - hyperbolic_cosine.o\ - hyperbolic_sine.o\ - hyperbolic_tangent.o\ - hyperbolic_trigonometric_function.o\ - imaginary_part.o\ - infinity.o\ - integer.o\ - integral.o\ - layout_helper.o\ - least_common_multiple.o\ - logarithm.o\ - matrix.o\ - matrix_complex.o\ - matrix_dimension.o\ - matrix_inverse.o\ - matrix_trace.o\ - matrix_transpose.o\ - multiplication.o\ - n_ary_expression_node.o\ - naperian_logarithm.o\ - nth_root.o\ - number.o\ - opposite.o\ - parametered_expression_helper.o\ - parenthesis.o\ - permute_coefficient.o\ - power.o\ - prediction_interval.o\ - preferences.o\ - print_float.o\ - product.o\ - randint.o\ - random.o\ - rational.o\ - real_part.o\ - round.o\ - sequence.o\ - serialization_helper.o\ - simplification_helper.o\ - sine.o\ - square_root.o\ - store.o\ - subtraction.o\ - sum.o\ - symbol.o\ - symbol_abstract.o\ - tangent.o\ - tree_handle.o\ - tree_node.o\ - tree_pool.o\ - trigonometry.o\ - undefined.o\ - variable_context.o\ +src += $(addprefix poincare/src/,\ + absolute_value.cpp \ + addition.cpp \ + approximation_helper.cpp \ + arc_cosine.cpp \ + arc_sine.cpp \ + arc_tangent.cpp \ + arithmetic.cpp \ + binomial_coefficient.cpp \ + ceiling.cpp \ + complex.cpp \ + complex_argument.cpp \ + complex_cartesian.cpp \ + confidence_interval.cpp \ + conjugate.cpp \ + constant.cpp \ + cosine.cpp \ + decimal.cpp \ + derivative.cpp \ + determinant.cpp \ + division.cpp \ + division_quotient.cpp \ + division_remainder.cpp \ + empty_expression.cpp \ + equal.cpp \ + evaluation.cpp \ + expression.cpp \ + expression_node.cpp \ + factor.cpp \ + factorial.cpp \ + float.cpp \ + floor.cpp \ + frac_part.cpp \ + function.cpp \ + great_common_divisor.cpp \ + hyperbolic_arc_cosine.cpp \ + hyperbolic_arc_sine.cpp \ + hyperbolic_arc_tangent.cpp \ + hyperbolic_cosine.cpp \ + hyperbolic_sine.cpp \ + hyperbolic_tangent.cpp \ + hyperbolic_trigonometric_function.cpp \ + imaginary_part.cpp \ + infinity.cpp \ + integer.cpp \ + integral.cpp \ + layout_helper.cpp \ + least_common_multiple.cpp \ + logarithm.cpp \ + matrix.cpp \ + matrix_complex.cpp \ + matrix_dimension.cpp \ + matrix_inverse.cpp \ + matrix_trace.cpp \ + matrix_transpose.cpp \ + multiplication.cpp \ + n_ary_expression_node.cpp \ + naperian_logarithm.cpp \ + nth_root.cpp \ + number.cpp \ + opposite.cpp \ + parametered_expression_helper.cpp \ + parenthesis.cpp \ + permute_coefficient.cpp \ + power.cpp \ + prediction_interval.cpp \ + preferences.cpp \ + print_float.cpp \ + product.cpp \ + randint.cpp \ + random.cpp \ + rational.cpp \ + real_part.cpp \ + round.cpp \ + sequence.cpp \ + serialization_helper.cpp \ + sign_function.cpp \ + simplification_helper.cpp \ + sine.cpp \ + square_root.cpp \ + store.cpp \ + subtraction.cpp \ + sum.cpp \ + symbol.cpp \ + symbol_abstract.cpp \ + tangent.cpp \ + tree_handle.cpp \ + tree_node.cpp \ + tree_pool.cpp \ + trigonometry.cpp \ + trigonometry_cheat_table.cpp \ + undefined.cpp \ + unreal.cpp \ + variable_context.cpp \ ) -objs += $(addprefix poincare/src/parsing/,\ - parser.o\ - tokenizer.o\ +src += $(addprefix poincare/src/parsing/,\ + parser.cpp \ + tokenizer.cpp \ ) tests += $(addprefix poincare/test/,\ @@ -140,11 +141,13 @@ tests += $(addprefix poincare/test/,\ addition.cpp\ arithmetic.cpp\ binomial_coefficient_layout.cpp\ + complex.cpp\ complex_to_expression.cpp\ convert_expression_to_text.cpp\ decimal.cpp\ division.cpp\ expression.cpp\ + expression_order.cpp\ factorial.cpp\ float.cpp\ fraction_layout.cpp\ @@ -164,7 +167,7 @@ tests += $(addprefix poincare/test/,\ power.cpp\ properties.cpp\ rational.cpp\ - simplify_mix.cpp\ + simplify.cpp\ store.cpp\ subtraction.cpp\ symbol.cpp\ @@ -180,7 +183,7 @@ test_objs += $(addprefix apps/shared/, global_context.o) ifdef POINCARE_TESTS_PRINT_EXPRESSIONS tests += poincare/src/expression_debug.o -objs += poincare/src/expression_debug.o +src += poincare/src/expression_debug.cpp SFLAGS += -DPOINCARE_TESTS_PRINT_EXPRESSIONS=1 endif diff --git a/poincare/include/poincare/absolute_value.h b/poincare/include/poincare/absolute_value.h index 3f8d5d546..07c5f1bbf 100644 --- a/poincare/include/poincare/absolute_value.h +++ b/poincare/include/poincare/absolute_value.h @@ -2,6 +2,7 @@ #define POINCARE_ABSOLUTE_VALUE_H #include +#include #include namespace Poincare { @@ -19,18 +20,21 @@ public: // Properties Type type() const override { return Type::AbsoluteValue; } - Sign sign() const override { return Sign::Positive; } - Expression setSign(Sign s, Context & context, Preferences::AngleUnit angleUnit) override; + Sign sign(Context * context) const override { return Sign::Positive; } + Expression setSign(Sign s, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) override; + + // Complex + bool isReal(Context & context) const override { return true; } // Approximation - template static Complex computeOnComplex(const std::complex c, Preferences::AngleUnit angleUnit) { - return Complex(std::abs(c)); + template static Complex computeOnComplex(const std::complex c, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) { + return Complex::Builder(std::abs(c)); } - Evaluation approximate(SinglePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { - return ApproximationHelper::Map(this, context, angleUnit, computeOnComplex); + Evaluation approximate(SinglePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { + return ApproximationHelper::Map(this, context, complexFormat, angleUnit, computeOnComplex); } - Evaluation approximate(DoublePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { - return ApproximationHelper::Map(this, context, angleUnit, computeOnComplex); + Evaluation approximate(DoublePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { + return ApproximationHelper::Map(this, context, complexFormat, angleUnit, computeOnComplex); } // Layout @@ -38,23 +42,20 @@ public: int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; // Simplification - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) override; + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) override; }; class AbsoluteValue final : public Expression { friend class AbsoluteValueNode; public: AbsoluteValue(const AbsoluteValueNode * n) : Expression(n) {} - static AbsoluteValue Builder(Expression child) { return AbsoluteValue(child); } - static Expression UntypedBuilder(Expression children) { return Builder(children.childAtIndex(0)); } - static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("abs", 1, &UntypedBuilder); + static AbsoluteValue Builder(Expression child) { return TreeHandle::FixedArityBuilder(&child, 1); } - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit); + static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("abs", 1, &UntypedBuilderOneChild); + + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); private: - explicit AbsoluteValue(Expression child) : Expression(TreePool::sharedPool()->createTreeNode()) { - replaceChildAtIndexInPlace(0, child); - } - Expression setSign(ExpressionNode::Sign s, Context & context, Preferences::AngleUnit angleUnit); + Expression setSign(ExpressionNode::Sign s, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit); }; } diff --git a/poincare/include/poincare/absolute_value_layout.h b/poincare/include/poincare/absolute_value_layout.h index 098b7f3ee..5a909f36d 100644 --- a/poincare/include/poincare/absolute_value_layout.h +++ b/poincare/include/poincare/absolute_value_layout.h @@ -33,11 +33,8 @@ private: class AbsoluteValueLayout final : public Layout { public: - explicit AbsoluteValueLayout(Layout l) : - Layout(TreePool::sharedPool()->createTreeNode()) - { - replaceChildAtIndexInPlace(0, l); - } + static AbsoluteValueLayout Builder(Layout child) { return TreeHandle::FixedArityBuilder(&child, 1); } + AbsoluteValueLayout() = delete; }; } diff --git a/poincare/include/poincare/addition.h b/poincare/include/poincare/addition.h index ca822b7a5..6a0c3d44b 100644 --- a/poincare/include/poincare/addition.h +++ b/poincare/include/poincare/addition.h @@ -28,12 +28,12 @@ public: int getPolynomialCoefficients(Context & context, const char * symbolName, Expression coefficients[]) const override; // Evaluation - template static Complex compute(const std::complex c, const std::complex d) { return Complex(c+d); } - template static MatrixComplex computeOnMatrices(const MatrixComplex m, const MatrixComplex n) { - return ApproximationHelper::ElementWiseOnComplexMatrices(m, n, compute); + template static Complex compute(const std::complex c, const std::complex d, Preferences::ComplexFormat complexFormat) { return Complex::Builder(c+d); } + template static MatrixComplex computeOnMatrices(const MatrixComplex m, const MatrixComplex n, Preferences::ComplexFormat complexFormat) { + return ApproximationHelper::ElementWiseOnComplexMatrices(m, n, complexFormat, compute); } - template static MatrixComplex computeOnComplexAndMatrix(const std::complex c, const MatrixComplex m) { - return ApproximationHelper::ElementWiseOnMatrixComplexAndComplex(m, c, compute); + template static MatrixComplex computeOnComplexAndMatrix(const std::complex c, const MatrixComplex m, Preferences::ComplexFormat complexFormat) { + return ApproximationHelper::ElementWiseOnMatrixComplexAndComplex(m, c, complexFormat, compute); } private: // Layout @@ -42,40 +42,31 @@ private: int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; // Simplification - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) override; - Expression shallowBeautify(Context & context, Preferences::AngleUnit angleUnit) override; + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) override; + Expression shallowBeautify(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) override; /* Evaluation */ - template static MatrixComplex computeOnMatrixAndComplex(const MatrixComplex m, const std::complex c) { - return ApproximationHelper::ElementWiseOnMatrixComplexAndComplex(m, c, compute); + template static MatrixComplex computeOnMatrixAndComplex(const MatrixComplex m, const std::complex c, Preferences::ComplexFormat complexFormat) { + return ApproximationHelper::ElementWiseOnMatrixComplexAndComplex(m, c, complexFormat, compute); } - Evaluation approximate(SinglePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { - return ApproximationHelper::MapReduce(this, context, angleUnit, compute, computeOnComplexAndMatrix, computeOnMatrixAndComplex, computeOnMatrices); + Evaluation approximate(SinglePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { + return ApproximationHelper::MapReduce(this, context, complexFormat, angleUnit, compute, computeOnComplexAndMatrix, computeOnMatrixAndComplex, computeOnMatrices); } - Evaluation approximate(DoublePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { - return ApproximationHelper::MapReduce(this, context, angleUnit, compute, computeOnComplexAndMatrix, computeOnMatrixAndComplex, computeOnMatrices); + Evaluation approximate(DoublePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { + return ApproximationHelper::MapReduce(this, context, complexFormat, angleUnit, compute, computeOnComplexAndMatrix, computeOnMatrixAndComplex, computeOnMatrices); } }; class Addition final : public NAryExpression { public: Addition(const AdditionNode * n) : NAryExpression(n) {} - Addition(); - explicit Addition(Expression e1) : Addition() { - addChildAtIndexInPlace(e1, 0, 0); - } - Addition(Expression e1, Expression e2) : Addition() { - addChildAtIndexInPlace(e2, 0, 0); - addChildAtIndexInPlace(e1, 0, numberOfChildren()); - } - Addition(Expression * children, size_t numberOfChildren) : Addition() { - for (size_t i = 0; i < numberOfChildren; i++) { - addChildAtIndexInPlace(children[i], i, i); - } - } + static Addition Builder() { return TreeHandle::NAryBuilder(); } + static Addition Builder(Expression e1) { return Addition::Builder(&e1, 1); } + static Addition Builder(Expression e1, Expression e2) { return Addition::Builder(ArrayBuilder(e1, e2).array(), 2); } + static Addition Builder(Expression * children, size_t numberOfChildren) { return TreeHandle::NAryBuilder(children, numberOfChildren); } // Expression - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); - Expression shallowBeautify(Context & context, Preferences::AngleUnit angleUnit); + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); + Expression shallowBeautify(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); int getPolynomialCoefficients(Context & context, const char * symbolName, Expression coefficients[]) const; private: static const Number NumeralFactor(const Expression & e); @@ -83,8 +74,8 @@ private: static inline const Expression FirstNonNumeralFactor(const Expression & e); static bool TermsHaveIdenticalNonNumeralFactors(const Expression & e1, const Expression & e2); - Expression factorizeOnCommonDenominator(Context & context, Preferences::AngleUnit angleUnit); - void factorizeChildrenAtIndexesInPlace(int index1, int index2, Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); + Expression factorizeOnCommonDenominator(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit); + void factorizeChildrenAtIndexesInPlace(int index1, int index2, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); AdditionNode * node() const { return static_cast(Expression::node()); } }; diff --git a/poincare/include/poincare/approximation_helper.h b/poincare/include/poincare/approximation_helper.h index 59d22f3fd..7160bca38 100644 --- a/poincare/include/poincare/approximation_helper.h +++ b/poincare/include/poincare/approximation_helper.h @@ -11,17 +11,17 @@ namespace Poincare { namespace ApproximationHelper { template std::complex TruncateRealOrImaginaryPartAccordingToArgument(std::complex c); - template using ComplexCompute = Complex(*)(const std::complex, Preferences::AngleUnit angleUnit); - template Evaluation Map(const ExpressionNode * expression, Context& context, Preferences::AngleUnit angleUnit, ComplexCompute compute); + template using ComplexCompute = Complex(*)(const std::complex, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit); + template Evaluation Map(const ExpressionNode * expression, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ComplexCompute compute); - template using ComplexAndComplexReduction = Complex(*)(const std::complex, const std::complex); - template using ComplexAndMatrixReduction = MatrixComplex(*)(const std::complex c, const MatrixComplex m); - template using MatrixAndComplexReduction = MatrixComplex(*)(const MatrixComplex m, const std::complex c); - template using MatrixAndMatrixReduction = MatrixComplex(*)(const MatrixComplex m, const MatrixComplex n); - template Evaluation MapReduce(const ExpressionNode * expression, Context& context, Preferences::AngleUnit angleUnit, ComplexAndComplexReduction computeOnComplexes, ComplexAndMatrixReduction computeOnComplexAndMatrix, MatrixAndComplexReduction computeOnMatrixAndComplex, MatrixAndMatrixReduction computeOnMatrices); + template using ComplexAndComplexReduction = Complex(*)(const std::complex, const std::complex, Preferences::ComplexFormat complexFormat); + template using ComplexAndMatrixReduction = MatrixComplex(*)(const std::complex c, const MatrixComplex m, Preferences::ComplexFormat complexFormat); + template using MatrixAndComplexReduction = MatrixComplex(*)(const MatrixComplex m, const std::complex c, Preferences::ComplexFormat complexFormat); + template using MatrixAndMatrixReduction = MatrixComplex(*)(const MatrixComplex m, const MatrixComplex n, Preferences::ComplexFormat complexFormat); + template Evaluation MapReduce(const ExpressionNode * expression, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ComplexAndComplexReduction computeOnComplexes, ComplexAndMatrixReduction computeOnComplexAndMatrix, MatrixAndComplexReduction computeOnMatrixAndComplex, MatrixAndMatrixReduction computeOnMatrices); - template MatrixComplex ElementWiseOnMatrixComplexAndComplex(const MatrixComplex n, std::complex c, ComplexAndComplexReduction computeOnComplexes); - template MatrixComplex ElementWiseOnComplexMatrices(const MatrixComplex m, const MatrixComplex n, ComplexAndComplexReduction computeOnComplexes); + template MatrixComplex ElementWiseOnMatrixComplexAndComplex(const MatrixComplex n, std::complex c, Preferences::ComplexFormat complexFormat, ComplexAndComplexReduction computeOnComplexes); + template MatrixComplex ElementWiseOnComplexMatrices(const MatrixComplex m, const MatrixComplex n, Preferences::ComplexFormat complexFormat, ComplexAndComplexReduction computeOnComplexes); }; } diff --git a/poincare/include/poincare/arc_cosine.h b/poincare/include/poincare/arc_cosine.h index ac84719cb..89a976098 100644 --- a/poincare/include/poincare/arc_cosine.h +++ b/poincare/include/poincare/arc_cosine.h @@ -27,29 +27,25 @@ private: int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; // Simplification - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) override; + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) override; //Evaluation - template static Complex computeOnComplex(const std::complex c, Preferences::AngleUnit angleUnit); - Evaluation approximate(SinglePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { - return ApproximationHelper::Map(this, context, angleUnit,computeOnComplex); + template static Complex computeOnComplex(const std::complex c, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit); + Evaluation approximate(SinglePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { + return ApproximationHelper::Map(this, context, complexFormat, angleUnit,computeOnComplex); } - Evaluation approximate(DoublePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { - return ApproximationHelper::Map(this, context, angleUnit, computeOnComplex); + Evaluation approximate(DoublePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { + return ApproximationHelper::Map(this, context, complexFormat, angleUnit, computeOnComplex); } }; class ArcCosine final : public Expression { public: ArcCosine(const ArcCosineNode * n) : Expression(n) {} - static ArcCosine Builder(Expression child) { return ArcCosine(child); } - static Expression UntypedBuilder(Expression children) { return Builder(children.childAtIndex(0)); } - static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("acos", 1, &UntypedBuilder); + static ArcCosine Builder(Expression child) { return TreeHandle::FixedArityBuilder(&child, 1); } - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); -private: - explicit ArcCosine(Expression child) : Expression(TreePool::sharedPool()->createTreeNode()) { - replaceChildAtIndexInPlace(0, child); - } + static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("acos", 1, &UntypedBuilderOneChild); + + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); }; diff --git a/poincare/include/poincare/arc_sine.h b/poincare/include/poincare/arc_sine.h index 1a0928e86..2cdf8626f 100644 --- a/poincare/include/poincare/arc_sine.h +++ b/poincare/include/poincare/arc_sine.h @@ -26,29 +26,25 @@ private: Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; // Simplification - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) override; + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) override; //Evaluation - template static Complex computeOnComplex(const std::complex c, Preferences::AngleUnit angleUnit); - Evaluation approximate(SinglePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { - return ApproximationHelper::Map(this, context, angleUnit,computeOnComplex); + template static Complex computeOnComplex(const std::complex c, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit); + Evaluation approximate(SinglePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { + return ApproximationHelper::Map(this, context, complexFormat, angleUnit,computeOnComplex); } - Evaluation approximate(DoublePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { - return ApproximationHelper::Map(this, context, angleUnit, computeOnComplex); + Evaluation approximate(DoublePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { + return ApproximationHelper::Map(this, context, complexFormat, angleUnit, computeOnComplex); } }; class ArcSine final : public Expression { public: ArcSine(const ArcSineNode * n) : Expression(n) {} - static ArcSine Builder(Expression child) { return ArcSine(child); } - static Expression UntypedBuilder(Expression children) { return Builder(children.childAtIndex(0)); } - static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("asin", 1, &UntypedBuilder); + static ArcSine Builder(Expression child) { return TreeHandle::FixedArityBuilder(&child, 1); } - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); -private: - explicit ArcSine(Expression child) : Expression(TreePool::sharedPool()->createTreeNode()) { - replaceChildAtIndexInPlace(0, child); - } + static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("asin", 1, &UntypedBuilderOneChild); + + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); }; } diff --git a/poincare/include/poincare/arc_tangent.h b/poincare/include/poincare/arc_tangent.h index d6d67fdac..b61438e64 100644 --- a/poincare/include/poincare/arc_tangent.h +++ b/poincare/include/poincare/arc_tangent.h @@ -21,34 +21,34 @@ public: // Properties Type type() const override { return Type::ArcTangent; } + + // Complex + bool isReal(Context & context) const override { return childAtIndex(0)->isReal(context); } + private: // Layout Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; // Simplification - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) override; + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) override; //Evaluation - template static Complex computeOnComplex(const std::complex c, Preferences::AngleUnit angleUnit); - Evaluation approximate(SinglePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { - return ApproximationHelper::Map(this, context, angleUnit,computeOnComplex); + template static Complex computeOnComplex(const std::complex c, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit); + Evaluation approximate(SinglePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { + return ApproximationHelper::Map(this, context, complexFormat, angleUnit,computeOnComplex); } - Evaluation approximate(DoublePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { - return ApproximationHelper::Map(this, context, angleUnit, computeOnComplex); + Evaluation approximate(DoublePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { + return ApproximationHelper::Map(this, context, complexFormat, angleUnit, computeOnComplex); } }; class ArcTangent final : public Expression { public: ArcTangent(const ArcTangentNode * n) : Expression(n) {} - static ArcTangent Builder(Expression child) { return ArcTangent(child); } - static Expression UntypedBuilder(Expression children) { return Builder(children.childAtIndex(0)); } - static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("atan", 1, &UntypedBuilder); + static ArcTangent Builder(Expression child) { return TreeHandle::FixedArityBuilder(&child, 1); } - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); -private: - explicit ArcTangent(Expression child) : Expression(TreePool::sharedPool()->createTreeNode()) { - replaceChildAtIndexInPlace(0, child); - } + static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("atan", 1, &UntypedBuilderOneChild); + + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); }; } diff --git a/poincare/include/poincare/array_builder.h b/poincare/include/poincare/array_builder.h new file mode 100644 index 000000000..ed7b232f1 --- /dev/null +++ b/poincare/include/poincare/array_builder.h @@ -0,0 +1,20 @@ +#ifndef POINCARE_ARRAY_BUILDER_H +#define POINCARE_ARRAY_BUILDER_H + + +namespace Poincare { + +template +class ArrayBuilder { +public: + ArrayBuilder(T e1 = T(), T e2 = T(), T e3 = T(), T e4 = T()) : + m_data{e1, e2, e3, e4} + {} + T * array() { return const_cast(m_data); } +private: + T m_data[4]; +}; + +} + +#endif diff --git a/poincare/include/poincare/binomial_coefficient.h b/poincare/include/poincare/binomial_coefficient.h index 14af31fa6..6eeb90dd4 100644 --- a/poincare/include/poincare/binomial_coefficient.h +++ b/poincare/include/poincare/binomial_coefficient.h @@ -18,7 +18,8 @@ public: } #endif - // ExpressionNode + // Complex + bool isReal(Context & context) const override { return true; } // Properties Type type() const override{ return Type::BinomialCoefficient; } @@ -28,27 +29,22 @@ private: Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; // Simplification - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) override; + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) override; // Evaluation - Evaluation approximate(SinglePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } - Evaluation approximate(DoublePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } - template Complex templatedApproximate(Context& context, Preferences::AngleUnit angleUnit) const; + Evaluation approximate(SinglePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, complexFormat, angleUnit); } + Evaluation approximate(DoublePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, complexFormat, angleUnit); } + template Complex templatedApproximate(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; }; class BinomialCoefficient final : public Expression { public: BinomialCoefficient(const BinomialCoefficientNode * n) : Expression(n) {} - static BinomialCoefficient Builder(Expression child0, Expression child1) { return BinomialCoefficient(child0, child1); } - static Expression UntypedBuilder(Expression children) { return Builder(children.childAtIndex(0), children.childAtIndex(1)); } - static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("binomial", 2, &UntypedBuilder); + static BinomialCoefficient Builder(Expression child0, Expression child1) { return TreeHandle::FixedArityBuilder(ArrayBuilder(child0, child1).array(), 2); } + static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("binomial", 2, &UntypedBuilderTwoChildren); // Expression - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit); + Expression shallowReduce(); private: - BinomialCoefficient(Expression child0, Expression child1) : Expression(TreePool::sharedPool()->createTreeNode()) { - replaceChildAtIndexInPlace(0, child0); - replaceChildAtIndexInPlace(1, child1); - } constexpr static int k_maxNValue = 300; }; diff --git a/poincare/include/poincare/binomial_coefficient_layout.h b/poincare/include/poincare/binomial_coefficient_layout.h index 222d2e9b0..2187496ce 100644 --- a/poincare/include/poincare/binomial_coefficient_layout.h +++ b/poincare/include/poincare/binomial_coefficient_layout.h @@ -43,12 +43,8 @@ private: class BinomialCoefficientLayout final : public Layout { public: - BinomialCoefficientLayout(Layout n, Layout k) : - Layout(TreePool::sharedPool()->createTreeNode()) - { - replaceChildAtIndexInPlace(0, n); - replaceChildAtIndexInPlace(1, k); - } + static BinomialCoefficientLayout Builder(Layout child0, Layout child1) { return TreeHandle::FixedArityBuilder(ArrayBuilder(child0, child1).array(), 2); } + BinomialCoefficientLayout() = delete; }; } diff --git a/poincare/include/poincare/ceiling.h b/poincare/include/poincare/ceiling.h index 9604ec5d6..a0f9cb615 100644 --- a/poincare/include/poincare/ceiling.h +++ b/poincare/include/poincare/ceiling.h @@ -18,6 +18,9 @@ public: } #endif + // Complex + bool isReal(Context & context) const override { return true; } + // Properties Type type() const override { return Type::Ceiling; } private: @@ -25,29 +28,25 @@ private: Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; // Simplification - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) override; + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) override; // Evaluation - template static Complex computeOnComplex(const std::complex c, Preferences::AngleUnit angleUnit); - Evaluation approximate(SinglePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { - return ApproximationHelper::Map(this, context, angleUnit, computeOnComplex); + template static Complex computeOnComplex(const std::complex c, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit); + Evaluation approximate(SinglePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { + return ApproximationHelper::Map(this, context, complexFormat, angleUnit, computeOnComplex); } - Evaluation approximate(DoublePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { - return ApproximationHelper::Map(this, context, angleUnit, computeOnComplex); + Evaluation approximate(DoublePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { + return ApproximationHelper::Map(this, context, complexFormat, angleUnit, computeOnComplex); } }; class Ceiling final : public Expression { public: Ceiling(const CeilingNode * n) : Expression(n) {} - static Ceiling Builder(Expression child) { return Ceiling(child); } - static Expression UntypedBuilder(Expression children) { return Builder(children.childAtIndex(0)); } - static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("ceil", 1, &UntypedBuilder); + static Ceiling Builder(Expression child) { return TreeHandle::FixedArityBuilder(&child, 1); } - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit); -private: - explicit Ceiling(Expression child) : Expression(TreePool::sharedPool()->createTreeNode()) { - replaceChildAtIndexInPlace(0, child); - } + static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("ceil", 1, &UntypedBuilderOneChild); + + Expression shallowReduce(); }; } diff --git a/poincare/include/poincare/ceiling_layout.h b/poincare/include/poincare/ceiling_layout.h index 4264e318f..6a3755175 100644 --- a/poincare/include/poincare/ceiling_layout.h +++ b/poincare/include/poincare/ceiling_layout.h @@ -30,9 +30,8 @@ protected: class CeilingLayout final : public Layout { public: - explicit CeilingLayout(Layout l) : Layout(TreePool::sharedPool()->createTreeNode()) { - replaceChildAtIndexInPlace(0, l); - } + static CeilingLayout Builder(Layout child) { return TreeHandle::FixedArityBuilder(&child, 1); } + CeilingLayout() = delete; }; } diff --git a/poincare/include/poincare/char_layout.h b/poincare/include/poincare/char_layout.h index 8d1290d18..c206d39b8 100644 --- a/poincare/include/poincare/char_layout.h +++ b/poincare/include/poincare/char_layout.h @@ -17,10 +17,8 @@ public: {} // CharLayout - virtual void setChar(char c) { m_char = c; } char character() const { return m_char; } const KDFont * font() const { return m_font; } - void setFont(const KDFont * font) { m_font = font; } // LayoutNode void moveCursorLeft(LayoutCursor * cursor, bool * shouldRecomputeLayout) override; @@ -28,6 +26,7 @@ public: int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; bool isChar() const override { return true; } bool isCollapsable(int * numberOfOpenParenthesis, bool goingLeft) const override; + bool canBeOmittedMultiplicationLeftFactor() const override; bool canBeOmittedMultiplicationRightFactor() const override; // TreeNode @@ -53,6 +52,7 @@ protected: private: void render(KDContext * ctx, KDPoint p, KDColor expressionColor, KDColor backgroundColor) override; + bool isMultiplicationChar() const; char m_char; const KDFont * m_font; }; @@ -60,7 +60,7 @@ private: class CharLayout final : public Layout { public: CharLayout(const CharLayoutNode * n) : Layout(n) {} - CharLayout(char c, const KDFont * font = KDFont::LargeFont); + static CharLayout Builder(char c, const KDFont * font = KDFont::LargeFont); const KDFont * font() const { return const_cast(this)->node()->font(); } char character() const {return const_cast(this)->node()->character();} private: diff --git a/poincare/include/poincare/complex.h b/poincare/include/poincare/complex.h index 9d443ea65..1f9e82652 100644 --- a/poincare/include/poincare/complex.h +++ b/poincare/include/poincare/complex.h @@ -9,9 +9,9 @@ template class Complex; template -class ComplexNode final : public std::complex, public EvaluationNode { +class ComplexNode final : public EvaluationNode, public std::complex { public: - ComplexNode() : std::complex(NAN, NAN) {} + ComplexNode(std::complex c); // TreeNode size_t size() const override { return sizeof(ComplexNode); } @@ -26,7 +26,6 @@ public: } #endif - virtual void setComplex(std::complex c); typename Poincare::EvaluationNode::Type type() const override { return Poincare::EvaluationNode::Type::Complex; } bool isUndefined() const override { return (std::isnan(this->real()) && std::isnan(this->imag())); @@ -41,10 +40,10 @@ template class Complex final : public Evaluation { public: Complex(ComplexNode * n) : Evaluation(n) {} - explicit Complex(T a, T b = 0.0) : Complex(std::complex(a, b)) {} - explicit Complex(std::complex c); + static Complex Builder(std::complex c); + static Complex Builder(T a, T b = 0.0) { return Complex::Builder(std::complex(a, b)); } static Complex Undefined() { - return Complex(NAN, NAN); + return Complex::Builder(NAN, NAN); } std::complex stdComplex() { return *node(); } T real() { return node()->real(); } diff --git a/poincare/include/poincare/complex_argument.h b/poincare/include/poincare/complex_argument.h index a4a026641..b6a9b9fad 100644 --- a/poincare/include/poincare/complex_argument.h +++ b/poincare/include/poincare/complex_argument.h @@ -17,6 +17,8 @@ public: stream << "ComplexArgument"; } #endif + // Complex + bool isReal(Context & context) const override { return true; } // Properties Type type() const override { return Type::ComplexArgument; } @@ -25,29 +27,25 @@ private: Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; // Simplification - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) override; + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) override; // Evaluation - template static Complex computeOnComplex(const std::complex c, Preferences::AngleUnit angleUnit); - Evaluation approximate(SinglePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { - return ApproximationHelper::Map(this, context, angleUnit,computeOnComplex); + template static Complex computeOnComplex(const std::complex c, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit); + Evaluation approximate(SinglePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { + return ApproximationHelper::Map(this, context, complexFormat, angleUnit,computeOnComplex); } - Evaluation approximate(DoublePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { - return ApproximationHelper::Map(this, context, angleUnit, computeOnComplex); + Evaluation approximate(DoublePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { + return ApproximationHelper::Map(this, context, complexFormat, angleUnit, computeOnComplex); } }; class ComplexArgument final : public Expression { public: ComplexArgument(const ComplexArgumentNode * n) : Expression(n) {} - static ComplexArgument Builder(Expression child) { return ComplexArgument(child); } - static Expression UntypedBuilder(Expression children) { return Builder(children.childAtIndex(0)); } - static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("arg", 1, &UntypedBuilder); + static ComplexArgument Builder(Expression child) { return TreeHandle::FixedArityBuilder(&child, 1); } - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit); -private: - explicit ComplexArgument(Expression child) : Expression(TreePool::sharedPool()->createTreeNode()) { - replaceChildAtIndexInPlace(0, child); - } + static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("arg", 1, &UntypedBuilderOneChild); + + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); }; } diff --git a/poincare/include/poincare/complex_cartesian.h b/poincare/include/poincare/complex_cartesian.h new file mode 100644 index 000000000..2ac6e865f --- /dev/null +++ b/poincare/include/poincare/complex_cartesian.h @@ -0,0 +1,72 @@ +#ifndef POINCARE_COMPLEX_CARTESIAN_H +#define POINCARE_COMPLEX_CARTESIAN_H + +#include +#include + +namespace Poincare { + +class ComplexCartesianNode : public ExpressionNode { +public: + + // TreeNode + size_t size() const override { return sizeof(ComplexCartesianNode); } + int numberOfChildren() const override { return 2; } +#if POINCARE_TREE_LOG + virtual void logNodeName(std::ostream & stream) const override { + stream << "ComplexCartesian"; + } +#endif + + // Properties + Type type() const override { return Type::ComplexCartesian; } +private: + // Layout + Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override { assert(false); return Layout(); } + // Evaluation + Evaluation approximate(SinglePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, complexFormat, angleUnit); } + Evaluation approximate(DoublePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, complexFormat, angleUnit); } + // Simplification + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) override; + Expression shallowBeautify(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) override; + +private: + template Complex templatedApproximate(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; +}; + +class ComplexCartesian final : public Expression { +public: + ComplexCartesian() : Expression() {} + ComplexCartesian(const ComplexCartesianNode * node) : Expression(node) {} + static ComplexCartesian Builder() { return TreeHandle::FixedArityBuilder(); } + static ComplexCartesian Builder(Expression child0, Expression child1) { return TreeHandle::FixedArityBuilder(ArrayBuilder(child0, child1).array(), 2); } + + // Getters + Expression real() { return childAtIndex(0); } + Expression imag() { return childAtIndex(1); } + + // Simplification + Expression shallowReduce(); + Expression shallowBeautify(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); + + // Common operations (done in-place) + Expression squareNorm(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); + Expression norm(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); + Expression argument(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); + ComplexCartesian inverse(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); + ComplexCartesian squareRoot(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); + ComplexCartesian powerInteger(int n, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); + ComplexCartesian multiply(ComplexCartesian & other, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); + ComplexCartesian power(ComplexCartesian & other, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); +private: + static constexpr int k_maxNumberOfNodesBeforeInterrupting = 50; + void factorAndArgumentOfFunction(Expression e, ExpressionNode::Type searchedType, Expression * factor, Expression * argument, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); + ComplexCartesian interruptComputationIfManyNodes(); + static Multiplication squareRootHelper(Expression e, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); + static Expression powerHelper(Expression norm, Expression trigo, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); +}; + + +} + +#endif diff --git a/poincare/include/poincare/condensed_sum_layout.h b/poincare/include/poincare/condensed_sum_layout.h index a705afbf2..93c8ca9f9 100644 --- a/poincare/include/poincare/condensed_sum_layout.h +++ b/poincare/include/poincare/condensed_sum_layout.h @@ -51,7 +51,8 @@ private: class CondensedSumLayout final : public Layout { public: - CondensedSumLayout(Layout base, Layout subscript, Layout superscript); + static CondensedSumLayout Builder(Layout base, Layout subscript, Layout superscript) { return TreeHandle::FixedArityBuilder(ArrayBuilder(base, subscript, superscript).array(), 3); } + CondensedSumLayout() = delete; }; } diff --git a/poincare/include/poincare/confidence_interval.h b/poincare/include/poincare/confidence_interval.h index 78c526f68..8f9652b89 100644 --- a/poincare/include/poincare/confidence_interval.h +++ b/poincare/include/poincare/confidence_interval.h @@ -27,11 +27,11 @@ private: Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; // Simplification - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) override; + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) override; // Evaluation - Evaluation approximate(SinglePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } - Evaluation approximate(DoublePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } - template Evaluation templatedApproximate(Context& context, Preferences::AngleUnit angleUnit) const; + Evaluation approximate(SinglePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, complexFormat, angleUnit); } + Evaluation approximate(DoublePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, complexFormat, angleUnit); } + template Evaluation templatedApproximate(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; }; class SimplePredictionIntervalNode final : public ConfidenceIntervalNode { @@ -45,31 +45,20 @@ class ConfidenceInterval : public Expression { friend class SimplePredictionInterval; public: ConfidenceInterval(const ConfidenceIntervalNode * n) : Expression(n) {} - static ConfidenceInterval Builder(Expression child0, Expression child1) { return ConfidenceInterval(child0, child1); } - static Expression UntypedBuilder(Expression children) { return Builder(children.childAtIndex(0), children.childAtIndex(1)); } - static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("confidence", 2, &UntypedBuilder); + static ConfidenceInterval Builder(Expression child0, Expression child1) { return TreeHandle::FixedArityBuilder(ArrayBuilder(child0, child1).array(), 2); } + static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("confidence", 2, &UntypedBuilderTwoChildren); // Expression - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); private: - ConfidenceInterval(Expression child0, Expression child1) : Expression(TreePool::sharedPool()->createTreeNode()) { - replaceChildAtIndexInPlace(0, child0); - replaceChildAtIndexInPlace(1, child1); - } constexpr static int k_maxNValue = 300; }; class SimplePredictionInterval final : public ConfidenceInterval { public: SimplePredictionInterval(const SimplePredictionIntervalNode * n) : ConfidenceInterval(n) {} - static SimplePredictionInterval Builder(Expression child0, Expression child1) { return SimplePredictionInterval(child0, child1); } - static Expression UntypedBuilder(Expression children) { return Builder(children.childAtIndex(0), children.childAtIndex(1)); } - static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("prediction", 2, &UntypedBuilder); -private: - SimplePredictionInterval(Expression child0, Expression child1) : ConfidenceInterval(TreePool::sharedPool()->createTreeNode()) { - replaceChildAtIndexInPlace(0, child0); - replaceChildAtIndexInPlace(1, child1); - } + static SimplePredictionInterval Builder(Expression child0, Expression child1) { return TreeHandle::FixedArityBuilder(ArrayBuilder(child0, child1).array(), 2); } + static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("prediction", 2, &UntypedBuilderTwoChildren); }; } diff --git a/poincare/include/poincare/conjugate.h b/poincare/include/poincare/conjugate.h index 840f5e490..f2324c1f4 100644 --- a/poincare/include/poincare/conjugate.h +++ b/poincare/include/poincare/conjugate.h @@ -18,6 +18,8 @@ public: } #endif + bool isReal(Context & context) const override { return childAtIndex(0)->isReal(context); } + // Properties Type type() const override { return Type::Conjugate; } private: @@ -25,29 +27,25 @@ private: Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; // Simplification - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) override; + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) override; // Evaluation - template static Complex computeOnComplex(const std::complex c, Preferences::AngleUnit angleUnit); - Evaluation approximate(SinglePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { - return ApproximationHelper::Map(this, context, angleUnit,computeOnComplex); + template static Complex computeOnComplex(const std::complex c, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit); + Evaluation approximate(SinglePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { + return ApproximationHelper::Map(this, context, complexFormat, angleUnit,computeOnComplex); } - Evaluation approximate(DoublePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { - return ApproximationHelper::Map(this, context, angleUnit, computeOnComplex); + Evaluation approximate(DoublePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { + return ApproximationHelper::Map(this, context, complexFormat, angleUnit, computeOnComplex); } }; class Conjugate final : public Expression { public: Conjugate(const ConjugateNode * n) : Expression(n) {} - static Conjugate Builder(Expression child) { return Conjugate(child); } - static Expression UntypedBuilder(Expression children) { return Builder(children.childAtIndex(0)); } - static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("conj", 1, &UntypedBuilder);; + static Conjugate Builder(Expression child) { return TreeHandle::FixedArityBuilder(&child, 1); } - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit); -private: - explicit Conjugate(Expression child) : Expression(TreePool::sharedPool()->createTreeNode()) { - replaceChildAtIndexInPlace(0, child); - } + static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("conj", 1, &UntypedBuilderOneChild);; + + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); }; } diff --git a/poincare/include/poincare/conjugate_layout.h b/poincare/include/poincare/conjugate_layout.h index 4e04da6f4..8dabdf3cf 100644 --- a/poincare/include/poincare/conjugate_layout.h +++ b/poincare/include/poincare/conjugate_layout.h @@ -40,9 +40,8 @@ private: class ConjugateLayout final : public Layout { public: - explicit ConjugateLayout(Layout l) : Layout(TreePool::sharedPool()->createTreeNode()) { - replaceChildAtIndexInPlace(0, l); - } + static ConjugateLayout Builder(Layout child) { return TreeHandle::FixedArityBuilder(&child, 1); } + ConjugateLayout() = delete; }; } diff --git a/poincare/include/poincare/constant.h b/poincare/include/poincare/constant.h index ba975a6d5..9f09fbbe1 100644 --- a/poincare/include/poincare/constant.h +++ b/poincare/include/poincare/constant.h @@ -7,6 +7,8 @@ namespace Poincare { class ConstantNode final : public SymbolAbstractNode { public: + ConstantNode(const char * newName, int length); + const char * name() const override { return m_name; } // TreeNode @@ -17,40 +19,52 @@ public: } #endif + // Complex + bool isReal(Context & context) const override; + // Expression Properties Type type() const override { return Type::Constant; } - Sign sign() const override; + Sign sign(Context * context) const override; /* Layout */ Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; /* Approximation */ - Evaluation approximate(SinglePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } - Evaluation approximate(DoublePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } + Evaluation approximate(SinglePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, complexFormat, angleUnit); } + Evaluation approximate(DoublePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, complexFormat, angleUnit); } /* Symbol properties */ bool isPi() const { return isConstantChar(Ion::Charset::SmallPi); } bool isExponential() const { return isConstantChar(Ion::Charset::Exponential); } bool isIComplex() const { return isConstantChar(Ion::Charset::IComplex); } + + // Comparison + int simplificationOrderSameType(const ExpressionNode * e, bool ascending, bool canBeInterrupted) const override; + + // Simplification + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) override; private: char m_name[0]; // MUST be the last member variable size_t nodeSize() const override { return sizeof(ConstantNode); } - template Evaluation templatedApproximate(Context& context, Preferences::AngleUnit angleUnit) const; + template Evaluation templatedApproximate(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; bool isConstantChar(char c) const { const char constantName[2] = {c, 0}; return strcmp(m_name, constantName) == 0; } }; class Constant final : public SymbolAbstract { public: - Constant(char name); Constant(const ConstantNode * node) : SymbolAbstract(node) {} + static Constant Builder(char name) { return SymbolAbstract::Builder(&name, 1); } // Constant properties bool isPi() const { return node()->isPi(); } bool isExponential() const { return node()->isExponential(); } bool isIComplex() const { return node()->isIComplex(); } + // Simplification + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); + private: ConstantNode * node() const { return static_cast(Expression::node()); } }; diff --git a/poincare/include/poincare/cosine.h b/poincare/include/poincare/cosine.h index 944adb7b0..8617103ee 100644 --- a/poincare/include/poincare/cosine.h +++ b/poincare/include/poincare/cosine.h @@ -19,39 +19,38 @@ public: } #endif + // Complex + bool isReal(Context & context) const override { return childAtIndex(0)->isReal(context); } + // Properties Type type() const override { return Type::Cosine; } float characteristicXRange(Context & context, Preferences::AngleUnit angleUnit) const override; - template static Complex computeOnComplex(const std::complex c, Preferences::AngleUnit angleUnit = Preferences::AngleUnit::Radian); + template static Complex computeOnComplex(const std::complex c, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit = Preferences::AngleUnit::Radian); private: // Layout Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; // Simplication - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) override; + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) override; // Evaluation - Evaluation approximate(SinglePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { - return ApproximationHelper::Map(this, context, angleUnit,computeOnComplex); + Evaluation approximate(SinglePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { + return ApproximationHelper::Map(this, context, complexFormat, angleUnit,computeOnComplex); } - Evaluation approximate(DoublePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { - return ApproximationHelper::Map(this, context, angleUnit, computeOnComplex); + Evaluation approximate(DoublePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { + return ApproximationHelper::Map(this, context, complexFormat, angleUnit, computeOnComplex); } }; class Cosine final : public Expression { public: Cosine(const CosineNode * n) : Expression(n) {} - static Cosine Builder(Expression child) { return Cosine(child); } - static Expression UntypedBuilder(Expression children) { return Builder(children.childAtIndex(0)); } - static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("cos", 1, &UntypedBuilder); + static Cosine Builder(Expression child) { return TreeHandle::FixedArityBuilder(&child, 1); } - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); -private: - explicit Cosine(Expression child) : Expression(TreePool::sharedPool()->createTreeNode()) { - replaceChildAtIndexInPlace(0, child); - } + static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("cos", 1, &UntypedBuilderOneChild); + + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); }; } diff --git a/poincare/include/poincare/decimal.h b/poincare/include/poincare/decimal.h index 664518dcb..e55904fc7 100644 --- a/poincare/include/poincare/decimal.h +++ b/poincare/include/poincare/decimal.h @@ -6,7 +6,7 @@ namespace Poincare { -/* A decimal as 0.01234 is stored that way: +/* The decimal 0.01234 is stored as: * - bool m_negative = false * - int m_exponent = -2 * - int m_numberOfDigitsInMantissa = 1 @@ -18,19 +18,13 @@ class Decimal; class DecimalNode final : public NumberNode { friend class Decimal; public: - DecimalNode() : - m_negative(false), - m_exponent(0), - m_numberOfDigitsInMantissa(0) {} - - virtual void setValue(const native_uint_t * mantissaDigits, uint8_t mantissaSize, int exponent, bool negative); + DecimalNode(const native_uint_t * mantissaDigits, uint8_t mantissaSize, int exponent, bool negative); Integer signedMantissa() const; Integer unsignedMantissa() const; int exponent() const { return m_exponent; } // TreeNode - void initToMatchSize(size_t size) override; size_t size() const override; #if POINCARE_TREE_LOG virtual void logNodeName(std::ostream & stream) const override { @@ -39,7 +33,7 @@ public: virtual void logAttributes(std::ostream & stream) const override { stream << " negative=\"" << m_negative << "\""; stream << " mantissa=\""; - this->signedMantissa().log(stream); + this->signedMantissa().logInteger(stream); stream << "\""; stream << " exponent=\"" << m_exponent << "\""; } @@ -47,23 +41,23 @@ public: // Properties Type type() const override { return Type::Decimal; } - Sign sign() const override { return m_negative ? Sign::Negative : Sign::Positive; } - Expression setSign(Sign s, Context & context, Preferences::AngleUnit angleUnit) override; + Sign sign(Context * context) const override { return m_negative ? Sign::Negative : Sign::Positive; } + Expression setSign(Sign s, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) override; // Approximation - Evaluation approximate(SinglePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { - return Complex(templatedApproximate()); + Evaluation approximate(SinglePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { + return Complex::Builder(templatedApproximate()); } - Evaluation approximate(DoublePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { - return Complex(templatedApproximate()); + Evaluation approximate(DoublePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { + return Complex::Builder(templatedApproximate()); } // Comparison - int simplificationOrderSameType(const ExpressionNode * e, bool canBeInterrupted) const override; + int simplificationOrderSameType(const ExpressionNode * e, bool ascending, bool canBeInterrupted) const override; // Simplification - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) override; - Expression shallowBeautify(Context & context, Preferences::AngleUnit angleUnit) override; + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) override; + Expression shallowBeautify(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) override; // Serialization int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode = Preferences::PrintFloatMode::Decimal, int numberOfSignificantDigits = 0) const override; @@ -85,12 +79,15 @@ private: class Decimal final : public Number { friend class Number; friend class DecimalNode; +template +friend class ComplexNode; public: - static int Exponent(const char * integralPart, int integralPartLength, const char * fractionalPart, int fractionalPartLength, const char * exponent, int exponentLength, bool exponentIsNegative = false); - Decimal(const char * integralPart, int integralPartLength, const char * fractionalPart, int fractionalPartLength, int exponent); Decimal(DecimalNode * node) : Number(node) {} - Decimal(Integer m, int e); - template Decimal(T f); + static Decimal Builder(const char * integralPart, int integralPartLength, const char * fractionalPart, int fractionalPartLength, int exponent); + static Decimal Builder(Integer m, int e); + template static Decimal Builder(T f); + static int Exponent(const char * integralPart, int integralPartLength, const char * fractionalPart, int fractionalPartLength, const char * exponent, int exponentLength, bool exponentIsNegative = false); + /* k_maxExponentLength caps the string length we parse to create the exponent. * It prevents m_exponent (int32_t) from overflowing and giving wrong results. */ constexpr static int k_maxExponentLength = 8; @@ -99,11 +96,11 @@ public: private: constexpr static int k_maxMantissaLength = 20; DecimalNode * node() const { return static_cast(Number::node()); } - Decimal(size_t size, const Integer & m, int e); - Expression setSign(ExpressionNode::Sign s, Context & context, Preferences::AngleUnit angleUnit); + static Decimal Builder(size_t size, const Integer & m, int e); + Expression setSign(ExpressionNode::Sign s); // Simplification - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit); - Expression shallowBeautify(Context & context, Preferences::AngleUnit angleUnit); + Expression shallowReduce(); + Expression shallowBeautify(); }; } diff --git a/poincare/include/poincare/derivative.h b/poincare/include/poincare/derivative.h index 3e2458f7f..b4a76a19b 100644 --- a/poincare/include/poincare/derivative.h +++ b/poincare/include/poincare/derivative.h @@ -19,6 +19,9 @@ public: } #endif + // Complex + bool isReal(Context & context) const override { return true; } + // Properties Type type() const override { return Type::Derivative; } int polynomialDegree(Context & context, const char * symbolName) const override; @@ -30,15 +33,15 @@ private: int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; // Simplification - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) override; + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) override; // Evaluation - Evaluation approximate(SinglePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } - Evaluation approximate(DoublePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } - template Evaluation templatedApproximate(Context& context, Preferences::AngleUnit angleUnit) const; - template T approximateWithArgument(T x, Context & context, Preferences::AngleUnit angleUnit) const; - template T growthRateAroundAbscissa(T x, T h, Context & context, Preferences::AngleUnit angleUnit) const; - template T riddersApproximation(Context & context, Preferences::AngleUnit angleUnit, T x, T h, T * error) const; + Evaluation approximate(SinglePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, complexFormat, angleUnit); } + Evaluation approximate(DoublePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, complexFormat, angleUnit); } + template Evaluation templatedApproximate(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; + template T approximateWithArgument(T x, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; + template T growthRateAroundAbscissa(T x, T h, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; + template T riddersApproximation(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, T x, T h, T * error) const; // TODO: Change coefficients? constexpr static double k_maxErrorRateOnApproximation = 0.001; constexpr static double k_minInitialRate = 0.01; @@ -48,24 +51,11 @@ private: class Derivative final : public Expression { public: Derivative(const DerivativeNode * n) : Expression(n) {} - static Derivative Builder(Expression child0, Symbol child1, Expression child2) { return Derivative(child0, child1, child2); } - static Expression UntypedBuilder(Expression children) { - if (children.childAtIndex(1).type() != ExpressionNode::Type::Symbol) { - // Second parameter must be a Symbol. - return Expression(); - } - return Builder(children.childAtIndex(0), children.childAtIndex(1).convert(), children.childAtIndex(2)); - } + static Derivative Builder(Expression child0, Symbol child1, Expression child2) { return TreeHandle::FixedArityBuilder(ArrayBuilder(child0, child1, child2).array(), 3); } + static Expression UntypedBuilder(Expression children); static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("diff", 3, &UntypedBuilder); - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit); -private: - Derivative(Expression child0, Expression child1, Expression child2) : Expression(TreePool::sharedPool()->createTreeNode()) { - assert(child1.type() == ExpressionNode::Type::Symbol); - replaceChildAtIndexInPlace(0, child0); - replaceChildAtIndexInPlace(1, child1); - replaceChildAtIndexInPlace(2, child2); - } + Expression shallowReduce(); }; } diff --git a/poincare/include/poincare/determinant.h b/poincare/include/poincare/determinant.h index e48c0ffc4..e80c2ceb3 100644 --- a/poincare/include/poincare/determinant.h +++ b/poincare/include/poincare/determinant.h @@ -24,26 +24,22 @@ private: /* Serialization */ int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; /* Simplification */ - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) override; + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) override; /* Approximation */ - Evaluation approximate(SinglePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } - Evaluation approximate(DoublePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } - template Evaluation templatedApproximate(Context& context, Preferences::AngleUnit angleUnit) const; + Evaluation approximate(SinglePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, complexFormat, angleUnit); } + Evaluation approximate(DoublePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, complexFormat, angleUnit); } + template Evaluation templatedApproximate(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; }; class Determinant final : public Expression { public: Determinant(const DeterminantNode * n) : Expression(n) {} - static Determinant Builder(Expression child) { return Determinant(child); } - static Expression UntypedBuilder(Expression children) { return Builder(children.childAtIndex(0)); } - static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("det", 1, &UntypedBuilder); + static Determinant Builder(Expression child) { return TreeHandle::FixedArityBuilder(&child, 1); } - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit); -private: - explicit Determinant(Expression child) : Expression(TreePool::sharedPool()->createTreeNode()) { - replaceChildAtIndexInPlace(0, child); - } + static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("det", 1, &UntypedBuilderOneChild); + + Expression shallowReduce(Context & context); }; } diff --git a/poincare/include/poincare/division.h b/poincare/include/poincare/division.h index c7c799fde..e73617d7c 100644 --- a/poincare/include/poincare/division.h +++ b/poincare/include/poincare/division.h @@ -27,15 +27,15 @@ public: int polynomialDegree(Context & context, const char * symbolName) const override; // Approximation - virtual Evaluation approximate(SinglePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { + virtual Evaluation approximate(SinglePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { return ApproximationHelper::MapReduce( - this, context, angleUnit, compute, + this, context, complexFormat, angleUnit, compute, computeOnComplexAndMatrix, computeOnMatrixAndComplex, computeOnMatrices); } - virtual Evaluation approximate(DoublePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { + virtual Evaluation approximate(DoublePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { return ApproximationHelper::MapReduce( - this, context, angleUnit, compute, + this, context, complexFormat, angleUnit, compute, computeOnComplexAndMatrix, computeOnMatrixAndComplex, computeOnMatrices); } @@ -46,27 +46,25 @@ public: int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; // Simplification - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) override; + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) override; private: - template static Complex compute(const std::complex c, const std::complex d); - template static MatrixComplex computeOnMatrixAndComplex(const MatrixComplex m, const std::complex c) { - return ApproximationHelper::ElementWiseOnMatrixComplexAndComplex(m, c, compute); + // Approximation + template static Complex compute(const std::complex c, const std::complex d, Preferences::ComplexFormat complexFormat); + template static MatrixComplex computeOnMatrixAndComplex(const MatrixComplex m, const std::complex c, Preferences::ComplexFormat complexFormat) { + return ApproximationHelper::ElementWiseOnMatrixComplexAndComplex(m, c, complexFormat, compute); } - template static MatrixComplex computeOnComplexAndMatrix(const std::complex c, const MatrixComplex n); - template static MatrixComplex computeOnMatrices(const MatrixComplex m, const MatrixComplex n); + template static MatrixComplex computeOnComplexAndMatrix(const std::complex c, const MatrixComplex n, Preferences::ComplexFormat complexFormat); + template static MatrixComplex computeOnMatrices(const MatrixComplex m, const MatrixComplex n, Preferences::ComplexFormat complexFormat); }; class Division final : public Expression { public: - Division(); - Division(Expression numerator, Expression denominator) : Division() { - replaceChildAtIndexInPlace(0, numerator); - replaceChildAtIndexInPlace(1, denominator); - } Division(const DivisionNode * n) : Expression(n) {} + static Division Builder() { return TreeHandle::FixedArityBuilder(); } + static Division Builder(Expression numerator, Expression denominator) { return TreeHandle::FixedArityBuilder(ArrayBuilder(numerator, denominator).array(), 2); } - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); }; } diff --git a/poincare/include/poincare/division_quotient.h b/poincare/include/poincare/division_quotient.h index c2ec88224..f68c03b81 100644 --- a/poincare/include/poincare/division_quotient.h +++ b/poincare/include/poincare/division_quotient.h @@ -19,32 +19,30 @@ public: // ExpressionNode Type type() const override { return Type::DivisionQuotient; } + + // Complex + bool isReal(Context & context) const override { return true; } + private: // Layout Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; // Simplification - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) override; + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) override; // Evaluation - Evaluation approximate(SinglePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } - Evaluation approximate(DoublePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } - template Evaluation templatedApproximate(Context& context, Preferences::AngleUnit angleUnit) const; + Evaluation approximate(SinglePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, complexFormat, angleUnit); } + Evaluation approximate(DoublePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, complexFormat, angleUnit); } + template Evaluation templatedApproximate(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; }; class DivisionQuotient final : public Expression { public: DivisionQuotient(const DivisionQuotientNode * n) : Expression(n) {} - static DivisionQuotient Builder(Expression child0, Expression child1) { return DivisionQuotient(child0, child1); } - static Expression UntypedBuilder(Expression children) { return Builder(children.childAtIndex(0), children.childAtIndex(1)); } - static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("quo", 2, &UntypedBuilder); + static DivisionQuotient Builder(Expression child0, Expression child1) { return TreeHandle::FixedArityBuilder(ArrayBuilder(child0, child1).array(), 2); } + static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("quo", 2, &UntypedBuilderTwoChildren); // Expression - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit); -private: - DivisionQuotient(Expression child0, Expression child1) : Expression(TreePool::sharedPool()->createTreeNode()) { - replaceChildAtIndexInPlace(0, child0); - replaceChildAtIndexInPlace(1, child1); - } + Expression shallowReduce(); }; } diff --git a/poincare/include/poincare/division_remainder.h b/poincare/include/poincare/division_remainder.h index b99624809..9089fa60a 100644 --- a/poincare/include/poincare/division_remainder.h +++ b/poincare/include/poincare/division_remainder.h @@ -20,32 +20,30 @@ public: // ExpressionNode Type type() const override { return Type::DivisionRemainder; } + + // Complex + bool isReal(Context & context) const override { return true; } + private: // Layout Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; // Simplification - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) override; + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) override; // Evaluation - Evaluation approximate(SinglePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } - Evaluation approximate(DoublePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } - template Evaluation templatedApproximate(Context& context, Preferences::AngleUnit angleUnit) const; + Evaluation approximate(SinglePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, complexFormat, angleUnit); } + Evaluation approximate(DoublePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, complexFormat, angleUnit); } + template Evaluation templatedApproximate(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; }; class DivisionRemainder final : public Expression { public: DivisionRemainder(const DivisionRemainderNode * n) : Expression(n) {} - static DivisionRemainder Builder(Expression child0, Expression child1) { return DivisionRemainder(child0, child1); } - static Expression UntypedBuilder(Expression children) { return Builder(children.childAtIndex(0), children.childAtIndex(1)); } - static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("rem", 2, &UntypedBuilder); + static DivisionRemainder Builder(Expression child0, Expression child1) { return TreeHandle::FixedArityBuilder(ArrayBuilder(child0, child1).array(), 2); } + static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("rem", 2, &UntypedBuilderTwoChildren); // Expression - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit); -private: - DivisionRemainder(Expression child0, Expression child1) : Expression(TreePool::sharedPool()->createTreeNode()) { - replaceChildAtIndexInPlace(0, child0); - replaceChildAtIndexInPlace(1, child1); - } + Expression shallowReduce(); }; } diff --git a/poincare/include/poincare/empty_expression.h b/poincare/include/poincare/empty_expression.h index 32557fd8b..80b83ead8 100644 --- a/poincare/include/poincare/empty_expression.h +++ b/poincare/include/poincare/empty_expression.h @@ -26,14 +26,14 @@ private: // Layout Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; // Evaluation - Evaluation approximate(SinglePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } - Evaluation approximate(DoublePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } - template Evaluation templatedApproximate(Context& context, Preferences::AngleUnit angleUnit) const; + Evaluation approximate(SinglePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, complexFormat, angleUnit); } + Evaluation approximate(DoublePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, complexFormat, angleUnit); } + template Evaluation templatedApproximate(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; }; class EmptyExpression final : public Expression { public: - EmptyExpression(); + static EmptyExpression Builder() { return TreeHandle::FixedArityBuilder(); } EmptyExpression(const EmptyExpressionNode * n) : Expression(n) {} }; diff --git a/poincare/include/poincare/empty_layout.h b/poincare/include/poincare/empty_layout.h index 9cb5ea312..cc07406bb 100644 --- a/poincare/include/poincare/empty_layout.h +++ b/poincare/include/poincare/empty_layout.h @@ -26,8 +26,6 @@ public: void setColor(Color color) { m_color = color; } bool isVisible() const { return m_isVisible; } void setVisible(bool visible) { m_isVisible = visible; } - void setMargins(bool margins) { m_margins = margins; } - void setFont(const KDFont * font) { m_font = font; } // LayoutNode void deleteBeforeCursor(LayoutCursor * cursor) override; @@ -75,7 +73,7 @@ private: class EmptyLayout final : public Layout { public: EmptyLayout(const EmptyLayoutNode * n); - EmptyLayout(EmptyLayoutNode::Color color = EmptyLayoutNode::Color::Yellow, bool visible = true, const KDFont * font = KDFont::LargeFont, bool margins = true); + static EmptyLayout Builder(EmptyLayoutNode::Color color = EmptyLayoutNode::Color::Yellow, bool visible = true, const KDFont * font = KDFont::LargeFont, bool margins = true); void setVisible(bool visible) { node()->setVisible(visible); } diff --git a/poincare/include/poincare/equal.h b/poincare/include/poincare/equal.h index cb20d3bf4..7a7b87303 100644 --- a/poincare/include/poincare/equal.h +++ b/poincare/include/poincare/equal.h @@ -22,28 +22,25 @@ public: int polynomialDegree(Context & context, const char * symbolName) const override { return -1; } private: // Simplification - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) override; + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) override; // Layout Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; // Evalutation - Evaluation approximate(SinglePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } - Evaluation approximate(DoublePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } - template Evaluation templatedApproximate(Context& context, Preferences::AngleUnit angleUnit) const; + Evaluation approximate(SinglePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, complexFormat, angleUnit); } + Evaluation approximate(DoublePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, complexFormat, angleUnit); } + template Evaluation templatedApproximate(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; }; class Equal final : public Expression { public: Equal(const EqualNode * n) : Expression(n) {} - Equal(Expression child0, Expression child1) : Expression(TreePool::sharedPool()->createTreeNode()) { - replaceChildAtIndexInPlace(0, child0); - replaceChildAtIndexInPlace(1, child1); - } + static Equal Builder(Expression child0, Expression child1) { return TreeHandle::FixedArityBuilder(ArrayBuilder(child0, child1).array(), 2); } // For the equation A = B, create the reduced expression A-B - Expression standardEquation(Context & context, Preferences::AngleUnit angleUnit) const; + Expression standardEquation(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; // Expression - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit); + Expression shallowReduce(); }; } diff --git a/poincare/include/poincare/expression.h b/poincare/include/poincare/expression.h index 77a6a5fc4..e5c20b901 100644 --- a/poincare/include/poincare/expression.h +++ b/poincare/include/poincare/expression.h @@ -1,6 +1,7 @@ #ifndef POINCARE_EXPRESSION_REFERENCE_H #define POINCARE_EXPRESSION_REFERENCE_H +#include #include #include #include @@ -26,7 +27,11 @@ class Expression : public TreeHandle { friend class BinomialCoefficient; friend class Ceiling; friend class CommonLogarithm; + template + friend class ComplexNode; friend class ComplexArgument; + friend class ComplexCartesian; + friend class ComplexHelper; friend class ConfidenceInterval; friend class Conjugate; friend class Cosine; @@ -62,18 +67,23 @@ class Expression : public TreeHandle { friend class Parenthesis; friend class PermuteCoefficient; friend class Power; + friend class PowerNode; friend class PredictionInterval; friend class Product; friend class RealPart; friend class Round; + friend class SignFunction; friend class Sine; friend class SquareRoot; + friend class SquareRootNode; friend class Store; friend class Subtraction; friend class Sum; friend class Symbol; + friend class SymbolAbstractNode; friend class Tangent; friend class Trigonometry; + friend class TrigonometryCheatTable; friend class AdditionNode; friend class DerivativeNode; @@ -91,7 +101,7 @@ class Expression : public TreeHandle { friend class SymbolNode; public: - static bool isExpression() { return true; } + static bool IsExpression() { return true; } /* Constructor & Destructor */ Expression() : TreeHandle() {} @@ -101,9 +111,9 @@ public: /* Circuit breaker */ typedef bool (*CircuitBreaker)(); - static void setCircuitBreaker(CircuitBreaker cb); - static bool shouldStopProcessing(); - static void resetInterruption(); + static void SetCircuitBreaker(CircuitBreaker cb); + static bool ShouldStopProcessing(); + static void SetInterruption(bool interrupt); /* Hierarchy */ Expression childAtIndex(int i) const; @@ -111,14 +121,15 @@ public: /* Properties */ ExpressionNode::Type type() const { return node()->type(); } - ExpressionNode::Sign sign() const { return node()->sign(); } - bool isUndefined() const { return node()->type() == ExpressionNode::Type::Undefined; } + ExpressionNode::Sign sign(Context * context) const { return node()->sign(context); } + bool isUndefined() const { return node()->type() == ExpressionNode::Type::Undefined || node()->type() == ExpressionNode::Type::Unreal; } bool isNumber() const { return node()->isNumber(); } bool isRationalZero() const; bool isRationalOne() const; typedef bool (*ExpressionTest)(const Expression e, Context & context, bool replaceSymbols); bool recursivelyMatches(ExpressionTest test, Context & context, bool replaceSymbols) const; bool isApproximate(Context & context) const; + bool recursivelyMatchesInfinity(Context & context) { return recursivelyMatches([](const Expression e, Context & context, bool replaceSymbols) { return e.type() == ExpressionNode::Type::Infinity; }, context, true); } static bool IsMatrix(const Expression e, Context & context, bool replaceSymbols); /* 'characteristicXRange' tries to assess the range on x where the expression * (considered as a function on x) has an interesting evolution. For example, @@ -146,50 +157,74 @@ public: * the variables hold in 'variables'. Otherwise, it fills 'coefficients' with * the coefficients of the variables hold in 'variables' (following the same * order) and 'constant' with the constant of the expression. */ - bool getLinearCoefficients(char * variables, int maxVariableLength, Expression coefficients[], Expression constant[], Context & context, Preferences::AngleUnit angleUnit) const; + bool getLinearCoefficients(char * variables, int maxVariableLength, Expression coefficients[], Expression constant[], Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; /* getPolynomialCoefficients fills the table coefficients with the expressions * of the first 3 polynomial coefficients and returns the polynomial degree. * It is supposed to be called on a reduced expression. * coefficients has up to 3 entries. */ static constexpr int k_maxPolynomialDegree = 2; static constexpr int k_maxNumberOfPolynomialCoefficients = k_maxPolynomialDegree+1; - int getPolynomialReducedCoefficients(const char * symbolName, Expression coefficients[], Context & context, Preferences::AngleUnit angleUnit) const; + int getPolynomialReducedCoefficients(const char * symbolName, Expression coefficients[], Context & context, Preferences::ComplexFormat complexFormat, 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); + /* Complex */ + static bool EncounteredComplex(); + static void SetEncounteredComplex(bool encounterComplex); + static Preferences::ComplexFormat UpdatedComplexFormatWithTextInput(Preferences::ComplexFormat complexFormat, const char * textInput); + static Preferences::ComplexFormat UpdatedComplexFormatWithExpressionInput(Preferences::ComplexFormat complexFormat, const Expression & e, Context & context); + bool isReal(Context & context) const { return node()->isReal(context); } + /* Comparison */ /* isIdenticalTo is the "easy" equality, it returns true if both trees have * same structures and all their nodes have same types and values (ie, * sqrt(pi^2) is NOT identical to pi). */ bool isIdenticalTo(const Expression e) const; - bool isEqualToItsApproximationLayout(Expression approximation, char * buffer, int bufferSize, Preferences::AngleUnit angleUnit, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits, Context & context); + bool isEqualToItsApproximationLayout(Expression approximation, char * buffer, int bufferSize, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits, Context & context); /* Layout Helper */ Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const; int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode = Preferences::PrintFloatMode::Decimal, int numberOfSignificantDigits = PrintFloat::k_numberOfStoredSignificantDigits) const; /* Simplification */ - 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); + /* Simplification routines are divided in 2 groups: + * - ParseAndSimplify & simplify methods are used before approximating the + * expression. We simplify beforehand to avoid precision error but the + * simplified expression is never displayed. The ReductionTarget is + * therefore the System for these methods. + * - ParseAndSimplifyAndApproximate & simplifyAndApproximate methods are used + * to simplify and approximate the expression for the User. They take into + * account the complex format required in the expression they return. + * (For instance, in Polar mode, they return an expression of the form + * r*e^(i*th) reduced and approximated.) */ + static Expression ParseAndSimplify(const char * text, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit); + Expression simplify(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit); + + static void ParseAndSimplifyAndApproximate(const char * text, Expression * simplifiedExpression, Expression * approximateExpression, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit); + void simplifyAndApproximate(Expression * simplifiedExpression, Expression * approximateExpression, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit); + Expression reduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit); + static Expression ExpressionWithoutSymbols(Expression expressionWithSymbols, Context & context); + Expression radianToDegree(); + Expression degreeToRadian(); /* Approximation Helper */ - template static U epsilon(); - template Expression approximate(Context& context, Preferences::AngleUnit angleUnit, Preferences::ComplexFormat complexFormat) const; - template U approximateToScalar(Context& context, Preferences::AngleUnit angleUnit) const; - template static U approximateToScalar(const char * text, Context& context, Preferences::AngleUnit angleUnit); - template U approximateWithValueForSymbol(const char * symbol, U x, Context & context, Preferences::AngleUnit angleUnit) const; + // These methods reset the sApproximationEncounteredComplex flag. They should not be use to implement node approximation + template static U Epsilon(); + template Expression approximate(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; + template U approximateToScalar(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; + template static U ApproximateToScalar(const char * text, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit); + template U approximateWithValueForSymbol(const char * symbol, U x, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; /* Expression roots/extrema solver */ struct Coordinate2D { double abscissa; double value; }; - Coordinate2D nextMinimum(const char * symbol, double start, double step, double max, Context & context, Preferences::AngleUnit angleUnit) const; - Coordinate2D nextMaximum(const char * symbol, double start, double step, double max, Context & context, Preferences::AngleUnit angleUnit) const; - double nextRoot(const char * symbol, double start, double step, double max, Context & context, Preferences::AngleUnit angleUnit) const; - Coordinate2D nextIntersection(const char * symbol, double start, double step, double max, Context & context, Preferences::AngleUnit angleUnit, const Expression expression) const; + Coordinate2D nextMinimum(const char * symbol, double start, double step, double max, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; + Coordinate2D nextMaximum(const char * symbol, double start, double step, double max, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; + double nextRoot(const char * symbol, double start, double step, double max, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; + Coordinate2D nextIntersection(const char * symbol, double start, double step, double max, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const Expression expression) const; /* This class is meant to contain data about named functions (e.g. sin, tan...) * in one place: their name, their number of children and a pointer to a builder. @@ -212,7 +247,20 @@ public: static void Tidy() { sSymbolReplacementsCountLock = false; } protected: + static bool SimplificationHasBeenInterrupted(); Expression(const ExpressionNode * n) : TreeHandle(n) {} + Expression(int nodeIdentifier) : TreeHandle(nodeIdentifier) {} + template + static Expression UntypedBuilderOneChild(Expression children) { + assert(children.type() == ExpressionNode::Type::Matrix); + return U::Builder(children.childAtIndex(0)); + } + template + static Expression UntypedBuilderTwoChildren(Expression children) { + assert(children.type() == ExpressionNode::Type::Matrix); + return U::Builder(children.childAtIndex(0), children.childAtIndex(1)); + } + template T convert() const { /* This function allows to convert Expression to derived Expressions. @@ -226,7 +274,7 @@ protected: * ie, you can write: 'Rational a(2); AbsoluteValue b(a);' * */ - assert(T::isExpression()); + assert(T::IsExpression()); static_assert(sizeof(T) == sizeof(Expression), "Size mismatch"); return *reinterpret_cast(const_cast(this)); } @@ -238,7 +286,6 @@ protected: } /* Hierarchy */ - Expression(int nodeIdentifier) : TreeHandle(nodeIdentifier) {} Expression parent() const; // TODO try to inline void defaultSetChildrenInPlace(Expression other); void addChildAtIndexInPlace(TreeHandle t, int index, int currentNumberOfChildren) = delete; @@ -253,43 +300,58 @@ protected: Expression defaultReplaceReplaceableSymbols(Context & context); /* Simplification */ - Expression denominator(Context & context, Preferences::AngleUnit angleUnit) const { return node()->denominator(context, angleUnit); } - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { return node()->shallowReduce(context, angleUnit, target); } - Expression shallowBeautify(Context & context, Preferences::AngleUnit angleUnit) { return node()->shallowBeautify(context, angleUnit); } - Expression deepBeautify(Context & context, Preferences::AngleUnit angleUnit); - Expression setSign(ExpressionNode::Sign s, Context & context, Preferences::AngleUnit angleUnit); + /* makePositiveAnyNegativeNumeralFactor looks for: + * - a negative numeral + * - a multiplication who has one numeral child whose is negative + * and turns the negative factor into a positive one. + * The given Expression should already be reduced and the return Expression + * is reduced (only a numeral factor was potentially made positive, and if it + * was -1, it was removed from the multiplication). + */ + Expression makePositiveAnyNegativeNumeralFactor(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); + Expression denominator(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { return node()->denominator(context, complexFormat, angleUnit); } + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { return node()->shallowReduce(context, complexFormat, angleUnit, target); } + Expression shallowBeautify(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { return node()->shallowBeautify(context, complexFormat, angleUnit, target); } + Expression deepBeautify(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); + Expression setSign(ExpressionNode::Sign s, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); private: static constexpr int k_maxSymbolReplacementsCount = 10; static bool sSymbolReplacementsCountLock; /* Simplification */ - Expression deepReduce(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); - void deepReduceChildren(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { - return node()->deepReduceChildren(context, angleUnit, target); + Expression deepReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); + void deepReduceChildren(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { + return node()->deepReduceChildren(context, complexFormat, angleUnit, target); } - void defaultDeepReduceChildren(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); - Expression defaultShallowReduce(Context & context, Preferences::AngleUnit angleUnit); - Expression defaultShallowBeautify(Context & context, Preferences::AngleUnit angleUnit) { return *this; } + void defaultDeepReduceChildren(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); + Expression defaultShallowReduce(); + Expression defaultShallowBeautify() { return *this; } /* Approximation */ - template Evaluation approximateToEvaluation(Context& context, Preferences::AngleUnit angleUnit) const; + template Evaluation approximateToEvaluation(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; /* Properties */ Expression defaultReplaceSymbolWithExpression(const SymbolAbstract & symbol, const Expression expression); int defaultGetPolynomialCoefficients(Context & context, const char * symbol, Expression expression[]) const; + /* Builder */ + static bool IsZero(const Expression e); + static bool IsOne(const Expression e); + static bool IsMinusOne(const Expression e); + static Expression CreateComplexExpression(Expression ra, Expression tb, Preferences::ComplexFormat complexFormat, bool undefined, bool isZeroRa, bool isOneRa, bool isZeroTb, bool isOneTb, bool isNegativeRa, bool isNegativeTb); + /* Expression roots/extrema solver*/ constexpr static double k_solverPrecision = 1.0E-5; constexpr static double k_sqrtEps = 1.4901161193847656E-8; // sqrt(DBL_EPSILON) constexpr static double k_goldenRatio = 0.381966011250105151795413165634361882279690820194237137864; // (3-sqrt(5))/2 constexpr static double k_maxFloat = 1e100; - typedef double (*EvaluationAtAbscissa)(const char * symbol, double abscissa, Context & context, Preferences::AngleUnit angleUnit, const Expression expression0, const Expression expression1); - Coordinate2D nextMinimumOfExpression(const char * symbol, double start, double step, double max, EvaluationAtAbscissa evaluation, Context & context, Preferences::AngleUnit angleUnit, const Expression expression = Expression(), bool lookForRootMinimum = false) const; - void bracketMinimum(const char * symbol, double start, double step, double max, double result[3], EvaluationAtAbscissa evaluation, Context & context, Preferences::AngleUnit angleUnit, const Expression expression = Expression()) const; - Coordinate2D brentMinimum(const char * symbol, double ax, double bx, EvaluationAtAbscissa evaluation, Context & context, Preferences::AngleUnit angleUnit, const Expression expression = Expression()) const; - double nextIntersectionWithExpression(const char * symbol, double start, double step, double max, EvaluationAtAbscissa evaluation, Context & context, Preferences::AngleUnit angleUnit, const Expression expression) const; - void bracketRoot(const char * symbol, double start, double step, double max, double result[2], EvaluationAtAbscissa evaluation, Context & context, Preferences::AngleUnit angleUnit, const Expression expression) const; - double brentRoot(const char * symbol, double ax, double bx, double precision, EvaluationAtAbscissa evaluation, Context & context, Preferences::AngleUnit angleUnit, const Expression expression) const; + typedef double (*EvaluationAtAbscissa)(const char * symbol, double abscissa, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const Expression expression0, const Expression expression1); + Coordinate2D nextMinimumOfExpression(const char * symbol, double start, double step, double max, EvaluationAtAbscissa evaluation, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const Expression expression = Expression(), bool lookForRootMinimum = false) const; + void bracketMinimum(const char * symbol, double start, double step, double max, double result[3], EvaluationAtAbscissa evaluation, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const Expression expression = Expression()) const; + Coordinate2D brentMinimum(const char * symbol, double ax, double bx, EvaluationAtAbscissa evaluation, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const Expression expression = Expression()) const; + double nextIntersectionWithExpression(const char * symbol, double start, double step, double max, EvaluationAtAbscissa evaluation, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const Expression expression) const; + void bracketRoot(const char * symbol, double start, double step, double max, double result[2], EvaluationAtAbscissa evaluation, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const Expression expression) const; + double brentRoot(const char * symbol, double ax, double bx, double precision, EvaluationAtAbscissa evaluation, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const Expression expression) const; }; } diff --git a/poincare/include/poincare/expression_node.h b/poincare/include/poincare/expression_node.h index 64d65bc67..372da561c 100644 --- a/poincare/include/poincare/expression_node.h +++ b/poincare/include/poincare/expression_node.h @@ -14,6 +14,8 @@ namespace Poincare { class SymbolAbstract; class Symbol; +class ComplexCartesian; +class ComplexPolar; class ExpressionNode : public TreeNode { friend class AdditionNode; @@ -25,6 +27,7 @@ public: enum class Type : uint8_t { Uninitialized = 0, Undefined = 1, + Unreal, Rational, Decimal, Float, @@ -34,6 +37,8 @@ public: Addition, Factorial, Division, + Constant, + Symbol, Store, Equal, Sine, @@ -46,6 +51,7 @@ public: BinomialCoefficient, Ceiling, ComplexArgument, + ComplexPolar, Conjugate, Derivative, Determinant, @@ -77,11 +83,12 @@ public: Randint, RealPart, Round, + SignFunction, SquareRoot, Subtraction, Sum, - Symbol, - Constant, + + ComplexCartesian, Matrix, ConfidenceInterval, @@ -96,16 +103,20 @@ public: virtual Type type() const = 0; /* Properties */ + enum class ReductionTarget { + System = 0, + User + }; enum class Sign { Negative = -1, Unknown = 0, Positive = 1 }; - virtual Sign sign() const { return Sign::Unknown; } + virtual Sign sign(Context * context) 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 Expression setSign(Sign s, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target); virtual int polynomialDegree(Context & context, const char * symbolName) const; /*!*/ virtual int getPolynomialCoefficients(Context & context, const char * symbolName, Expression coefficients[]) const; /*!*/ virtual Expression shallowReplaceReplaceableSymbols(Context & context); @@ -114,6 +125,9 @@ public: virtual float characteristicXRange(Context & context, Preferences::AngleUnit angleUnit) const; bool isOfType(Type * types, int length) const; + /* Complex */ + virtual bool isReal(Context & context) const { return false; } + /* Simplification */ /* SimplificationOrder returns: * 1 if e1 > e2 @@ -125,15 +139,15 @@ public: * together (ie Pi, 2*Pi). * Because SimplificationOrder is a recursive call, we sometimes enable its * interruption to avoid freezing in the simplification process. */ - static int SimplificationOrder(const ExpressionNode * e1, const ExpressionNode * e2, bool canBeInterrupted = false); + static int SimplificationOrder(const ExpressionNode * e1, const ExpressionNode * e2, bool ascending, bool canBeInterrupted = false); /* In the simplification order, most expressions are compared by only * comparing their types. However hierarchical expressions of same type would * compare their operands and thus need to reimplement * simplificationOrderSameType. Besides, operations that can be simplified * (ie +, *, ^, !) have specific rules to group like terms together and thus * reimplement simplificationOrderGreaterType. */ - virtual int simplificationOrderGreaterType(const ExpressionNode * e, bool canBeInterrupted) const { return -1; } - virtual int simplificationOrderSameType(const ExpressionNode * e, bool canBeInterrupted) const; + virtual int simplificationOrderGreaterType(const ExpressionNode * e, bool ascending, bool canBeInterrupted) const { return ascending ? -1 : 1; } + virtual int simplificationOrderSameType(const ExpressionNode * e, bool ascending, bool canBeInterrupted) const; /* Layout Helper */ virtual Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const = 0; @@ -142,20 +156,15 @@ public: typedef float SinglePrecision; typedef double DoublePrecision; constexpr static int k_maxNumberOfSteps = 10000; - virtual Evaluation approximate(SinglePrecision p, Context& context, Preferences::AngleUnit angleUnit) const = 0; - virtual Evaluation approximate(DoublePrecision p, Context& context, Preferences::AngleUnit angleUnit) const = 0; + virtual Evaluation approximate(SinglePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const = 0; + virtual Evaluation approximate(DoublePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const = 0; /* Simplification */ - enum class ReductionTarget { - BottomUpComputation = 0, - TopDownComputation = 1, - User - }; - /*!*/ virtual void deepReduceChildren(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target); - /*!*/ virtual Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target); - /*!*/ virtual Expression shallowBeautify(Context & context, Preferences::AngleUnit angleUnit); + /*!*/ virtual void deepReduceChildren(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target); + /*!*/ virtual Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target); + /*!*/ virtual Expression shallowBeautify(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target); /* Return a clone of the denominator part of the expression */ - /*!*/ virtual Expression denominator(Context & context, Preferences::AngleUnit angleUnit) const; + /*!*/ virtual Expression denominator(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; /* Hierarchy */ ExpressionNode * childAtIndex(int i) const override { return static_cast(TreeNode::childAtIndex(i)); } diff --git a/poincare/include/poincare/factor.h b/poincare/include/poincare/factor.h index 1663727de..8ca451238 100644 --- a/poincare/include/poincare/factor.h +++ b/poincare/include/poincare/factor.h @@ -25,28 +25,24 @@ private: /* Serialization */ int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; /* Simplification */ - Expression shallowBeautify(Context & context, Preferences::AngleUnit angleUnit) override; + Expression shallowBeautify(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) override; /* Evaluation */ - Evaluation approximate(SinglePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } - Evaluation approximate(DoublePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } - template Evaluation templatedApproximate(Context& context, Preferences::AngleUnit angleUnit) const { - return childAtIndex(0)->approximate(T(), context, angleUnit); + Evaluation approximate(SinglePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, complexFormat, angleUnit); } + Evaluation approximate(DoublePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, complexFormat, angleUnit); } + template Evaluation templatedApproximate(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { + return childAtIndex(0)->approximate(T(), context, complexFormat, angleUnit); } }; class Factor final : public Expression { public: Factor(const FactorNode * n) : Expression(n) {} - static Factor Builder(Expression child) { return Factor(child); } - static Expression UntypedBuilder(Expression children) { return Builder(children.childAtIndex(0)); } - static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("factor", 1, &UntypedBuilder); + static Factor Builder(Expression child) { return TreeHandle::FixedArityBuilder(&child, 1); } - Expression shallowBeautify(Context & context, Preferences::AngleUnit angleUnit); - Multiplication createMultiplicationOfIntegerPrimeDecomposition(Integer i, Context & context, Preferences::AngleUnit angleUnit) const; -private: - explicit Factor(Expression child) : Expression(TreePool::sharedPool()->createTreeNode()) { - replaceChildAtIndexInPlace(0, child); - } + static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("factor", 1, &UntypedBuilderOneChild); + + Expression shallowBeautify(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit); + Multiplication createMultiplicationOfIntegerPrimeDecomposition(Integer i, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; }; } diff --git a/poincare/include/poincare/factorial.h b/poincare/include/poincare/factorial.h index 4d2b97d16..2ea2ddb59 100644 --- a/poincare/include/poincare/factorial.h +++ b/poincare/include/poincare/factorial.h @@ -3,7 +3,6 @@ #include #include -#include namespace Poincare { @@ -21,21 +20,27 @@ public: // Properties Type type() const override { return Type::Factorial; } + Sign sign(Context * context) const override { return Sign::Positive; } + Expression setSign(Sign s, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) override; + + // Complex + bool isReal(Context & context) const override { return true; } + private: // Layout bool childNeedsParenthesis(const TreeNode * child) const override; Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; // Simplication - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) override; - Expression shallowBeautify(Context & context, Preferences::AngleUnit angleUnit) override; + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) override; + Expression shallowBeautify(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) override; // Evaluation - template static Complex computeOnComplex(const std::complex c, Preferences::AngleUnit angleUnit); - Evaluation approximate(SinglePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { - return ApproximationHelper::Map(this, context, angleUnit,computeOnComplex); + template static Complex computeOnComplex(const std::complex c, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit); + Evaluation approximate(SinglePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { + return ApproximationHelper::Map(this, context, complexFormat, angleUnit,computeOnComplex); } - Evaluation approximate(DoublePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { - return ApproximationHelper::Map(this, context, angleUnit, computeOnComplex); + Evaluation approximate(DoublePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { + return ApproximationHelper::Map(this, context, complexFormat, angleUnit, computeOnComplex); } #if 0 @@ -46,14 +51,11 @@ private: class Factorial final : public Expression { public: - Factorial(); Factorial(const FactorialNode * n) : Expression(n) {} - explicit Factorial(Expression child) : Factorial() { - replaceChildAtIndexInPlace(0, child); - } + static Factorial Builder(Expression child) { return TreeHandle::FixedArityBuilder(&child, 1); } - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit); - Expression shallowBeautify(Context & context, Preferences::AngleUnit angleUnit); + Expression shallowReduce(); + Expression shallowBeautify(); private: constexpr static int k_maxOperandValue = 100; }; diff --git a/poincare/include/poincare/float.h b/poincare/include/poincare/float.h index 6c243c6bf..9ecdaab70 100644 --- a/poincare/include/poincare/float.h +++ b/poincare/include/poincare/float.h @@ -20,10 +20,8 @@ namespace Poincare { template class FloatNode final : public NumberNode { public: - FloatNode() : m_value(0.0) {} + FloatNode(T value = 0.0) : m_value(value) {} - - void setFloat(T a) { m_value = a; } T value() const { return m_value; } // TreeNode @@ -39,20 +37,20 @@ public: // Properties Type type() const override { return Type::Float; } - Sign sign() const override { return m_value < 0 ? Sign::Negative : Sign::Positive; } - Expression setSign(Sign s, Context & context, Preferences::AngleUnit angleUnit) override; - int simplificationOrderSameType(const ExpressionNode * e, bool canBeInterrupted) const override; + Sign sign(Context * context) const override { return m_value < 0 ? Sign::Negative : Sign::Positive; } + Expression setSign(Sign s, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) override; + int simplificationOrderSameType(const ExpressionNode * e, bool ascending, bool canBeInterrupted) const override; // Layout int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; /* Layout */ Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; /* Evaluation */ - Evaluation approximate(SinglePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } - Evaluation approximate(DoublePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } + Evaluation approximate(SinglePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, complexFormat, angleUnit); } + Evaluation approximate(DoublePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, complexFormat, angleUnit); } private: - template Evaluation templatedApproximate(Context& context, Preferences::AngleUnit angleUnit) const { - return Complex((U)m_value); + template Evaluation templatedApproximate(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { + return Complex::Builder((U)m_value); } T m_value; }; @@ -60,7 +58,7 @@ private: template class Float final : public Number { public: - Float(T value); + static Float Builder(T value); private: FloatNode * node() const { return static_cast *>(Number::node()); } }; diff --git a/poincare/include/poincare/floor.h b/poincare/include/poincare/floor.h index 50f0da020..a7a06be5a 100644 --- a/poincare/include/poincare/floor.h +++ b/poincare/include/poincare/floor.h @@ -20,34 +20,35 @@ public: // Properties Type type() const override { return Type::Floor; } + + + // Complex + bool isReal(Context & context) const override { return true; } + private: // Layout Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; // Simplification - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) override; + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) override; // Evaluation - template static Complex computeOnComplex(const std::complex c, Preferences::AngleUnit angleUnit); - Evaluation approximate(SinglePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { - return ApproximationHelper::Map(this, context, angleUnit, computeOnComplex); + template static Complex computeOnComplex(const std::complex c, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit); + Evaluation approximate(SinglePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { + return ApproximationHelper::Map(this, context, complexFormat, angleUnit, computeOnComplex); } - Evaluation approximate(DoublePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { - return ApproximationHelper::Map(this, context, angleUnit, computeOnComplex); + Evaluation approximate(DoublePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { + return ApproximationHelper::Map(this, context, complexFormat, angleUnit, computeOnComplex); } }; class Floor final : public Expression { public: Floor(const FloorNode * n) : Expression(n) {} - static Floor Builder(Expression child) { return Floor(child); } - static Expression UntypedBuilder(Expression children) { return Builder(children.childAtIndex(0)); } - static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("floor", 1, &UntypedBuilder); + static Floor Builder(Expression child) { return TreeHandle::FixedArityBuilder(&child, 1); } - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit); -private: - explicit Floor(Expression child) : Expression(TreePool::sharedPool()->createTreeNode()) { - replaceChildAtIndexInPlace(0, child); - } + static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("floor", 1, &UntypedBuilderOneChild); + + Expression shallowReduce(); }; } diff --git a/poincare/include/poincare/floor_layout.h b/poincare/include/poincare/floor_layout.h index eaada21f5..0e560a917 100644 --- a/poincare/include/poincare/floor_layout.h +++ b/poincare/include/poincare/floor_layout.h @@ -29,9 +29,8 @@ protected: class FloorLayout final : public Layout { public: - explicit FloorLayout(Layout l) : Layout(TreePool::sharedPool()->createTreeNode()) { - replaceChildAtIndexInPlace(0, l); - } + static FloorLayout Builder(Layout child) { return TreeHandle::FixedArityBuilder(&child, 1); } + FloorLayout() = delete; }; } diff --git a/poincare/include/poincare/frac_part.h b/poincare/include/poincare/frac_part.h index cf85f4eed..d4ea74b51 100644 --- a/poincare/include/poincare/frac_part.h +++ b/poincare/include/poincare/frac_part.h @@ -20,34 +20,35 @@ public: // Properties Type type() const override { return Type::FracPart; } + + + // Complex + bool isReal(Context & context) const override { return true; } + private: // Layout Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; // Simplification - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) override; + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) override; // Evaluation - template static Complex computeOnComplex(const std::complex c, Preferences::AngleUnit angleUnit); - Evaluation approximate(SinglePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { - return ApproximationHelper::Map(this, context, angleUnit, computeOnComplex); + template static Complex computeOnComplex(const std::complex c, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit); + Evaluation approximate(SinglePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { + return ApproximationHelper::Map(this, context, complexFormat, angleUnit, computeOnComplex); } - Evaluation approximate(DoublePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { - return ApproximationHelper::Map(this, context, angleUnit, computeOnComplex); + Evaluation approximate(DoublePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { + return ApproximationHelper::Map(this, context, complexFormat, angleUnit, computeOnComplex); } }; class FracPart final : public Expression { public: FracPart(const FracPartNode * n) : Expression(n) {} - static FracPart Builder(Expression child) { return FracPart(child); } - static Expression UntypedBuilder(Expression children) { return Builder(children.childAtIndex(0)); } - static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("frac", 1, &UntypedBuilder); + static FracPart Builder(Expression child) { return TreeHandle::FixedArityBuilder(&child, 1); } - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit); -private: - explicit FracPart(Expression child) : Expression(TreePool::sharedPool()->createTreeNode()) { - replaceChildAtIndexInPlace(0, child); - } + static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("frac", 1, &UntypedBuilderOneChild); + + Expression shallowReduce(); }; } diff --git a/poincare/include/poincare/fraction_layout.h b/poincare/include/poincare/fraction_layout.h index 9239ccd6e..03b3979b0 100644 --- a/poincare/include/poincare/fraction_layout.h +++ b/poincare/include/poincare/fraction_layout.h @@ -58,7 +58,8 @@ private: class FractionLayout final : public Layout { public: - FractionLayout(Layout numerator, Layout denominator); + static FractionLayout Builder(Layout child0, Layout child1) { return TreeHandle::FixedArityBuilder(ArrayBuilder(child0, child1).array(), 2); } + FractionLayout() = delete; }; } diff --git a/poincare/include/poincare/function.h b/poincare/include/poincare/function.h index e94b248f3..c1109f0bd 100644 --- a/poincare/include/poincare/function.h +++ b/poincare/include/poincare/function.h @@ -8,6 +8,8 @@ namespace Poincare { class FunctionNode : public SymbolAbstractNode { public: + FunctionNode(const char * newName, int length); + // SymbolAbstractNode const char * name() const override { return m_name; } @@ -21,13 +23,15 @@ public: // Properties Type type() const override { return Type::Function; } - Sign sign() const override { return Sign::Unknown; } Expression replaceSymbolWithExpression(const SymbolAbstract & symbol, const Expression & expression) 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; float characteristicXRange(Context & context, Preferences::AngleUnit angleUnit) const override; + // Complex + bool isReal(Context & context) const override; + private: char m_name[0]; // MUST be the last member variable @@ -36,35 +40,24 @@ private: Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; // Simplification - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) override; + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) override; Expression shallowReplaceReplaceableSymbols(Context & context) override; // Evaluation - Evaluation approximate(SinglePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override; - Evaluation approximate(DoublePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override; - template Evaluation templatedApproximate(Context& context, Preferences::AngleUnit angleUnit) const; + Evaluation approximate(SinglePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override; + Evaluation approximate(DoublePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override; + template Evaluation templatedApproximate(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; }; class Function : public SymbolAbstract { friend class FunctionNode; public: - Function(const char * name, size_t length); Function(const FunctionNode * n) : SymbolAbstract(n) {} - Function(const char * name, size_t length, Expression child) : Function(name, length) { - replaceChildAtIndexInPlace(0, child); - } - static Expression UntypedBuilder(const char * name, size_t length, Expression child, Context * context) { - /* Create an expression only if it is not in the context or defined as a - * function */ - Function f(name, length, child); - if (SymbolAbstract::ValidInContext(f, context)) { - return f; - } - return Expression(); - } + static Function Builder(const char * name, size_t length, Expression child = Expression()); + static Expression UntypedBuilder(const char * name, size_t length, Expression child, Context * context); // Simplification Expression replaceSymbolWithExpression(const SymbolAbstract & symbol, const Expression & expression); - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); Expression shallowReplaceReplaceableSymbols(Context & context); private: //VariableContext unknownXContext(Context & parentContext) const; diff --git a/poincare/include/poincare/ghost.h b/poincare/include/poincare/ghost.h index a00098aee..1c7d3b56c 100644 --- a/poincare/include/poincare/ghost.h +++ b/poincare/include/poincare/ghost.h @@ -12,7 +12,7 @@ namespace Poincare { class Ghost final : public TreeHandle { public: - Ghost() : TreeHandle(TreePool::sharedPool()->createTreeNode()) {} + static Ghost Builder() { return TreeHandle::FixedArityBuilder(); } }; } diff --git a/poincare/include/poincare/great_common_divisor.h b/poincare/include/poincare/great_common_divisor.h index 3e12416c5..89ffdb891 100644 --- a/poincare/include/poincare/great_common_divisor.h +++ b/poincare/include/poincare/great_common_divisor.h @@ -19,32 +19,29 @@ public: // ExpressionNode Type type() const override { return Type::GreatCommonDivisor; } + // Complex + bool isReal(Context & context) const override { return true; } + private: // Layout Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; // Simplification - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) override; + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) override; // Evaluation - Evaluation approximate(SinglePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } - Evaluation approximate(DoublePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } - template Evaluation templatedApproximate(Context& context, Preferences::AngleUnit angleUnit) const; + Evaluation approximate(SinglePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, complexFormat, angleUnit); } + Evaluation approximate(DoublePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, complexFormat, angleUnit); } + template Evaluation templatedApproximate(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; }; class GreatCommonDivisor final : public Expression { public: GreatCommonDivisor(const GreatCommonDivisorNode * n) : Expression(n) {} - static GreatCommonDivisor Builder(Expression child0, Expression child1) { return GreatCommonDivisor(child0, child1); } - static Expression UntypedBuilder(Expression children) { return Builder(children.childAtIndex(0), children.childAtIndex(1)); } - static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("gcd", 2, &UntypedBuilder); + static GreatCommonDivisor Builder(Expression child0, Expression child1) { return TreeHandle::FixedArityBuilder(ArrayBuilder(child0, child1).array(), 2); } + static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("gcd", 2, &UntypedBuilderTwoChildren); // Expression - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit); -private: - GreatCommonDivisor(Expression child0, Expression child1) : Expression(TreePool::sharedPool()->createTreeNode()) { - replaceChildAtIndexInPlace(0, child0); - replaceChildAtIndexInPlace(1, child1); - } + Expression shallowReduce(); }; } diff --git a/poincare/include/poincare/grid_layout.h b/poincare/include/poincare/grid_layout.h index 562928911..d6f8f8f70 100644 --- a/poincare/include/poincare/grid_layout.h +++ b/poincare/include/poincare/grid_layout.h @@ -89,7 +89,8 @@ private: class GridLayout : public Layout { public: GridLayout(const GridLayoutNode * n) : Layout(n) {} - GridLayout() : Layout(TreePool::sharedPool()->createTreeNode()) {} + static GridLayout Builder() { return TreeHandle::NAryBuilder(); } + void setDimensions(int rows, int columns); void addChildAtIndex(Layout l, int index, int currentNumberOfChildren, LayoutCursor * cursor) { Layout::addChildAtIndex(l, index, currentNumberOfChildren, cursor); diff --git a/poincare/include/poincare/horizontal_layout.h b/poincare/include/poincare/horizontal_layout.h index 9a2c06811..6f11a7b78 100644 --- a/poincare/include/poincare/horizontal_layout.h +++ b/poincare/include/poincare/horizontal_layout.h @@ -65,13 +65,15 @@ private: class HorizontalLayout final : public Layout { friend class HorizontalLayoutNode; public: + // Constructors HorizontalLayout(HorizontalLayoutNode * n) : Layout(n) {} - HorizontalLayout(); - explicit HorizontalLayout(Layout l); - HorizontalLayout(Layout l1, Layout l2); - HorizontalLayout(Layout l1, Layout l2, Layout l3); - HorizontalLayout(Layout l1, Layout l2, Layout l3, Layout l4); - HorizontalLayout(const Layout * children, size_t numberOfChildren); + static HorizontalLayout Builder() { return TreeHandle::NAryBuilder(); } + static HorizontalLayout Builder(Layout l) { return HorizontalLayout::Builder(&l, 1); } + static HorizontalLayout Builder(Layout l1, Layout l2) { return HorizontalLayout::Builder(ArrayBuilder(l1, l2).array(), 2); } + static HorizontalLayout Builder(Layout l1, Layout l2, Layout l3) { return HorizontalLayout::Builder(ArrayBuilder(l1, l2, l3).array(), 3); } + static HorizontalLayout Builder(Layout l1, Layout l2, Layout l3, Layout l4) { return HorizontalLayout::Builder(ArrayBuilder(l1, l2, l3, l4).array(), 4); } + static HorizontalLayout Builder(Layout * children, size_t numberOfChildren) { return TreeHandle::NAryBuilder(static_cast(children), numberOfChildren); } + void addChildAtIndex(Layout l, int index, int currentNumberOfChildren, LayoutCursor * cursor, bool removeEmptyChildren = false); // Remove puts a child at the end of the pool void removeChild(Layout l, LayoutCursor * cursor, bool force = false) { @@ -82,6 +84,7 @@ public: } void addOrMergeChildAtIndex(Layout l, int index, bool removeEmptyChildren, LayoutCursor * cursor = nullptr); void mergeChildrenAtIndex(HorizontalLayout h, int index, bool removeEmptyChildren, LayoutCursor * cursor = nullptr); + private: void removeEmptyChildBeforeInsertionAtIndex(int * index, int * currentNumberOfChildren, bool shouldRemoveOnLeft, LayoutCursor * cursor = nullptr); }; diff --git a/poincare/include/poincare/hyperbolic_arc_cosine.h b/poincare/include/poincare/hyperbolic_arc_cosine.h index e1e86c1ed..59f6e725f 100644 --- a/poincare/include/poincare/hyperbolic_arc_cosine.h +++ b/poincare/include/poincare/hyperbolic_arc_cosine.h @@ -24,25 +24,21 @@ private: Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; //Evaluation - template static Complex computeOnComplex(const std::complex c, Preferences::AngleUnit angleUnit); - Evaluation approximate(SinglePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { - return ApproximationHelper::Map(this, context, angleUnit,computeOnComplex); + template static Complex computeOnComplex(const std::complex c, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit); + Evaluation approximate(SinglePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { + return ApproximationHelper::Map(this, context, complexFormat, angleUnit,computeOnComplex); } - Evaluation approximate(DoublePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { - return ApproximationHelper::Map(this, context, angleUnit, computeOnComplex); + Evaluation approximate(DoublePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { + return ApproximationHelper::Map(this, context, complexFormat, angleUnit, computeOnComplex); } }; class HyperbolicArcCosine final : public HyperbolicTrigonometricFunction { public: HyperbolicArcCosine(const HyperbolicArcCosineNode * n) : HyperbolicTrigonometricFunction(n) {} - static HyperbolicArcCosine Builder(Expression child) { return HyperbolicArcCosine(child); } - static Expression UntypedBuilder(Expression children) { return Builder(children.childAtIndex(0)); } - static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("acosh", 1, &UntypedBuilder); -private: - explicit HyperbolicArcCosine(Expression child) : HyperbolicTrigonometricFunction(TreePool::sharedPool()->createTreeNode()) { - replaceChildAtIndexInPlace(0, child); - } + static HyperbolicArcCosine Builder(Expression child) { return TreeHandle::FixedArityBuilder(&child, 1); } + + static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("acosh", 1, &UntypedBuilderOneChild); }; } diff --git a/poincare/include/poincare/hyperbolic_arc_sine.h b/poincare/include/poincare/hyperbolic_arc_sine.h index 57f0a4f1c..5f8b4d4b7 100644 --- a/poincare/include/poincare/hyperbolic_arc_sine.h +++ b/poincare/include/poincare/hyperbolic_arc_sine.h @@ -24,25 +24,21 @@ private: Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; //Evaluation - template static Complex computeOnComplex(const std::complex c, Preferences::AngleUnit angleUnit); - Evaluation approximate(SinglePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { - return ApproximationHelper::Map(this, context, angleUnit,computeOnComplex); + template static Complex computeOnComplex(const std::complex c, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit); + Evaluation approximate(SinglePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { + return ApproximationHelper::Map(this, context, complexFormat, angleUnit,computeOnComplex); } - Evaluation approximate(DoublePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { - return ApproximationHelper::Map(this, context, angleUnit, computeOnComplex); + Evaluation approximate(DoublePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { + return ApproximationHelper::Map(this, context, complexFormat, angleUnit, computeOnComplex); } }; class HyperbolicArcSine final : public HyperbolicTrigonometricFunction { public: HyperbolicArcSine(const HyperbolicArcSineNode * n) : HyperbolicTrigonometricFunction(n) {} - static HyperbolicArcSine Builder(Expression child) { return HyperbolicArcSine(child); } - static Expression UntypedBuilder(Expression children) { return Builder(children.childAtIndex(0)); } - static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("asinh", 1, &UntypedBuilder); -private: - explicit HyperbolicArcSine(Expression child) : HyperbolicTrigonometricFunction(TreePool::sharedPool()->createTreeNode()) { - replaceChildAtIndexInPlace(0, child); - } + static HyperbolicArcSine Builder(Expression child) { return TreeHandle::FixedArityBuilder(&child, 1); } + + static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("asinh", 1, &UntypedBuilderOneChild); }; } diff --git a/poincare/include/poincare/hyperbolic_arc_tangent.h b/poincare/include/poincare/hyperbolic_arc_tangent.h index fa11c457a..2244efbf5 100644 --- a/poincare/include/poincare/hyperbolic_arc_tangent.h +++ b/poincare/include/poincare/hyperbolic_arc_tangent.h @@ -24,25 +24,21 @@ private: Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; //Evaluation - template static Complex computeOnComplex(const std::complex c, Preferences::AngleUnit angleUnit); - Evaluation approximate(SinglePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { - return ApproximationHelper::Map(this, context, angleUnit,computeOnComplex); + template static Complex computeOnComplex(const std::complex c, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit); + Evaluation approximate(SinglePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { + return ApproximationHelper::Map(this, context, complexFormat, angleUnit,computeOnComplex); } - Evaluation approximate(DoublePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { - return ApproximationHelper::Map(this, context, angleUnit, computeOnComplex); + Evaluation approximate(DoublePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { + return ApproximationHelper::Map(this, context, complexFormat, angleUnit, computeOnComplex); } }; class HyperbolicArcTangent final : public HyperbolicTrigonometricFunction { public: HyperbolicArcTangent(const HyperbolicArcTangentNode * n) : HyperbolicTrigonometricFunction(n) {} - static HyperbolicArcTangent Builder(Expression child) { return HyperbolicArcTangent(child); } - static Expression UntypedBuilder(Expression children) { return Builder(children.childAtIndex(0)); } - static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("atanh", 1, &UntypedBuilder); -private: - explicit HyperbolicArcTangent(Expression child) : HyperbolicTrigonometricFunction(TreePool::sharedPool()->createTreeNode()) { - replaceChildAtIndexInPlace(0, child); - } + static HyperbolicArcTangent Builder(Expression child) { return TreeHandle::FixedArityBuilder(&child, 1); } + + static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("atanh", 1, &UntypedBuilderOneChild); }; } diff --git a/poincare/include/poincare/hyperbolic_cosine.h b/poincare/include/poincare/hyperbolic_cosine.h index c165ca1b2..67ab4cf80 100644 --- a/poincare/include/poincare/hyperbolic_cosine.h +++ b/poincare/include/poincare/hyperbolic_cosine.h @@ -24,25 +24,21 @@ private: Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; //Evaluation - template static Complex computeOnComplex(const std::complex c, Preferences::AngleUnit angleUnit); - Evaluation approximate(SinglePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { - return ApproximationHelper::Map(this, context, angleUnit,computeOnComplex); + template static Complex computeOnComplex(const std::complex c, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit); + Evaluation approximate(SinglePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { + return ApproximationHelper::Map(this, context, complexFormat, angleUnit,computeOnComplex); } - Evaluation approximate(DoublePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { - return ApproximationHelper::Map(this, context, angleUnit, computeOnComplex); + Evaluation approximate(DoublePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { + return ApproximationHelper::Map(this, context, complexFormat, angleUnit, computeOnComplex); } }; class HyperbolicCosine final : public HyperbolicTrigonometricFunction { public: HyperbolicCosine(const HyperbolicCosineNode * n) : HyperbolicTrigonometricFunction(n) {} - static HyperbolicCosine Builder(Expression child) { return HyperbolicCosine(child); } - static Expression UntypedBuilder(Expression children) { return Builder(children.childAtIndex(0)); } - static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("cosh", 1, &UntypedBuilder); -private: - explicit HyperbolicCosine(Expression child) : HyperbolicTrigonometricFunction(TreePool::sharedPool()->createTreeNode()) { - replaceChildAtIndexInPlace(0, child); - } + static HyperbolicCosine Builder(Expression child) { return TreeHandle::FixedArityBuilder(&child, 1); } + + static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("cosh", 1, &UntypedBuilderOneChild); }; } diff --git a/poincare/include/poincare/hyperbolic_sine.h b/poincare/include/poincare/hyperbolic_sine.h index 105163ba7..6686b4647 100644 --- a/poincare/include/poincare/hyperbolic_sine.h +++ b/poincare/include/poincare/hyperbolic_sine.h @@ -24,25 +24,21 @@ private: Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; //Evaluation - template static Complex computeOnComplex(const std::complex c, Preferences::AngleUnit angleUnit); - Evaluation approximate(SinglePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { - return ApproximationHelper::Map(this, context, angleUnit,computeOnComplex); + template static Complex computeOnComplex(const std::complex c, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit); + Evaluation approximate(SinglePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { + return ApproximationHelper::Map(this, context, complexFormat, angleUnit,computeOnComplex); } - Evaluation approximate(DoublePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { - return ApproximationHelper::Map(this, context, angleUnit, computeOnComplex); + Evaluation approximate(DoublePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { + return ApproximationHelper::Map(this, context, complexFormat, angleUnit, computeOnComplex); } }; class HyperbolicSine final : public HyperbolicTrigonometricFunction { public: HyperbolicSine(const HyperbolicSineNode * n) : HyperbolicTrigonometricFunction(n) {} - static HyperbolicSine Builder(Expression child) { return HyperbolicSine(child); } - static Expression UntypedBuilder(Expression children) { return Builder(children.childAtIndex(0)); } - static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("sinh", 1, &UntypedBuilder); -private: - explicit HyperbolicSine(Expression child) : HyperbolicTrigonometricFunction(TreePool::sharedPool()->createTreeNode()) { - replaceChildAtIndexInPlace(0, child); - } + static HyperbolicSine Builder(Expression child) { return TreeHandle::FixedArityBuilder(&child, 1); } + + static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("sinh", 1, &UntypedBuilderOneChild); }; } diff --git a/poincare/include/poincare/hyperbolic_tangent.h b/poincare/include/poincare/hyperbolic_tangent.h index faf011013..ba6ec8693 100644 --- a/poincare/include/poincare/hyperbolic_tangent.h +++ b/poincare/include/poincare/hyperbolic_tangent.h @@ -24,25 +24,21 @@ private: Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; //Evaluation - template static Complex computeOnComplex(const std::complex c, Preferences::AngleUnit angleUnit); - Evaluation approximate(SinglePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { - return ApproximationHelper::Map(this, context, angleUnit,computeOnComplex); + template static Complex computeOnComplex(const std::complex c, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit); + Evaluation approximate(SinglePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { + return ApproximationHelper::Map(this, context, complexFormat, angleUnit,computeOnComplex); } - Evaluation approximate(DoublePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { - return ApproximationHelper::Map(this, context, angleUnit, computeOnComplex); + Evaluation approximate(DoublePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { + return ApproximationHelper::Map(this, context, complexFormat, angleUnit, computeOnComplex); } }; class HyperbolicTangent final : public HyperbolicTrigonometricFunction { public: HyperbolicTangent(const HyperbolicTangentNode * n) : HyperbolicTrigonometricFunction(n) {} - static HyperbolicTangent Builder(Expression child) { return HyperbolicTangent(child); } - static Expression UntypedBuilder(Expression children) { return Builder(children.childAtIndex(0)); } - static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("tanh", 1, &UntypedBuilder); -private: - explicit HyperbolicTangent(Expression child) : HyperbolicTrigonometricFunction(TreePool::sharedPool()->createTreeNode()) { - replaceChildAtIndexInPlace(0, child); - } + static HyperbolicTangent Builder(Expression child) { return TreeHandle::FixedArityBuilder(&child, 1); } + + static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("tanh", 1, &UntypedBuilderOneChild); }; } diff --git a/poincare/include/poincare/hyperbolic_trigonometric_function.h b/poincare/include/poincare/hyperbolic_trigonometric_function.h index 3f9edac25..98b4a8d7a 100644 --- a/poincare/include/poincare/hyperbolic_trigonometric_function.h +++ b/poincare/include/poincare/hyperbolic_trigonometric_function.h @@ -12,13 +12,13 @@ public: int numberOfChildren() const override { return 1; } private: // Simplification - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) override; + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) override; }; class HyperbolicTrigonometricFunction : public Expression { public: HyperbolicTrigonometricFunction(const HyperbolicTrigonometricFunctionNode * n) : Expression(n) {} - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit); + Expression shallowReduce(); }; } diff --git a/poincare/include/poincare/imaginary_part.h b/poincare/include/poincare/imaginary_part.h index f79044cb8..c26367424 100644 --- a/poincare/include/poincare/imaginary_part.h +++ b/poincare/include/poincare/imaginary_part.h @@ -20,36 +20,36 @@ public: // Properties Type type() const override { return Type::ImaginaryPart; } + + // Complex + bool isReal(Context & context) const override { return true; } + private: // Layout Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; // Simplification - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) override; + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) override; // Evaluation - template static Complex computeOnComplex(const std::complex c, Preferences::AngleUnit angleUnit) { - return Complex(std::imag(c)); + template static Complex computeOnComplex(const std::complex c, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) { + return Complex::Builder(std::imag(c)); } - Evaluation approximate(SinglePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { - return ApproximationHelper::Map(this, context, angleUnit,computeOnComplex); + Evaluation approximate(SinglePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { + return ApproximationHelper::Map(this, context, complexFormat, angleUnit,computeOnComplex); } - Evaluation approximate(DoublePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { - return ApproximationHelper::Map(this, context, angleUnit, computeOnComplex); + Evaluation approximate(DoublePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { + return ApproximationHelper::Map(this, context, complexFormat, angleUnit, computeOnComplex); } }; class ImaginaryPart final : public Expression { public: ImaginaryPart(const ImaginaryPartNode * n) : Expression(n) {} - static ImaginaryPart Builder(Expression child) { return ImaginaryPart(child); } - static Expression UntypedBuilder(Expression children) { return Builder(children.childAtIndex(0)); } - static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("im", 1, &UntypedBuilder); + static ImaginaryPart Builder(Expression child) { return TreeHandle::FixedArityBuilder(&child, 1); } - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit); -private: - explicit ImaginaryPart(Expression child) : Expression(TreePool::sharedPool()->createTreeNode()) { - replaceChildAtIndexInPlace(0, child); - } + static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("im", 1, &UntypedBuilderOneChild); + + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); }; } diff --git a/poincare/include/poincare/infinity.h b/poincare/include/poincare/infinity.h index 6cd6efdb9..b1b552394 100644 --- a/poincare/include/poincare/infinity.h +++ b/poincare/include/poincare/infinity.h @@ -7,9 +7,9 @@ namespace Poincare { class InfinityNode final : public NumberNode { public: + InfinityNode(bool negative) : NumberNode(), m_negative(negative) {} - void setNegative(bool negative) { m_negative = negative; } - Expression setSign(Sign s, Context & context, Preferences::AngleUnit angleUnit) override; + Expression setSign(Sign s, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) override; // TreeNode size_t size() const override { return sizeof(InfinityNode); } @@ -24,13 +24,13 @@ public: // Properties Type type() const override { return Type::Infinity; } - Sign sign() const override { return m_negative ? Sign::Negative : Sign::Positive; } + Sign sign(Context * context) const override { return m_negative ? Sign::Negative : Sign::Positive; } // Approximation - Evaluation approximate(SinglePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { + Evaluation approximate(SinglePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(); } - Evaluation approximate(DoublePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { + Evaluation approximate(DoublePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(); } @@ -45,10 +45,8 @@ private: class Infinity final : public Number { public: Infinity(InfinityNode * n) : Number(n) {} - Infinity(bool negative) : Number(TreePool::sharedPool()->createTreeNode()) { - node()->setNegative(negative); - } - Expression setSign(ExpressionNode::Sign s, Context & context, Preferences::AngleUnit angleUnit); + static Infinity Builder(bool negative); + Expression setSign(ExpressionNode::Sign s, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit); static const char * Name() { return "inf"; } diff --git a/poincare/include/poincare/integer.h b/poincare/include/poincare/integer.h index 041cff08e..11aea50db 100644 --- a/poincare/include/poincare/integer.h +++ b/poincare/include/poincare/integer.h @@ -29,32 +29,69 @@ static_assert(sizeof(double_native_int_t) == 2*sizeof(native_int_t), "double_nat struct IntegerDivision; -class Integer final { +class IntegerNode final : public TreeNode { +public: + IntegerNode(const native_uint_t * digits, uint8_t numberOfDigits); + // TreeNode + size_t size() const override; + int numberOfChildren() const override { return 0; } +#if POINCARE_TREE_LOG + void log(std::ostream & stream) const; + virtual void logNodeName(std::ostream & stream) const override { + stream << "Integer"; + } + virtual void logAttributes(std::ostream & stream) const override; +#endif + + const native_uint_t * digits() const { return m_digits; } + uint8_t numberOfDigits() const { return m_numberOfDigits; } +private: + uint8_t m_numberOfDigits; // In base native_uint_t + native_uint_t m_digits[0]; // Little-endian +}; + +class Integer final : public TreeHandle { public: /* Constructors & Destructors */ + static Integer BuildInteger(native_uint_t * digits, uint16_t numberOfDigits, bool negative, bool enableOverflow = false); Integer(native_int_t i = 0); Integer(double_native_int_t i); Integer(const char * digits, size_t length, bool negative); Integer(const char * digits) : Integer(digits, strlen(digits), false) {} - static Integer Overflow(bool negative) { return Integer((native_uint_t *)nullptr, k_maxNumberOfDigits+1, negative); } - static Integer BuildInteger(native_uint_t * digits, uint16_t numberOfDigits, bool negative, bool enableOverflow = false); - ~Integer(); - static void TidyIntegerBuffer(); - + static Integer Overflow(bool negative) { return Integer(OverflowIdentifier, negative); } #if POINCARE_TREE_LOG - void log(std::ostream & stream = std::cout) const; + void logInteger(std::ostream & stream) const { + if (isOverflow()) { + stream << "overflow"; + return; + } else if (usesImmediateDigit()) { + stream << m_digit; + return; + } + node()->log(stream); + } #endif - /* Copy/Move constructors/assignments */ - Integer(Integer&& other); // C++11 move constructor - Integer& operator=(Integer&& other); // C++11 move assignment operator - Integer(const Integer& other); // C++11 copy constructor - Integer& operator=(const Integer& other); // C++11 copy assignment operator - // Getters - const native_uint_t * digits() const { return usesImmediateDigit() ? &m_digit : m_digits; } - uint8_t numberOfDigits() const { return m_numberOfDigits; } + const native_uint_t * digits() const { + if (usesImmediateDigit()) { + return &m_digit; + } else if (isOverflow()) { + return nullptr; + } + return node()->digits(); + } + uint8_t numberOfDigits() const { + if (usesImmediateDigit()) { + return m_digit == 0 ? 0 : 1; + } else if (isOverflow()) { + return k_maxNumberOfDigits+1; + } + return node()->numberOfDigits(); + } + bool isNegative() const { return m_negative; } + void setNegative(bool negative) { m_negative = numberOfDigits() > 0 ? negative : false; } // 0 is always positive // Serialization int serialize(char * buffer, int bufferSize) const; @@ -66,21 +103,22 @@ public: template T approximate() const; // Sign - bool isNegative() const { return m_negative; } - void setNegative(bool negative) { m_negative = m_numberOfDigits > 0 ? negative : false; } // Properties + /* An integer can have (k_maxNumberOfDigits + 1) digits: either when it is an + * overflow, or when we want to have one more digit than usual to compute a + * big division. */ + bool isOverflow() const { return m_identifier == OverflowIdentifier; } static int NumberOfBase10DigitsWithoutSign(const Integer & i); - bool isOne() const { return (m_numberOfDigits == 1 && digit(0) == 1 && !m_negative); }; - bool isTwo() const { return (m_numberOfDigits == 1 && digit(0) == 2 && !m_negative); }; - bool isTen() const { return (m_numberOfDigits == 1 && digit(0) == 10 && !m_negative); }; - bool isMinusOne() const { return (m_numberOfDigits == 1 && digit(0) == 1 && m_negative); }; - bool isZero() const { return (m_numberOfDigits == 0); }; - bool isInfinity() const { return m_numberOfDigits > k_maxNumberOfDigits; } + bool isOne() const { return (numberOfDigits() == 1 && digit(0) == 1 && !m_negative); }; + bool isTwo() const { return (numberOfDigits() == 1 && digit(0) == 2 && !m_negative); }; + bool isTen() const { return (numberOfDigits() == 1 && digit(0) == 10 && !m_negative); }; + bool isMinusOne() const { return (numberOfDigits() == 1 && digit(0) == 1 && m_negative); }; + bool isZero() const { return (numberOfDigits() == 0); }; bool isEven() const { return ((digit(0) & 1) == 0); } constexpr static int k_maxExtractableInteger = 0x7FFFFFFF; - int extractedInt() const { assert(m_numberOfDigits == 0 || (m_numberOfDigits <= 1 && digit(0) <= k_maxExtractableInteger)); return m_numberOfDigits == 0 ? 0 : (m_negative ? -digit(0) : digit(0)); } + int extractedInt() const { assert(numberOfDigits() == 0 || (numberOfDigits() <= 1 && digit(0) <= k_maxExtractableInteger)); return numberOfDigits() == 0 ? 0 : (m_negative ? -digit(0) : digit(0)); } // Comparison static int NaturalOrder(const Integer & i, const Integer & j); @@ -100,23 +138,14 @@ public: static Integer Factorial(const Integer & i); constexpr static int k_maxNumberOfDigits = 32; - constexpr static int k_maxNumberOfIntegerSimutaneously = 16; private: constexpr static int k_maxNumberOfDigitsBase10 = 308; // (2^32)^k_maxNumberOfDigits ~ 1E308 - - Integer(native_uint_t * digits, uint16_t numberOfDigits, bool negative, bool enableOverflow = false); - - // Dynamic allocation - /* In order to guarantee the potential existence of 16 Integers simutaneously, - * we keep a table uint32_t that can contain up to 16 Integers with the - * maximal numbers of digits. We also give them one extra digit to be able to - * perform complex operations (like division) which involve Integers with one - * additional digit. */ - static native_uint_t * allocDigits(int numberOfDigits); - static void freeDigits(native_uint_t * digits); + static constexpr int OverflowIdentifier = TreeNode::NoNodeIdentifier - 1; // Constructors - void releaseDynamicIvars(); + Integer(native_uint_t * digits, uint16_t numberOfDigits, bool negative); + Integer(int identifier, bool negative) : TreeHandle(identifier), m_negative(negative) {} + IntegerNode * node() const { return static_cast(TreeHandle::node()); } // Arithmetic static Integer addition(const Integer & a, const Integer & b, bool inverseBNegative, bool enableOneDigitOverflow = false); @@ -133,36 +162,29 @@ private: // HalfDigits uint16_t numberOfHalfDigits() const { - if (m_numberOfDigits == 0) { return 0; } - native_uint_t d = digit(m_numberOfDigits-1); + if (numberOfDigits() == 0) { return 0; } + native_uint_t d = digit(numberOfDigits()-1); native_uint_t halfBase = 1 << (8*sizeof(half_native_uint_t)); - return (d >= halfBase ? 2*m_numberOfDigits : 2*m_numberOfDigits-1); + return (d >= halfBase ? 2*numberOfDigits() : 2*numberOfDigits()-1); } half_native_uint_t halfDigit(int i) const { assert(i >= 0); if (i >= numberOfHalfDigits()) { return 0; } - return (usesImmediateDigit() ? ((half_native_uint_t *)&m_digit)[i] : ((half_native_uint_t *)m_digits)[i]); + return (usesImmediateDigit() ? ((half_native_uint_t *)&m_digit)[i] : ((half_native_uint_t *)digits())[i]); } - bool usesImmediateDigit() const { return m_numberOfDigits == 1; } native_uint_t digit(uint8_t i) const { - assert(i >= 0 && i < m_numberOfDigits); - return (usesImmediateDigit() ? m_digit : m_digits[i]); + assert(!isOverflow()); + assert(i >= 0 && i < numberOfDigits()); + return (usesImmediateDigit() ? m_digit : digits()[i]); } - /* An integer can have (k_maxNumberOfDigits + 1) digits: either when it is an - * overflow, or when we want to have one more digit than usual to compute a - * big division. */ - bool isOverflow() const { return m_numberOfDigits == k_maxNumberOfDigits + 1 && m_digits == nullptr; } + bool usesImmediateDigit() const { return m_identifier == TreeNode::NoNodeIdentifier; } bool m_negative; - uint8_t m_numberOfDigits; // In base native_uint_t - union { - native_uint_t * m_digits; // Little-endian - native_uint_t m_digit; - }; + native_uint_t m_digit; }; struct IntegerDivision { diff --git a/poincare/include/poincare/integral.h b/poincare/include/poincare/integral.h index 9c2f08dfa..241521fc2 100644 --- a/poincare/include/poincare/integral.h +++ b/poincare/include/poincare/integral.h @@ -23,16 +23,19 @@ public: int polynomialDegree(Context & context, const char * symbolName) const override; Expression replaceUnknown(const Symbol & symbol) override; + // Complex + bool isReal(Context & context) const override { return true; } + private: // Layout Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; // Simplification - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) override; + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) override; // Evaluation - Evaluation approximate(SinglePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } - Evaluation approximate(DoublePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } - template Evaluation templatedApproximate(Context& context, Preferences::AngleUnit angleUnit) const; + Evaluation approximate(SinglePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, complexFormat, angleUnit); } + Evaluation approximate(DoublePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, complexFormat, angleUnit); } + template Evaluation templatedApproximate(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; template struct DetailedResult { @@ -41,37 +44,24 @@ private: }; constexpr static int k_maxNumberOfIterations = 10; #ifdef LAGRANGE_METHOD - template T lagrangeGaussQuadrature(T a, T b, Context & context, Preferences::AngleUnit angleUnit) const; + template T lagrangeGaussQuadrature(T a, T b, Context Context & context, Preferences::AngleUnit angleUnit context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; #else - template DetailedResult kronrodGaussQuadrature(T a, T b, Context & context, Preferences::AngleUnit angleUnit) const; - template T adaptiveQuadrature(T a, T b, T eps, int numberOfIterations, Context & context, Preferences::AngleUnit angleUnit) const; + template DetailedResult kronrodGaussQuadrature(T a, T b, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; + template T adaptiveQuadrature(T a, T b, T eps, int numberOfIterations, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; #endif - template T functionValueAtAbscissa(T x, Context & xcontext, Preferences::AngleUnit angleUnit) const; + template T functionValueAtAbscissa(T x, Context & xcontext, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; }; class Integral final : public Expression { public: Integral(const IntegralNode * n) : Expression(n) {} - static Integral Builder(Expression child0, Symbol child1, Expression child2, Expression child3) { return Integral(child0, child1, child2, child3); } - static Expression UntypedBuilder(Expression children) { - if (children.childAtIndex(1).type() != ExpressionNode::Type::Symbol) { - // Second parameter must be a Symbol. - return Expression(); - } - return Builder(children.childAtIndex(0), children.childAtIndex(1).convert(), children.childAtIndex(2), children.childAtIndex(3)); - } + static Integral Builder(Expression child0, Symbol child1, Expression child2, Expression child3) { return TreeHandle::FixedArityBuilder(ArrayBuilder(child0, child1, child2, child3).array(), 4); } + static Expression UntypedBuilder(Expression children); + static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("int", 4, &UntypedBuilder); // Expression - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit); -private: - Integral(Expression child0, Expression child1, Expression child2, Expression child3) : Expression(TreePool::sharedPool()->createTreeNode()) { - assert(child1.type() == ExpressionNode::Type::Symbol); - replaceChildAtIndexInPlace(0, child0); - replaceChildAtIndexInPlace(1, child1); - replaceChildAtIndexInPlace(2, child2); - replaceChildAtIndexInPlace(3, child3); - } + Expression shallowReduce(); }; } diff --git a/poincare/include/poincare/integral_layout.h b/poincare/include/poincare/integral_layout.h index f252c308f..30b8901cf 100644 --- a/poincare/include/poincare/integral_layout.h +++ b/poincare/include/poincare/integral_layout.h @@ -56,14 +56,8 @@ private: class IntegralLayout final : public Layout { public: - IntegralLayout(Layout integrand, Layout differential, Layout lowerBound, Layout upperBound) : - Layout(TreePool::sharedPool()->createTreeNode()) - { - replaceChildAtIndexInPlace(0, integrand); - replaceChildAtIndexInPlace(1, differential); - replaceChildAtIndexInPlace(2, lowerBound); - replaceChildAtIndexInPlace(3, upperBound); - } + static IntegralLayout Builder(Layout integrand, Layout differential, Layout lowerBound, Layout upperBound) { return TreeHandle::FixedArityBuilder(ArrayBuilder(integrand, differential, lowerBound, upperBound).array(), 4); } + IntegralLayout() = delete; }; } diff --git a/poincare/include/poincare/layout.h b/poincare/include/poincare/layout.h index 53d3dda14..2dfffdcd9 100644 --- a/poincare/include/poincare/layout.h +++ b/poincare/include/poincare/layout.h @@ -1,6 +1,7 @@ #ifndef POINCARE_LAYOUT_REFERENCE_H #define POINCARE_LAYOUT_REFERENCE_H +#include #include #include diff --git a/poincare/include/poincare/least_common_multiple.h b/poincare/include/poincare/least_common_multiple.h index c36b64ec3..1fc06b42a 100644 --- a/poincare/include/poincare/least_common_multiple.h +++ b/poincare/include/poincare/least_common_multiple.h @@ -18,32 +18,30 @@ public: #endif // ExpressionNode Type type() const override { return Type::LeastCommonMultiple; } + + // Complex + bool isReal(Context & context) const override { return true; } + private: /* Layout */ Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; /* Simplification */ - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) override; + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) override; /* Evaluation */ - Evaluation approximate(SinglePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } - Evaluation approximate(DoublePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } - template Evaluation templatedApproximate(Context& context, Preferences::AngleUnit angleUnit) const; + Evaluation approximate(SinglePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, complexFormat, angleUnit); } + Evaluation approximate(DoublePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, complexFormat, angleUnit); } + template Evaluation templatedApproximate(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; }; class LeastCommonMultiple final : public Expression { public: LeastCommonMultiple(const LeastCommonMultipleNode * n) : Expression(n) {} - static LeastCommonMultiple Builder(Expression child0, Expression child1) { return LeastCommonMultiple(child0, child1); } - static Expression UntypedBuilder(Expression children) { return Builder(children.childAtIndex(0), children.childAtIndex(1)); } - static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("lcm", 2, &UntypedBuilder); + static LeastCommonMultiple Builder(Expression child0, Expression child1) { return TreeHandle::FixedArityBuilder(ArrayBuilder(child0, child1).array(), 2); } + static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("lcm", 2, &UntypedBuilderTwoChildren); // Expression - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit); -private: - LeastCommonMultiple(Expression child0, Expression child1) : Expression(TreePool::sharedPool()->createTreeNode()) { - replaceChildAtIndexInPlace(0, child0); - replaceChildAtIndexInPlace(1, child1); - } + Expression shallowReduce(); }; } diff --git a/poincare/include/poincare/left_parenthesis_layout.h b/poincare/include/poincare/left_parenthesis_layout.h index 59747765a..ffc0fe6df 100644 --- a/poincare/include/poincare/left_parenthesis_layout.h +++ b/poincare/include/poincare/left_parenthesis_layout.h @@ -35,7 +35,8 @@ protected: class LeftParenthesisLayout final : public Layout { public: - LeftParenthesisLayout(); + static LeftParenthesisLayout Builder() { return TreeHandle::FixedArityBuilder(); } + LeftParenthesisLayout() = delete; }; } diff --git a/poincare/include/poincare/left_square_bracket_layout.h b/poincare/include/poincare/left_square_bracket_layout.h index 80dc2e75e..00720398a 100644 --- a/poincare/include/poincare/left_square_bracket_layout.h +++ b/poincare/include/poincare/left_square_bracket_layout.h @@ -29,7 +29,8 @@ protected: class LeftSquareBracketLayout final : public Layout { public: - LeftSquareBracketLayout(); + static LeftSquareBracketLayout Builder() { return TreeHandle::FixedArityBuilder(); } + LeftSquareBracketLayout() = delete; }; } diff --git a/poincare/include/poincare/logarithm.h b/poincare/include/poincare/logarithm.h index c3cf81943..0826fe76e 100644 --- a/poincare/include/poincare/logarithm.h +++ b/poincare/include/poincare/logarithm.h @@ -27,53 +27,44 @@ public: Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; // Simplification - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) override; - Expression shallowBeautify(Context & context, Preferences::AngleUnit angleUnit) override; + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) override; + Expression shallowBeautify(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) override; // Evaluation - template static Complex computeOnComplex(const std::complex c, Preferences::AngleUnit angleUnit) { + template static Complex computeOnComplex(const std::complex c, Preferences::ComplexFormat, Preferences::AngleUnit angleUnit) { /* log has a branch cut on ]-inf, 0]: it is then multivalued on this cut. We * followed the convention chosen by the lib c++ of llvm on ]-inf+0i, 0+0i] * (warning: log takes the other side of the cut values on ]-inf-0i, 0-0i]). */ - return Complex(std::log10(c)); + return Complex::Builder(std::log10(c)); } - Evaluation approximate(SinglePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } - Evaluation approximate(DoublePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } - template Evaluation templatedApproximate(Context& context, Preferences::AngleUnit angleUnit) const; + Evaluation approximate(SinglePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, complexFormat, angleUnit); } + Evaluation approximate(DoublePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, complexFormat, angleUnit); } + template Evaluation templatedApproximate(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; }; class Logarithm final : public Expression { public: Logarithm(const LogarithmNode<2> * n) : Expression(n) {} - static Logarithm Builder(Expression child0, Expression child1) { return Logarithm(child0, child1); } - static Expression UntypedBuilder(Expression children) { return Builder(children.childAtIndex(0), children.childAtIndex(1)); } - static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("log", 2, &UntypedBuilder); + static Logarithm Builder(Expression child0, Expression child1) { return TreeHandle::FixedArityBuilder>(ArrayBuilder(child0, child1).array(), 2); } + static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("log", 2, &UntypedBuilderTwoChildren); - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); - Expression shallowBeautify(Context & context, Preferences::AngleUnit angleUnit); + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); + Expression shallowBeautify(); private: - Logarithm(Expression child0, Expression child1) : Expression(TreePool::sharedPool()->createTreeNode >()) { - replaceChildAtIndexInPlace(0, child0); - replaceChildAtIndexInPlace(1, child1); - } - Expression simpleShallowReduce(Context & context, Preferences::AngleUnit angleUnit); + Expression simpleShallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit); Integer simplifyLogarithmIntegerBaseInteger(Integer i, Integer & base, Addition & a, bool isDenominator); - Expression splitLogarithmInteger(Integer i, bool isDenominator, Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); + Expression splitLogarithmInteger(Integer i, bool isDenominator, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); bool parentIsAPowerOfSameBase() const; }; class CommonLogarithm : public Expression { public: CommonLogarithm(const LogarithmNode<1> * n) : Expression(n) {} - static CommonLogarithm Builder(Expression child) { return CommonLogarithm(child); } - static Expression UntypedBuilder(Expression children) { return Builder(children.childAtIndex(0)); } - static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("log", 1, &UntypedBuilder); + static CommonLogarithm Builder(Expression child) { return TreeHandle::FixedArityBuilder>(&child, 1); } - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); -private: - explicit CommonLogarithm(Expression child) : Expression(TreePool::sharedPool()->createTreeNode >()) { - replaceChildAtIndexInPlace(0, child); - } + static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("log", 1, &UntypedBuilderOneChild); + + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); }; } diff --git a/poincare/include/poincare/matrix.h b/poincare/include/poincare/matrix.h index 1742543ec..96188ed5a 100644 --- a/poincare/include/poincare/matrix.h +++ b/poincare/include/poincare/matrix.h @@ -39,18 +39,18 @@ public: int polynomialDegree(Context & context, const char * symbolName) const override; // Approximation - Evaluation approximate(SinglePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { - return templatedApproximate(context, angleUnit); + Evaluation approximate(SinglePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { + return templatedApproximate(context, complexFormat, angleUnit); } - Evaluation approximate(DoublePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { - return templatedApproximate(context, angleUnit); + Evaluation approximate(DoublePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { + return templatedApproximate(context, complexFormat, angleUnit); } // Layout Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode = Preferences::PrintFloatMode::Decimal, int numberOfSignificantDigits = 0) const override; private: - template Evaluation templatedApproximate(Context& context, Preferences::AngleUnit angleUnit) const; + template Evaluation templatedApproximate(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; /* We could store 2 uint8_t but multiplying m_numberOfRows and * m_numberOfColumns could then lead to overflow. As we are unlikely to use * greater matrix than 100*100, uint16_t is fine. */ @@ -62,14 +62,8 @@ class Matrix final : public Expression { template friend class MatrixComplexNode; friend class GlobalContext; public: - static Matrix EmptyMatrix() { - return Matrix(TreePool::sharedPool()->createTreeNode()); - } - Matrix() : Matrix(TreePool::sharedPool()->createTreeNode()) {} Matrix(const MatrixNode * node) : Expression(node) {} - explicit Matrix(Expression e) : Matrix() { - addChildAtIndexInPlace(e, 0, 0); - } + static Matrix Builder() { return TreeHandle::NAryBuilder(); } void setDimensions(int rows, int columns); int numberOfRows() const { return node()->numberOfRows(); } @@ -79,7 +73,7 @@ public: Expression matrixChild(int i, int j) { return childAtIndex(i*numberOfColumns()+j); } /* Operation on matrix */ - int rank(Context & context, Preferences::AngleUnit angleUnit, bool inPlace = false); + int rank(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, bool inPlace = false); // Inverse the array in-place. Array has to be given in the form array[row_index][column_index] template static int ArrayInverse(T * array, int numberOfRows, int numberOfColumns); #if MATRIX_EXACT_REDUCING @@ -88,7 +82,7 @@ public: Matrix transpose() const; static Matrix createIdentity(int dim); /* createInverse can be called on any matrix reduce or not, approximate or not. */ - Expression inverse(Context & context, Preferences::AngleUnit angleUnit) const; + Expression inverse(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; #endif private: // TODO: find another solution for inverse and determinant (avoid capping the matrix) @@ -98,7 +92,7 @@ private: void setNumberOfRows(int rows) { assert(rows >= 0); node()->setNumberOfRows(rows); } void setNumberOfColumns(int columns) { assert(columns >= 0); node()->setNumberOfColumns(columns); } /* rowCanonize turns a matrix in its reduced row echelon form. */ - Matrix rowCanonize(Context & context, Preferences::AngleUnit angleUnit, Multiplication m = Multiplication()); + Matrix rowCanonize(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, Multiplication m = Multiplication::Builder()); // Row canonize the array in place template static void ArrayRowCanonize(T * array, int numberOfRows, int numberOfColumns, T * c = nullptr); }; diff --git a/poincare/include/poincare/matrix_complex.h b/poincare/include/poincare/matrix_complex.h index 83ddd941e..2566b1d44 100644 --- a/poincare/include/poincare/matrix_complex.h +++ b/poincare/include/poincare/matrix_complex.h @@ -57,8 +57,8 @@ class MatrixComplex final : public Evaluation { friend class MatrixComplexNode; public: MatrixComplex(MatrixComplexNode * node) : Evaluation(node) {} - MatrixComplex(); - MatrixComplex(std::complex * operands, int numberOfRows, int numberOfColumns); + static MatrixComplex Builder() { return TreeHandle::NAryBuilder, MatrixComplexNode>(); } + static MatrixComplex Builder(std::complex * operands, int numberOfRows, int numberOfColumns); static MatrixComplex Undefined(); static MatrixComplex createIdentity(int dim); MatrixComplex inverse() const { return node()->inverse(); } diff --git a/poincare/include/poincare/matrix_dimension.h b/poincare/include/poincare/matrix_dimension.h index d1ebdb252..ec1aa0663 100644 --- a/poincare/include/poincare/matrix_dimension.h +++ b/poincare/include/poincare/matrix_dimension.h @@ -19,30 +19,27 @@ public: // Properties Type type() const override { return Type::MatrixDimension; } + private: // Layout Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; // Simplification - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) override; + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) override; // Evaluation - Evaluation approximate(SinglePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } - Evaluation approximate(DoublePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } - template Evaluation templatedApproximate(Context& context, Preferences::AngleUnit angleUnit) const; + Evaluation approximate(SinglePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, complexFormat, angleUnit); } + Evaluation approximate(DoublePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, complexFormat, angleUnit); } + template Evaluation templatedApproximate(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; }; class MatrixDimension final : public Expression { public: MatrixDimension(const MatrixDimensionNode * n) : Expression(n) {} - static MatrixDimension Builder(Expression child) { return MatrixDimension(child); } - static Expression UntypedBuilder(Expression children) { return Builder(children.childAtIndex(0)); } - static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("dim", 1, &UntypedBuilder); + static MatrixDimension Builder(Expression child) { return TreeHandle::FixedArityBuilder(&child, 1); } - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit); -private: - explicit MatrixDimension(Expression child) : Expression(TreePool::sharedPool()->createTreeNode()) { - replaceChildAtIndexInPlace(0, child); - } + static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("dim", 1, &UntypedBuilderOneChild); + + Expression shallowReduce(); }; } diff --git a/poincare/include/poincare/matrix_inverse.h b/poincare/include/poincare/matrix_inverse.h index 119d63108..cd551cf21 100644 --- a/poincare/include/poincare/matrix_inverse.h +++ b/poincare/include/poincare/matrix_inverse.h @@ -24,25 +24,21 @@ private: Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; // Simplification - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) override; + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) override; // Evaluation - Evaluation approximate(SinglePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } - Evaluation approximate(DoublePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } - template Evaluation templatedApproximate(Context& context, Preferences::AngleUnit angleUnit) const; + Evaluation approximate(SinglePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, complexFormat, angleUnit); } + Evaluation approximate(DoublePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, complexFormat, angleUnit); } + template Evaluation templatedApproximate(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; }; class MatrixInverse final : public Expression { public: MatrixInverse(const MatrixInverseNode * n) : Expression(n) {} - static MatrixInverse Builder(Expression child) { return MatrixInverse(child); } - static Expression UntypedBuilder(Expression children) { return Builder(children.childAtIndex(0)); } - static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("inverse", 1, &UntypedBuilder); + static MatrixInverse Builder(Expression child) { return TreeHandle::FixedArityBuilder(&child, 1); } - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); -private: - explicit MatrixInverse(Expression child) : Expression(TreePool::sharedPool()->createTreeNode()) { - replaceChildAtIndexInPlace(0, child); - } + static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("inverse", 1, &UntypedBuilderOneChild); + + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); }; } diff --git a/poincare/include/poincare/matrix_layout.h b/poincare/include/poincare/matrix_layout.h index 28374c557..21cf04985 100644 --- a/poincare/include/poincare/matrix_layout.h +++ b/poincare/include/poincare/matrix_layout.h @@ -57,9 +57,10 @@ private: class MatrixLayout /*final*/ : public GridLayout { friend class MatrixLayoutNode; public: - MatrixLayout(const MatrixLayoutNode * n); - MatrixLayout(); - MatrixLayout(Layout l1, Layout l2, Layout l3, Layout l4); + MatrixLayout(const MatrixLayoutNode * n) : GridLayout(n) {} + static MatrixLayout Builder() { return TreeHandle::NAryBuilder(); } + static MatrixLayout Builder(Layout l1, Layout l2, Layout l3, Layout l4); + bool hasGreySquares() const { return node()->hasGreySquares(); } void addGreySquares() { node()->addGreySquares(); } void removeGreySquares() { node()->removeGreySquares(); } diff --git a/poincare/include/poincare/matrix_trace.h b/poincare/include/poincare/matrix_trace.h index c5aec75d2..2192d36c9 100644 --- a/poincare/include/poincare/matrix_trace.h +++ b/poincare/include/poincare/matrix_trace.h @@ -24,25 +24,21 @@ private: Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; // Simplification - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) override; + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) override; // Evaluation - Evaluation approximate(SinglePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } - Evaluation approximate(DoublePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } - template Evaluation templatedApproximate(Context& context, Preferences::AngleUnit angleUnit) const; + Evaluation approximate(SinglePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, complexFormat, angleUnit); } + Evaluation approximate(DoublePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, complexFormat, angleUnit); } + template Evaluation templatedApproximate(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; }; class MatrixTrace final : public Expression { public: MatrixTrace(const MatrixTraceNode * n) : Expression(n) {} - static MatrixTrace Builder(Expression child) { return MatrixTrace(child); } - static Expression UntypedBuilder(Expression children) { return Builder(children.childAtIndex(0)); } - static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("trace", 1, &UntypedBuilder); + static MatrixTrace Builder(Expression child) { return TreeHandle::FixedArityBuilder(&child, 1); } - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit); -private: - explicit MatrixTrace(Expression child) : Expression(TreePool::sharedPool()->createTreeNode()) { - replaceChildAtIndexInPlace(0, child); - } + static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("trace", 1, &UntypedBuilderOneChild); + + Expression shallowReduce(); }; } diff --git a/poincare/include/poincare/matrix_transpose.h b/poincare/include/poincare/matrix_transpose.h index eba119f8e..25e35102d 100644 --- a/poincare/include/poincare/matrix_transpose.h +++ b/poincare/include/poincare/matrix_transpose.h @@ -24,25 +24,21 @@ private: Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; // Simplification - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) override; + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) override; // Evaluation - Evaluation approximate(SinglePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } - Evaluation approximate(DoublePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } - template Evaluation templatedApproximate(Context& context, Preferences::AngleUnit angleUnit) const; + Evaluation approximate(SinglePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, complexFormat, angleUnit); } + Evaluation approximate(DoublePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, complexFormat, angleUnit); } + template Evaluation templatedApproximate(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; }; class MatrixTranspose final : public Expression { public: MatrixTranspose(const MatrixTransposeNode * n) : Expression(n) {} - static MatrixTranspose Builder(Expression child) { return MatrixTranspose(child); } - static Expression UntypedBuilder(Expression children) { return Builder(children.childAtIndex(0)); } - static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("transpose", 1, &UntypedBuilder); + static MatrixTranspose Builder(Expression child) { return TreeHandle::FixedArityBuilder(&child, 1); } - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit); -private: - explicit MatrixTranspose(Expression child) : Expression(TreePool::sharedPool()->createTreeNode()) { - replaceChildAtIndexInPlace(0, child); - } + static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("transpose", 1, &UntypedBuilderOneChild); + + Expression shallowReduce(); }; } diff --git a/poincare/include/poincare/multiplication.h b/poincare/include/poincare/multiplication.h index da6a19330..e2a191c6f 100644 --- a/poincare/include/poincare/multiplication.h +++ b/poincare/include/poincare/multiplication.h @@ -21,20 +21,20 @@ public: // Properties Type type() const override { return Type::Multiplication; } - Sign sign() const override; + Sign sign(Context * context) const override; int polynomialDegree(Context & context, const char * symbolName) const override; int getPolynomialCoefficients(Context & context, const char * symbolName, Expression coefficients[]) const override; // Approximation - template static Complex compute(const std::complex c, const std::complex d) { return Complex(c*d); } - template static MatrixComplex computeOnComplexAndMatrix(const std::complex c, const MatrixComplex m) { - return ApproximationHelper::ElementWiseOnMatrixComplexAndComplex(m, c, compute); + template static Complex compute(const std::complex c, const std::complex d, Preferences::ComplexFormat complexFormat) { return Complex::Builder(c*d); } + template static MatrixComplex computeOnComplexAndMatrix(const std::complex c, const MatrixComplex m, Preferences::ComplexFormat complexFormat) { + return ApproximationHelper::ElementWiseOnMatrixComplexAndComplex(m, c, complexFormat, compute); } - template static MatrixComplex computeOnMatrices(const MatrixComplex m, const MatrixComplex n); + template static MatrixComplex computeOnMatrices(const MatrixComplex m, const MatrixComplex n, Preferences::ComplexFormat complexFormat); private: // Property - Expression setSign(Sign s, Context & context, Preferences::AngleUnit angleUnit) override; + Expression setSign(Sign s, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) override; // Layout bool childNeedsParenthesis(const TreeNode * child) const override; @@ -44,19 +44,19 @@ private: int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; // Simplification - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) override; - Expression shallowBeautify(Context & context, Preferences::AngleUnit angleUnit) override; - Expression denominator(Context & context, Preferences::AngleUnit angleUnit) const override; + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) override; + Expression shallowBeautify(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) override; + Expression denominator(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override; /* Approximation */ - template static MatrixComplex computeOnMatrixAndComplex(const MatrixComplex m, const std::complex c) { - return ApproximationHelper::ElementWiseOnMatrixComplexAndComplex(m, c, compute); + template static MatrixComplex computeOnMatrixAndComplex(const MatrixComplex m, const std::complex c, Preferences::ComplexFormat complexFormat) { + return ApproximationHelper::ElementWiseOnMatrixComplexAndComplex(m, c, complexFormat, compute); } - Evaluation approximate(SinglePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { - return ApproximationHelper::MapReduce(this, context, angleUnit, compute, computeOnComplexAndMatrix, computeOnMatrixAndComplex, computeOnMatrices); + Evaluation approximate(SinglePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { + return ApproximationHelper::MapReduce(this, context, complexFormat, angleUnit, compute, computeOnComplexAndMatrix, computeOnMatrixAndComplex, computeOnMatrices); } - Evaluation approximate(DoublePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { - return ApproximationHelper::MapReduce(this, context, angleUnit, compute, computeOnComplexAndMatrix, computeOnMatrixAndComplex, computeOnMatrices); + Evaluation approximate(DoublePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { + return ApproximationHelper::MapReduce(this, context, complexFormat, angleUnit, compute, computeOnComplexAndMatrix, computeOnMatrixAndComplex, computeOnMatrices); } }; @@ -66,35 +66,31 @@ class Multiplication final : public NAryExpression { friend class Power; public: Multiplication(const MultiplicationNode * n) : NAryExpression(n) {} - Multiplication(); - explicit Multiplication(Expression e1) : Multiplication() { - addChildAtIndexInPlace(e1, 0, 0); - } - Multiplication(Expression e1, Expression e2) : Multiplication() { - addChildAtIndexInPlace(e2, 0, 0); - addChildAtIndexInPlace(e1, 0, numberOfChildren()); - } - Multiplication(Expression e1, Expression e2, Expression e3) : Multiplication(e2, e3) { - addChildAtIndexInPlace(e1, 0, numberOfChildren()); - } + static Multiplication Builder() { return TreeHandle::NAryBuilder(); } + static Multiplication Builder(Expression e1) { return Multiplication::Builder(&e1, 1); } + static Multiplication Builder(Expression e1, Expression e2) { return Multiplication::Builder(ArrayBuilder(e1, e2).array(), 2); } + static Multiplication Builder(Expression e1, Expression e2, Expression e3) { return Multiplication::Builder(ArrayBuilder(e1, e2, e3).array(), 3); } template static void computeOnArrays(T * m, T * n, T * result, int mNumberOfColumns, int mNumberOfRows, int nNumberOfColumns); // Expression - Expression setSign(ExpressionNode::Sign s, Context & context, Preferences::AngleUnit angleUnit); - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); - Expression shallowBeautify(Context & context, Preferences::AngleUnit angleUnit); + Expression setSign(ExpressionNode::Sign s, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); + Expression shallowBeautify(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); int getPolynomialCoefficients(Context & context, const char * symbolName, Expression coefficients[]) const; - Expression denominator(Context & context, Preferences::AngleUnit angleUnit) const; + Expression denominator(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; private: + // Constructors + static Multiplication Builder(Expression * children, size_t numberOfChildren) { return TreeHandle::NAryBuilder(children, numberOfChildren); } + // Simplification - Expression privateShallowReduce(Context& context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target, bool expand, bool canBeInterrupted); + Expression privateShallowReduce(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target, bool expand, bool canBeInterrupted); void mergeMultiplicationChildrenInPlace(); - void factorizeBase(int i, int j, Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); - void mergeInChildByFactorizingBase(int i, Expression e, Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); - void factorizeExponent(int i, int j, Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); - Expression distributeOnOperandAtIndex(int index, Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); - void addMissingFactors(Expression factor, Context & context, Preferences::AngleUnit angleUnit); - void factorizeSineAndCosine(int i, int j, Context & context, Preferences::AngleUnit angleUnit); + void factorizeBase(int i, int j, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); + void mergeInChildByFactorizingBase(int i, Expression e, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); + void factorizeExponent(int i, int j, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); + Expression distributeOnOperandAtIndex(int index, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); + void addMissingFactors(Expression factor, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit); + void factorizeSineAndCosine(int i, int j, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit); static bool HaveSameNonNumeralFactors(const Expression & e1, const Expression & e2); static bool TermsHaveIdenticalBase(const Expression & e1, const Expression & e2); static bool TermsHaveIdenticalExponent(const Expression & e1, const Expression & e2); @@ -103,7 +99,7 @@ private: static const Expression CreateExponent(Expression e); /* Warning: mergeNegativePower doesnot always return a multiplication: * *(b^-1,c^-1) -> (bc)^-1 */ - Expression mergeNegativePower(Context & context, Preferences::AngleUnit angleUnit); + Expression mergeNegativePower(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit); static inline const Expression Base(const Expression e); }; diff --git a/poincare/include/poincare/n_ary_expression_node.h b/poincare/include/poincare/n_ary_expression_node.h index 99fd3219f..b86dbf5eb 100644 --- a/poincare/include/poincare/n_ary_expression_node.h +++ b/poincare/include/poincare/n_ary_expression_node.h @@ -18,6 +18,9 @@ public: } void eraseNumberOfChildren() override { m_numberOfChildren = 0; } + // Complex + bool isReal(Context & context) const override; + // Comparison typedef int (*ExpressionOrder)(const ExpressionNode * e1, const ExpressionNode * e2, bool canBeInterrupted); @@ -30,8 +33,8 @@ protected: * than 6144 children which fit in uint16_t. */ uint16_t m_numberOfChildren; private: - int simplificationOrderSameType(const ExpressionNode * e, bool canBeInterrupted) const override; - int simplificationOrderGreaterType(const ExpressionNode * e, bool canBeInterrupted) const override; + int simplificationOrderSameType(const ExpressionNode * e, bool ascending, bool canBeInterrupted) const override; + int simplificationOrderGreaterType(const ExpressionNode * e, bool ascending, bool canBeInterrupted) const override; }; class NAryExpression : public Expression { @@ -48,6 +51,11 @@ public: Expression squashUnaryHierarchyInPlace() { return node()->squashUnaryHierarchyInPlace(); } + /* allChildrenAreReal returns: + * - 1 if all children are real + * - 0 if all non real children are ComplexCartesian + * - -1 if some chidren are non-real and non ComplexCartesian */ + int allChildrenAreReal(Context & context) const; protected: NAryExpressionNode * node() const { return static_cast(Expression::node()); } }; diff --git a/poincare/include/poincare/naperian_logarithm.h b/poincare/include/poincare/naperian_logarithm.h index 26c4ca390..acb96931d 100644 --- a/poincare/include/poincare/naperian_logarithm.h +++ b/poincare/include/poincare/naperian_logarithm.h @@ -25,34 +25,30 @@ private: Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; // Simplification - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) override; + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) override; /* Evaluation */ - template static Complex computeOnComplex(const std::complex c, Preferences::AngleUnit angleUnit) { + template static Complex computeOnComplex(const std::complex c, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) { /* ln has a branch cut on ]-inf, 0]: it is then multivalued on this cut. We * followed the convention chosen by the lib c++ of llvm on ]-inf+0i, 0+0i] * (warning: ln takes the other side of the cut values on ]-inf-0i, 0-0i]). */ - return Complex(std::log(c)); + return Complex::Builder(std::log(c)); } - Evaluation approximate(SinglePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { - return ApproximationHelper::Map(this, context, angleUnit,computeOnComplex); + Evaluation approximate(SinglePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { + return ApproximationHelper::Map(this, context, complexFormat, angleUnit,computeOnComplex); } - Evaluation approximate(DoublePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { - return ApproximationHelper::Map(this, context, angleUnit, computeOnComplex); + Evaluation approximate(DoublePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { + return ApproximationHelper::Map(this, context, complexFormat, angleUnit, computeOnComplex); } }; class NaperianLogarithm final : public Expression { public: NaperianLogarithm(const NaperianLogarithmNode * n) : Expression(n) {} - static NaperianLogarithm Builder(Expression child) { return NaperianLogarithm(child); } - static Expression UntypedBuilder(Expression children) { return Builder(children.childAtIndex(0)); } - static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("ln", 1, &UntypedBuilder); + static NaperianLogarithm Builder(Expression child) { return TreeHandle::FixedArityBuilder(&child, 1); } - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); -private: - explicit NaperianLogarithm(Expression child) : Expression(TreePool::sharedPool()->createTreeNode()) { - replaceChildAtIndexInPlace(0, child); - } + static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("ln", 1, &UntypedBuilderOneChild); + + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); }; } diff --git a/poincare/include/poincare/nth_root.h b/poincare/include/poincare/nth_root.h index 191d881db..a043a7e2c 100644 --- a/poincare/include/poincare/nth_root.h +++ b/poincare/include/poincare/nth_root.h @@ -24,27 +24,21 @@ private: Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; // Simplification - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) override; + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) override; // Evaluation - Evaluation approximate(SinglePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } - Evaluation approximate(DoublePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } - template Evaluation templatedApproximate(Context& context, Preferences::AngleUnit angleUnit) const; + Evaluation approximate(SinglePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, complexFormat, angleUnit); } + Evaluation approximate(DoublePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, complexFormat, angleUnit); } + template Evaluation templatedApproximate(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; }; class NthRoot final : public Expression { public: NthRoot(const NthRootNode * n) : Expression(n) {} - static NthRoot Builder(Expression child0, Expression child1) { return NthRoot(child0, child1); } - static Expression UntypedBuilder(Expression children) { return Builder(children.childAtIndex(0), children.childAtIndex(1)); } - static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("root", 2, &UntypedBuilder); + static NthRoot Builder(Expression child0, Expression child1) { return TreeHandle::FixedArityBuilder(ArrayBuilder(child0, child1).array(), 2); } + static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("root", 2, &UntypedBuilderTwoChildren); - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); -private: - NthRoot(Expression child0, Expression child1) : Expression(TreePool::sharedPool()->createTreeNode()) { - replaceChildAtIndexInPlace(0, child0); - replaceChildAtIndexInPlace(1, child1); - } + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); }; } diff --git a/poincare/include/poincare/nth_root_layout.h b/poincare/include/poincare/nth_root_layout.h index 8f9e59ebf..1b2e27fc9 100644 --- a/poincare/include/poincare/nth_root_layout.h +++ b/poincare/include/poincare/nth_root_layout.h @@ -14,9 +14,9 @@ public: constexpr static KDCoordinate k_leftRadixHeight = 8; constexpr static KDCoordinate k_leftRadixWidth = 5; - NthRootLayoutNode() : + NthRootLayoutNode(bool hasIndex) : LayoutNode(), - m_hasIndex(false) + m_hasIndex(hasIndex) {} // LayoutNode @@ -50,14 +50,6 @@ private: constexpr static KDCoordinate k_heightMargin = 2; constexpr static KDCoordinate k_widthMargin = 2; constexpr static KDCoordinate k_radixLineThickness = 1; - void setNumberOfChildren(int number) { - assert(number == 1 || number == 2); - if (number == 1) { - m_hasIndex = false; - } else { - m_hasIndex = true; - } - } KDSize adjustedIndexSize(); void render(KDContext * ctx, KDPoint p, KDColor expressionColor, KDColor backgroundColor) override; LayoutNode * radicandLayout() { return childAtIndex(0); } @@ -67,10 +59,9 @@ private: class NthRootLayout final : public Layout { public: - explicit NthRootLayout(Layout radicand); - NthRootLayout(Layout radicand, Layout index); -private: - NthRootLayout(); + NthRootLayout() = delete; + static NthRootLayout Builder(Layout child); + static NthRootLayout Builder(Layout radicand, Layout index); }; } diff --git a/poincare/include/poincare/number.h b/poincare/include/poincare/number.h index 8de3464bd..aee56a55c 100644 --- a/poincare/include/poincare/number.h +++ b/poincare/include/poincare/number.h @@ -11,7 +11,9 @@ namespace Poincare { * - Float * - Decimal * - Infinity - */ + * + * Before being approximated, an expression should be reduced and thus should + * not contain any Decimal. */ class Rational; @@ -21,6 +23,9 @@ public: int numberOfChildren() const override { return 0; } double doubleApproximation() const; + + // Complex + bool isReal(Context & context) const override { return true; } }; class Number : public Expression { @@ -41,7 +46,13 @@ public: static Number Power(const Number & i, const Number & j); static int NaturalOrder(const Number & i, const Number & j); - Number setSign(ExpressionNode::Sign s, Context & context, Preferences::AngleUnit angleUnit) { return Expression::setSign(s, context, angleUnit).convert(); } + /* Number::sign() or Number::setSign does not need a context or an angle unit + * (a number can be Infinity, Undefined, Float, Decimal, Rational). */ + ExpressionNode::Sign sign() { return Expression::sign(nullptr); } + Number setSign(ExpressionNode::Sign s) { + assert(s == ExpressionNode::Sign::Positive || s == ExpressionNode::Sign::Negative); + return Expression::setSign(s, nullptr, Preferences::ComplexFormat::Real, Preferences::AngleUnit::Degree, ExpressionNode::ReductionTarget::User).convert(); + } protected: Number() : Expression() {} NumberNode * node() const { return static_cast(Expression::node()); } diff --git a/poincare/include/poincare/opposite.h b/poincare/include/poincare/opposite.h index 6a030ff9e..40bbdd5a1 100644 --- a/poincare/include/poincare/opposite.h +++ b/poincare/include/poincare/opposite.h @@ -10,7 +10,7 @@ class Opposite; class OppositeNode /*final*/ : public ExpressionNode { public: - template static Complex compute(const std::complex c, Preferences::AngleUnit angleUnit = Preferences::AngleUnit::Degree) { return Complex(-c); } + template static Complex compute(const std::complex c, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit = Preferences::AngleUnit::Degree) { return Complex::Builder(-c); } // TreeNode @@ -25,14 +25,14 @@ public: // Properties Type type() const override { return Type::Opposite; } int polynomialDegree(Context & context, const char * symbolName) const override; - Sign sign() const override; + Sign sign(Context * context) const override; // Approximation - Evaluation approximate(SinglePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { - return ApproximationHelper::Map(this, context, angleUnit, compute); + Evaluation approximate(SinglePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { + return ApproximationHelper::Map(this, context, complexFormat, angleUnit, compute); } - Evaluation approximate(DoublePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { - return ApproximationHelper::Map(this, context, angleUnit, compute); + Evaluation approximate(DoublePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { + return ApproximationHelper::Map(this, context, complexFormat, angleUnit, compute); } // Layout @@ -41,18 +41,16 @@ public: int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode = Preferences::PrintFloatMode::Decimal, int numberOfSignificantDigits = 0) const override; // Simplification - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) override; + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) override; }; class Opposite final : public Expression { public: - Opposite(); Opposite(const OppositeNode * n) : Expression(n) {} - explicit Opposite(Expression child) : Expression(TreePool::sharedPool()->createTreeNode()) { - replaceChildAtIndexInPlace(0, child); - } + static Opposite Builder() { return TreeHandle::FixedArityBuilder(); } + static Opposite Builder(Expression child) { return TreeHandle::FixedArityBuilder(&child, 1); } - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); }; } diff --git a/poincare/include/poincare/parenthesis.h b/poincare/include/poincare/parenthesis.h index eaf35caf9..bf1aa5c23 100644 --- a/poincare/include/poincare/parenthesis.h +++ b/poincare/include/poincare/parenthesis.h @@ -2,6 +2,7 @@ #define POINCARE_PARENTHESIS_H #include +#include namespace Poincare { @@ -25,23 +26,21 @@ public: Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; // Simplification - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) override; + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) override; // Approximation - Evaluation approximate(SinglePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } - Evaluation approximate(DoublePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } + Evaluation approximate(SinglePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, complexFormat, angleUnit); } + Evaluation approximate(DoublePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, complexFormat, angleUnit); } private: - template Evaluation templatedApproximate(Context& context, Preferences::AngleUnit angleUnit) const; + template Evaluation templatedApproximate(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; }; class Parenthesis final : public Expression { public: Parenthesis(const ParenthesisNode * n) : Expression(n) {} - Parenthesis(Expression exp) : Expression(TreePool::sharedPool()->createTreeNode()) { - replaceChildAtIndexInPlace(0, exp); - } + static Parenthesis Builder(Expression child) { return TreeHandle::FixedArityBuilder(&child, 1); } // Expression - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit); + Expression shallowReduce(); }; } diff --git a/poincare/include/poincare/permute_coefficient.h b/poincare/include/poincare/permute_coefficient.h index c1366a29e..f80b81096 100644 --- a/poincare/include/poincare/permute_coefficient.h +++ b/poincare/include/poincare/permute_coefficient.h @@ -22,32 +22,31 @@ public: // Properties Type type() const override{ return Type::PermuteCoefficient; } + + // Complex + bool isReal(Context & context) const override { return true; } + private: // Layout Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; // Simplification - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) override; + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) override; // Evaluation - Evaluation approximate(SinglePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } - Evaluation approximate(DoublePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } - template Evaluation templatedApproximate(Context& context, Preferences::AngleUnit angleUnit) const; + Evaluation approximate(SinglePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, complexFormat, angleUnit); } + Evaluation approximate(DoublePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, complexFormat, angleUnit); } + template Evaluation templatedApproximate(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; }; class PermuteCoefficient final : public Expression { public: PermuteCoefficient(const PermuteCoefficientNode * n) : Expression(n) {} - static PermuteCoefficient Builder(Expression child0, Expression child1) { return PermuteCoefficient(child0, child1); } - static Expression UntypedBuilder(Expression children) { return Builder(children.childAtIndex(0), children.childAtIndex(1)); } - static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("permute", 2, &UntypedBuilder); + static PermuteCoefficient Builder(Expression child0, Expression child1) { return TreeHandle::FixedArityBuilder(ArrayBuilder(child0, child1).array(), 2); } + static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("permute", 2, &UntypedBuilderTwoChildren); // Expression - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit); -private: - PermuteCoefficient(Expression child0, Expression child1) : Expression(TreePool::sharedPool()->createTreeNode()) { - replaceChildAtIndexInPlace(0, child0); - replaceChildAtIndexInPlace(1, child1); - } + Expression shallowReduce(); + constexpr static int k_maxNValue = 100; }; diff --git a/poincare/include/poincare/power.h b/poincare/include/poincare/power.h index 9da73580d..3799bec05 100644 --- a/poincare/include/poincare/power.h +++ b/poincare/include/poincare/power.h @@ -21,15 +21,18 @@ public: } #endif + // Complex + bool isReal(Context & context) const override; + // Properties Type type() const override { return Type::Power; } - Sign sign() const override; - Expression setSign(Sign s, Context & context, Preferences::AngleUnit angleUnit) override; + Sign sign(Context * context) const override; + Expression setSign(Sign s, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) override; int polynomialDegree(Context & context, const char * symbolName) const override; int getPolynomialCoefficients(Context & context, const char * symbolName, Expression coefficients[]) const override; - template static Complex compute(const std::complex c, const std::complex d); + template static Complex compute(const std::complex c, const std::complex d, Preferences::ComplexFormat complexFormat); private: constexpr static int k_maxApproximatePowerMatrix = 1000; @@ -42,20 +45,20 @@ private: int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; // Simplify - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) override; - Expression shallowBeautify(Context & context, Preferences::AngleUnit angleUnit) override; - int simplificationOrderGreaterType(const ExpressionNode * e, bool canBeInterrupted) const override; - int simplificationOrderSameType(const ExpressionNode * e, bool canBeInterrupted) const override; - Expression denominator(Context & context, Preferences::AngleUnit angleUnit) const override; + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) override; + Expression shallowBeautify(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) override; + int simplificationOrderGreaterType(const ExpressionNode * e, bool ascending, bool canBeInterrupted) const override; + int simplificationOrderSameType(const ExpressionNode * e, bool ascending, bool canBeInterrupted) const override; + Expression denominator(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override; // Evaluation - template static MatrixComplex computeOnComplexAndMatrix(const std::complex c, const MatrixComplex n); - template static MatrixComplex computeOnMatrixAndComplex(const MatrixComplex m, const std::complex d); - template static MatrixComplex computeOnMatrices(const MatrixComplex m, const MatrixComplex n); - Evaluation approximate(SinglePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { - return ApproximationHelper::MapReduce(this, context, angleUnit, compute, computeOnComplexAndMatrix, computeOnMatrixAndComplex, computeOnMatrices); + template static MatrixComplex computeOnComplexAndMatrix(const std::complex c, const MatrixComplex n, Preferences::ComplexFormat complexFormat); + template static MatrixComplex computeOnMatrixAndComplex(const MatrixComplex m, const std::complex d, Preferences::ComplexFormat complexFormat); + template static MatrixComplex computeOnMatrices(const MatrixComplex m, const MatrixComplex n, Preferences::ComplexFormat complexFormat); + Evaluation approximate(SinglePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { + return ApproximationHelper::MapReduce(this, context, complexFormat, angleUnit, compute, computeOnComplexAndMatrix, computeOnMatrixAndComplex, computeOnMatrices); } - Evaluation approximate(DoublePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { - return ApproximationHelper::MapReduce(this, context, angleUnit, compute, computeOnComplexAndMatrix, computeOnMatrixAndComplex, computeOnMatrices); + Evaluation approximate(DoublePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { + return ApproximationHelper::MapReduce(this, context, complexFormat, angleUnit, compute, computeOnComplexAndMatrix, computeOnMatrixAndComplex, computeOnMatrices); } }; @@ -63,29 +66,31 @@ class Power final : public Expression { friend class PowerNode; friend class Round; public: - Power(Expression base, Expression exponent); Power(const PowerNode * n) : Expression(n) {} - Expression setSign(ExpressionNode::Sign s, Context & context, Preferences::AngleUnit angleUnit); + static Power Builder(Expression base, Expression exponent) { return TreeHandle::FixedArityBuilder(ArrayBuilder(base, exponent).array(), 2); } + + Expression setSign(ExpressionNode::Sign s, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); int getPolynomialCoefficients(Context & context, const char * symbolName, Expression coefficients[]) const; - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); - Expression shallowBeautify(Context & context, Preferences::AngleUnit angleUnit); + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); + Expression shallowBeautify(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); private: constexpr static int k_maxExactPowerMatrix = 100; constexpr static int k_maxNumberOfTermsInExpandedMultinome = 25; // Simplification - Expression denominator(Context & context, Preferences::AngleUnit angleUnit) const; + Expression denominator(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; - Expression simplifyPowerPower(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); - Expression simplifyPowerMultiplication(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); - Expression simplifyRationalRationalPower(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); + Expression simplifyPowerPower(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); + Expression simplifyPowerMultiplication(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); + Expression simplifyRationalRationalPower(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); - static Expression CreateSimplifiedIntegerRationalPower(Integer i, Rational r, bool isDenominator, Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); - Expression removeSquareRootsFromDenominator(Context & context, Preferences::AngleUnit angleUnit); + static Expression CreateSimplifiedIntegerRationalPower(Integer i, Rational r, bool isDenominator, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); + Expression removeSquareRootsFromDenominator(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit); bool parentIsALogarithmOfSameBase() const; bool isNthRootOfUnity() const; - static Expression CreateComplexExponent(const Expression & r); // Returns e^(i*pi*r) + Expression equivalentExpressionUsingStandardExpression() const; + static Expression CreateComplexExponent(const Expression & r, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); // Returns e^(i*pi*r) static bool TermIsARationalSquareRootOrRational(const Expression& e); static const Rational RadicandInExpression(const Expression & e); static const Rational RationalFactorInExpression(const Expression & e); diff --git a/poincare/include/poincare/prediction_interval.h b/poincare/include/poincare/prediction_interval.h index 9b27a7861..a664cfcea 100644 --- a/poincare/include/poincare/prediction_interval.h +++ b/poincare/include/poincare/prediction_interval.h @@ -27,27 +27,21 @@ private: Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; // Simplification - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) override; + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) override; // Evaluation - Evaluation approximate(SinglePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } - Evaluation approximate(DoublePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } - template Evaluation templatedApproximate(Context& context, Preferences::AngleUnit angleUnit) const; + Evaluation approximate(SinglePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, complexFormat, angleUnit); } + Evaluation approximate(DoublePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, complexFormat, angleUnit); } + template Evaluation templatedApproximate(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; }; class PredictionInterval final : public Expression { public: PredictionInterval(const PredictionIntervalNode * n) : Expression(n) {} - static PredictionInterval Builder(Expression child0, Expression child1) { return PredictionInterval(child0, child1); } - static Expression UntypedBuilder(Expression children) { return Builder(children.childAtIndex(0), children.childAtIndex(1)); } - static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("prediction95", 2, &UntypedBuilder); + static PredictionInterval Builder(Expression child0, Expression child1) { return TreeHandle::FixedArityBuilder(ArrayBuilder(child0, child1).array(), 2); } + static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("prediction95", 2, &UntypedBuilderTwoChildren); // Expression - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); -private: - PredictionInterval(Expression child0, Expression child1) : Expression(TreePool::sharedPool()->createTreeNode()) { - replaceChildAtIndexInPlace(0, child0); - replaceChildAtIndexInPlace(1, child1); - } + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); }; } diff --git a/poincare/include/poincare/preferences.h b/poincare/include/poincare/preferences.h index 2df15f82a..1b4e84170 100644 --- a/poincare/include/poincare/preferences.h +++ b/poincare/include/poincare/preferences.h @@ -19,8 +19,9 @@ public: Scientific = 1, }; enum class ComplexFormat { - Cartesian = 0, - Polar = 1 + Real = 0, + Cartesian = 1, + Polar = 2 }; enum class AngleUnit { Degree = 0, diff --git a/poincare/include/poincare/print_float.h b/poincare/include/poincare/print_float.h index d2c435892..7509878b0 100644 --- a/poincare/include/poincare/print_float.h +++ b/poincare/include/poincare/print_float.h @@ -10,7 +10,7 @@ class Integer; namespace PrintFloat { constexpr static int bufferSizeForFloatsWithPrecision(int numberOfSignificantDigits) { - // The wors case is -1.234E-328 + // The worst case is -1.234E-328 return numberOfSignificantDigits + 8; } /* This function prints the integer i in the buffer with a '.' at the position @@ -45,9 +45,9 @@ namespace PrintFloat { * ConvertFloatToText return the number of characters that have been written * in buffer (excluding the last \O character) */ template - int convertFloatToText(T d, char * buffer, int bufferSize, int numberOfSignificantDigits, Preferences::PrintFloatMode mode); + int convertFloatToText(T d, char * buffer, int bufferSize, int numberOfSignificantDigits, Preferences::PrintFloatMode mode, bool allowRounding = true); template - static int convertFloatToTextPrivate(T f, char * buffer, int numberOfSignificantDigits, Preferences::PrintFloatMode mode); + static int convertFloatToTextPrivate(T f, char * buffer, int numberOfSignificantDigits, Preferences::PrintFloatMode mode, int * numberOfRemovedZeros); } } diff --git a/poincare/include/poincare/product.h b/poincare/include/poincare/product.h index fd98fa751..c9dbaeb66 100644 --- a/poincare/include/poincare/product.h +++ b/poincare/include/poincare/product.h @@ -22,36 +22,23 @@ private: float emptySequenceValue() const override { return 1.0f; } Layout createSequenceLayout(Layout argumentLayout, Layout symbolLayout, Layout subscriptLayout, Layout superscriptLayout) const override; int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; - Evaluation evaluateWithNextTerm(DoublePrecision p, Evaluation a, Evaluation b) const override { - return templatedApproximateWithNextTerm(a, b); + Evaluation evaluateWithNextTerm(DoublePrecision p, Evaluation a, Evaluation b, Preferences::ComplexFormat complexFormat) const override { + return templatedApproximateWithNextTerm(a, b, complexFormat); } - Evaluation evaluateWithNextTerm(SinglePrecision p, Evaluation a, Evaluation b) const override { - return templatedApproximateWithNextTerm(a, b); + Evaluation evaluateWithNextTerm(SinglePrecision p, Evaluation a, Evaluation b, Preferences::ComplexFormat complexFormat) const override { + return templatedApproximateWithNextTerm(a, b, complexFormat); } - template Evaluation templatedApproximateWithNextTerm(Evaluation a, Evaluation b) const; + template Evaluation templatedApproximateWithNextTerm(Evaluation a, Evaluation b, Preferences::ComplexFormat complexFormat) const; }; class Product final : public Expression { friend class ProductNode; public: Product(const ProductNode * n) : Expression(n) {} - static Product Builder(Expression child0, Symbol child1, Expression child2, Expression child3) { return Product(child0, child1, child2, child3); } - static Expression UntypedBuilder(Expression children) { - if (children.childAtIndex(1).type() != ExpressionNode::Type::Symbol) { - // Second parameter must be a Symbol. - return Expression(); - } - return Builder(children.childAtIndex(0), children.childAtIndex(1).convert(), children.childAtIndex(2), children.childAtIndex(3)); - } + static Product Builder(Expression child0, Symbol child1, Expression child2, Expression child3) { return TreeHandle::FixedArityBuilder(ArrayBuilder(child0, child1, child2, child3).array(), 4); } + static Expression UntypedBuilder(Expression children); + static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("product", 4, &UntypedBuilder); -private: - Product(Expression child0, Expression child1, Expression child2, Expression child3) : Expression(TreePool::sharedPool()->createTreeNode()) { - assert(child1.type() == ExpressionNode::Type::Symbol); - replaceChildAtIndexInPlace(0, child0); - replaceChildAtIndexInPlace(1, child1); - replaceChildAtIndexInPlace(2, child2); - replaceChildAtIndexInPlace(3, child3); - } }; } diff --git a/poincare/include/poincare/product_layout.h b/poincare/include/poincare/product_layout.h index 8eb2adccd..3ea063232 100644 --- a/poincare/include/poincare/product_layout.h +++ b/poincare/include/poincare/product_layout.h @@ -25,14 +25,8 @@ private: class ProductLayout final : public Layout { public: - ProductLayout(Layout argument, Layout variable, Layout lowerB, Layout upperB) : - Layout(TreePool::sharedPool()->createTreeNode()) - { - replaceChildAtIndexInPlace(0, argument); - replaceChildAtIndexInPlace(1, variable); - replaceChildAtIndexInPlace(2, lowerB); - replaceChildAtIndexInPlace(3, upperB); - } + static ProductLayout Builder(Layout argument, Layout variable, Layout lowerB, Layout upperB) { return TreeHandle::FixedArityBuilder(ArrayBuilder(argument, variable, lowerB, upperB).array(), 4); } + ProductLayout() = delete; }; } diff --git a/poincare/include/poincare/randint.h b/poincare/include/poincare/randint.h index 26baebae0..6d1b93516 100644 --- a/poincare/include/poincare/randint.h +++ b/poincare/include/poincare/randint.h @@ -19,32 +19,30 @@ public: // Properties Type type() const override { return Type::Randint; } + + // Complex + bool isReal(Context & context) const override { return true; } + private: // Layout Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; // Evaluation - Evaluation approximate(SinglePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { - return templateApproximate(context, angleUnit); + Evaluation approximate(SinglePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { + return templateApproximate(context, complexFormat, angleUnit); } - Evaluation approximate(DoublePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { - return templateApproximate(context, angleUnit); + Evaluation approximate(DoublePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { + return templateApproximate(context, complexFormat, angleUnit); } - template Evaluation templateApproximate(Context& context, Preferences::AngleUnit angleUnit) const; + template Evaluation templateApproximate(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; }; class Randint final : public Expression { friend class RandintNode; public: Randint(const RandintNode * n) : Expression(n) {} - static Randint Builder(Expression child0, Expression child1) { return Randint(child0, child1); } - static Expression UntypedBuilder(Expression children) { return Builder(children.childAtIndex(0), children.childAtIndex(1)); } - static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("randint", 2, &UntypedBuilder); -private: - Randint(Expression child0, Expression child1) : Expression(TreePool::sharedPool()->createTreeNode()) { - replaceChildAtIndexInPlace(0, child0); - replaceChildAtIndexInPlace(1, child1); - } + static Randint Builder(Expression child0, Expression child1) { return TreeHandle::FixedArityBuilder(ArrayBuilder(child0, child1).array(), 2); } + static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("randint", 2, &UntypedBuilderTwoChildren); }; } diff --git a/poincare/include/poincare/random.h b/poincare/include/poincare/random.h index 8ea1a4b01..b1982bceb 100644 --- a/poincare/include/poincare/random.h +++ b/poincare/include/poincare/random.h @@ -18,19 +18,22 @@ public: } #endif + // Complex + bool isReal(Context & context) const override { return true; } + // Properties Type type() const override { return Type::Random; } - Sign sign() const override { return Sign::Positive; } - Expression setSign(Sign s, Context & context, Preferences::AngleUnit angleUnit) override; + Sign sign(Context * context) const override { return Sign::Positive; } + Expression setSign(Sign s, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) override; private: // Layout Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; // Evaluation - Evaluation approximate(SinglePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { + Evaluation approximate(SinglePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { return templateApproximate(); } - Evaluation approximate(DoublePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { + Evaluation approximate(DoublePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { return templateApproximate(); } template Evaluation templateApproximate() const; @@ -40,14 +43,13 @@ class Random final : public Expression { friend class RandomNode; public: Random(const RandomNode * n) : Expression(n) {} - static Random Builder() { return Random(); } - static Expression UntypedBuilder(Expression children) { return Builder(); } + static Random Builder() { return TreeHandle::FixedArityBuilder(); } + static Expression UntypedBuilder(Expression children) { assert(children.type() == ExpressionNode::Type::Matrix); return Builder(); } static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("random", 0, &UntypedBuilder); template static T random(); private: - Random() : Expression(TreePool::sharedPool()->createTreeNode()) {} - Expression setSign(ExpressionNode::Sign s, Context & context, Preferences::AngleUnit angleUnit); + Expression setSign(ExpressionNode::Sign s, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit); }; } diff --git a/poincare/include/poincare/rational.h b/poincare/include/poincare/rational.h index dcaa4175f..03e241a28 100644 --- a/poincare/include/poincare/rational.h +++ b/poincare/include/poincare/rational.h @@ -9,11 +9,7 @@ namespace Poincare { class RationalNode final : public NumberNode { public: - RationalNode() : - m_negative(false), - m_numberOfDigitsNumerator(0), - m_numberOfDigitsDenominator(0) {} - virtual void setDigits(const native_uint_t * i, uint8_t numeratorSize, const native_uint_t * j, uint8_t denominatorSize, bool negative); + RationalNode(const native_uint_t * i, uint8_t numeratorSize, const native_uint_t * j, uint8_t denominatorSize, bool negative); Integer signedNumerator() const; Integer unsignedNumerator() const; @@ -22,7 +18,6 @@ public: void setNegative(bool negative) { m_negative = negative; } // TreeNode - void initToMatchSize(size_t goalSize) override; size_t size() const override; #if POINCARE_TREE_LOG virtual void logNodeName(std::ostream & stream) const override { @@ -36,14 +31,14 @@ public: // Expression subclassing Type type() const override { return Type::Rational; } - Sign sign() const override { return m_negative ? Sign::Negative : Sign::Positive; } + Sign sign(Context * context) const override { return m_negative ? Sign::Negative : Sign::Positive; } // Layout Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; // Approximation - Evaluation approximate(SinglePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { return Complex(templatedApproximate()); } - Evaluation approximate(DoublePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { return Complex(templatedApproximate()); } + Evaluation approximate(SinglePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { return Complex::Builder(templatedApproximate()); } + Evaluation approximate(DoublePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { return Complex::Builder(templatedApproximate()); } template T templatedApproximate() const; // Basic test @@ -56,11 +51,11 @@ public: static int NaturalOrder(const RationalNode * i, const RationalNode * j); private: - int simplificationOrderSameType(const ExpressionNode * e, bool canBeInterrupted) const override; - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) override; - Expression shallowBeautify(Context & context, Preferences::AngleUnit angleUnit) override; - Expression setSign(Sign s, Context & context, Preferences::AngleUnit angleUnit) override; - Expression denominator(Context & context, Preferences::AngleUnit angleUnit) const override; + int simplificationOrderSameType(const ExpressionNode * e, bool ascending, bool canBeInterrupted) const override; + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) override; + Expression shallowBeautify(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) override; + Expression setSign(Sign s, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) override; + Expression denominator(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override; bool m_negative; uint8_t m_numberOfDigitsNumerator; uint8_t m_numberOfDigitsDenominator; @@ -74,11 +69,11 @@ class Rational final : public Number { public: /* The constructor build a irreductible fraction */ Rational(const RationalNode * node) : Number(node) {} - Rational(Integer & num, Integer & den); - Rational(const Integer & numerator); - Rational(native_int_t i); - Rational(native_int_t i, native_int_t j); - Rational(const char * iString, const char * jString); + static Rational Builder(Integer & num, Integer & den); + static Rational Builder(const Integer & numerator); + static Rational Builder(native_int_t i); + static Rational Builder(native_int_t i, native_int_t j); + static Rational Builder(const char * iString, const char * jString); // TreeNode RationalNode * node() const { return static_cast(Number::node()); } @@ -108,15 +103,16 @@ public: static int NaturalOrder(const Rational & i, const Rational & j) { return RationalNode::NaturalOrder(i.node(), j.node()); } // Simplification - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit); + Expression shallowReduce(); private: - Rational(const native_uint_t * i, uint8_t numeratorSize, const native_uint_t * j, uint8_t denominatorSize, bool negative); + static Rational Builder(const native_uint_t * i, uint8_t numeratorSize, const native_uint_t * j, uint8_t denominatorSize, bool negative); + RationalNode * node() { return static_cast(Number::node()); } /* Simplification */ - Expression shallowBeautify(Context & context, Preferences::AngleUnit angleUnit); - Expression denominator(Context & context, Preferences::AngleUnit angleUnit) const; + Expression shallowBeautify(); + Expression denominator(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; Expression setSign(ExpressionNode::Sign s); }; diff --git a/poincare/include/poincare/real_part.h b/poincare/include/poincare/real_part.h index 61200e3c9..eefdc61eb 100644 --- a/poincare/include/poincare/real_part.h +++ b/poincare/include/poincare/real_part.h @@ -20,36 +20,36 @@ public: // Properties Type type() const override { return Type::RealPart; } + + // Complex + bool isReal(Context & context) const override { return true; } + private: // Layout Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; // Simplification - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) override; + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) override; // Evaluation - template static Complex computeOnComplex(const std::complex c, Preferences::AngleUnit angleUnit) { - return Complex(std::real(c)); + template static Complex computeOnComplex(const std::complex c, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) { + return Complex::Builder(std::real(c)); } - Evaluation approximate(SinglePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { - return ApproximationHelper::Map(this, context, angleUnit,computeOnComplex); + Evaluation approximate(SinglePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { + return ApproximationHelper::Map(this, context, complexFormat, angleUnit,computeOnComplex); } - Evaluation approximate(DoublePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { - return ApproximationHelper::Map(this, context, angleUnit, computeOnComplex); + Evaluation approximate(DoublePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { + return ApproximationHelper::Map(this, context, complexFormat, angleUnit, computeOnComplex); } }; class RealPart final : public Expression { public: RealPart(const RealPartNode * n) : Expression(n) {} - static RealPart Builder(Expression child) { return RealPart(child); } - static Expression UntypedBuilder(Expression children) { return Builder(children.childAtIndex(0)); } - static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("re", 1, &UntypedBuilder); + static RealPart Builder(Expression child) { return TreeHandle::FixedArityBuilder(&child, 1); } - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit); -private: - explicit RealPart(Expression child) : Expression(TreePool::sharedPool()->createTreeNode()) { - replaceChildAtIndexInPlace(0, child); - } + static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("re", 1, &UntypedBuilderOneChild); + + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); }; } diff --git a/poincare/include/poincare/right_parenthesis_layout.h b/poincare/include/poincare/right_parenthesis_layout.h index 6f127afb3..2f3f03d9f 100644 --- a/poincare/include/poincare/right_parenthesis_layout.h +++ b/poincare/include/poincare/right_parenthesis_layout.h @@ -36,7 +36,8 @@ protected: class RightParenthesisLayout final : public Layout { public: - RightParenthesisLayout(); + static RightParenthesisLayout Builder() { return TreeHandle::FixedArityBuilder(); } + RightParenthesisLayout() = delete; }; } diff --git a/poincare/include/poincare/right_square_bracket_layout.h b/poincare/include/poincare/right_square_bracket_layout.h index 1f6c5925f..161ade869 100644 --- a/poincare/include/poincare/right_square_bracket_layout.h +++ b/poincare/include/poincare/right_square_bracket_layout.h @@ -29,7 +29,8 @@ protected: class RightSquareBracketLayout final : public Layout { public: - RightSquareBracketLayout(); + static RightSquareBracketLayout Builder() { return TreeHandle::FixedArityBuilder(); } + RightSquareBracketLayout() = delete; }; } diff --git a/poincare/include/poincare/round.h b/poincare/include/poincare/round.h index 3965e3c73..f08c6ee41 100644 --- a/poincare/include/poincare/round.h +++ b/poincare/include/poincare/round.h @@ -18,6 +18,9 @@ public: } #endif + // Complex + bool isReal(Context & context) const override { return true; } + // Properties Type type() const override { return Type::Round; } private: @@ -25,26 +28,20 @@ private: Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; // Simplification - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) override; + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) override; // Evaluation - Evaluation approximate(SinglePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } - Evaluation approximate(DoublePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } - template Evaluation templatedApproximate(Context& context, Preferences::AngleUnit angleUnit) const; + Evaluation approximate(SinglePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, complexFormat, angleUnit); } + Evaluation approximate(DoublePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, complexFormat, angleUnit); } + template Evaluation templatedApproximate(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; }; class Round final : public Expression { public: Round(const RoundNode * n) : Expression(n) {} - static Round Builder(Expression child0, Expression child1) { return Round(child0, child1); } - static Expression UntypedBuilder(Expression children) { return Builder(children.childAtIndex(0), children.childAtIndex(1)); } - static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("round", 2, &UntypedBuilder); + static Round Builder(Expression child0, Expression child1) { return TreeHandle::FixedArityBuilder(ArrayBuilder(child0, child1).array(), 2); } + static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("round", 2, &UntypedBuilderTwoChildren); - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit); -private: - Round(Expression child0, Expression child1) : Expression(TreePool::sharedPool()->createTreeNode()) { - replaceChildAtIndexInPlace(0, child0); - replaceChildAtIndexInPlace(1, child1); - } + Expression shallowReduce(); }; } diff --git a/poincare/include/poincare/sequence.h b/poincare/include/poincare/sequence.h index 36fe8eb7c..e732c744e 100644 --- a/poincare/include/poincare/sequence.h +++ b/poincare/include/poincare/sequence.h @@ -14,12 +14,12 @@ private: Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; virtual Layout createSequenceLayout(Layout argumentLayout, Layout symbolLayout, Layout subscriptLayout, Layout superscriptLayout) const = 0; /* Approximation */ - Evaluation approximate(SinglePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } - Evaluation approximate(DoublePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } - template Evaluation templatedApproximate(Context& context, Preferences::AngleUnit angleUnit) const; + Evaluation approximate(SinglePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, complexFormat, angleUnit); } + Evaluation approximate(DoublePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, complexFormat, angleUnit); } + template Evaluation templatedApproximate(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; virtual float emptySequenceValue() const = 0; - virtual Evaluation evaluateWithNextTerm(SinglePrecision p, Evaluation a, Evaluation b) const = 0; - virtual Evaluation evaluateWithNextTerm(DoublePrecision p, Evaluation a, Evaluation b) const = 0; + virtual Evaluation evaluateWithNextTerm(SinglePrecision p, Evaluation a, Evaluation b, Preferences::ComplexFormat complexFormat) const = 0; + virtual Evaluation evaluateWithNextTerm(DoublePrecision p, Evaluation a, Evaluation b, Preferences::ComplexFormat complexFormat) const = 0; }; } diff --git a/poincare/include/poincare/sign_function.h b/poincare/include/poincare/sign_function.h new file mode 100644 index 000000000..23d197933 --- /dev/null +++ b/poincare/include/poincare/sign_function.h @@ -0,0 +1,57 @@ +#ifndef POINCARE_SIGN_FUNCTION_H +#define POINCARE_SIGN_FUNCTION_H + +#include +#include + +namespace Poincare { + +class SignFunctionNode final : public ExpressionNode { +public: + + // TreeNode + size_t size() const override { return sizeof(SignFunctionNode); } + int numberOfChildren() const override; +#if POINCARE_TREE_LOG + virtual void logNodeName(std::ostream & stream) const override { + stream << "SignFunction"; + } +#endif + + // Properties + Type type() const override { return Type::SignFunction; } + Sign sign(Context * context) const override; + Expression setSign(Sign s, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) override; + + // Complex + bool isReal(Context & context) const override { return true; } + +private: + // Layout + Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; + int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; + // Simplification + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) override; + // Evaluation + template static Complex computeOnComplex(const std::complex c, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit); + Evaluation approximate(SinglePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { + return ApproximationHelper::Map(this, context, complexFormat, angleUnit, computeOnComplex); + } + Evaluation approximate(DoublePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { + return ApproximationHelper::Map(this, context, complexFormat, angleUnit, computeOnComplex); + } +}; + +class SignFunction final : public Expression { +public: + SignFunction(const SignFunctionNode * n) : Expression(n) {} + static SignFunction Builder(Expression child) { return TreeHandle::FixedArityBuilder(&child, 1); } + + static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("sign", 1, &UntypedBuilderOneChild); + + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); +}; + +} + +#endif diff --git a/poincare/include/poincare/sine.h b/poincare/include/poincare/sine.h index 70c6dc88f..5137f0b5f 100644 --- a/poincare/include/poincare/sine.h +++ b/poincare/include/poincare/sine.h @@ -19,11 +19,14 @@ public: } #endif + // Complex + bool isReal(Context & context) const override { return childAtIndex(0)->isReal(context); } + // Properties Type type() const override { return Type::Sine; } float characteristicXRange(Context & context, Preferences::AngleUnit angleUnit) const override; - template static Complex computeOnComplex(const std::complex c, Preferences::AngleUnit angleUnit = Preferences::AngleUnit::Radian); + template static Complex computeOnComplex(const std::complex c, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit = Preferences::AngleUnit::Radian); private: // Layout @@ -31,29 +34,25 @@ private: int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; // Simplication - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) override; + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) override; // Evaluation - Evaluation approximate(SinglePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { - return ApproximationHelper::Map(this, context, angleUnit,computeOnComplex); + Evaluation approximate(SinglePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { + return ApproximationHelper::Map(this, context, complexFormat, angleUnit,computeOnComplex); } - Evaluation approximate(DoublePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { - return ApproximationHelper::Map(this, context, angleUnit, computeOnComplex); + Evaluation approximate(DoublePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { + return ApproximationHelper::Map(this, context, complexFormat, angleUnit, computeOnComplex); } }; class Sine final : public Expression { public: Sine(const SineNode * n) : Expression(n) {} - static Sine Builder(Expression child) { return Sine(child); } - static Expression UntypedBuilder(Expression children) { return Builder(children.childAtIndex(0)); } - static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("sin", 1, &UntypedBuilder); + static Sine Builder(Expression child) { return TreeHandle::FixedArityBuilder(&child, 1); } - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); -private: - explicit Sine(Expression child) : Expression(TreePool::sharedPool()->createTreeNode()) { - replaceChildAtIndexInPlace(0, child); - } + static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("sin", 1, &UntypedBuilderOneChild); + + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); }; } diff --git a/poincare/include/poincare/square_root.h b/poincare/include/poincare/square_root.h index 7e140afd1..4ea8737d1 100644 --- a/poincare/include/poincare/square_root.h +++ b/poincare/include/poincare/square_root.h @@ -3,6 +3,7 @@ #include #include +#include #include namespace Poincare { @@ -21,36 +22,32 @@ public: } #endif - private: // Layout Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; // Simplification - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) override; + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) override; // Evaluation - template static Complex computeOnComplex(const std::complex c, Preferences::AngleUnit angleUnit); - Evaluation approximate(SinglePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { - return ApproximationHelper::Map(this, context, angleUnit,computeOnComplex); + template static Complex computeOnComplex(const std::complex c, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit); + Evaluation approximate(SinglePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { + return ApproximationHelper::Map(this, context, complexFormat, angleUnit,computeOnComplex); } - Evaluation approximate(DoublePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { - return ApproximationHelper::Map(this, context, angleUnit, computeOnComplex); + Evaluation approximate(DoublePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { + return ApproximationHelper::Map(this, context, complexFormat, angleUnit, computeOnComplex); } }; class SquareRoot final : public Expression { public: SquareRoot(const SquareRootNode * n) : Expression(n) {} - static SquareRoot Builder(Expression child) { return SquareRoot(child); } - static Expression UntypedBuilder(Expression children) { return Builder(children.childAtIndex(0)); } - static_assert('\x91' == Ion::Charset::Root, "Charset error"); - static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("\x91", 1, &UntypedBuilder); + static SquareRoot Builder(Expression child) { return TreeHandle::FixedArityBuilder(&child, 1); } - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); + static_assert('\x91' == Ion::Charset::Root, "Charset error"); + static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("\x91", 1, &UntypedBuilderOneChild); + + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); private: - explicit SquareRoot(Expression child) : Expression(TreePool::sharedPool()->createTreeNode()) { - replaceChildAtIndexInPlace(0, child); - } static const char k_name[2]; }; diff --git a/poincare/include/poincare/store.h b/poincare/include/poincare/store.h index eeb6f9573..dbedd4df0 100644 --- a/poincare/include/poincare/store.h +++ b/poincare/include/poincare/store.h @@ -25,24 +25,22 @@ public: private: // Simplification - void deepReduceChildren(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) override; - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) override; + void deepReduceChildren(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) override; + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) override; // Layout Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; // Evalutation - Evaluation approximate(SinglePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } - Evaluation approximate(DoublePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } - template Evaluation templatedApproximate(Context& context, Preferences::AngleUnit angleUnit) const; + Evaluation approximate(SinglePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, complexFormat, angleUnit); } + Evaluation approximate(DoublePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, complexFormat, angleUnit); } + template Evaluation templatedApproximate(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; }; class Store final : public Expression { +friend class StoreNode; public: Store(const StoreNode * n) : Expression(n) {} - Store(Expression value, SymbolAbstract symbol) : Expression(TreePool::sharedPool()->createTreeNode()) { - replaceChildAtIndexInPlace(0, value); - replaceChildAtIndexInPlace(1, symbol); - } + static Store Builder(Expression value, SymbolAbstract symbol) { return TreeHandle::FixedArityBuilder(ArrayBuilder(value, symbol).array(), 2); } // Store const SymbolAbstract symbol() const { @@ -55,7 +53,11 @@ public: } // Expression - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit); + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); + +private: + Expression storeValueForSymbol(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; + StoreNode * node() const { return static_cast(Expression::node()); } }; } diff --git a/poincare/include/poincare/subtraction.h b/poincare/include/poincare/subtraction.h index 122c4f994..9c43f93af 100644 --- a/poincare/include/poincare/subtraction.h +++ b/poincare/include/poincare/subtraction.h @@ -25,12 +25,12 @@ public: int polynomialDegree(Context & context, const char * symbolName) const override; // Approximation - template static Complex compute(const std::complex c, const std::complex d) { return Complex(c - d); } - Evaluation approximate(SinglePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { - return ApproximationHelper::MapReduce(this, context, angleUnit, compute, computeOnComplexAndMatrix, computeOnMatrixAndComplex, computeOnMatrices); + template static Complex compute(const std::complex c, const std::complex d, Preferences::ComplexFormat complexFormat) { return Complex::Builder(c - d); } + Evaluation approximate(SinglePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { + return ApproximationHelper::MapReduce(this, context, complexFormat, angleUnit, compute, computeOnComplexAndMatrix, computeOnMatrixAndComplex, computeOnMatrices); } - Evaluation approximate(DoublePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { - return ApproximationHelper::MapReduce(this, context, angleUnit, compute, computeOnComplexAndMatrix, computeOnMatrixAndComplex, computeOnMatrices); + Evaluation approximate(DoublePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { + return ApproximationHelper::MapReduce(this, context, complexFormat, angleUnit, compute, computeOnComplexAndMatrix, computeOnMatrixAndComplex, computeOnMatrices); } /* Layout */ @@ -39,30 +39,28 @@ public: int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; /* Simplification */ - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) override; + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) override; private: /* Evaluation */ - template static MatrixComplex computeOnMatrixAndComplex(const MatrixComplex m, const std::complex c) { - return ApproximationHelper::ElementWiseOnMatrixComplexAndComplex(m, c, compute); + template static MatrixComplex computeOnMatrixAndComplex(const MatrixComplex m, const std::complex c, Preferences::ComplexFormat complexFormat) { + return ApproximationHelper::ElementWiseOnMatrixComplexAndComplex(m, c, complexFormat, compute); } - template static MatrixComplex computeOnComplexAndMatrix(const std::complex c, const MatrixComplex n); - template static MatrixComplex computeOnMatrices(const MatrixComplex m, const MatrixComplex n) { - return ApproximationHelper::ElementWiseOnComplexMatrices(m, n, compute); + template static MatrixComplex computeOnComplexAndMatrix(const std::complex c, const MatrixComplex n, Preferences::ComplexFormat complexFormat); + template static MatrixComplex computeOnMatrices(const MatrixComplex m, const MatrixComplex n, Preferences::ComplexFormat complexFormat) { + return ApproximationHelper::ElementWiseOnComplexMatrices(m, n, complexFormat, compute); } }; class Subtraction final : public Expression { public: - Subtraction(); Subtraction(const SubtractionNode * n) : Expression(n) {} - Subtraction(Expression child0, Expression child1) : Subtraction() { - replaceChildAtIndexInPlace(0, child0); - replaceChildAtIndexInPlace(1, child1); - } + static Subtraction Builder() { return TreeHandle::FixedArityBuilder(); } + static Subtraction Builder(Expression child0, Expression child1) { return TreeHandle::FixedArityBuilder(ArrayBuilder(child0, child1).array(), 2); } // Expression - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); + }; } diff --git a/poincare/include/poincare/sum.h b/poincare/include/poincare/sum.h index 0ac12d2b1..4256a18c5 100644 --- a/poincare/include/poincare/sum.h +++ b/poincare/include/poincare/sum.h @@ -22,36 +22,23 @@ private: float emptySequenceValue() const override { return 0.0f; } Layout createSequenceLayout(Layout argumentLayout, Layout symbolLayout, Layout subscriptLayout, Layout superscriptLayout) const override; int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; - Evaluation evaluateWithNextTerm(DoublePrecision p, Evaluation a, Evaluation b) const override { - return templatedApproximateWithNextTerm(a, b); + Evaluation evaluateWithNextTerm(DoublePrecision p, Evaluation a, Evaluation b, Preferences::ComplexFormat complexFormat) const override { + return templatedApproximateWithNextTerm(a, b, complexFormat); } - Evaluation evaluateWithNextTerm(SinglePrecision p, Evaluation a, Evaluation b) const override { - return templatedApproximateWithNextTerm(a, b); + Evaluation evaluateWithNextTerm(SinglePrecision p, Evaluation a, Evaluation b, Preferences::ComplexFormat complexFormat) const override { + return templatedApproximateWithNextTerm(a, b, complexFormat); } - template Evaluation templatedApproximateWithNextTerm(Evaluation a, Evaluation b) const; + template Evaluation templatedApproximateWithNextTerm(Evaluation a, Evaluation b, Preferences::ComplexFormat complexFormat) const; }; class Sum final : public Expression { friend class SumNode; public: Sum(const SumNode * n) : Expression(n) {} - static Sum Builder(Expression child0, Symbol child1, Expression child2, Expression child3) { return Sum(child0, child1, child2, child3); } - static Expression UntypedBuilder(Expression children) { - if (children.childAtIndex(1).type() != ExpressionNode::Type::Symbol) { - // Second parameter must be a Symbol. - return Expression(); - } - return Builder(children.childAtIndex(0), children.childAtIndex(1).convert(), children.childAtIndex(2), children.childAtIndex(3)); - } + static Sum Builder(Expression child0, Symbol child1, Expression child2, Expression child3) { return TreeHandle::FixedArityBuilder(ArrayBuilder(child0, child1, child2, child3).array(), 4); } + static Expression UntypedBuilder(Expression children); + static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("sum", 4, &UntypedBuilder); -private: - Sum(Expression child0, Expression child1, Expression child2, Expression child3) : Expression(TreePool::sharedPool()->createTreeNode()) { - assert(child1.type() == ExpressionNode::Type::Symbol); - replaceChildAtIndexInPlace(0, child0); - replaceChildAtIndexInPlace(1, child1); - replaceChildAtIndexInPlace(2, child2); - replaceChildAtIndexInPlace(3, child3); - } }; } diff --git a/poincare/include/poincare/sum_layout.h b/poincare/include/poincare/sum_layout.h index f201a8e69..57c6206db 100644 --- a/poincare/include/poincare/sum_layout.h +++ b/poincare/include/poincare/sum_layout.h @@ -23,14 +23,8 @@ private: class SumLayout final : public Layout { public: - SumLayout(Layout argument, Layout variable, Layout lowerB, Layout upperB) : - Layout(TreePool::sharedPool()->createTreeNode()) - { - replaceChildAtIndexInPlace(0, argument); - replaceChildAtIndexInPlace(1, variable); - replaceChildAtIndexInPlace(2, lowerB); - replaceChildAtIndexInPlace(3, upperB); - } + static SumLayout Builder(Layout argument, Layout variable, Layout lowerB, Layout upperB) { return TreeHandle::FixedArityBuilder(ArrayBuilder(argument, variable, lowerB, upperB).array(), 4); } + SumLayout() = delete; }; } diff --git a/poincare/include/poincare/symbol.h b/poincare/include/poincare/symbol.h index eb6a90389..3c4c93ec4 100644 --- a/poincare/include/poincare/symbol.h +++ b/poincare/include/poincare/symbol.h @@ -7,6 +7,8 @@ namespace Poincare { class SymbolNode final : public SymbolAbstractNode { public: + SymbolNode(const char * newName, int length); + const char * name() const override { return m_name; } // TreeNode @@ -26,23 +28,26 @@ public: int getVariables(Context & context, isVariableTest isVariable, char * variables, int maxSizeVariable) const override; float characteristicXRange(Context & context, Preferences::AngleUnit angleUnit) const override; + // Complex + bool isReal(Context & context) const override; + /* Layout */ Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; /* Simplification */ - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) override; + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) override; Expression shallowReplaceReplaceableSymbols(Context & context) override; /* Approximation */ - Evaluation approximate(SinglePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } - Evaluation approximate(DoublePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, angleUnit); } + Evaluation approximate(SinglePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, complexFormat, angleUnit); } + Evaluation approximate(DoublePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(context, complexFormat, angleUnit); } private: char m_name[0]; // MUST be the last member variable size_t nodeSize() const override { return sizeof(SymbolNode); } - template Evaluation templatedApproximate(Context& context, Preferences::AngleUnit angleUnit) const; + template Evaluation templatedApproximate(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; }; class Symbol final : public SymbolAbstract { @@ -58,25 +63,19 @@ public: * characters but events as 'end of text', 'backspace'... */ UnknownX = 1, }; - Symbol(const char * name, int length); - Symbol(char name); Symbol(const SymbolNode * node) : SymbolAbstract(node) {} - static Symbol Ans() { return Symbol(k_ans, k_ansLength); } - static Expression UntypedBuilder(const char * name, size_t length, Context * context) { - // create an expression only if it is not in the context or defined as a symbol - Symbol s(name, length); - if (SymbolAbstract::ValidInContext(s, context)) { - return s; - } - return Expression(); - } + static Symbol Builder(const char * name, int length) { return SymbolAbstract::Builder(name, length); } + static Symbol Builder(char name) { return Symbol::Builder(&name, 1); } + static Symbol Ans() { return Symbol::Builder(k_ans, k_ansLength); } + static Expression UntypedBuilder(const char * name, size_t length, Context * context); + // Symbol properties bool isSystemSymbol() const { return name()[0] == SpecialSymbols::UnknownX && name()[1] == 0; } static bool isSeriesSymbol(const char * c); static bool isRegressionSymbol(const char * c); // Expression - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, 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; diff --git a/poincare/include/poincare/symbol_abstract.h b/poincare/include/poincare/symbol_abstract.h index 04580b1c0..2c2e10a13 100644 --- a/poincare/include/poincare/symbol_abstract.h +++ b/poincare/include/poincare/symbol_abstract.h @@ -27,12 +27,14 @@ class SymbolAbstractNode : public ExpressionNode { friend class Store; public: virtual const char * name() const = 0; - void setName(const char * newName, int length); size_t size() const override; - void initToMatchSize(size_t goalSize) override; // ExpressionNode - int simplificationOrderSameType(const ExpressionNode * e, bool canBeInterrupted) const override; + int simplificationOrderSameType(const ExpressionNode * e, bool ascending, bool canBeInterrupted) const override; + + // Property + Sign sign(Context * context) const override; + Expression setSign(ExpressionNode::Sign s, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) override; // TreeNode #if POINCARE_TREE_LOG @@ -73,10 +75,13 @@ public: protected: SymbolAbstract(const SymbolAbstractNode * node) : Expression(node) {} + template + static T Builder(const char * name, int length); + SymbolAbstractNode * node() const { return static_cast(Expression::node()); } private: static Expression Expand(const SymbolAbstract & symbol, Context & context, bool clone); - static size_t AlignedNodeSize(size_t nameLength, size_t nodeSize); + static bool isReal(const SymbolAbstract & symbol, Context & context); }; } diff --git a/poincare/include/poincare/tangent.h b/poincare/include/poincare/tangent.h index 3f32fd99e..ee1ff3ec4 100644 --- a/poincare/include/poincare/tangent.h +++ b/poincare/include/poincare/tangent.h @@ -22,36 +22,35 @@ public: Type type() const override { return Type::Tangent; } float characteristicXRange(Context & context, Preferences::AngleUnit angleUnit) const override; + // Complex + bool isReal(Context & context) const override { return childAtIndex(0)->isReal(context); } + private: // Layout Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; // Simplication - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) override; + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) override; // Evaluation - template static Complex computeOnComplex(const std::complex c, Preferences::AngleUnit angleUnit = Preferences::AngleUnit::Radian); - Evaluation approximate(SinglePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { - return ApproximationHelper::Map(this, context, angleUnit,computeOnComplex); + template static Complex computeOnComplex(const std::complex c, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit = Preferences::AngleUnit::Radian); + Evaluation approximate(SinglePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { + return ApproximationHelper::Map(this, context, complexFormat, angleUnit,computeOnComplex); } - Evaluation approximate(DoublePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { - return ApproximationHelper::Map(this, context, angleUnit, computeOnComplex); + Evaluation approximate(DoublePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { + return ApproximationHelper::Map(this, context, complexFormat, angleUnit, computeOnComplex); } }; class Tangent final : public Expression { public: Tangent(const TangentNode * n) : Expression(n) {} - static Tangent Builder(Expression child) { return Tangent(child); } - static Expression UntypedBuilder(Expression children) { return Builder(children.childAtIndex(0)); } - static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("tan", 1, &UntypedBuilder); + static Tangent Builder(Expression child) { return TreeHandle::FixedArityBuilder(&child, 1); } - Expression shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); -private: - explicit Tangent(Expression child) : Expression(TreePool::sharedPool()->createTreeNode()) { - replaceChildAtIndexInPlace(0, child); - } + static constexpr Expression::FunctionHelper s_functionHelper = Expression::FunctionHelper("tan", 1, &UntypedBuilderOneChild); + + Expression shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); }; } diff --git a/poincare/include/poincare/tree_handle.h b/poincare/include/poincare/tree_handle.h index f9e58440b..959141249 100644 --- a/poincare/include/poincare/tree_handle.h +++ b/poincare/include/poincare/tree_handle.h @@ -20,6 +20,8 @@ namespace Poincare { * equivalent to Logarithm l = Logarithm(clone())). */ class TreeHandle { + template + friend class ArrayBuilder; friend class TreeNode; friend class TreePool; public: @@ -103,10 +105,26 @@ public: protected: /* Constructor */ TreeHandle(const TreeNode * node); - TreeHandle(int nodeIndentifier = TreeNode::NoNodeIdentifier) : m_identifier(nodeIndentifier) {} + // Un-inlining this constructor actually inscreases the firmware size + TreeHandle(int nodeIndentifier = TreeNode::NoNodeIdentifier) : m_identifier(nodeIndentifier) { + if (hasNode(nodeIndentifier)) { + node()->retain(); + } + } + + // WARNING: if the children table is the result of a cast, the object downcasted has to be the same size as a TreeHandle. + template + static T NAryBuilder(TreeHandle * children = nullptr, size_t numberOfChildren = 0); + template + static T FixedArityBuilder(TreeHandle * children = nullptr, size_t numberOfChildren = 0); + + static TreeHandle BuildWithGhostChildren(TreeNode * node); + void setIdentifierAndRetain(int newId); void setTo(const TreeHandle & tr); + static bool hasNode(int identifier) { return identifier > TreeNode::NoNodeIdentifier; } + /* Hierarchy operations */ // Add void addChildAtIndexInPlace(TreeHandle t, int index, int currentNumberOfChildren); @@ -118,6 +136,9 @@ protected: int m_identifier; private: + template + static TreeHandle Builder(); + void detachFromParent(); // Add ghost children on layout construction void buildGhostChildren(); diff --git a/poincare/include/poincare/tree_node.h b/poincare/include/poincare/tree_node.h index 4a5393b4f..26b9efb57 100644 --- a/poincare/include/poincare/tree_node.h +++ b/poincare/include/poincare/tree_node.h @@ -42,7 +42,6 @@ public: static constexpr int NoNodeIdentifier = -1; // Constructor and destructor - virtual void initToMatchSize(size_t goalSize) { assert(false); } virtual ~TreeNode() {} // Attributes diff --git a/poincare/include/poincare/tree_pool.h b/poincare/include/poincare/tree_pool.h index 3399f2f18..78c8a7342 100644 --- a/poincare/include/poincare/tree_pool.h +++ b/poincare/include/poincare/tree_pool.h @@ -31,8 +31,8 @@ public: return m_nodeForIdentifier[identifier]; } - template - T * createTreeNode(size_t size = sizeof(T)); + // Pool memory + void * alloc(size_t size); void move(TreeNode * destination, TreeNode * source, int realNumberOfSourceChildren); void moveChildren(TreeNode * destination, TreeNode * sourceParent); void removeChildren(TreeNode * node, int nodeNumberOfChildren); @@ -54,7 +54,6 @@ private: static TreePool * SharedStaticPool; // TreeNode - void addGhostChildrenAndRename(TreeNode * node); void discardTreeNode(TreeNode * node); void registerNode(TreeNode * node); void unregisterNode(TreeNode * node) { @@ -106,7 +105,6 @@ private: RootNodes roots() { return RootNodes(first()); } // Pool memory - void * alloc(size_t size); void dealloc(TreeNode * ptr, size_t size); void moveNodes(TreeNode * destination, TreeNode * source, size_t moveLength); diff --git a/poincare/include/poincare/trigonometry.h b/poincare/include/poincare/trigonometry.h index 6092d0aa5..794a65b37 100644 --- a/poincare/include/poincare/trigonometry.h +++ b/poincare/include/poincare/trigonometry.h @@ -13,11 +13,13 @@ public: Sine = 1, }; static float characteristicXRange(const Expression & e, Context & context, Preferences::AngleUnit angleUnit); - static Expression shallowReduceDirectFunction(Expression & e, Context& context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); - static Expression shallowReduceInverseFunction(Expression & e, Context& context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); + static bool isDirectTrigonometryFunction(const Expression & e); + static bool isInverseTrigonometryFunction(const Expression & e); + static bool AreInverseFunctions(const Expression & directFunction, const Expression & inverseFunction); static bool ExpressionIsEquivalentToTangent(const Expression & e); - constexpr static int k_numberOfEntries = 37; - static Expression table(const Expression e, ExpressionNode::Type type, Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); // , Function f, bool inverse + static Expression shallowReduceDirectFunction(Expression & e, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); + static Expression shallowReduceInverseFunction(Expression & e, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); + static Expression table(const Expression e, ExpressionNode::Type type, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target); // , Function f, bool inverse template static std::complex ConvertToRadian(const std::complex c, Preferences::AngleUnit angleUnit); template static std::complex ConvertRadianToAngleUnit(const std::complex c, Preferences::AngleUnit angleUnit); template static std::complex RoundToMeaningfulDigits(const std::complex result, const std::complex input); diff --git a/poincare/include/poincare/trigonometry_cheat_table.h b/poincare/include/poincare/trigonometry_cheat_table.h new file mode 100644 index 000000000..eb81172d5 --- /dev/null +++ b/poincare/include/poincare/trigonometry_cheat_table.h @@ -0,0 +1,83 @@ +#ifndef POINCARE_TRIGONOMETRY_CHEAT_TABLE_H +#define POINCARE_TRIGONOMETRY_CHEAT_TABLE_H + +#include +#include + +namespace Poincare { + + /* We use the cheat table to look for known simplifications (e.g. cos(0)=1, + * cos(Pi/2)=1...). For each entry of the table, we store its expression and + * its float approximation in order to quickly scan the table looking for our + * input approximation. If one entry matches the float approximation, we then + * check that the actual expression of our input is equivalent to the table + * expression. + * + * Example: ArcSine(1/2) + * -> Approximate 1/2: 0.5 + * -> Look up the row for 0.5 in the "Sine" column + * -> Return the "Angle" value of this row */ + +class TrigonometryCheatTable { +public: + constexpr static int k_numberOfEntries = 37; + enum class Type { + AngleInDegrees = 0, + AngleInRadians = 1, + Cosine = 2, + Sine = 3, + Tangent = 4 + }; + static const TrigonometryCheatTable * Table(); + Expression simplify(const Expression e, ExpressionNode::Type type, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) const; + +private: + + // ROW CLASS + class Row { + public: + + // PAIR + class Pair { + public: + constexpr Pair(const char * expression, float value = NAN) : + m_expression(expression), m_value(value) {} + Expression reducedExpression(bool assertNotUninitialized, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) const; + float value() const { return m_value; } + private: + const char * m_expression; + float m_value; + }; + // END OF PAIR CLASS + + constexpr Row(Pair angleInRadians, Pair angleInDegrees, Pair sine, Pair cosine, Pair tangent) : + m_pairs{angleInRadians, angleInDegrees, sine, cosine, tangent} {} + float floatForType(Type t) const { + assert(((int) t) >= 0 && ((int) t) < k_numberOfPairs); + return m_pairs[(int)t].value(); + } + Expression expressionForType(Type t, bool assertNotUninitialized, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) const { + assert(((int) t) >= 0 && ((int) t) < k_numberOfPairs); + return m_pairs[(int)t].reducedExpression(assertNotUninitialized, context, complexFormat, angleUnit, target); + } + private: + constexpr static int k_numberOfPairs = 5; + const Pair m_pairs[k_numberOfPairs]; + }; + // END OF ROW CLASS + + constexpr TrigonometryCheatTable(const Row * rows) : m_rows(rows) {} + float floatForTypeAtIndex(Type t, int i) const { + assert(i >= 0 && i < k_numberOfEntries); + return m_rows[i].floatForType(t); + } + Expression expressionForTypeAtIndex(Type t, int i, bool assertNotUninitialized, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) const { + assert(i >= 0 && i < k_numberOfEntries); + return m_rows[i].expressionForType(t, assertNotUninitialized, context, complexFormat, angleUnit, target); + } + const Row * m_rows; +}; + +} + +#endif diff --git a/poincare/include/poincare/undefined.h b/poincare/include/poincare/undefined.h index 198f4adb6..3413d8bd9 100644 --- a/poincare/include/poincare/undefined.h +++ b/poincare/include/poincare/undefined.h @@ -5,7 +5,7 @@ namespace Poincare { -class UndefinedNode final : public NumberNode { +class UndefinedNode : public NumberNode { public: // TreeNode @@ -16,29 +16,33 @@ public: } #endif + // Complex + bool isReal(Context & context) const override { return false; } + // Properties Type type() const override { return Type::Undefined; } int polynomialDegree(Context & context, const char * symbolName) const override; - Expression setSign(Sign s, Context & context, Preferences::AngleUnit angleUnit) override; + Expression setSign(Sign s, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) override; // Approximation - Evaluation approximate(SinglePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { + Evaluation approximate(SinglePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(); } - Evaluation approximate(DoublePrecision p, Context& context, Preferences::AngleUnit angleUnit) const override { + Evaluation approximate(DoublePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { return templatedApproximate(); } // Layout Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode = Preferences::PrintFloatMode::Decimal, int numberOfSignificantDigits = 0) const override; -private: +protected: template Evaluation templatedApproximate() const; }; class Undefined final : public Number { public: - Undefined() : Number(TreePool::sharedPool()->createTreeNode()) {} + Undefined(const UndefinedNode * n) : Number(n) {} + static Undefined Builder() { return TreeHandle::FixedArityBuilder(); } static const char * Name() { return "undef"; } diff --git a/poincare/include/poincare/unreal.h b/poincare/include/poincare/unreal.h new file mode 100644 index 000000000..fdc54ac68 --- /dev/null +++ b/poincare/include/poincare/unreal.h @@ -0,0 +1,54 @@ +#ifndef POINCARE_UNREAL_H +#define POINCARE_UNREAL_H + +#include + +namespace Poincare { + +class UnrealNode final : public UndefinedNode { +public: + + // TreeNode + size_t size() const override { return sizeof(UnrealNode); } +#if POINCARE_TREE_LOG + virtual void logNodeName(std::ostream & stream) const override { + stream << "Unreal"; + } +#endif + + // Properties + Type type() const override { return Type::Unreal; } + + // Approximation + Evaluation approximate(SinglePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { + return templatedApproximate(); + } + Evaluation approximate(DoublePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const override { + return templatedApproximate(); + } + + // Layout + Layout createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const override; + int serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode = Preferences::PrintFloatMode::Decimal, int numberOfSignificantDigits = 0) const override; +private: + template Evaluation templatedApproximate() const { + Expression::SetEncounteredComplex(true); + return UndefinedNode::templatedApproximate(); + } +}; + +class Unreal final : public Number { +public: + static Unreal Builder() { return TreeHandle::FixedArityBuilder(); } + Unreal() = delete; + static const char * Name() { + return "unreal"; + } + static int NameSize() { + return 7; + } +}; + +} + +#endif diff --git a/poincare/include/poincare/vertical_offset_layout.h b/poincare/include/poincare/vertical_offset_layout.h index c46caba12..94162aa44 100644 --- a/poincare/include/poincare/vertical_offset_layout.h +++ b/poincare/include/poincare/vertical_offset_layout.h @@ -20,7 +20,6 @@ public: // VerticalOffsetLayoutNode Type type() const { return m_type; } - void setType(Type type) { m_type = type; } // LayoutNode void moveCursorLeft(LayoutCursor * cursor, bool * shouldRecomputeLayout) override; @@ -59,7 +58,8 @@ private: class VerticalOffsetLayout final : public Layout { public: - VerticalOffsetLayout(Layout l, VerticalOffsetLayoutNode::Type type); + static VerticalOffsetLayout Builder(Layout l, VerticalOffsetLayoutNode::Type type); + VerticalOffsetLayout() = delete; }; } diff --git a/poincare/include/poincare_nodes.h b/poincare/include/poincare_nodes.h index 5020ecace..63d1eea25 100644 --- a/poincare/include/poincare_nodes.h +++ b/poincare/include/poincare_nodes.h @@ -67,6 +67,7 @@ #include #include #include +#include #include #include #include @@ -75,6 +76,7 @@ #include #include #include +#include #include #endif diff --git a/poincare/src/absolute_value.cpp b/poincare/src/absolute_value.cpp index 6a16ccb95..a1053c10b 100644 --- a/poincare/src/absolute_value.cpp +++ b/poincare/src/absolute_value.cpp @@ -3,6 +3,8 @@ #include #include #include +#include +#include #include #include @@ -12,33 +14,34 @@ constexpr Expression::FunctionHelper AbsoluteValue::s_functionHelper; int AbsoluteValueNode::numberOfChildren() const { return AbsoluteValue::s_functionHelper.numberOfChildren(); } -Expression AbsoluteValueNode::setSign(Sign s, Context & context, Preferences::AngleUnit angleUnit) { - return AbsoluteValue(this).setSign(s, context, angleUnit); +Expression AbsoluteValueNode::setSign(Sign s, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) { + assert(s == ExpressionNode::Sign::Positive); + return AbsoluteValue(this).setSign(s, context, complexFormat, angleUnit); } Layout AbsoluteValueNode::createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const { - return AbsoluteValueLayout(childAtIndex(0)->createLayout(floatDisplayMode, numberOfSignificantDigits)); + return AbsoluteValueLayout::Builder(childAtIndex(0)->createLayout(floatDisplayMode, numberOfSignificantDigits)); } int AbsoluteValueNode::serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const { return SerializationHelper::Prefix(this, buffer, bufferSize, floatDisplayMode, numberOfSignificantDigits, AbsoluteValue::s_functionHelper.name()); } -Expression AbsoluteValueNode::shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) { - return AbsoluteValue(this).shallowReduce(context, angleUnit); +Expression AbsoluteValueNode::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) { + return AbsoluteValue(this).shallowReduce(context, complexFormat, angleUnit, target); } -Expression AbsoluteValue::setSign(ExpressionNode::Sign s, Context & context, Preferences::AngleUnit angleUnit) { + +Expression AbsoluteValue::setSign(ExpressionNode::Sign s, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) { assert(s == ExpressionNode::Sign::Positive); return *this; } -Expression AbsoluteValue::shallowReduce(Context & context, Preferences::AngleUnit angleUnit) { - Expression e = Expression::defaultShallowReduce(context, angleUnit); +Expression AbsoluteValue::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { + Expression e = Expression::defaultShallowReduce(); if (e.isUndefined()) { return e; } - Expression c = childAtIndex(0); #if MATRIX_EXACT_REDUCING #if 0 if (c->type() == Type::Matrix) { @@ -46,15 +49,33 @@ Expression AbsoluteValue::shallowReduce(Context & context, Preferences::AngleUni } #endif #endif - if (c.sign() == ExpressionNode::Sign::Positive) { - replaceWithInPlace(c); - return c; + Expression c = childAtIndex(0); + if (c.isReal(context)) { + float app = c.node()->approximate(float(), context, complexFormat, angleUnit).toScalar(); + if (!std::isnan(app) && + ((c.isNumber() && app >= 0) || app >= Expression::Epsilon())) { + /* abs(a) = a with a >= 0 + * To check that a > 0, if a is a number we can use float comparison; + * in other cases, we are more conservative and rather check that + * a > epsilon ~ 1E-7 to avoid potential error due to float precision. */ + replaceWithInPlace(c); + return c; + } else if (!std::isnan(app) && + ((c.isNumber() && app < 0.0f) || app <= -Expression::Epsilon())) { + // abs(a) = -a with a < 0 (same comment as above to check that a < 0) + Multiplication m = Multiplication::Builder(Rational::Builder(-1), c); + replaceWithInPlace(m); + return m.shallowReduce(context, complexFormat, angleUnit, target); + } } - if (c.sign() == ExpressionNode::Sign::Negative) { - Expression result = c.setSign(ExpressionNode::Sign::Positive, context, angleUnit); - replaceWithInPlace(result); - return result; + if (c.type() == ExpressionNode::Type::ComplexCartesian) { + ComplexCartesian complexChild = static_cast(c); + Expression childNorm = complexChild.norm(context, complexFormat, angleUnit, target); + replaceWithInPlace(childNorm); + return childNorm.shallowReduce(context, complexFormat, angleUnit, target); } + // abs(-x) = abs(x) + c.makePositiveAnyNegativeNumeralFactor(context, complexFormat, angleUnit, target); return *this; } diff --git a/poincare/src/addition.cpp b/poincare/src/addition.cpp index d3de4068e..98a2f4928 100644 --- a/poincare/src/addition.cpp +++ b/poincare/src/addition.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -27,12 +28,10 @@ int AdditionNode::getPolynomialCoefficients(Context & context, const char * symb return Addition(this).getPolynomialCoefficients(context, symbolName, coefficients); } -// Private - // Layout bool AdditionNode::childNeedsParenthesis(const TreeNode * child) const { if (((static_cast(child)->isNumber() - && static_cast(child)->sign() == Sign::Negative) + && Number(static_cast(child)).sign() == Sign::Negative) || static_cast(child)->type() == Type::Opposite) && child != childAtIndex(0)) { @@ -51,23 +50,22 @@ int AdditionNode::serialize(char * buffer, int bufferSize, Preferences::PrintFlo // Simplication -Expression AdditionNode::shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) { - return Addition(this).shallowReduce(context, angleUnit, target); +Expression AdditionNode::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) { + return Addition(this).shallowReduce(context, complexFormat, angleUnit, target); } -Expression AdditionNode::shallowBeautify(Context & context, Preferences::AngleUnit angleUnit) { - return Addition(this).shallowBeautify(context, angleUnit); +Expression AdditionNode::shallowBeautify(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) { + return Addition(this).shallowBeautify(context, complexFormat, angleUnit, target); } // Addition -Addition::Addition() : NAryExpression(TreePool::sharedPool()->createTreeNode()) {} const Number Addition::NumeralFactor(const Expression & e) { if (e.type() == ExpressionNode::Type::Multiplication && e.childAtIndex(0).isNumber()) { Number result = e.childAtIndex(0).convert(); return result; } - return Rational(1); + return Rational::Builder(1); } int Addition::getPolynomialCoefficients(Context & context, const char * symbolName, Expression coefficients[]) const { @@ -76,7 +74,7 @@ int Addition::getPolynomialCoefficients(Context & context, const char * symbolNa return -1; } for (int k = 0; k < deg+1; k++) { - coefficients[k] = Addition(); + coefficients[k] = Addition::Builder(); } Expression intermediateCoefficients[Expression::k_maxNumberOfPolynomialCoefficients]; for (int i = 0; i < numberOfChildren(); i++) { @@ -89,38 +87,34 @@ int Addition::getPolynomialCoefficients(Context & context, const char * symbolNa return deg; } -Expression Addition::shallowBeautify(Context & context, Preferences::AngleUnit angleUnit) { +Expression Addition::shallowBeautify(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { /* Beautifying AdditionNode essentially consists in adding Subtractions if * needed. * In practice, we want to turn "a+(-1)*b" into "a-b". Or, more precisely, any * "a+(-r)*b" into "a-r*b" where r is a positive Rational. * Note: the process will slightly differ if the negative product occurs on - * the first term: we want to turn "AdditionNode(Multiplication(-1,b))" into + * the first term: we want to turn "Addition(Multiplication(-1,b))" into * "Opposite(b)". * Last but not least, special care must be taken when iterating over children * since we may remove some during the process. */ + /* Sort children in decreasing order: + * 1+x+x^2 --> x^2+x+1 + * 1+R(2) --> R(2)+1 */ + sortChildrenInPlace([](const ExpressionNode * e1, const ExpressionNode * e2, bool canBeInterrupted) { return ExpressionNode::SimplificationOrder(e1, e2, false, canBeInterrupted); }, true); + for (int i = 0; i < numberOfChildren(); i++) { - Expression childI = childAtIndex(i); - if (childI.type() != ExpressionNode::Type::Multiplication - || !childI.childAtIndex(0).isNumber() - || childI.childAtIndex(0).sign() != ExpressionNode::Sign::Negative) + // Try to make the child i positive if any negative numeral factor is found + Expression subtractant = childAtIndex(i).makePositiveAnyNegativeNumeralFactor(context, complexFormat, angleUnit, target); + if (subtractant.isUninitialized()) { - // Ignore terms which are not like "(-r)*a" + // if subtractant is not initialized, it means the child i had no negative numeral factor + // we ignore terms which are not like "(-r)*a" continue; } - Multiplication m = static_cast(childI); - - if (m.childAtIndex(0).type() == ExpressionNode::Type::Rational && m.childAtIndex(0).convert().isMinusOne()) { - m.removeChildAtIndexInPlace(0); - } else { - m.childAtIndex(0).setSign(ExpressionNode::Sign::Positive, context, angleUnit); - } - Expression subtractant = m.squashUnaryHierarchyInPlace(); - if (i == 0) { - Opposite o = Opposite(subtractant); + Opposite o = Opposite::Builder(subtractant); replaceChildAtIndexInPlace(i, o); } else { Expression leftSibling = childAtIndex(i-1); @@ -128,7 +122,7 @@ Expression Addition::shallowBeautify(Context & context, Preferences::AngleUnit a /* CAUTION: we removed a child. So we need to decrement i to make sure * the next iteration is actually on the next child. */ i--; - Subtraction s = Subtraction(leftSibling, subtractant); + Subtraction s = Subtraction::Builder(leftSibling, subtractant); /* We stole subtractant from this which replaced it by a ghost. We thus * need to put the subtraction at the previous index of subtractant, which * is still i because we updated i after removing a child. */ @@ -141,9 +135,9 @@ Expression Addition::shallowBeautify(Context & context, Preferences::AngleUnit a return result; } -Expression Addition::shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { +Expression Addition::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { { - Expression e = Expression::defaultShallowReduce(context, angleUnit); + Expression e = Expression::defaultShallowReduce(); if (e.isUndefined()) { return e; } @@ -161,7 +155,7 @@ Expression Addition::shallowReduce(Context & context, Preferences::AngleUnit ang } // Step 2: Sort the children - sortChildrenInPlace(ExpressionNode::SimplificationOrder, true); + sortChildrenInPlace([](const ExpressionNode * e1, const ExpressionNode * e2, bool canBeInterrupted) { return ExpressionNode::SimplificationOrder(e1, e2, true, canBeInterrupted); }, true); #if MATRIX_EXACT_REDUCING #if 0 @@ -188,7 +182,7 @@ Expression Addition::shallowReduce(Context & context, Preferences::AngleUnit ang int on = currentMatrix->numberOfRows(); int om = currentMatrix->numberOfColumns(); if (on != n || om != m) { - return replaceWith(new Undefined(), true); + return replaceWith(new Undefined::Builder(), true); } // Dispatch the current matrix children in the created additions matrix for (int j = 0; j < n*m; j++) { @@ -197,7 +191,7 @@ Expression Addition::shallowReduce(Context & context, Preferences::AngleUnit ang resultMatrix->replaceOperand(resultMatrixEntryJ, a, false); a->addOperand(currentMatrix->childAtIndex(j)); a->addOperand(resultMatrixEntryJ); - a->shallowReduce(context, angleUnit); + a->shallowReduce(context, complexFormat, angleUnit); } currentMatrix->detachOperands(); removeOperand(currentMatrix, true); @@ -209,9 +203,9 @@ Expression Addition::shallowReduce(Context & context, Preferences::AngleUnit ang Expression * entryI = resultMatrix->childAtIndex(i); resultMatrix->replaceOperand(entryI, a, false); a->addOperand(entryI); - a->shallowReduce(context, angleUnit); + a->shallowReduce(context, complexFormat, angleUnit); } - return replaceWith(resultMatrix, true)->shallowReduce(context, angleUnit); + return replaceWith(resultMatrix, true)->shallowReduce(context, complexFormat, angleUnit); } #endif #endif @@ -231,7 +225,7 @@ Expression Addition::shallowReduce(Context & context, Preferences::AngleUnit ang continue; } if (TermsHaveIdenticalNonNumeralFactors(e1, e2)) { - factorizeChildrenAtIndexesInPlace(i, i+1, context, angleUnit, target); + factorizeChildrenAtIndexesInPlace(i, i+1, context, complexFormat, angleUnit, target); continue; } i++; @@ -253,14 +247,50 @@ Expression Addition::shallowReduce(Context & context, Preferences::AngleUnit ang // Step 5: Let's remove the addition altogether if it has a single child Expression result = squashUnaryHierarchyInPlace(); + if (result != *this) { + return result; + } - /* Step 6: Let's put everything under a common denominator. + /* Step 6: Let's bubble up the complex operator if possible + * 3 cases: + * - All children are real, we do nothing (allChildrenAreReal == 1) + * - One of the child is non-real and not a ComplexCartesian: it means a + * complex expression could not be resolved as a ComplexCartesian, we cannot + * do anything about it now (allChildrenAreReal == -1) + * - All children are either real or ComplexCartesian (allChildrenAreReal == 0) + * We can bubble up ComplexCartesian nodes. */ + if (allChildrenAreReal(context) == 0) { + /* We turn (a+ib)+(c+id) into (a+c)+i(c+d)*/ + Addition imag = Addition::Builder(); // we store all imaginary parts in 'imag' + Addition real = *this; // we store all real parts in 'real' + i = numberOfChildren() - 1; + while (i >= 0) { + Expression c = childAtIndex(i); + if (c.type() == ExpressionNode::Type::ComplexCartesian) { + real.replaceChildAtIndexInPlace(i, c.childAtIndex(0)); + imag.addChildAtIndexInPlace(c.childAtIndex(1), imag.numberOfChildren(), imag.numberOfChildren()); + } else { + // the Addition is sorted so ComplexCartesian nodes are the last ones + break; + } + i--; + } + ComplexCartesian newComplexCartesian = ComplexCartesian::Builder(); + replaceWithInPlace(newComplexCartesian); + newComplexCartesian.replaceChildAtIndexInPlace(0, real); + newComplexCartesian.replaceChildAtIndexInPlace(1, imag); + real.shallowReduce(context, complexFormat, angleUnit, target); + imag.shallowReduce(context, complexFormat, angleUnit, target); + return newComplexCartesian.shallowReduce(); + } + + /* Step 7: Let's put everything under a common denominator. * This step is done only for ReductionTarget::User if the parent expression * is not an addition. */ Expression p = result.parent(); if (target == ExpressionNode::ReductionTarget::User && result == *this && (p.isUninitialized() || p.type() != ExpressionNode::Type::Addition)) { // squashUnaryHierarchy didn't do anything: we're not an unary hierarchy - result = factorizeOnCommonDenominator(context, angleUnit); + result = factorizeOnCommonDenominator(context, complexFormat, angleUnit); } return result; } @@ -306,16 +336,16 @@ bool Addition::TermsHaveIdenticalNonNumeralFactors(const Expression & e1, const } } -Expression Addition::factorizeOnCommonDenominator(Context & context, Preferences::AngleUnit angleUnit) { +Expression Addition::factorizeOnCommonDenominator(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) { // We want to turn (a/b+c/d+e/b) into (a*d+b*c+e*d)/(b*d) // Step 1: We want to compute the common denominator, b*d - Multiplication commonDenominator = Multiplication(); + Multiplication commonDenominator = Multiplication::Builder(); for (int i = 0; i < numberOfChildren(); i++) { - Expression currentDenominator = childAtIndex(i).denominator(context, angleUnit); + Expression currentDenominator = childAtIndex(i).denominator(context, complexFormat, angleUnit); if (!currentDenominator.isUninitialized()) { // Make commonDenominator = LeastCommonMultiple(commonDenominator, denominator); - commonDenominator.addMissingFactors(currentDenominator, context, angleUnit); + commonDenominator.addMissingFactors(currentDenominator, context, complexFormat, angleUnit); } } if (commonDenominator.numberOfChildren() == 0) { @@ -325,30 +355,30 @@ Expression Addition::factorizeOnCommonDenominator(Context & context, Preferences /* Step 2: Create the numerator. We start with this being a/b+c/d+e/b and we * want to create numerator = a/b*b*d + c/d*b*d + e/b*b*d = a*d + c*b + e*d */ - Addition numerator = Addition(); + Addition numerator = Addition::Builder(); for (int i = 0; i < numberOfChildren(); i++) { - Multiplication m = Multiplication(childAtIndex(i), commonDenominator.clone()); + Multiplication m = Multiplication::Builder(childAtIndex(i), commonDenominator.clone()); numerator.addChildAtIndexInPlace(m, numerator.numberOfChildren(), numerator.numberOfChildren()); - m.privateShallowReduce(context, angleUnit, ExpressionNode::ReductionTarget::User, true, false); + m.privateShallowReduce(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::User, true, false); } // Step 3: Add the denominator - Power inverseDenominator = Power(commonDenominator, Rational(-1)); - Multiplication result = Multiplication(numerator, inverseDenominator); + Power inverseDenominator = Power::Builder(commonDenominator, Rational::Builder(-1)); + Multiplication result = Multiplication::Builder(numerator, inverseDenominator); // Step 4: Simplify the numerator - numerator.shallowReduce(context, angleUnit, ExpressionNode::ReductionTarget::User); + numerator.shallowReduce(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::User); // Step 5: Simplify the denominator (in case it's a rational number) - inverseDenominator.deepReduce(context, angleUnit, ExpressionNode::ReductionTarget::User); + inverseDenominator.deepReduce(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::User); /* Step 6: We simplify the resulting multiplication forbidding any * distribution of multiplication on additions (to avoid an infinite loop). */ replaceWithInPlace(result); - return result.privateShallowReduce(context, angleUnit, ExpressionNode::ReductionTarget::User, false, true); + return result.privateShallowReduce(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::User, false, true); } -void Addition::factorizeChildrenAtIndexesInPlace(int index1, int index2, Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { +void Addition::factorizeChildrenAtIndexesInPlace(int index1, int index2, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { /* This function factorizes two children which only differ by a rational * factor. For example, if this is AdditionNode(2*pi, 3*pi), then 2*pi and 3*pi * could be merged, and this turned into AdditionNode(5*pi). */ @@ -365,7 +395,7 @@ void Addition::factorizeChildrenAtIndexesInPlace(int index1, int index2, Context removeChildAtIndexInPlace(index2); // Step 3: Create a multiplication - Multiplication m; + Multiplication m = Multiplication::Builder(); if (e1.type() == ExpressionNode::Type::Multiplication) { m = static_cast(e1); } else { @@ -384,16 +414,16 @@ void Addition::factorizeChildrenAtIndexesInPlace(int index1, int index2, Context } // Step 5: Reduce the multiplication (in case the new rational factor is zero) - m.shallowReduce(context, angleUnit, target); + m.shallowReduce(context, complexFormat, angleUnit, target); } -template Complex Poincare::AdditionNode::compute(std::complex, std::complex); -template Complex Poincare::AdditionNode::compute(std::complex, std::complex); +template Complex Poincare::AdditionNode::compute(std::complex, std::complex, Preferences::ComplexFormat); +template Complex Poincare::AdditionNode::compute(std::complex, std::complex, Preferences::ComplexFormat); -template MatrixComplex AdditionNode::computeOnMatrices(const MatrixComplex,const MatrixComplex); -template MatrixComplex AdditionNode::computeOnMatrices(const MatrixComplex,const MatrixComplex); +template MatrixComplex AdditionNode::computeOnMatrices(const MatrixComplex,const MatrixComplex, Preferences::ComplexFormat complexFormat); +template MatrixComplex AdditionNode::computeOnMatrices(const MatrixComplex,const MatrixComplex, Preferences::ComplexFormat complexFormat); -template MatrixComplex AdditionNode::computeOnComplexAndMatrix(std::complex const, const MatrixComplex); -template MatrixComplex AdditionNode::computeOnComplexAndMatrix(std::complex const, const MatrixComplex); +template MatrixComplex AdditionNode::computeOnComplexAndMatrix(std::complex const, const MatrixComplex, Preferences::ComplexFormat complexFormat); +template MatrixComplex AdditionNode::computeOnComplexAndMatrix(std::complex const, const MatrixComplex, Preferences::ComplexFormat complexFormat); } diff --git a/poincare/src/approximation_helper.cpp b/poincare/src/approximation_helper.cpp index d0f22e9f8..bb4935d00 100644 --- a/poincare/src/approximation_helper.cpp +++ b/poincare/src/approximation_helper.cpp @@ -16,7 +16,7 @@ template T absMod(T a, T b) { template std::complex ApproximationHelper::TruncateRealOrImaginaryPartAccordingToArgument(std::complex c) { T arg = std::arg(c); - T precision = 10*Expression::epsilon(); + T precision = 10*Expression::Epsilon(); if (absMod(arg, (T)M_PI) <= precision) { c.imag(0); } @@ -26,41 +26,41 @@ template std::complex ApproximationHelper::TruncateRealOrImagina return c; } -template Evaluation ApproximationHelper::Map(const ExpressionNode * expression, Context& context, Preferences::AngleUnit angleUnit, ComplexCompute compute) { +template Evaluation ApproximationHelper::Map(const ExpressionNode * expression, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ComplexCompute compute) { assert(expression->numberOfChildren() == 1); - Evaluation input = expression->childAtIndex(0)->approximate(T(), context, angleUnit); + Evaluation input = expression->childAtIndex(0)->approximate(T(), context, complexFormat, angleUnit); if (input.type() == EvaluationNode::Type::Complex) { - return compute(static_cast &>(input).stdComplex(), angleUnit); + return compute(static_cast &>(input).stdComplex(), complexFormat, angleUnit); } else { assert(input.type() == EvaluationNode::Type::MatrixComplex); MatrixComplex m = static_cast &>(input); - MatrixComplex result; + MatrixComplex result = MatrixComplex::Builder(); for (int i = 0; i < m.numberOfChildren(); i++) { - result.addChildAtIndexInPlace(compute(m.complexAtIndex(i), angleUnit), i, i); + result.addChildAtIndexInPlace(compute(m.complexAtIndex(i), complexFormat, angleUnit), i, i); } result.setDimensions(m.numberOfRows(), m.numberOfColumns()); return result; } } -template Evaluation ApproximationHelper::MapReduce(const ExpressionNode * expression, Context& context, Preferences::AngleUnit angleUnit, ComplexAndComplexReduction computeOnComplexes, ComplexAndMatrixReduction computeOnComplexAndMatrix, MatrixAndComplexReduction computeOnMatrixAndComplex, MatrixAndMatrixReduction computeOnMatrices) { +template Evaluation ApproximationHelper::MapReduce(const ExpressionNode * expression, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ComplexAndComplexReduction computeOnComplexes, ComplexAndMatrixReduction computeOnComplexAndMatrix, MatrixAndComplexReduction computeOnMatrixAndComplex, MatrixAndMatrixReduction computeOnMatrices) { assert(expression->numberOfChildren() > 0); - Evaluation result = expression->childAtIndex(0)->approximate(T(), context, angleUnit); + Evaluation result = expression->childAtIndex(0)->approximate(T(), context, complexFormat, angleUnit); for (int i = 1; i < expression->numberOfChildren(); i++) { Evaluation intermediateResult; - Evaluation nextOperandEvaluation = expression->childAtIndex(i)->approximate(T(), context, angleUnit); + Evaluation nextOperandEvaluation = expression->childAtIndex(i)->approximate(T(), context, complexFormat, angleUnit); if (result.type() == EvaluationNode::Type::Complex && nextOperandEvaluation.type() == EvaluationNode::Type::Complex) { - intermediateResult = computeOnComplexes(static_cast &>(result).stdComplex(), static_cast &>(nextOperandEvaluation).stdComplex()); + intermediateResult = computeOnComplexes(static_cast &>(result).stdComplex(), static_cast &>(nextOperandEvaluation).stdComplex(), complexFormat); } else if (result.type() == EvaluationNode::Type::Complex) { assert(nextOperandEvaluation.type() == EvaluationNode::Type::MatrixComplex); - intermediateResult = computeOnComplexAndMatrix(static_cast &>(result).stdComplex(), static_cast &>(nextOperandEvaluation)); + intermediateResult = computeOnComplexAndMatrix(static_cast &>(result).stdComplex(), static_cast &>(nextOperandEvaluation), complexFormat); } else if (nextOperandEvaluation.type() == EvaluationNode::Type::Complex) { assert(result.type() == EvaluationNode::Type::MatrixComplex); - intermediateResult = computeOnMatrixAndComplex(static_cast &>(result), static_cast &>(nextOperandEvaluation).stdComplex()); + intermediateResult = computeOnMatrixAndComplex(static_cast &>(result), static_cast &>(nextOperandEvaluation).stdComplex(), complexFormat); } else { assert(result.node()->type() == EvaluationNode::Type::MatrixComplex); assert(nextOperandEvaluation.node()->type() == EvaluationNode::Type::MatrixComplex); - intermediateResult = computeOnMatrices(static_cast &>(result), static_cast &>(nextOperandEvaluation)); + intermediateResult = computeOnMatrices(static_cast &>(result), static_cast &>(nextOperandEvaluation), complexFormat); } result = intermediateResult; if (result.isUndefined()) { @@ -70,22 +70,22 @@ template Evaluation ApproximationHelper::MapReduce(const Expressi return result; } -template MatrixComplex ApproximationHelper::ElementWiseOnMatrixComplexAndComplex(const MatrixComplex m, const std::complex c, ComplexAndComplexReduction computeOnComplexes) { - MatrixComplex matrix; +template MatrixComplex ApproximationHelper::ElementWiseOnMatrixComplexAndComplex(const MatrixComplex m, const std::complex c, Poincare::Preferences::ComplexFormat complexFormat, ComplexAndComplexReduction computeOnComplexes) { + MatrixComplex matrix = MatrixComplex::Builder(); for (int i = 0; i < m.numberOfChildren(); i++) { - matrix.addChildAtIndexInPlace(computeOnComplexes(m.complexAtIndex(i), c), i, i); + matrix.addChildAtIndexInPlace(computeOnComplexes(m.complexAtIndex(i), c, complexFormat), i, i); } matrix.setDimensions(m.numberOfRows(), m.numberOfColumns()); return matrix; } -template MatrixComplex ApproximationHelper::ElementWiseOnComplexMatrices(const MatrixComplex m, const MatrixComplex n, ComplexAndComplexReduction computeOnComplexes) { +template MatrixComplex ApproximationHelper::ElementWiseOnComplexMatrices(const MatrixComplex m, const MatrixComplex n, Poincare::Preferences::ComplexFormat complexFormat, ComplexAndComplexReduction computeOnComplexes) { if (m.numberOfRows() != n.numberOfRows() || m.numberOfColumns() != n.numberOfColumns()) { return MatrixComplex::Undefined(); } - MatrixComplex matrix; + MatrixComplex matrix = MatrixComplex::Builder(); for (int i = 0; i < m.numberOfChildren(); i++) { - matrix.addChildAtIndexInPlace(computeOnComplexes(m.complexAtIndex(i), n.complexAtIndex(i)), i, i); + matrix.addChildAtIndexInPlace(computeOnComplexes(m.complexAtIndex(i), n.complexAtIndex(i), complexFormat), i, i); } matrix.setDimensions(m.numberOfRows(), m.numberOfColumns()); return matrix; @@ -93,14 +93,14 @@ template MatrixComplex ApproximationHelper::ElementWiseOnComplexM template std::complex Poincare::ApproximationHelper::TruncateRealOrImaginaryPartAccordingToArgument(std::complex); template std::complex Poincare::ApproximationHelper::TruncateRealOrImaginaryPartAccordingToArgument(std::complex); -template Poincare::Evaluation Poincare::ApproximationHelper::Map(const Poincare::ExpressionNode * expression, Poincare::Context& context, Poincare::Preferences::AngleUnit angleUnit, Poincare::ApproximationHelper::ComplexCompute compute); -template Poincare::Evaluation Poincare::ApproximationHelper::Map(const Poincare::ExpressionNode * expression, Poincare::Context& context, Poincare::Preferences::AngleUnit angleUnit, Poincare::ApproximationHelper::ComplexCompute compute); -template Poincare::Evaluation Poincare::ApproximationHelper::MapReduce(const Poincare::ExpressionNode * expression, Poincare::Context& context, Poincare::Preferences::AngleUnit angleUnit, Poincare::ApproximationHelper::ComplexAndComplexReduction computeOnComplexes, Poincare::ApproximationHelper::ComplexAndMatrixReduction computeOnComplexAndMatrix, Poincare::ApproximationHelper::MatrixAndComplexReduction computeOnMatrixAndComplex, Poincare::ApproximationHelper::MatrixAndMatrixReduction computeOnMatrices); -template Poincare::Evaluation Poincare::ApproximationHelper::MapReduce(const Poincare::ExpressionNode * expression, Poincare::Context& context, Poincare::Preferences::AngleUnit angleUnit, Poincare::ApproximationHelper::ComplexAndComplexReduction computeOnComplexes, Poincare::ApproximationHelper::ComplexAndMatrixReduction computeOnComplexAndMatrix, Poincare::ApproximationHelper::MatrixAndComplexReduction computeOnMatrixAndComplex, Poincare::ApproximationHelper::MatrixAndMatrixReduction computeOnMatrices); -template Poincare::MatrixComplex Poincare::ApproximationHelper::ElementWiseOnMatrixComplexAndComplex(const Poincare::MatrixComplex, const std::complex, Poincare::Complex (*)(std::complex, std::complex)); -template Poincare::MatrixComplex Poincare::ApproximationHelper::ElementWiseOnMatrixComplexAndComplex(const Poincare::MatrixComplex, std::complex const, Poincare::Complex (*)(std::complex, std::complex)); -template Poincare::MatrixComplex Poincare::ApproximationHelper::ElementWiseOnComplexMatrices(const Poincare::MatrixComplex, const Poincare::MatrixComplex, Poincare::Complex (*)(std::complex, std::complex)); -template Poincare::MatrixComplex Poincare::ApproximationHelper::ElementWiseOnComplexMatrices(const Poincare::MatrixComplex, const Poincare::MatrixComplex, Poincare::Complex (*)(std::complex, std::complex)); +template Poincare::Evaluation Poincare::ApproximationHelper::Map(const Poincare::ExpressionNode * expression, Poincare::Context& context, Poincare::Preferences::ComplexFormat, Poincare::Preferences::AngleUnit angleUnit, Poincare::ApproximationHelper::ComplexCompute compute); +template Poincare::Evaluation Poincare::ApproximationHelper::Map(const Poincare::ExpressionNode * expression, Poincare::Context& context, Poincare::Preferences::ComplexFormat, Poincare::Preferences::AngleUnit angleUnit, Poincare::ApproximationHelper::ComplexCompute compute); +template Poincare::Evaluation Poincare::ApproximationHelper::MapReduce(const Poincare::ExpressionNode * expression, Poincare::Context& context, Poincare::Preferences::ComplexFormat, Poincare::Preferences::AngleUnit angleUnit, Poincare::ApproximationHelper::ComplexAndComplexReduction computeOnComplexes, Poincare::ApproximationHelper::ComplexAndMatrixReduction computeOnComplexAndMatrix, Poincare::ApproximationHelper::MatrixAndComplexReduction computeOnMatrixAndComplex, Poincare::ApproximationHelper::MatrixAndMatrixReduction computeOnMatrices); +template Poincare::Evaluation Poincare::ApproximationHelper::MapReduce(const Poincare::ExpressionNode * expression, Poincare::Context& context, Poincare::Preferences::ComplexFormat, Poincare::Preferences::AngleUnit angleUnit, Poincare::ApproximationHelper::ComplexAndComplexReduction computeOnComplexes, Poincare::ApproximationHelper::ComplexAndMatrixReduction computeOnComplexAndMatrix, Poincare::ApproximationHelper::MatrixAndComplexReduction computeOnMatrixAndComplex, Poincare::ApproximationHelper::MatrixAndMatrixReduction computeOnMatrices); +template Poincare::MatrixComplex Poincare::ApproximationHelper::ElementWiseOnMatrixComplexAndComplex(const Poincare::MatrixComplex, const std::complex, Poincare::Preferences::ComplexFormat, Poincare::Complex (*)(std::complex, std::complex, Poincare::Preferences::ComplexFormat)); +template Poincare::MatrixComplex Poincare::ApproximationHelper::ElementWiseOnMatrixComplexAndComplex(const Poincare::MatrixComplex, std::complex const, Poincare::Preferences::ComplexFormat, Poincare::Complex (*)(std::complex, std::complex, Poincare::Preferences::ComplexFormat)); +template Poincare::MatrixComplex Poincare::ApproximationHelper::ElementWiseOnComplexMatrices(const Poincare::MatrixComplex, const Poincare::MatrixComplex, Poincare::Preferences::ComplexFormat, Poincare::Complex (*)(std::complex, std::complex, Poincare::Preferences::ComplexFormat)); +template Poincare::MatrixComplex Poincare::ApproximationHelper::ElementWiseOnComplexMatrices(const Poincare::MatrixComplex, const Poincare::MatrixComplex, Poincare::Preferences::ComplexFormat, Poincare::Complex (*)(std::complex, std::complex, Poincare::Preferences::ComplexFormat)); } diff --git a/poincare/src/arc_cosine.cpp b/poincare/src/arc_cosine.cpp index 8bd1baae2..f0895fe00 100644 --- a/poincare/src/arc_cosine.cpp +++ b/poincare/src/arc_cosine.cpp @@ -19,12 +19,12 @@ int ArcCosineNode::serialize(char * buffer, int bufferSize, Preferences::PrintFl return SerializationHelper::Prefix(this, buffer, bufferSize, floatDisplayMode, numberOfSignificantDigits, ArcCosine::s_functionHelper.name()); } -Expression ArcCosineNode::shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) { - return ArcCosine(this).shallowReduce(context, angleUnit, target); +Expression ArcCosineNode::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) { + return ArcCosine(this).shallowReduce(context, complexFormat, angleUnit, target); } template -Complex ArcCosineNode::computeOnComplex(const std::complex c, Preferences::AngleUnit angleUnit) { +Complex ArcCosineNode::computeOnComplex(const std::complex c, Preferences::ComplexFormat, Preferences::AngleUnit angleUnit) { std::complex result; if (c.imag() == 0 && std::fabs(c.real()) <= 1.0) { /* acos: [-1;1] -> R @@ -45,12 +45,13 @@ Complex ArcCosineNode::computeOnComplex(const std::complex c, Preferences: } } result = Trigonometry::RoundToMeaningfulDigits(result, c); - return Complex(Trigonometry::ConvertRadianToAngleUnit(result, angleUnit)); + return Complex::Builder(Trigonometry::ConvertRadianToAngleUnit(result, angleUnit)); } -Expression ArcCosine::shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { + +Expression ArcCosine::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { { - Expression e = Expression::defaultShallowReduce(context, angleUnit); + Expression e = Expression::defaultShallowReduce(); if (e.isUndefined()) { return e; } @@ -60,7 +61,7 @@ Expression ArcCosine::shallowReduce(Context & context, Preferences::AngleUnit an return SimplificationHelper::Map(*this, context, angleUnit); } #endif - return Trigonometry::shallowReduceInverseFunction(*this, context, angleUnit, target); + return Trigonometry::shallowReduceInverseFunction(*this, context, complexFormat, angleUnit, target); } } diff --git a/poincare/src/arc_sine.cpp b/poincare/src/arc_sine.cpp index cd28c1fcf..eb2fc0d21 100644 --- a/poincare/src/arc_sine.cpp +++ b/poincare/src/arc_sine.cpp @@ -19,12 +19,12 @@ int ArcSineNode::serialize(char * buffer, int bufferSize, Preferences::PrintFloa return SerializationHelper::Prefix(this, buffer, bufferSize, floatDisplayMode, numberOfSignificantDigits, ArcSine::s_functionHelper.name()); } -Expression ArcSineNode::shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) { - return ArcSine(this).shallowReduce(context, angleUnit, target); +Expression ArcSineNode::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) { + return ArcSine(this).shallowReduce(context, complexFormat, angleUnit, target); } template -Complex ArcSineNode::computeOnComplex(const std::complex c, Preferences::AngleUnit angleUnit) { +Complex ArcSineNode::computeOnComplex(const std::complex c, Preferences::ComplexFormat, Preferences::AngleUnit angleUnit) { std::complex result; if (c.imag() == 0 && std::fabs(c.real()) <= 1.0) { /* asin: [-1;1] -> R @@ -45,12 +45,13 @@ Complex ArcSineNode::computeOnComplex(const std::complex c, Preferences::A } } result = Trigonometry::RoundToMeaningfulDigits(result, c); - return Complex(Trigonometry::ConvertRadianToAngleUnit(result, angleUnit)); + return Complex::Builder(Trigonometry::ConvertRadianToAngleUnit(result, angleUnit)); } -Expression ArcSine::shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { + +Expression ArcSine::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { { - Expression e = Expression::defaultShallowReduce(context, angleUnit); + Expression e = Expression::defaultShallowReduce(); if (e.isUndefined()) { return e; } @@ -60,7 +61,7 @@ Expression ArcSine::shallowReduce(Context & context, Preferences::AngleUnit angl return SimplificationHelper::Map(*this, context, angleUnit); } #endif - return Trigonometry::shallowReduceInverseFunction(*this, context, angleUnit, target); + return Trigonometry::shallowReduceInverseFunction(*this, context, complexFormat, angleUnit, target); } } diff --git a/poincare/src/arc_tangent.cpp b/poincare/src/arc_tangent.cpp index e5cc533d7..c63d9c7f6 100644 --- a/poincare/src/arc_tangent.cpp +++ b/poincare/src/arc_tangent.cpp @@ -20,7 +20,7 @@ int ArcTangentNode::serialize(char * buffer, int bufferSize, Preferences::PrintF } template -Complex ArcTangentNode::computeOnComplex(const std::complex c, Preferences::AngleUnit angleUnit) { +Complex ArcTangentNode::computeOnComplex(const std::complex c, Preferences::ComplexFormat, Preferences::AngleUnit angleUnit) { std::complex result; if (c.imag() == 0 && std::fabs(c.real()) <= 1.0) { /* atan: R -> R @@ -41,16 +41,17 @@ Complex ArcTangentNode::computeOnComplex(const std::complex c, Preferences } } result = Trigonometry::RoundToMeaningfulDigits(result, c); - return Complex(Trigonometry::ConvertRadianToAngleUnit(result, angleUnit)); + return Complex::Builder(Trigonometry::ConvertRadianToAngleUnit(result, angleUnit)); } -Expression ArcTangentNode::shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) { - return ArcTangent(this).shallowReduce(context, angleUnit, target); +Expression ArcTangentNode::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) { + return ArcTangent(this).shallowReduce(context, complexFormat, angleUnit, target); } -Expression ArcTangent::shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { + +Expression ArcTangent::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { { - Expression e = Expression::defaultShallowReduce(context, angleUnit); + Expression e = Expression::defaultShallowReduce(); if (e.isUndefined()) { return e; } @@ -60,7 +61,7 @@ Expression ArcTangent::shallowReduce(Context & context, Preferences::AngleUnit a return SimplificationHelper::Map(*this, context, angleUnit); } #endif - return Trigonometry::shallowReduceInverseFunction(*this, context, angleUnit, target); + return Trigonometry::shallowReduceInverseFunction(*this, context, complexFormat, angleUnit, target); } } diff --git a/poincare/src/arithmetic.cpp b/poincare/src/arithmetic.cpp index 7f13e49cd..f4563db79 100644 --- a/poincare/src/arithmetic.cpp +++ b/poincare/src/arithmetic.cpp @@ -13,7 +13,7 @@ Integer Arithmetic::LCM(const Integer & a, const Integer & b) { } Integer Arithmetic::GCD(const Integer & a, const Integer & b) { - if (a.isInfinity() || b.isInfinity()) { + if (a.isOverflow() || b.isOverflow()) { return Integer::Overflow(false); } @@ -41,7 +41,7 @@ const short primeFactors[Arithmetic::k_numberOfPrimeFactors] = {2, 3, 5, 7, 11, // we can go to 7907*7907 = 62 520 649 int Arithmetic::PrimeFactorization(const Integer & n, Integer outputFactors[], Integer outputCoefficients[], int outputLength) { - assert(!n.isInfinity()); + assert(!n.isOverflow()); // Compute the absolute value of n Integer m = n; diff --git a/poincare/src/binomial_coefficient.cpp b/poincare/src/binomial_coefficient.cpp index 9269fd53d..0c77913ff 100644 --- a/poincare/src/binomial_coefficient.cpp +++ b/poincare/src/binomial_coefficient.cpp @@ -14,12 +14,12 @@ constexpr Expression::FunctionHelper BinomialCoefficient::s_functionHelper; int BinomialCoefficientNode::numberOfChildren() const { return BinomialCoefficient::s_functionHelper.numberOfChildren(); } -Expression BinomialCoefficientNode::shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) { - return BinomialCoefficient(this).shallowReduce(context, angleUnit); +Expression BinomialCoefficientNode::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) { + return BinomialCoefficient(this).shallowReduce(); } Layout BinomialCoefficientNode::createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const { - return BinomialCoefficientLayout( + return BinomialCoefficientLayout::Builder( childAtIndex(0)->createLayout(floatDisplayMode, numberOfSignificantDigits), childAtIndex(1)->createLayout(floatDisplayMode, numberOfSignificantDigits)); } @@ -29,12 +29,12 @@ int BinomialCoefficientNode::serialize(char * buffer, int bufferSize, Preference } template -Complex BinomialCoefficientNode::templatedApproximate(Context& context, Preferences::AngleUnit angleUnit) const { - Evaluation nInput = childAtIndex(0)->approximate(T(), context, angleUnit); - Evaluation kInput = childAtIndex(1)->approximate(T(), context, angleUnit); +Complex BinomialCoefficientNode::templatedApproximate(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { + Evaluation nInput = childAtIndex(0)->approximate(T(), context, complexFormat, angleUnit); + Evaluation kInput = childAtIndex(1)->approximate(T(), context, complexFormat, angleUnit); T n = nInput.toScalar(); T k = kInput.toScalar(); - return Complex(compute(k, n)); + return Complex::Builder(compute(k, n)); } template @@ -53,9 +53,10 @@ T BinomialCoefficientNode::compute(T k, T n) { return std::round(result); } -Expression BinomialCoefficient::shallowReduce(Context & context, Preferences::AngleUnit angleUnit) { + +Expression BinomialCoefficient::shallowReduce() { { - Expression e = Expression::defaultShallowReduce(context, angleUnit); + Expression e = Expression::defaultShallowReduce(); if (e.isUndefined()) { return e; } @@ -64,13 +65,13 @@ Expression BinomialCoefficient::shallowReduce(Context & context, Preferences::An Expression c1 = childAtIndex(1); #if MATRIX_EXACT_REDUCING if (c0.type() == ExpressionNode::Type::Matrix || c1.type() == ExpressionNode::Type::Matrix) { - return Undefined(); + return Undefined::Builder(); } #endif if (c0.type() == ExpressionNode::Type::Rational) { Rational r0 = static_cast(c0); if (!r0.integerDenominator().isOne() || r0.isNegative()) { - Expression result = Undefined(); + Expression result = Undefined::Builder(); replaceWithInPlace(result); return result; } @@ -78,7 +79,7 @@ Expression BinomialCoefficient::shallowReduce(Context & context, Preferences::An if (c1.type() == ExpressionNode::Type::Rational) { Rational r1 = static_cast(c1); if (!r1.integerDenominator().isOne() || r1.isNegative()) { - Expression result = Undefined(); + Expression result = Undefined::Builder(); replaceWithInPlace(result); return result; } @@ -92,7 +93,7 @@ Expression BinomialCoefficient::shallowReduce(Context & context, Preferences::An Integer n = r0.signedIntegerNumerator(); Integer k = r1.signedIntegerNumerator(); if (n.isLowerThan(k)) { - Expression result = Undefined(); + Expression result = Undefined::Builder(); replaceWithInPlace(result); return result; } @@ -101,21 +102,20 @@ Expression BinomialCoefficient::shallowReduce(Context & context, Preferences::An if (Integer(k_maxNValue).isLowerThan(n)) { return *this; } - Rational result(1); + Rational result = Rational::Builder(1); Integer kBis = Integer::Subtraction(n, k); k = kBis.isLowerThan(k) ? kBis : k; int clippedK = k.extractedInt(); // Authorized because k < n < k_maxNValue for (int i = 0; i < clippedK; i++) { Integer nMinusI = Integer::Subtraction(n, Integer(i)); Integer kMinusI = Integer::Subtraction(k, Integer(i)); - Rational factor = Rational(nMinusI, kMinusI); + Rational factor = Rational::Builder(nMinusI, kMinusI); result = Rational::Multiplication(result, factor); } // As we cap the n < k_maxNValue = 300, result < binomial(300, 150) ~2^89 assert(!result.numeratorOrDenominatorIsInfinity()); - Expression rationalResult = Rational(result); - replaceWithInPlace(rationalResult); - return rationalResult; + replaceWithInPlace(result); + return result; } template double BinomialCoefficientNode::compute(double k, double n); diff --git a/poincare/src/binomial_coefficient_layout.cpp b/poincare/src/binomial_coefficient_layout.cpp index 97872cc55..410861766 100644 --- a/poincare/src/binomial_coefficient_layout.cpp +++ b/poincare/src/binomial_coefficient_layout.cpp @@ -106,4 +106,4 @@ void BinomialCoefficientLayoutNode::render(KDContext * ctx, KDPoint p, KDColor e RightParenthesisLayoutNode::RenderWithChildHeight(childHeight, ctx, p.translatedBy(KDPoint(rightParenthesisPointX, 0)), expressionColor, backgroundColor); } -} +} \ No newline at end of file diff --git a/poincare/src/ceiling.cpp b/poincare/src/ceiling.cpp index cc0ab7f1f..7dfeaef7f 100644 --- a/poincare/src/ceiling.cpp +++ b/poincare/src/ceiling.cpp @@ -16,7 +16,7 @@ constexpr Expression::FunctionHelper Ceiling::s_functionHelper; int CeilingNode::numberOfChildren() const { return Ceiling::s_functionHelper.numberOfChildren(); } Layout CeilingNode::createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const { - return CeilingLayout(childAtIndex(0)->createLayout(floatDisplayMode, numberOfSignificantDigits)); + return CeilingLayout::Builder(childAtIndex(0)->createLayout(floatDisplayMode, numberOfSignificantDigits)); } int CeilingNode::serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const { @@ -24,20 +24,21 @@ int CeilingNode::serialize(char * buffer, int bufferSize, Preferences::PrintFloa } template -Complex CeilingNode::computeOnComplex(const std::complex c, Preferences::AngleUnit angleUnit) { +Complex CeilingNode::computeOnComplex(const std::complex c, Preferences::ComplexFormat, Preferences::AngleUnit angleUnit) { if (c.imag() != 0) { return Complex::Undefined(); } - return Complex(std::ceil(c.real())); + return Complex::Builder(std::ceil(c.real())); } -Expression CeilingNode::shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) { - return Ceiling(this).shallowReduce(context, angleUnit); +Expression CeilingNode::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) { + return Ceiling(this).shallowReduce(); } -Expression Ceiling::shallowReduce(Context & context, Preferences::AngleUnit angleUnit) { + +Expression Ceiling::shallowReduce() { { - Expression e = Expression::defaultShallowReduce(context, angleUnit); + Expression e = Expression::defaultShallowReduce(); if (e.isUndefined()) { return e; } @@ -52,10 +53,10 @@ Expression Ceiling::shallowReduce(Context & context, Preferences::AngleUnit angl Constant s = static_cast(c); Expression result; if (s.isPi()) { - result = Rational(4); + result = Rational::Builder(4); } if (s.isExponential()) { - result = Rational(3); + result = Rational::Builder(3); } if (!result.isUninitialized()) { replaceWithInPlace(result); @@ -68,17 +69,17 @@ Expression Ceiling::shallowReduce(Context & context, Preferences::AngleUnit angl } Rational r = c.convert(); IntegerDivision div = Integer::Division(r.signedIntegerNumerator(), r.integerDenominator()); - assert(!div.remainder.isInfinity()); + assert(!div.remainder.isOverflow()); if (div.remainder.isZero()) { - Expression result = Rational(div.quotient); + Expression result = Rational::Builder(div.quotient); replaceWithInPlace(result); return result; } Integer result = Integer::Addition(div.quotient, Integer(1)); - if (result.isInfinity()) { + if (result.isOverflow()) { return *this; } - Expression rationalResult = Rational(result); + Expression rationalResult = Rational::Builder(result); replaceWithInPlace(rationalResult); return rationalResult; } diff --git a/poincare/src/ceiling_layout.cpp b/poincare/src/ceiling_layout.cpp deleted file mode 100644 index 3a7c5aff3..000000000 --- a/poincare/src/ceiling_layout.cpp +++ /dev/null @@ -1,5 +0,0 @@ -#include - -namespace Poincare { - -} diff --git a/poincare/src/char_layout.cpp b/poincare/src/char_layout.cpp index b6d42f81d..c938c06ff 100644 --- a/poincare/src/char_layout.cpp +++ b/poincare/src/char_layout.cpp @@ -34,9 +34,6 @@ int CharLayoutNode::serialize(char * buffer, int bufferSize, Preferences::PrintF bool CharLayoutNode::isCollapsable(int * numberOfOpenParenthesis, bool goingLeft) const { if (*numberOfOpenParenthesis <= 0) { if (m_char == '+' - || m_char == '*' - || m_char == Ion::Charset::MultiplicationSign - || m_char == Ion::Charset::MiddleDot || m_char == Ion::Charset::Sto || m_char == '=' || m_char == ',') @@ -65,8 +62,15 @@ bool CharLayoutNode::isCollapsable(int * numberOfOpenParenthesis, bool goingLeft return true; } +bool CharLayoutNode::canBeOmittedMultiplicationLeftFactor() const { + if (isMultiplicationChar()) { + return false; + } + return LayoutNode::canBeOmittedMultiplicationRightFactor(); +} + bool CharLayoutNode::canBeOmittedMultiplicationRightFactor() const { - if (m_char == '!') { + if (m_char == '!' || isMultiplicationChar()) { return false; } return LayoutNode::canBeOmittedMultiplicationRightFactor(); @@ -86,11 +90,17 @@ void CharLayoutNode::render(KDContext * ctx, KDPoint p, KDColor expressionColor, ctx->drawString(string, p, m_font, expressionColor, backgroundColor); } -CharLayout::CharLayout(char c, const KDFont * font) : - Layout(TreePool::sharedPool()->createTreeNode()) -{ - node()->setChar(c); - node()->setFont(font); +bool CharLayoutNode::isMultiplicationChar() const { + return m_char == '*' + || m_char == Ion::Charset::MultiplicationSign + || m_char == Ion::Charset::MiddleDot; +} + +CharLayout CharLayout::Builder(char c, const KDFont * font) { + void * bufferNode = TreePool::sharedPool()->alloc(sizeof(CharLayoutNode)); + CharLayoutNode * node = new (bufferNode) CharLayoutNode(c, font); + TreeHandle h = TreeHandle::BuildWithGhostChildren(node); + return static_cast(h); } } diff --git a/poincare/src/complex.cpp b/poincare/src/complex.cpp index 2319f2eec..1dc2cca01 100644 --- a/poincare/src/complex.cpp +++ b/poincare/src/complex.cpp @@ -15,15 +15,20 @@ extern "C" { #include #include #include +#include #include #include namespace Poincare { template -void ComplexNode::setComplex(std::complex c) { - this->real(c.real()); - this->imag(c.imag()); +ComplexNode::ComplexNode(std::complex c) : + EvaluationNode(), + std::complex(c.real(), c.imag()) +{ + if (!std::isnan(c.imag()) && c.imag() != 0.0) { + Expression::SetEncounteredComplex(true); + } if (this->real() == -0) { this->real(0); } @@ -42,81 +47,39 @@ T ComplexNode::toScalar() const { template Expression ComplexNode::complexToExpression(Preferences::ComplexFormat complexFormat) const { - if (std::isnan(this->real()) || std::isnan(this->imag())) { - return Undefined(); + if (complexFormat == Preferences::ComplexFormat::Real && Expression::EncounteredComplex()) { + return Unreal::Builder(); } - if (complexFormat == Preferences::ComplexFormat::Cartesian) { - Expression real; - Expression imag; - if (this->real() != 0 || this->imag() == 0) { - real = Number::DecimalNumber(this->real()); - } - if (this->imag() != 0) { - if (this->imag() == 1.0 || this->imag() == -1) { - imag = Constant(Ion::Charset::IComplex); - } else if (this->imag() > 0) { - imag = Multiplication(Number::DecimalNumber(this->imag()), Constant(Ion::Charset::IComplex)); - } else { - imag = Multiplication(Number::DecimalNumber(-this->imag()), Constant(Ion::Charset::IComplex)); - } - } - if (imag.isUninitialized()) { - return real; - } else if (real.isUninitialized()) { - if (this->imag() > 0) { - return imag; - } else { - return Opposite(imag); - } - return imag; - } else if (this->imag() > 0) { - return Addition(real, imag); - } else { - return Subtraction(real, imag); - } - } - assert(complexFormat == Preferences::ComplexFormat::Polar); - Expression norm; - Expression exp; - T r = std::abs(*this); - T th = std::arg(*this); - if (r != 1 || th == 0) { - norm = Number::DecimalNumber(r); - } - if (r != 0 && th != 0) { - Expression arg; - if (th == 1.0) { - arg = Constant(Ion::Charset::IComplex); - } else if (th == -1.0) { - arg = Opposite(Constant(Ion::Charset::IComplex)); - } else if (th > 0) { - arg = Multiplication(Number::DecimalNumber(th), Constant(Ion::Charset::IComplex)); - } else { - arg = Opposite(Multiplication(Number::DecimalNumber(-th), Constant(Ion::Charset::IComplex))); - } - exp = Power(Constant(Ion::Charset::Exponential), arg); - } - if (exp.isUninitialized()) { - return norm; - } else if (norm.isUninitialized()) { - return exp; + T ra, tb; + if (complexFormat == Preferences::ComplexFormat::Polar) { + ra = std::abs(*this); + tb = std::arg(*this); } else { - return Multiplication(norm, exp); + ra = this->real(); + tb = this->imag(); } + return Expression::CreateComplexExpression( + Number::DecimalNumber(std::fabs(ra)), + Number::DecimalNumber(std::fabs(tb)), + complexFormat, + (std::isnan(this->real()) || std::isnan(this->imag())), + ra == 0.0, std::fabs(ra) == 1.0, tb == 0.0, std::fabs(tb) == 1.0, ra < 0.0, tb < 0.0 + ); } template -Complex::Complex(std::complex c) : - Evaluation(TreePool::sharedPool()->createTreeNode>()) -{ - node()->setComplex(c); +Complex Complex::Builder(std::complex c) { + void * bufferNode = TreePool::sharedPool()->alloc(sizeof(ComplexNode)); + ComplexNode * node = new (bufferNode) ComplexNode(c); + TreeHandle h = TreeHandle::BuildWithGhostChildren(node); + return static_cast &>(h); } template class ComplexNode; template class ComplexNode; -template Complex::Complex(float a, float b); -template Complex::Complex(double a, double b); -template Complex::Complex(std::complex c); -template Complex::Complex(std::complex c); +template Complex Complex::Builder(float a, float b); +template Complex Complex::Builder(double a, double b); +template Complex Complex::Builder(std::complex c); +template Complex Complex::Builder(std::complex c); } diff --git a/poincare/src/complex_argument.cpp b/poincare/src/complex_argument.cpp index c7d6d0396..eb6983ec3 100644 --- a/poincare/src/complex_argument.cpp +++ b/poincare/src/complex_argument.cpp @@ -1,7 +1,10 @@ #include +#include #include #include #include +#include +#include extern "C" { #include } @@ -21,28 +24,50 @@ int ComplexArgumentNode::serialize(char * buffer, int bufferSize, Preferences::P return SerializationHelper::Prefix(this, buffer, bufferSize, floatDisplayMode, numberOfSignificantDigits, ComplexArgument::s_functionHelper.name()); } -Expression ComplexArgumentNode::shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) { - return ComplexArgument(this).shallowReduce(context, angleUnit); +Expression ComplexArgumentNode::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) { + return ComplexArgument(this).shallowReduce(context, complexFormat, angleUnit, target); } template -Complex ComplexArgumentNode::computeOnComplex(const std::complex c, Preferences::AngleUnit angleUnit) { - return Complex(std::arg(c)); +Complex ComplexArgumentNode::computeOnComplex(const std::complex c, Preferences::ComplexFormat, Preferences::AngleUnit angleUnit) { + return Complex::Builder(std::arg(c)); } -Expression ComplexArgument::shallowReduce(Context & context, Preferences::AngleUnit angleUnit) { + +Expression ComplexArgument::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { { - Expression e = Expression::defaultShallowReduce(context, angleUnit); + Expression e = Expression::defaultShallowReduce(); if (e.isUndefined()) { return e; } } -#if MATRIX_EXACT_REDUCING Expression c = childAtIndex(0); +#if MATRIX_EXACT_REDUCING if (c.type() == ExpressionNode::Type::Matrix) { return SimplificationHelper::Map(*this, context, angleUnit); } #endif + bool real = c.isReal(context); + if (real) { + float app = c.node()->approximate(float(), context, complexFormat, angleUnit).toScalar(); + if (!std::isnan(app) && app >= Expression::Epsilon()) { + // arg(x) = 0 if x > 0 + Expression result = Rational::Builder(0); + replaceWithInPlace(result); + return result; + } else if (!std::isnan(app) && app <= -Expression::Epsilon()) { + // arg(x) = Pi if x < 0 + Expression result = Constant::Builder(Ion::Charset::SmallPi); + replaceWithInPlace(result); + return result; + } + } + if (real || c.type() == ExpressionNode::Type::ComplexCartesian) { + ComplexCartesian complexChild = real ? ComplexCartesian::Builder(c, Rational::Builder(0)) : static_cast(c); + Expression childArg = complexChild.argument(context, complexFormat, angleUnit, target); + replaceWithInPlace(childArg); + return childArg.shallowReduce(context, complexFormat, angleUnit, target); + } return *this; } diff --git a/poincare/src/complex_cartesian.cpp b/poincare/src/complex_cartesian.cpp new file mode 100644 index 000000000..ce0416d42 --- /dev/null +++ b/poincare/src/complex_cartesian.cpp @@ -0,0 +1,378 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Poincare { + +Expression ComplexCartesianNode::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) { + return ComplexCartesian(this).shallowReduce(); +} + +Expression ComplexCartesianNode::shallowBeautify(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) { + return ComplexCartesian(this).shallowBeautify(context, complexFormat, angleUnit, target); +} + +template +Complex ComplexCartesianNode::templatedApproximate(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { + Evaluation realEvaluation = childAtIndex(0)->approximate(T(), context, complexFormat, angleUnit); + Evaluation imagEvalution = childAtIndex(1)->approximate(T(), context, complexFormat, angleUnit); + assert(realEvaluation.type() == EvaluationNode::Type::Complex); + assert(imagEvalution.type() == EvaluationNode::Type::Complex); + std::complex a = static_cast &>(realEvaluation).stdComplex(); + std::complex b = static_cast &>(imagEvalution).stdComplex(); + if ((a.imag() != 0.0 && !std::isnan(a.imag())) || (b.imag() != 0.0 && !std::isnan(b.imag()))) { + /* a and b are supposed to be real (if they are not undefined). However, + * due to double precision limit, the approximation of the real part or the + * imaginary part can lead to complex values. + * For instance, let the real part be + * sqrt(2*sqrt(5E23+1)-1E12*sqrt(2)) ~ 1.1892E-6. Due to std::sqrt(2.0) + * unprecision, 2*sqrt(5E23+1)-1E12*sqrt(2) < 0 which leads to + * sqrt(2*sqrt(5E23+1)-1E12*sqrt(2)) being a complex number. + * In this case, we return an undefined complex because the approximation + * is very likely to be false. */ + return Complex::Undefined(); + } + assert(a.imag() == 0.0 || std::isnan(a.imag())); + assert(b.imag() == 0.0 || std::isnan(b.imag())); + return Complex::Builder(a.real(), b.real()); +} + +Expression ComplexCartesian::shallowReduce() { + if (imag().isRationalZero()) { + Expression r = real(); + replaceWithInPlace(r); + return r; + } + return *this; +} + +Expression ComplexCartesian::shallowBeautify(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { + Expression a = real(); + Expression b = imag(); + Expression oppositeA = a.makePositiveAnyNegativeNumeralFactor(context, complexFormat, angleUnit, target); + Expression oppositeB = b.makePositiveAnyNegativeNumeralFactor(context, complexFormat, angleUnit, target); + a = oppositeA.isUninitialized() ? a : oppositeA; + b = oppositeB.isUninitialized() ? b : oppositeB; + Expression e = Expression::CreateComplexExpression(a, b, Preferences::ComplexFormat::Cartesian, + a.isUndefined() || b.isUndefined(), + Expression::IsZero(a), Expression::IsOne(a), Expression::IsZero(b), Expression::IsOne(b), + !oppositeA.isUninitialized(), + !oppositeB.isUninitialized() + ); + replaceWithInPlace(e); + return e; +} + +void ComplexCartesian::factorAndArgumentOfFunction(Expression e, ExpressionNode::Type searchedType, Expression * factor, Expression * argument, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { + if (e.type() == searchedType) { + *factor = Rational::Builder(1); + *argument = e.childAtIndex(0); + return; + } + if (e.type() == ExpressionNode::Type::Multiplication) { + for (int i = 0; i < e.numberOfChildren(); i++) { + if (e.childAtIndex(i).type() == searchedType) { + *argument = e.childAtIndex(i).childAtIndex(0); + *factor = e.clone(); + static_cast(factor)->removeChildAtIndexInPlace(i); + *factor = factor->shallowReduce(context, complexFormat, angleUnit, target); + Expression positiveFactor = factor->makePositiveAnyNegativeNumeralFactor(context, complexFormat, angleUnit, target); + *factor = positiveFactor.isUninitialized() ? *factor : positiveFactor; + return; + } + } + } +} + +Expression ComplexCartesian::squareNorm(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { + Expression a = real(); + Expression b = imag(); + Expression aFactor, bFactor, aArgument, bArgument; + factorAndArgumentOfFunction(a, ExpressionNode::Type::Cosine, &aFactor, &aArgument, context, complexFormat, angleUnit, target); + factorAndArgumentOfFunction(b, ExpressionNode::Type::Sine, &bFactor, &bArgument, context, complexFormat, angleUnit, target); + if (!aFactor.isUninitialized() && !aArgument.isUninitialized() && !bFactor.isUninitialized() && !bArgument.isUninitialized() && aFactor.isIdenticalTo(bFactor) && aArgument.isIdenticalTo(bArgument)) { + Power result = Power::Builder(aFactor, Rational::Builder(2)); + aFactor.shallowReduce(context, complexFormat, angleUnit, target); + return result; + } + Expression a2 = Power::Builder(a, Rational::Builder(2)); + Expression b2 = Power::Builder(b, Rational::Builder(2)); + Addition add = Addition::Builder(a2, b2); + a2.shallowReduce(context, complexFormat, angleUnit, target); + b2.shallowReduce(context, complexFormat, angleUnit, target); + return add; +} + +Expression ComplexCartesian::norm(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { + if (imag().isRationalZero()) { + Expression a = real(); + ExpressionNode::Sign s = a.sign(&context); + if (s == ExpressionNode::Sign::Positive) { + // Case 1: the expression is positive real + return a;; + } else if (s == ExpressionNode::Sign::Negative) { + // Case 2: the argument is negative real + return a.setSign(ExpressionNode::Sign::Positive, &context, complexFormat, angleUnit, target); + } + } + Expression n2 = squareNorm(context, complexFormat, angleUnit, target); + Expression n = SquareRoot::Builder(n2); + n2.shallowReduce(context, complexFormat, angleUnit, target); + return n; +} + +Expression ComplexCartesian::argument(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { + Expression a = real(); + Expression b = imag(); + if (!b.isRationalZero()) { + // if b != 0, argument = sign(b) * Pi/2 - arctan(a/b) + // First, compute arctan(a/b) or (Pi/180)*arctan(a/b) + Expression divab = Division::Builder(a, b.clone()); + Expression arcTangent = ArcTangent::Builder(divab); + divab.shallowReduce(context, complexFormat, angleUnit, target); + if (angleUnit == Preferences::AngleUnit::Degree) { + Expression temp = arcTangent.degreeToRadian(); + arcTangent.shallowReduce(context, complexFormat, angleUnit, target); + arcTangent = temp; + } + // Then, compute sign(b) * Pi/2 - arctan(a/b) + Expression signb = SignFunction::Builder(b); + Expression signbPi2 = Multiplication::Builder(Rational::Builder(1,2), signb, Constant::Builder(Ion::Charset::SmallPi)); + signb.shallowReduce(context, complexFormat, angleUnit, target); + Expression sub = Subtraction::Builder(signbPi2, arcTangent); + signbPi2.shallowReduce(context, complexFormat, angleUnit, target); + arcTangent.shallowReduce(context, complexFormat, angleUnit, target); + return sub; + } else { + // if b == 0, argument = (1-sign(a))*Pi/2 + Expression signa = SignFunction::Builder(a).shallowReduce(context, complexFormat, angleUnit, target); + Subtraction sub = Subtraction::Builder(Rational::Builder(1), signa); + signa.shallowReduce(context, complexFormat, angleUnit, target); + Multiplication mul = Multiplication::Builder(Rational::Builder(1,2), Constant::Builder(Ion::Charset::SmallPi), sub); + sub.shallowReduce(context, complexFormat, angleUnit, target); + return mul; + } +} + +ComplexCartesian ComplexCartesian::inverse(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { + Expression a = real(); + Expression b = imag(); + // 1/(a+ib) = a/(a^2+b^2)+i*(-b/(a^2+b^2)) + Expression denominatorReal = clone().convert().squareNorm(context, complexFormat, angleUnit, target); + Expression denominatorImag = denominatorReal.clone(); + Expression denominatorRealInv = Power::Builder(denominatorReal, Rational::Builder(-1)); + denominatorReal.shallowReduce(context, complexFormat, angleUnit, target); + Expression denominatorImagInv = Power::Builder(denominatorImag, Rational::Builder(-1)); + denominatorImag.shallowReduce(context, complexFormat, angleUnit, target); + Multiplication A = Multiplication::Builder(a, denominatorRealInv); + denominatorRealInv.shallowReduce(context, complexFormat, angleUnit, target); + Expression numeratorImag = Multiplication::Builder(Rational::Builder(-1), b); + Multiplication B = Multiplication::Builder(numeratorImag, denominatorImagInv); + numeratorImag.shallowReduce(context, complexFormat, angleUnit, target); + denominatorImagInv.shallowReduce(context, complexFormat, angleUnit, target); + ComplexCartesian result = ComplexCartesian::Builder(A,B); + A.shallowReduce(context, complexFormat, angleUnit, target); + B.shallowReduce(context, complexFormat, angleUnit, target); + return result.interruptComputationIfManyNodes(); +} + +Multiplication ComplexCartesian::squareRootHelper(Expression e, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { + //(1/2)*sqrt(2*e) + Multiplication doubleE = Multiplication::Builder(Rational::Builder(2), e); + e.shallowReduce(context, complexFormat, angleUnit, target); + Expression sqrt = SquareRoot::Builder(doubleE); + doubleE.shallowReduce(context, complexFormat, angleUnit, target); + Multiplication result = Multiplication::Builder(Rational::Builder(1,2), sqrt); + sqrt.shallowReduce(context, complexFormat, angleUnit, target); + return result; +} + +ComplexCartesian ComplexCartesian::squareRoot(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { + Expression a = real(); + Expression b = imag(); + // A: (1/2)*sqrt(2*(sqrt(a^2+b^2)+a)) + // B: (1/2)*sqrt(2*(sqrt(a^2+b^2)-a))*sign(b) + Expression normA = clone().convert().norm(context, complexFormat, angleUnit, target); + Expression normB = normA.clone(); + // A = (1/2)*sqrt(2*(sqrt(a^2+b^2)+a)) + Addition normAdda = Addition::Builder(normA, a.clone()); + normA.shallowReduce(context, complexFormat, angleUnit, target); + Multiplication A = squareRootHelper(normAdda, context, complexFormat, angleUnit, target); + // B = B: (1/2)*sqrt(2*(sqrt(a^2+b^2)-a)) + Subtraction normSuba = Subtraction::Builder(normB, a); + normB.shallowReduce(context, complexFormat, angleUnit, target); + Multiplication B = squareRootHelper(normSuba, context, complexFormat, angleUnit, target); + // B = B: (1/2)*sqrt(2*(sqrt(a^2+b^2)-a))*sign(b) + Expression signb = SignFunction::Builder(b); + B.addChildAtIndexInPlace(signb, B.numberOfChildren(), B.numberOfChildren()); + signb.shallowReduce(context, complexFormat, angleUnit, target); + ComplexCartesian result = ComplexCartesian::Builder(A, B); + A.shallowReduce(context, complexFormat, angleUnit, target); + B.shallowReduce(context, complexFormat, angleUnit, target); + return result.interruptComputationIfManyNodes(); +} + + +ComplexCartesian ComplexCartesian::powerInteger(int n, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { + Expression a = real(); + Expression b = imag(); + assert(n > 0); + assert(!b.isRationalZero()); + + // Special case: a == 0 (otherwise, we are going to introduce undefined expressions - a^0 = NAN) + // (b*i)^n = b^n*i^n with i^n == i, -i, 1 or -1 + if (a.isRationalZero()) { + ComplexCartesian result; + Expression bpow = Power::Builder(b, Rational::Builder(n)); + if (n/2%2 == 1) { + Expression temp = Multiplication::Builder(Rational::Builder(-1), bpow); + bpow.shallowReduce(context, complexFormat, angleUnit, target); + bpow = temp; + } + if (n%2 == 0) { + result = ComplexCartesian::Builder(bpow, Rational::Builder(0)); + } else { + result = ComplexCartesian::Builder(Rational::Builder(0), bpow); + } + bpow.shallowReduce(context, complexFormat, angleUnit, target); + return result; + } + // (a+ib) = a^n+i*b*a^(n-1)+(-1)*b^2*a^(n-2)+(-i)*b^3*a^(n-3)+b^3*a^(n-4)+... + // Real part: A = a^n+(-1)*b^2*a^(n-2)+... + // Imaginary part: B = b*a^(n-1) + Addition A = Addition::Builder(); + Addition B = Addition::Builder(); + ComplexCartesian result = ComplexCartesian::Builder(A, B); + for (int i = 0; i <= n; i++) { + BinomialCoefficient binom = BinomialCoefficient::Builder(Rational::Builder(n), Rational::Builder(i)); + Expression aclone = i == n ? a : a.clone(); + Expression bclone = i == n ? b : b.clone(); + Power apow = Power::Builder(aclone, Rational::Builder(n-i)); + Power bpow = Power::Builder(bclone, Rational::Builder(i)); + Multiplication m = Multiplication::Builder(binom, apow, bpow); + binom.shallowReduce(); + apow.shallowReduce(context, complexFormat, angleUnit, target); + bpow.shallowReduce(context, complexFormat, angleUnit, target); + if (i/2%2 == 1) { + m.addChildAtIndexInPlace(Rational::Builder(-1), 0, m.numberOfChildren()); + } + if (i%2 == 0) { + A.addChildAtIndexInPlace(m, A.numberOfChildren(), A.numberOfChildren()); + } else { + B.addChildAtIndexInPlace(m, B.numberOfChildren(), B.numberOfChildren()); + } + m.shallowReduce(context, complexFormat, angleUnit, target); + result = result.interruptComputationIfManyNodes(); + if (result.real().isUndefined()) { + return result; + } + } + A.shallowReduce(context, complexFormat, angleUnit, target); + B.shallowReduce(context, complexFormat, angleUnit, target); + return result; +} + +ComplexCartesian ComplexCartesian::multiply(ComplexCartesian & other, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { + Expression a = real(); + Expression b = imag(); + Expression c = other.real(); + Expression d = other.imag(); + // (a+ib) * (c+id) = (ac-bd)+i*(ad+bc) + // Compute ac-bd + Expression ac = Multiplication::Builder(a.clone(), c.clone()); + Expression bd = Multiplication::Builder(b.clone(), d.clone()); + Subtraction A = Subtraction::Builder(ac, bd); + ac.shallowReduce(context, complexFormat, angleUnit, target); + bd.shallowReduce(context, complexFormat, angleUnit, target); + // Compute ad+bc + Expression ad = Multiplication::Builder(a, d); + Expression bc = Multiplication::Builder(b, c); + Addition B = Addition::Builder(ad, bc); + ad.shallowReduce(context, complexFormat, angleUnit, target); + bc.shallowReduce(context, complexFormat, angleUnit, target); + ComplexCartesian result = ComplexCartesian::Builder(A, B); + A.shallowReduce(context, complexFormat, angleUnit, target); + B.shallowReduce(context, complexFormat, angleUnit, target); + return result.interruptComputationIfManyNodes(); +} + +Expression ComplexCartesian::powerHelper(Expression norm, Expression trigo, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { + Multiplication m = Multiplication::Builder(norm, trigo); + norm.shallowReduce(context, complexFormat, angleUnit, target); + trigo.shallowReduce(context, complexFormat, angleUnit, target); + return m; +} + +ComplexCartesian ComplexCartesian::power(ComplexCartesian & other, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { + Expression r = clone().convert().norm(context, complexFormat, angleUnit, target); + Expression rclone = r.clone(); + Expression th = argument(context, complexFormat, angleUnit, target); + Expression thclone = th.clone(); + Expression c = other.real(); + Expression d = other.imag(); + // R = r^c*e^(-th*d) + Expression rpowc = Power::Builder(rclone, c.clone()); + rclone.shallowReduce(context, complexFormat, angleUnit, target); + Expression thmuld = Multiplication::Builder(Rational::Builder(-1), thclone, d.clone()); + thclone.shallowReduce(context, complexFormat, angleUnit, target); + Expression exp = Power::Builder(Constant::Builder(Ion::Charset::Exponential), thmuld); + thmuld.shallowReduce(context, complexFormat, angleUnit, target); + Multiplication norm = Multiplication::Builder(rpowc, exp); + rpowc.shallowReduce(context, complexFormat, angleUnit, target); + exp.shallowReduce(context, complexFormat, angleUnit, target); + + // TH = d*ln(r)+c*th + Expression lnr = NaperianLogarithm::Builder(r); + r.shallowReduce(context, complexFormat, angleUnit, target); + Multiplication dlnr = Multiplication::Builder(d, lnr); + lnr.shallowReduce(context, complexFormat, angleUnit, target); + Multiplication thc = Multiplication::Builder(th, c); + th.shallowReduce(context, complexFormat, angleUnit, target); + Expression argument = Addition::Builder(thc, dlnr); + thc.shallowReduce(context, complexFormat, angleUnit, target); + dlnr.shallowReduce(context, complexFormat, angleUnit, target); + + if (angleUnit == Preferences::AngleUnit::Degree) { + Expression temp = argument.radianToDegree(); + argument.shallowReduce(context, complexFormat, angleUnit, target); + argument = temp; + } + // Result = (norm*cos(argument), norm*sin(argument)) + Expression normClone = norm.clone(); + Expression argClone = argument.clone(); + Expression cos = Cosine::Builder(argClone); + argClone.shallowReduce(context, complexFormat, angleUnit, target); + Expression normcosarg = powerHelper(normClone, cos, context, complexFormat, angleUnit, target); + Expression sin = Sine::Builder(argument); + argument.shallowReduce(context, complexFormat, angleUnit, target); + Expression normsinarg = powerHelper(norm, sin, context, complexFormat, angleUnit, target); + ComplexCartesian result = ComplexCartesian::Builder(normcosarg, normsinarg); + normcosarg.shallowReduce(context, complexFormat, angleUnit, target); + normsinarg.shallowReduce(context, complexFormat, angleUnit, target); + return result.interruptComputationIfManyNodes(); +} + +ComplexCartesian ComplexCartesian::interruptComputationIfManyNodes() { + if (numberOfDescendants(true) > k_maxNumberOfNodesBeforeInterrupting) { + Expression::SetInterruption(true); + return ComplexCartesian::Builder(Undefined::Builder(), Undefined::Builder()); + } + return *this; +} + +} diff --git a/poincare/src/condensed_sum_layout.cpp b/poincare/src/condensed_sum_layout.cpp index 68a74e188..c6c60bb14 100644 --- a/poincare/src/condensed_sum_layout.cpp +++ b/poincare/src/condensed_sum_layout.cpp @@ -37,12 +37,4 @@ KDPoint CondensedSumLayoutNode::positionOfChild(LayoutNode * child) { return KDPoint(x,y); } -CondensedSumLayout::CondensedSumLayout(Layout base, Layout subscript, Layout superscript) : - Layout(TreePool::sharedPool()->createTreeNode()) -{ - replaceChildAtIndexInPlace(0, base); - replaceChildAtIndexInPlace(1, subscript); - replaceChildAtIndexInPlace(2, superscript); -} - } diff --git a/poincare/src/confidence_interval.cpp b/poincare/src/confidence_interval.cpp index 1d99e4644..1f7716e57 100644 --- a/poincare/src/confidence_interval.cpp +++ b/poincare/src/confidence_interval.cpp @@ -25,14 +25,14 @@ int ConfidenceIntervalNode::serialize(char * buffer, int bufferSize, Preferences return SerializationHelper::Prefix(this, buffer, bufferSize, floatDisplayMode, numberOfSignificantDigits, ConfidenceInterval::s_functionHelper.name()); } -Expression ConfidenceIntervalNode::shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) { - return ConfidenceInterval(this).shallowReduce(context, angleUnit, target); +Expression ConfidenceIntervalNode::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) { + return ConfidenceInterval(this).shallowReduce(context, complexFormat, angleUnit, target); } template -Evaluation ConfidenceIntervalNode::templatedApproximate(Context& context, Preferences::AngleUnit angleUnit) const { - Evaluation fInput = childAtIndex(0)->approximate(T(), context, angleUnit); - Evaluation nInput = childAtIndex(1)->approximate(T(), context, angleUnit); +Evaluation ConfidenceIntervalNode::templatedApproximate(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { + Evaluation fInput = childAtIndex(0)->approximate(T(), context, complexFormat, angleUnit); + Evaluation nInput = childAtIndex(1)->approximate(T(), context, complexFormat, angleUnit); T f = static_cast &>(fInput).toScalar(); T n = static_cast &>(nInput).toScalar(); if (std::isnan(f) || std::isnan(n) || n != (int)n || n < 0 || f < 0 || f > 1) { @@ -41,7 +41,7 @@ Evaluation ConfidenceIntervalNode::templatedApproximate(Context& context, Pre std::complex operands[2]; operands[0] = std::complex(f - 1/std::sqrt(n)); operands[1] = std::complex(f + 1/std::sqrt(n)); - return MatrixComplex(operands, 1, 2); + return MatrixComplex::Builder(operands, 1, 2); } Layout SimplePredictionIntervalNode::createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const { @@ -52,9 +52,10 @@ int SimplePredictionIntervalNode::serialize(char * buffer, int bufferSize, Prefe return SerializationHelper::Prefix(this, buffer, bufferSize, floatDisplayMode, numberOfSignificantDigits, SimplePredictionInterval::s_functionHelper.name()); } -Expression ConfidenceInterval::shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { + +Expression ConfidenceInterval::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { { - Expression e = Expression::defaultShallowReduce(context, angleUnit); + Expression e = Expression::defaultShallowReduce(); if (e.isUndefined()) { return e; } @@ -63,13 +64,13 @@ Expression ConfidenceInterval::shallowReduce(Context & context, Preferences::Ang Expression c1 = childAtIndex(1); #if MATRIX_EXACT_REDUCING if (c0.type() == ExpressionNode::Type::Matrix || c1.type() == ExpressionNode::Type::Matrix) { - return Undefined(); + return Undefined::Builder(); } #endif if (c0.type() == ExpressionNode::Type::Rational) { Rational r0 = static_cast(c0); if (r0.signedIntegerNumerator().isNegative() || Integer::NaturalOrder(r0.signedIntegerNumerator(), r0.integerDenominator()) > 0) { - Expression result = Undefined(); + Expression result = Undefined::Builder(); replaceWithInPlace(result); return result; } @@ -77,7 +78,7 @@ Expression ConfidenceInterval::shallowReduce(Context & context, Preferences::Ang if (c1.type() == ExpressionNode::Type::Rational) { Rational r1 = static_cast(c1); if (!r1.integerDenominator().isOne() || r1.signedIntegerNumerator().isNegative()) { - Expression result = Undefined(); + Expression result = Undefined::Builder(); replaceWithInPlace(result); return result; } @@ -88,14 +89,15 @@ Expression ConfidenceInterval::shallowReduce(Context & context, Preferences::Ang Rational r0 = static_cast(c0); Rational r1 = static_cast(c1); // Compute [r0-1/sqr(r1), r0+1/sqr(r1)] - Expression sqr = Power(r1, Rational(-1, 2)); - Matrix matrix; - matrix.addChildAtIndexInPlace(Addition(r0.clone(), Multiplication(Rational(-1), sqr.clone())), 0, 0); - matrix.addChildAtIndexInPlace(Addition(r0, sqr), 1, 1); + Expression sqr = Power::Builder(r1, Rational::Builder(-1, 2)); + Matrix matrix = Matrix::Builder(); + matrix.addChildAtIndexInPlace(Addition::Builder(r0.clone(), Multiplication::Builder(Rational::Builder(-1), sqr.clone())), 0, 0); + matrix.addChildAtIndexInPlace(Addition::Builder(r0, sqr), 1, 1); matrix.setDimensions(1, 2); replaceWithInPlace(matrix); - matrix.deepReduceChildren(context, angleUnit, target); + matrix.deepReduceChildren(context, complexFormat, angleUnit, target); return matrix; } + } diff --git a/poincare/src/conjugate.cpp b/poincare/src/conjugate.cpp index 49076891f..ba6bfe143 100644 --- a/poincare/src/conjugate.cpp +++ b/poincare/src/conjugate.cpp @@ -1,7 +1,11 @@ #include #include +#include +#include +#include #include #include +#include #include #include @@ -12,25 +16,25 @@ constexpr Expression::FunctionHelper Conjugate::s_functionHelper; int ConjugateNode::numberOfChildren() const { return Conjugate::s_functionHelper.numberOfChildren(); } Layout ConjugateNode::createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const { - return ConjugateLayout(childAtIndex(0)->createLayout(floatDisplayMode, numberOfSignificantDigits)); + return ConjugateLayout::Builder(childAtIndex(0)->createLayout(floatDisplayMode, numberOfSignificantDigits)); } int ConjugateNode::serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const { return SerializationHelper::Prefix(this, buffer, bufferSize, floatDisplayMode, numberOfSignificantDigits, Conjugate::s_functionHelper.name()); } -Expression ConjugateNode::shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) { - return Conjugate(this).shallowReduce(context, angleUnit); +Expression ConjugateNode::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) { + return Conjugate(this).shallowReduce(context, complexFormat, angleUnit, target); } template -Complex ConjugateNode::computeOnComplex(const std::complex c, Preferences::AngleUnit angleUnit) { - return Complex(std::conj(c)); +Complex ConjugateNode::computeOnComplex(const std::complex c, Preferences::ComplexFormat, Preferences::AngleUnit angleUnit) { + return Complex::Builder(std::conj(c)); } -Expression Conjugate::shallowReduce(Context & context, Preferences::AngleUnit angleUnit) { +Expression Conjugate::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { { - Expression e = Expression::defaultShallowReduce(context, angleUnit); + Expression e = Expression::defaultShallowReduce(); if (e.isUndefined()) { return e; } @@ -41,6 +45,18 @@ Expression Conjugate::shallowReduce(Context & context, Preferences::AngleUnit an return SimplificationHelper::Map(*this, context, angleUnit); } #endif + if (c.isReal(context)) { + replaceWithInPlace(c); + return c; + } + if (c.type() == ExpressionNode::Type::ComplexCartesian) { + ComplexCartesian complexChild = static_cast(c); + Multiplication m = Multiplication::Builder(Rational::Builder(-1), complexChild.imag()); + complexChild.replaceChildAtIndexInPlace(1, m); + m.shallowReduce(context, complexFormat, angleUnit, target); + replaceWithInPlace(complexChild); + return complexChild; + } if (c.type() == ExpressionNode::Type::Rational) { replaceWithInPlace(c); return c; @@ -48,4 +64,5 @@ Expression Conjugate::shallowReduce(Context & context, Preferences::AngleUnit an return *this; } + } diff --git a/poincare/src/constant.cpp b/poincare/src/constant.cpp index 7fdc38003..6c4039c03 100644 --- a/poincare/src/constant.cpp +++ b/poincare/src/constant.cpp @@ -2,19 +2,52 @@ #include #include #include +#include +#include +#include #include #include #include namespace Poincare { -ExpressionNode::Sign ConstantNode::sign() const { +ConstantNode::ConstantNode(const char * newName, int length) : SymbolAbstractNode() { + strlcpy(const_cast(name()), newName, length+1); +} + +ExpressionNode::Sign ConstantNode::sign(Context * context) const { if (isPi() || isExponential()) { return Sign::Positive; } return Sign::Unknown; } +bool ConstantNode::isReal(Context & context) const { + return !isIComplex(); +} + +int rankOfConstant(char c) { + switch (c) { + case Ion::Charset::IComplex: + return 0; + case Ion::Charset::SmallPi: + return 1; + case Ion::Charset::Exponential: + return 2; + default: + assert(false); + return -1; + } +} + +int ConstantNode::simplificationOrderSameType(const ExpressionNode * e, bool ascending, bool canBeInterrupted) const { + if (!ascending) { + return e->simplificationOrderSameType(this, true, canBeInterrupted); + } + assert(type() == e->type()); + return (rankOfConstant(name()[0]) - rankOfConstant(static_cast(e)->name()[0])); +} + Layout ConstantNode::createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const { return LayoutHelper::String(m_name, strlen(m_name)); } @@ -27,20 +60,36 @@ int ConstantNode::serialize(char * buffer, int bufferSize, Preferences::PrintFlo } template -Evaluation ConstantNode::templatedApproximate(Context& context, Preferences::AngleUnit angleUnit) const { +Evaluation ConstantNode::templatedApproximate(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { if (isIComplex()) { assert(m_name[1] == 0); - return Complex(0.0, 1.0); + return Complex::Builder(0.0, 1.0); } if (isPi()) { - return Complex(M_PI); + return Complex::Builder(M_PI); } assert(isExponential()); - return Complex(M_E); + return Complex::Builder(M_E); } -Constant::Constant(char name) : SymbolAbstract(TreePool::sharedPool()->createTreeNode(SymbolAbstract::AlignedNodeSize(1, sizeof(ConstantNode)))) { - node()->setName(&name, 1); +Expression ConstantNode::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) { + return Constant(this).shallowReduce(context, complexFormat, angleUnit, target); } +Expression Constant::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { + Expression result; + if (complexFormat == Preferences::ComplexFormat::Real && isIComplex()) { + result = Unreal::Builder(); + } else if (target == ExpressionNode::ReductionTarget::User && isIComplex()) { + result = ComplexCartesian::Builder(Rational::Builder(0), Rational::Builder(1)); + } + if (!result.isUninitialized()) { + replaceWithInPlace(result); + return result; + } + return *this; +} + +template Evaluation ConstantNode::templatedApproximate(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; +template Evaluation ConstantNode::templatedApproximate(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; } diff --git a/poincare/src/cosine.cpp b/poincare/src/cosine.cpp index f5e9fbf04..3f904d83a 100644 --- a/poincare/src/cosine.cpp +++ b/poincare/src/cosine.cpp @@ -16,10 +16,10 @@ float CosineNode::characteristicXRange(Context & context, Preferences::AngleUnit } template -Complex CosineNode::computeOnComplex(const std::complex c, Preferences::AngleUnit angleUnit) { +Complex CosineNode::computeOnComplex(const std::complex c, Preferences::ComplexFormat, Preferences::AngleUnit angleUnit) { std::complex angleInput = Trigonometry::ConvertToRadian(c, angleUnit); std::complex res = std::cos(angleInput); - return Complex(Trigonometry::RoundToMeaningfulDigits(res, angleInput)); + return Complex::Builder(Trigonometry::RoundToMeaningfulDigits(res, angleInput)); } Layout CosineNode::createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const { @@ -30,13 +30,13 @@ int CosineNode::serialize(char * buffer, int bufferSize, Preferences::PrintFloat return SerializationHelper::Prefix(this, buffer, bufferSize, floatDisplayMode, numberOfSignificantDigits, Cosine::s_functionHelper.name()); } -Expression CosineNode::shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) { - return Cosine(this).shallowReduce(context, angleUnit, target); +Expression CosineNode::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) { + return Cosine(this).shallowReduce(context, complexFormat, angleUnit, target); } -Expression Cosine::shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { +Expression Cosine::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { { - Expression e = Expression::defaultShallowReduce(context, angleUnit); + Expression e = Expression::defaultShallowReduce(); if (e.isUndefined()) { return e; } @@ -47,7 +47,8 @@ Expression Cosine::shallowReduce(Context & context, Preferences::AngleUnit angle return SimplificationHelper::Map(*this, context, angleUnit); } #endif - return Trigonometry::shallowReduceDirectFunction(*this, context, angleUnit, target); + return Trigonometry::shallowReduceDirectFunction(*this, context, complexFormat, angleUnit, target); } + } diff --git a/poincare/src/decimal.cpp b/poincare/src/decimal.cpp index 8aeb5feca..1e88397e9 100644 --- a/poincare/src/decimal.cpp +++ b/poincare/src/decimal.cpp @@ -22,13 +22,14 @@ void removeZeroAtTheEnd(Integer * i) { *i = d.quotient; d = Integer::Division(*i, base); } - assert(!i->isInfinity()); + assert(!i->isOverflow()); } -void DecimalNode::setValue(const native_uint_t * mantissaDigits, uint8_t mantissaSize, int exponent, bool negative) { - m_negative = negative; - m_exponent = exponent; - m_numberOfDigitsInMantissa = mantissaSize; +DecimalNode::DecimalNode(const native_uint_t * mantissaDigits, uint8_t mantissaSize, int exponent, bool negative) : + m_negative(negative), + m_exponent(exponent), + m_numberOfDigitsInMantissa(mantissaSize) +{ memcpy(m_mantissa, mantissaDigits, mantissaSize*sizeof(native_uint_t)); } @@ -40,14 +41,6 @@ Integer DecimalNode::unsignedMantissa() const { return Integer::BuildInteger((native_uint_t *)m_mantissa, m_numberOfDigitsInMantissa, false); } -void DecimalNode::initToMatchSize(size_t goalSize) { - assert(goalSize != sizeof(DecimalNode)); - int mantissaSize = goalSize - sizeof(DecimalNode); - assert(mantissaSize%sizeof(native_uint_t) == 0); - m_numberOfDigitsInMantissa = mantissaSize/sizeof(native_uint_t); - assert(size() == goalSize); -} - static size_t DecimalSize(uint8_t numberOfDigitsInMantissa) { return sizeof(DecimalNode)+ sizeof(native_uint_t)*numberOfDigitsInMantissa; } @@ -56,11 +49,15 @@ size_t DecimalNode::size() const { return DecimalSize(m_numberOfDigitsInMantissa); } -Expression DecimalNode::setSign(Sign s, Context & context, Preferences::AngleUnit angleUnit) { - return Decimal(this).setSign(s, context, angleUnit); +Expression DecimalNode::setSign(Sign s, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) { + assert(s == ExpressionNode::Sign::Positive || s == ExpressionNode::Sign::Negative); + return Decimal(this).setSign(s); } -int DecimalNode::simplificationOrderSameType(const ExpressionNode * e, bool canBeInterrupted) const { +int DecimalNode::simplificationOrderSameType(const ExpressionNode * e, bool ascending, bool canBeInterrupted) const { + if (!ascending) { + return e->simplificationOrderSameType(this, true, canBeInterrupted); + } assert(e->type() == Type::Decimal); const DecimalNode * other = static_cast(e); if (m_negative && !other->m_negative) { @@ -82,15 +79,15 @@ int DecimalNode::simplificationOrderSameType(const ExpressionNode * e, bool canB double approx1 = other->templatedApproximate(); return (approx0 == approx1 ? 0 : (approx0 < approx1 ? -1 : 1)); } - return ((int)sign())*unsignedComparison; + return ((int)Number(this).sign())*unsignedComparison; } -Expression DecimalNode::shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) { - return Decimal(this).shallowReduce(context, angleUnit); +Expression DecimalNode::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) { + return Decimal(this).shallowReduce(); } -Expression DecimalNode::shallowBeautify(Context & context, Preferences::AngleUnit angleUnit) { - return Decimal(this).shallowBeautify(context, angleUnit); +Expression DecimalNode::shallowBeautify(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) { + return Decimal(this).shallowBeautify(); } Layout DecimalNode::createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const { @@ -264,7 +261,7 @@ int Decimal::Exponent(const char * integralPart, int integralPartLength, const c return exp; } -Decimal::Decimal(const char * integralPart, int integralPartLength, const char * fractionalPart, int fractionalPartLength, int exponent) : Number() { +Decimal Decimal::Builder(const char * integralPart, int integralPartLength, const char * fractionalPart, int fractionalPartLength, int exponent) { /* Create a Decimal whose mantissa has less than * k_numberOfStoredSignificantDigits. We round exceeding number if necessary. */ Integer zero(0); @@ -279,7 +276,7 @@ Decimal::Decimal(const char * integralPart, int integralPartLength, const char * // Cap the length of the integralPart integralPartLength = integralPartLength > PrintFloat::k_numberOfStoredSignificantDigits ? PrintFloat::k_numberOfStoredSignificantDigits : integralPartLength; Integer numerator(integralPart, integralPartLength, false); - assert(!numerator.isInfinity()); + assert(!numerator.isOverflow()); // Special case for 0.??? : get rid of useless 0s in front of the integralPartLength if (fractionalPart != nullptr && integralPartLength == 1 && integralPart[0] == '0') { integralPartLength = 0; @@ -297,11 +294,11 @@ Decimal::Decimal(const char * integralPart, int integralPartLength, const char * } numerator = rounding ? Integer::Addition(numerator, Integer(1)) : numerator; exponent = numerator.isZero() ? 0 : exponent; - new (this) Decimal(numerator, exponent); + return Decimal::Builder(numerator, exponent); } template -Decimal::Decimal(T f) : Number() { +Decimal Decimal::Builder(T f) { assert(!std::isnan(f) && !std::isinf(f)); int exp = IEEE754::exponentBase10(f); /* We keep 7 significant digits for if the the Decimal was built from a float @@ -323,27 +320,31 @@ Decimal::Decimal(T f) : Number() { Integer m = Integer((int64_t)(std::round(mantissaf))); /* We get rid of extra 0 at the end of the mantissa. */ removeZeroAtTheEnd(&m); - new (this) Decimal(m, exp); + return Decimal::Builder(m, exp); } /* We do not get rid of the useless 0s ending the mantissa here because we want * to keep them if they were entered by the user. */ -Decimal::Decimal(Integer m, int e) : - Decimal(DecimalSize(m.numberOfDigits()), m, e) {} - - -Decimal::Decimal(size_t size, const Integer & m, int e) : Number(TreePool::sharedPool()->createTreeNode(size)) { - node()->setValue(m.digits(), m.numberOfDigits(), e, m.isNegative()); +Decimal Decimal::Builder(Integer m, int e) { + return Decimal::Builder(DecimalSize(m.numberOfDigits()), m, e); } -Expression Decimal::setSign(ExpressionNode::Sign s, Context & context, Preferences::AngleUnit angleUnit) { +Decimal Decimal::Builder(size_t size, const Integer & m, int e) { + void * bufferNode = TreePool::sharedPool()->alloc(size); + DecimalNode * node = new (bufferNode) DecimalNode(m.digits(), m.numberOfDigits(), e, m.isNegative()); + TreeHandle h = TreeHandle::BuildWithGhostChildren(node); + return static_cast(h); +} + +Expression Decimal::setSign(ExpressionNode::Sign s) { + assert(s == ExpressionNode::Sign::Positive || s == ExpressionNode::Sign::Negative); Decimal result = *this; result.node()->setNegative(s == ExpressionNode::Sign::Negative); return result; } -Expression Decimal::shallowReduce(Context & context, Preferences::AngleUnit angleUnit) { - Expression e = Expression::defaultShallowReduce(context, angleUnit); +Expression Decimal::shallowReduce() { + Expression e = Expression::defaultShallowReduce(); if (e.isUndefined()) { return e; } @@ -361,19 +362,19 @@ Expression Decimal::shallowReduce(Context & context, Preferences::AngleUnit angl denominator = Integer::Power(Integer(10), Integer(numberOfDigits-1-exp)); } Expression result; - if (numerator.isInfinity() || denominator.isInfinity()) { + if (numerator.isOverflow() || denominator.isOverflow()) { result = Number::FloatNumber(node()->signedMantissa().template approximate()*std::pow(10.0, (double)exp)); } else { - result = Rational(numerator, denominator); + result = Rational::Builder(numerator, denominator); } replaceWithInPlace(result); return result; } -Expression Decimal::shallowBeautify(Context & context, Preferences::AngleUnit angleUnit) { +Expression Decimal::shallowBeautify() { if (sign() == ExpressionNode::Sign::Negative) { - Expression abs = setSign(ExpressionNode::Sign::Positive, context, angleUnit); - Opposite o; + Expression abs = setSign(ExpressionNode::Sign::Positive); + Opposite o = Opposite::Builder(); replaceWithInPlace(o); o.replaceChildAtIndexInPlace(0, abs); return o; @@ -381,7 +382,7 @@ Expression Decimal::shallowBeautify(Context & context, Preferences::AngleUnit an return *this; } -template Decimal::Decimal(double); -template Decimal::Decimal(float); +template Decimal Decimal::Decimal::Builder(double); +template Decimal Decimal::Decimal::Builder(float); } diff --git a/poincare/src/derivative.cpp b/poincare/src/derivative.cpp index 33ac1b490..cb6f1de12 100644 --- a/poincare/src/derivative.cpp +++ b/poincare/src/derivative.cpp @@ -39,17 +39,17 @@ int DerivativeNode::serialize(char * buffer, int bufferSize, Preferences::PrintF return SerializationHelper::Prefix(this, buffer, bufferSize, floatDisplayMode, numberOfSignificantDigits, Derivative::s_functionHelper.name()); } -Expression DerivativeNode::shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) { - return Derivative(this).shallowReduce(context, angleUnit); +Expression DerivativeNode::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) { + return Derivative(this).shallowReduce(); } template -Evaluation DerivativeNode::templatedApproximate(Context& context, Preferences::AngleUnit angleUnit) const { +Evaluation DerivativeNode::templatedApproximate(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { static T min = sizeof(T) == sizeof(double) ? DBL_MIN : FLT_MIN; static T epsilon = sizeof(T) == sizeof(double) ? DBL_EPSILON : FLT_EPSILON; - Evaluation evaluationArgumentInput = childAtIndex(2)->approximate(T(), context, angleUnit); + Evaluation evaluationArgumentInput = childAtIndex(2)->approximate(T(), context, complexFormat, angleUnit); T evaluationArgument = evaluationArgumentInput.toScalar(); - T functionValue = approximateWithArgument(evaluationArgument, context, angleUnit); + T functionValue = approximateWithArgument(evaluationArgument, context, complexFormat, angleUnit); // No complex/matrix version of Derivative if (std::isnan(evaluationArgument) || std::isnan(functionValue)) { return Complex::Undefined(); @@ -58,7 +58,7 @@ Evaluation DerivativeNode::templatedApproximate(Context& context, Preferences T error, result; T h = k_minInitialRate; do { - result = riddersApproximation(context, angleUnit, evaluationArgument, h, &error); + result = riddersApproximation(context, complexFormat, angleUnit, evaluationArgument, h, &error); h /= 10.0; } while ((std::fabs(error/result) > k_maxErrorRateOnApproximation || std::isnan(error)) && h >= epsilon); @@ -67,27 +67,30 @@ Evaluation DerivativeNode::templatedApproximate(Context& context, Preferences return Complex::Undefined(); } if (std::fabs(error) < min) { - return Complex(result); + return Complex::Builder(result); } error = std::pow((T)10, std::floor(std::log10(std::fabs(error)))+2); - return Complex(std::round(result/error)*error); + return Complex::Builder(std::round(result/error)*error); } template -T DerivativeNode::approximateWithArgument(T x, Context & context, Preferences::AngleUnit angleUnit) const { +T DerivativeNode::approximateWithArgument(T x, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { assert(childAtIndex(1)->type() == Type::Symbol); - return Expression(childAtIndex(0)).approximateWithValueForSymbol(static_cast(childAtIndex(1))->name(), x, context, angleUnit); + VariableContext variableContext = VariableContext(static_cast(childAtIndex(1))->name(), &context); + variableContext.setApproximationForVariable(x); + // Here we cannot use Expression::approximateWithValueForSymbol which would reset the sApproximationEncounteredComplex flag + return childAtIndex(0)->approximate(T(), variableContext, complexFormat, angleUnit).toScalar(); } template -T DerivativeNode::growthRateAroundAbscissa(T x, T h, Context & context, Preferences::AngleUnit angleUnit) const { - T expressionPlus = approximateWithArgument(x+h, context, angleUnit); - T expressionMinus = approximateWithArgument(x-h, context, angleUnit); +T DerivativeNode::growthRateAroundAbscissa(T x, T h, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { + T expressionPlus = approximateWithArgument(x+h, context, complexFormat, angleUnit); + T expressionMinus = approximateWithArgument(x-h, context, complexFormat, angleUnit); return (expressionPlus - expressionMinus)/(2*h); } template -T DerivativeNode::riddersApproximation(Context & context, Preferences::AngleUnit angleUnit, T x, T h, T * error) const { +T DerivativeNode::riddersApproximation(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, T x, T h, T * error) const { /* Ridders' Algorithm * Blibliography: * - Ridders, C.J.F. 1982, Advances in Helperering Software, vol. 4, no. 2, @@ -106,7 +109,7 @@ T DerivativeNode::riddersApproximation(Context & context, Preferences::AngleUnit a[i][j] = 1; } } - a[0][0] = growthRateAroundAbscissa(x, hh, context, angleUnit); + a[0][0] = growthRateAroundAbscissa(x, hh, context, complexFormat, angleUnit); T ans = 0; T errt = 0; // Loop on i: change the step size @@ -115,7 +118,7 @@ T DerivativeNode::riddersApproximation(Context & context, Preferences::AngleUnit // Make hh an exactly representable number volatile T temp = x+hh; hh = temp - x; - a[0][i] = growthRateAroundAbscissa(x, hh, context, angleUnit); + a[0][i] = growthRateAroundAbscissa(x, hh, context, complexFormat, angleUnit); T fac = k_rateStepSize*k_rateStepSize; // Loop on j: compute extrapolation for several orders for (int j = 1; j < 10; j++) { @@ -137,20 +140,29 @@ T DerivativeNode::riddersApproximation(Context & context, Preferences::AngleUnit return ans; } -Expression Derivative::shallowReduce(Context & context, Preferences::AngleUnit angleUnit) { +Expression Derivative::shallowReduce() { { - Expression e = Expression::defaultShallowReduce(context, angleUnit); + Expression e = Expression::defaultShallowReduce(); if (e.isUndefined()) { return e; } } #if MATRIX_EXACT_REDUCING if (childAtIndex(0).type() == ExpressionNode::Type::Matrix || || childAtIndex(1).type() == ExpressionNode::Type::Matrix || childAtIndex(2).type() == ExpressionNode::Type::Matrix) { - return Undefined(); + return Undefined::Builder(); } #endif // TODO: to be implemented diff(+) -> +diff() etc return *this; } +Expression Derivative::UntypedBuilder(Expression children) { + assert(children.type() == ExpressionNode::Type::Matrix); + if (children.childAtIndex(1).type() != ExpressionNode::Type::Symbol) { + // Second parameter must be a Symbol. + return Expression(); + } + return Builder(children.childAtIndex(0), children.childAtIndex(1).convert(), children.childAtIndex(2)); +} + } diff --git a/poincare/src/determinant.cpp b/poincare/src/determinant.cpp index 36bed8767..7a4a7b110 100644 --- a/poincare/src/determinant.cpp +++ b/poincare/src/determinant.cpp @@ -23,18 +23,19 @@ int DeterminantNode::serialize(char * buffer, int bufferSize, Preferences::Print // TODO: handle this exactly in shallowReduce for small dimensions. template -Evaluation DeterminantNode::templatedApproximate(Context& context, Preferences::AngleUnit angleUnit) const { - Evaluation input = childAtIndex(0)->approximate(T(), context, angleUnit); - return Complex(input.determinant()); +Evaluation DeterminantNode::templatedApproximate(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { + Evaluation input = childAtIndex(0)->approximate(T(), context, complexFormat, angleUnit); + return Complex::Builder(input.determinant()); } -Expression DeterminantNode::shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) { - return Determinant(this).shallowReduce(context, angleUnit); +Expression DeterminantNode::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) { + return Determinant(this).shallowReduce(context); } -Expression Determinant::shallowReduce(Context & context, Preferences::AngleUnit angleUnit) { + +Expression Determinant::shallowReduce(Context & context) { { - Expression e = Expression::defaultShallowReduce(context, angleUnit); + Expression e = Expression::defaultShallowReduce(); if (e.isUndefined()) { return e; } diff --git a/poincare/src/division.cpp b/poincare/src/division.cpp index a6843f97b..ae63cb861 100644 --- a/poincare/src/division.cpp +++ b/poincare/src/division.cpp @@ -4,6 +4,8 @@ #include #include #include +#include +#include #include #include #include @@ -20,7 +22,7 @@ int DivisionNode::polynomialDegree(Context & context, const char * symbolName) c } bool DivisionNode::childNeedsParenthesis(const TreeNode * child) const { - if (static_cast(child)->isNumber() && static_cast(child)->sign() == Sign::Negative) { + if (static_cast(child)->isNumber() && Number(static_cast(child)).sign() == Sign::Negative) { return true; } if (static_cast(child)->type() == Type::Rational && !static_cast(child)->denominator().isOne()) { @@ -33,52 +35,53 @@ bool DivisionNode::childNeedsParenthesis(const TreeNode * child) const { Layout DivisionNode::createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const { const ExpressionNode * numerator = childAtIndex(0)->type() == Type::Parenthesis ? childAtIndex(0)->childAtIndex(0) : childAtIndex(0); const ExpressionNode * denominator = childAtIndex(1)->type() == Type::Parenthesis ? childAtIndex(1)->childAtIndex(0) : childAtIndex(1); - return FractionLayout(numerator->createLayout(floatDisplayMode, numberOfSignificantDigits), denominator->createLayout(floatDisplayMode, numberOfSignificantDigits)); + return FractionLayout::Builder(numerator->createLayout(floatDisplayMode, numberOfSignificantDigits), denominator->createLayout(floatDisplayMode, numberOfSignificantDigits)); } int DivisionNode::serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const { return SerializationHelper::Infix(this, buffer, bufferSize, floatDisplayMode, numberOfSignificantDigits, "/"); } -Expression DivisionNode::shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) { - return Division(this).shallowReduce(context, angleUnit, target); +Expression DivisionNode::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) { + return Division(this).shallowReduce(context, complexFormat, angleUnit, target); } -template Complex DivisionNode::compute(const std::complex c, const std::complex d) { - return Complex(c/d); +template Complex DivisionNode::compute(const std::complex c, const std::complex d, Preferences::ComplexFormat complexFormat) { + if (d.real() == 0.0 && d.imag() == 0.0) { + return Complex::Undefined(); + } + return Complex::Builder(c/d); } -template MatrixComplex DivisionNode::computeOnComplexAndMatrix(const std::complex c, const MatrixComplex n) { +template MatrixComplex DivisionNode::computeOnComplexAndMatrix(const std::complex c, const MatrixComplex n, Preferences::ComplexFormat complexFormat) { MatrixComplex inverse = n.inverse(); - MatrixComplex result = MultiplicationNode::computeOnComplexAndMatrix(c, inverse); + MatrixComplex result = MultiplicationNode::computeOnComplexAndMatrix(c, inverse, complexFormat); return result; } -template MatrixComplex DivisionNode::computeOnMatrices(const MatrixComplex m, const MatrixComplex n) { +template MatrixComplex DivisionNode::computeOnMatrices(const MatrixComplex m, const MatrixComplex n, Preferences::ComplexFormat complexFormat) { if (m.numberOfColumns() != n.numberOfColumns()) { return MatrixComplex::Undefined(); } MatrixComplex inverse = n.inverse(); - MatrixComplex result = MultiplicationNode::computeOnMatrices(m, inverse); + MatrixComplex result = MultiplicationNode::computeOnMatrices(m, inverse, complexFormat); return result; } // Division -Division::Division() : Expression(TreePool::sharedPool()->createTreeNode()) {} - -Expression Division::shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { +Expression Division::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { { - Expression e = Expression::defaultShallowReduce(context, angleUnit); + Expression e = Expression::defaultShallowReduce(); if (e.isUndefined()) { return e; } } - Expression p = Power(childAtIndex(1), Rational(-1)); - Multiplication m = Multiplication(childAtIndex(0), p); - p.shallowReduce(context, angleUnit, target); // Imagine Division(2,1). p would be 1^(-1) which can be simplified + Expression p = Power::Builder(childAtIndex(1), Rational::Builder(-1)); + Multiplication m = Multiplication::Builder(childAtIndex(0), p); + p.shallowReduce(context, complexFormat, angleUnit, target); // Imagine Division::Builder(2,1). p would be 1^(-1) which can be simplified replaceWithInPlace(m); - return m.shallowReduce(context, angleUnit, target); + return m.shallowReduce(context, complexFormat, angleUnit, target); } } diff --git a/poincare/src/division_quotient.cpp b/poincare/src/division_quotient.cpp index bb2e5f4d2..853eb1b56 100644 --- a/poincare/src/division_quotient.cpp +++ b/poincare/src/division_quotient.cpp @@ -12,8 +12,8 @@ constexpr Expression::FunctionHelper DivisionQuotient::s_functionHelper; int DivisionQuotientNode::numberOfChildren() const { return DivisionQuotient::s_functionHelper.numberOfChildren(); } -Expression DivisionQuotientNode::shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) { - return DivisionQuotient(this).shallowReduce(context, angleUnit); +Expression DivisionQuotientNode::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) { + return DivisionQuotient(this).shallowReduce(); } Layout DivisionQuotientNode::createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const { @@ -24,20 +24,21 @@ int DivisionQuotientNode::serialize(char * buffer, int bufferSize, Preferences:: } template -Evaluation DivisionQuotientNode::templatedApproximate(Context& context, Preferences::AngleUnit angleUnit) const { - Evaluation f1Input = childAtIndex(0)->approximate(T(), context, angleUnit); - Evaluation f2Input = childAtIndex(1)->approximate(T(), context, angleUnit); +Evaluation DivisionQuotientNode::templatedApproximate(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { + Evaluation f1Input = childAtIndex(0)->approximate(T(), context, complexFormat, angleUnit); + Evaluation f2Input = childAtIndex(1)->approximate(T(), context, complexFormat, angleUnit); T f1 = f1Input.toScalar(); T f2 = f2Input.toScalar(); if (std::isnan(f1) || std::isnan(f2) || f1 != (int)f1 || f2 != (int)f2) { return Complex::Undefined(); } - return Complex(std::floor(f1/f2)); + return Complex::Builder(std::floor(f1/f2)); } -Expression DivisionQuotient::shallowReduce(Context & context, Preferences::AngleUnit angleUnit) { + +Expression DivisionQuotient::shallowReduce() { { - Expression e = Expression::defaultShallowReduce(context, angleUnit); + Expression e = Expression::defaultShallowReduce(); if (e.isUndefined()) { return e; } @@ -46,7 +47,7 @@ Expression DivisionQuotient::shallowReduce(Context & context, Preferences::Angle Expression c1 = childAtIndex(1); #if MATRIX_EXACT_REDUCING if (c0.type() == ExpressionNode::Type::Matrix || c1.type() == ExpressionNode::Type::Matrix) { - Expression result = Undefined(); + Expression result = Undefined::Builder(); replaceWithInPlace(result); return result; } @@ -54,7 +55,7 @@ Expression DivisionQuotient::shallowReduce(Context & context, Preferences::Angle if (c0.type() == ExpressionNode::Type::Rational) { Rational r0 = static_cast(c0); if (!r0.integerDenominator().isOne()) { - Expression result = Undefined(); + Expression result = Undefined::Builder(); replaceWithInPlace(result); return result; } @@ -62,7 +63,7 @@ Expression DivisionQuotient::shallowReduce(Context & context, Preferences::Angle if (c1.type() == ExpressionNode::Type::Rational) { Rational r1 = static_cast(c1); if (!r1.integerDenominator().isOne()) { - Expression result = Undefined(); + Expression result = Undefined::Builder(); replaceWithInPlace(result); return result; } @@ -76,13 +77,13 @@ Expression DivisionQuotient::shallowReduce(Context & context, Preferences::Angle Integer a = r0.signedIntegerNumerator(); Integer b = r1.signedIntegerNumerator(); if (b.isZero()) { - Expression result = Infinity(a.isNegative()); + Expression result = Infinity::Builder(a.isNegative()); replaceWithInPlace(result); return result; } Integer result = Integer::Division(a, b).quotient; - assert(!result.isInfinity()); - Expression rationalResult = Rational(result); + assert(!result.isOverflow()); + Expression rationalResult = Rational::Builder(result); replaceWithInPlace(rationalResult); return rationalResult; } diff --git a/poincare/src/division_remainder.cpp b/poincare/src/division_remainder.cpp index 6551d115d..f4334e0ee 100644 --- a/poincare/src/division_remainder.cpp +++ b/poincare/src/division_remainder.cpp @@ -20,25 +20,26 @@ int DivisionRemainderNode::serialize(char * buffer, int bufferSize, Preferences: return SerializationHelper::Prefix(this, buffer, bufferSize, floatDisplayMode, numberOfSignificantDigits, DivisionRemainder::s_functionHelper.name()); } -Expression DivisionRemainderNode::shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) { - return DivisionRemainder(this).shallowReduce(context, angleUnit); +Expression DivisionRemainderNode::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) { + return DivisionRemainder(this).shallowReduce(); } template -Evaluation DivisionRemainderNode::templatedApproximate(Context& context, Preferences::AngleUnit angleUnit) const { - Evaluation f1Input = childAtIndex(0)->approximate(T(), context, angleUnit); - Evaluation f2Input = childAtIndex(1)->approximate(T(), context, angleUnit); +Evaluation DivisionRemainderNode::templatedApproximate(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { + Evaluation f1Input = childAtIndex(0)->approximate(T(), context, complexFormat, angleUnit); + Evaluation f2Input = childAtIndex(1)->approximate(T(), context, complexFormat, angleUnit); T f1 = f1Input.toScalar(); T f2 = f2Input.toScalar(); if (std::isnan(f1) || std::isnan(f2) || f1 != (int)f1 || f2 != (int)f2) { return Complex::Undefined(); } - return Complex(std::round(f1-f2*std::floor(f1/f2))); + return Complex::Builder(std::round(f1-f2*std::floor(f1/f2))); } -Expression DivisionRemainder::shallowReduce(Context & context, Preferences::AngleUnit angleUnit) { + +Expression DivisionRemainder::shallowReduce() { { - Expression e = Expression::defaultShallowReduce(context, angleUnit); + Expression e = Expression::defaultShallowReduce(); if (e.isUndefined()) { return e; } @@ -47,13 +48,13 @@ Expression DivisionRemainder::shallowReduce(Context & context, Preferences::Angl Expression c1 = childAtIndex(1); #if MATRIX_EXACT_REDUCING if (c0.type() == ExpressionNode::Type::Matrix || c1.type() == ExpressionNode::Type::Matrix) { - return Undefined(); + return Undefined::Builder(); } #endif if (c0.type() == ExpressionNode::Type::Rational) { Rational r0 = static_cast(c0); if (!r0.integerDenominator().isOne()) { - Expression result = Undefined(); + Expression result = Undefined::Builder(); replaceWithInPlace(result); return result; } @@ -61,7 +62,7 @@ Expression DivisionRemainder::shallowReduce(Context & context, Preferences::Angl if (c1.type() == ExpressionNode::Type::Rational) { Rational r1 = static_cast(c1); if (!r1.integerDenominator().isOne()) { - Expression result = Undefined(); + Expression result = Undefined::Builder(); replaceWithInPlace(result); return result; } @@ -75,13 +76,13 @@ Expression DivisionRemainder::shallowReduce(Context & context, Preferences::Angl Integer a = r0.signedIntegerNumerator(); Integer b = r1.signedIntegerNumerator(); if (b.isZero()) { - Expression result = Infinity(a.isNegative()); + Expression result = Infinity::Builder(a.isNegative()); replaceWithInPlace(result); return result; } Integer result = Integer::Division(a, b).remainder; - assert(!result.isInfinity()); - Expression rationalResult = Rational(result); + assert(!result.isOverflow()); + Expression rationalResult = Rational::Builder(result); replaceWithInPlace(rationalResult); return rationalResult; } diff --git a/poincare/src/empty_expression.cpp b/poincare/src/empty_expression.cpp index 538057093..882d6311f 100644 --- a/poincare/src/empty_expression.cpp +++ b/poincare/src/empty_expression.cpp @@ -11,13 +11,11 @@ int EmptyExpressionNode::serialize(char * buffer, int bufferSize, Preferences::P } Layout EmptyExpressionNode::createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const { - return EmptyLayout(); + return EmptyLayout::Builder(); } -template Evaluation EmptyExpressionNode::templatedApproximate(Context& context, Preferences::AngleUnit angleUnit) const { +template Evaluation EmptyExpressionNode::templatedApproximate(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { return Complex::Undefined(); } -EmptyExpression::EmptyExpression() : Expression(TreePool::sharedPool()->createTreeNode()) {} - } diff --git a/poincare/src/empty_layout.cpp b/poincare/src/empty_layout.cpp index 00f0b642b..4bed5b777 100644 --- a/poincare/src/empty_layout.cpp +++ b/poincare/src/empty_layout.cpp @@ -95,13 +95,12 @@ void EmptyLayoutNode::render(KDContext * ctx, KDPoint p, KDColor expressionColor } EmptyLayout::EmptyLayout(const EmptyLayoutNode * n) : Layout(n) {} -EmptyLayout::EmptyLayout(EmptyLayoutNode::Color color, bool visible, const KDFont * font, bool margins) : - Layout(TreePool::sharedPool()->createTreeNode()) -{ - node()->setColor(color); - node()->setVisible(visible); - node()->setFont(font); - node()->setMargins(margins); + +EmptyLayout EmptyLayout::Builder(EmptyLayoutNode::Color color, bool visible, const KDFont * font, bool margins) { + void * bufferNode = TreePool::sharedPool()->alloc(sizeof(EmptyLayoutNode)); + EmptyLayoutNode * node = new (bufferNode) EmptyLayoutNode(color, visible, font, margins); + TreeHandle h = TreeHandle::BuildWithGhostChildren(node); + return static_cast(h); } } diff --git a/poincare/src/equal.cpp b/poincare/src/equal.cpp index a9e898d22..0f8c758fe 100644 --- a/poincare/src/equal.cpp +++ b/poincare/src/equal.cpp @@ -20,14 +20,14 @@ extern "C" { } namespace Poincare { -Expression EqualNode::shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) { - return Equal(this).shallowReduce(context, angleUnit); +Expression EqualNode::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) { + return Equal(this).shallowReduce(); } Layout EqualNode::createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const { - HorizontalLayout result; + HorizontalLayout result = HorizontalLayout::Builder(); result.addOrMergeChildAtIndex(childAtIndex(0)->createLayout(floatDisplayMode, numberOfSignificantDigits), 0, false); - result.addChildAtIndex(CharLayout('='), result.numberOfChildren(), result.numberOfChildren(), nullptr); + result.addChildAtIndex(CharLayout::Builder('='), result.numberOfChildren(), result.numberOfChildren(), nullptr); result.addOrMergeChildAtIndex(childAtIndex(1)->createLayout(floatDisplayMode, numberOfSignificantDigits), result.numberOfChildren(), false); return result; } @@ -37,24 +37,25 @@ int EqualNode::serialize(char * buffer, int bufferSize, Preferences::PrintFloatM } template -Evaluation EqualNode::templatedApproximate(Context& context, Preferences::AngleUnit angleUnit) const { +Evaluation EqualNode::templatedApproximate(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { return Complex::Undefined(); } -Expression Equal::standardEquation(Context & context, Preferences::AngleUnit angleUnit) const { - Expression sub = Subtraction(childAtIndex(0).clone(), childAtIndex(1).clone()); - return sub.reduce(context, angleUnit); + +Expression Equal::standardEquation(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { + Expression sub = Subtraction::Builder(childAtIndex(0).clone(), childAtIndex(1).clone()); + return sub.reduce(context, complexFormat, angleUnit); } -Expression Equal::shallowReduce(Context & context, Preferences::AngleUnit angleUnit) { +Expression Equal::shallowReduce() { { - Expression e = Expression::defaultShallowReduce(context, angleUnit); + Expression e = Expression::defaultShallowReduce(); if (e.isUndefined()) { return e; } } if (childAtIndex(0).isIdenticalTo(childAtIndex(1))) { - Expression result = Rational(1); + Expression result = Rational::Builder(1); replaceWithInPlace(result); return result; } diff --git a/poincare/src/expression.cpp b/poincare/src/expression.cpp index 99a630389..ec19edd2f 100644 --- a/poincare/src/expression.cpp +++ b/poincare/src/expression.cpp @@ -14,6 +14,7 @@ namespace Poincare { bool Expression::sSymbolReplacementsCountLock = false; +static bool sApproximationEncounteredComplex = false; /* Constructor & Destructor */ @@ -44,11 +45,11 @@ Expression Expression::ExpressionFromAddress(const void * address, size_t size) static Expression::CircuitBreaker sCircuitBreaker = nullptr; static bool sSimplificationHasBeenInterrupted = false; -void Expression::setCircuitBreaker(CircuitBreaker cb) { +void Expression::SetCircuitBreaker(CircuitBreaker cb) { sCircuitBreaker = cb; } -bool Expression::shouldStopProcessing() { +bool Expression::ShouldStopProcessing() { if (sCircuitBreaker == nullptr) { return false; } @@ -59,8 +60,8 @@ bool Expression::shouldStopProcessing() { return false; } -void Expression::resetInterruption() { - sSimplificationHasBeenInterrupted = false; +void Expression::SetInterruption(bool interrupt) { + sSimplificationHasBeenInterrupted = interrupt; } /* Hierarchy */ @@ -147,9 +148,7 @@ bool containsVariables(const Expression e, char * variables, int maxVariableSize return false; } -bool Expression::getLinearCoefficients(char * variables, int maxVariableSize, Expression coefficients[], Expression constant[], Context & context, Preferences::AngleUnit angleUnit) const { - // Reset interrupting flag because we use deepReduce - sSimplificationHasBeenInterrupted = false; +bool Expression::getLinearCoefficients(char * variables, int maxVariableSize, Expression coefficients[], Expression constant[], Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { assert(!recursivelyMatches(IsMatrix, context, true)); // variables is in fact of type char[k_maxNumberOfVariables][maxVariableSize] int index = 0; @@ -164,10 +163,10 @@ bool Expression::getLinearCoefficients(char * variables, int maxVariableSize, Ex index = 0; Expression polynomialCoefficients[k_maxNumberOfPolynomialCoefficients]; while (variables[index*maxVariableSize] != 0) { - int degree = equation.getPolynomialReducedCoefficients(&variables[index*maxVariableSize], polynomialCoefficients, context, angleUnit); + int degree = equation.getPolynomialReducedCoefficients(&variables[index*maxVariableSize], polynomialCoefficients, context, complexFormat, angleUnit); switch (degree) { case 0: - coefficients[index] = Rational(0); + coefficients[index] = Rational::Builder(0); break; case 1: coefficients[index] = polynomialCoefficients[1]; @@ -176,7 +175,7 @@ bool Expression::getLinearCoefficients(char * variables, int maxVariableSize, Ex /* degree is supposed to be 0 or 1. Otherwise, it means that equation * is 'undefined' due to the reduction of 0*inf for example. * (ie, x*y*inf = 0) */ - assert(!recursivelyMatches([](const Expression e, Context & context, bool replaceSymbols) { return e.type() == ExpressionNode::Type::Undefined; }, context, true)); + assert(!recursivelyMatches([](const Expression e, Context & context, bool replaceSymbols) { return e.isUndefined(); }, context, true)); return false; } /* The equation is can be written: a_1*x+a_0 with a_1 and a_0 x-independent. @@ -185,7 +184,7 @@ bool Expression::getLinearCoefficients(char * variables, int maxVariableSize, Ex equation = polynomialCoefficients[0]; index++; } - constant[0] = Opposite(equation.clone()).deepReduce(context, angleUnit, ExpressionNode::ReductionTarget::TopDownComputation); + constant[0] = Opposite::Builder(equation.clone()).reduce(context, complexFormat, angleUnit); /* The expression can be linear on all coefficients taken one by one but * non-linear (ex: xy = 2). We delete the results and return false if one of * the coefficients contains a variable. */ @@ -201,23 +200,36 @@ bool Expression::getLinearCoefficients(char * variables, int maxVariableSize, Ex // Private -void Expression::defaultDeepReduceChildren(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { +void Expression::defaultDeepReduceChildren(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { for (int i = 0; i < numberOfChildren(); i++) { - childAtIndex(i).deepReduce(context, angleUnit, target); + childAtIndex(i).deepReduce(context, complexFormat, angleUnit, target); } } -Expression Expression::defaultShallowReduce(Context & context, Preferences::AngleUnit angleUnit) { +Expression Expression::defaultShallowReduce() { + Expression result; for (int i = 0; i < numberOfChildren(); i++) { - if (childAtIndex(i).type() == ExpressionNode::Type::Undefined) { - Expression result = Undefined(); - replaceWithInPlace(result); - return result; + // The reduction is shortcutted if one child is unreal or undefined: + // - the result is unreal is at least one child is unreal + // - the result is undefined is at least one child is undefined but no child is unreal + if (childAtIndex(i).type() == ExpressionNode::Type::Unreal) { + result = Unreal::Builder(); + break; + } else if (childAtIndex(i).type() == ExpressionNode::Type::Undefined) { + result = Undefined::Builder(); } } + if (!result.isUninitialized()) { + replaceWithInPlace(result); + return result; + } return *this; } +bool Expression::SimplificationHasBeenInterrupted() { + return sSimplificationHasBeenInterrupted; +} + Expression Expression::parent() const { TreeHandle p = TreeHandle::parent(); return static_cast(p); @@ -248,11 +260,38 @@ Expression Expression::defaultReplaceReplaceableSymbols(Context & context) { return *this; } +Expression Expression::makePositiveAnyNegativeNumeralFactor(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { + // The expression is a negative number + if (isNumber() && sign(&context) == ExpressionNode::Sign::Negative) { + return setSign(ExpressionNode::Sign::Positive, &context, complexFormat, angleUnit, target); + } + // The expression is a multiplication whose numeral factor is negative + if (type() == ExpressionNode::Type::Multiplication && numberOfChildren() > 0 && childAtIndex(0).isNumber() && childAtIndex(0).sign(&context) == ExpressionNode::Sign::Negative) { + Multiplication m = convert(); + if (m.childAtIndex(0).type() == ExpressionNode::Type::Rational && m.childAtIndex(0).convert().isMinusOne()) { + // The negative numeral factor is -1, we just remove it + m.removeChildAtIndexInPlace(0); + // The multiplication can have only one child after removing -1 + return m.squashUnaryHierarchyInPlace(); + } else { + // Otherwise, we make it positive + m.childAtIndex(0).setSign(ExpressionNode::Sign::Positive, &context, complexFormat, angleUnit, target); + } + return m; + } + return Expression(); +} + template -Evaluation Expression::approximateToEvaluation(Context& context, Preferences::AngleUnit angleUnit) const { +Evaluation Expression::approximateToEvaluation(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { + sApproximationEncounteredComplex = false; // Reset interrupting flag because some evaluation methods use it sSimplificationHasBeenInterrupted = false; - return node()->approximate(U(), context, angleUnit); + Evaluation e = node()->approximate(U(), context, complexFormat, angleUnit); + if (complexFormat == Preferences::ComplexFormat::Real && sApproximationEncounteredComplex) { + e = Complex::Undefined(); + } + return e; } Expression Expression::defaultReplaceSymbolWithExpression(const SymbolAbstract & symbol, const Expression expression) { @@ -271,12 +310,11 @@ int Expression::defaultGetPolynomialCoefficients(Context & context, const char * return -1; } -int Expression::getPolynomialReducedCoefficients(const char * symbolName, Expression coefficients[], Context & context, Preferences::AngleUnit angleUnit) const { +int Expression::getPolynomialReducedCoefficients(const char * symbolName, Expression coefficients[], Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { // Reset interrupting flag because we use deepReduce - sSimplificationHasBeenInterrupted = false; int degree = getPolynomialCoefficients(context, symbolName, coefficients); for (int i = 0; i <= degree; i++) { - coefficients[i] = coefficients[i].deepReduce(context, angleUnit, ExpressionNode::ReductionTarget::TopDownComputation); + coefficients[i] = coefficients[i].reduce(context, complexFormat, angleUnit); } return degree; } @@ -293,15 +331,39 @@ Expression Expression::defaultReplaceUnknown(const Symbol & symbol) { return *this; } +/* Complex */ + +bool Expression::EncounteredComplex() { + return sApproximationEncounteredComplex; +} + +void Expression::SetEncounteredComplex(bool encounterComplex) { + sApproximationEncounteredComplex = encounterComplex; +} + +Preferences::ComplexFormat Expression::UpdatedComplexFormatWithTextInput(Preferences::ComplexFormat complexFormat, const char * textInput) { + if (complexFormat == Preferences::ComplexFormat::Real && strchr(textInput, Ion::Charset::IComplex) != nullptr) { + return Preferences::ComplexFormat::Cartesian; + } + return complexFormat; +} + +Preferences::ComplexFormat Expression::UpdatedComplexFormatWithExpressionInput(Preferences::ComplexFormat complexFormat, const Expression & exp, Context & context) { + if (complexFormat == Preferences::ComplexFormat::Real && exp.recursivelyMatches([](const Expression e, Context & context, bool replaceSymbols) { return e.type() == ExpressionNode::Type::Constant && static_cast(e).isIComplex(); }, context, true)) { + return Preferences::ComplexFormat::Cartesian; + } + return complexFormat; +} + /* Comparison */ bool Expression::isIdenticalTo(const Expression e) const { /* We use the simplification order only because it is a already-coded total * order on expresssions. */ - return ExpressionNode::SimplificationOrder(node(), e.node(), true) == 0; + return ExpressionNode::SimplificationOrder(node(), e.node(), true, true) == 0; } -bool Expression::isEqualToItsApproximationLayout(Expression approximation, char * buffer, int bufferSize, Preferences::AngleUnit angleUnit, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits, Context & context) { +bool Expression::isEqualToItsApproximationLayout(Expression approximation, char * buffer, int bufferSize, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits, Context & context) { approximation.serialize(buffer, bufferSize, floatDisplayMode, numberOfSignificantDigits); /* Warning: we cannot use directly the the approximate expression but we have * to re-serialize it because the number of stored significative @@ -309,7 +371,7 @@ bool Expression::isEqualToItsApproximationLayout(Expression approximation, char * identical. (For example, 0.000025 might be displayed "0.00003" and stored * as Decimal(0.000025) and isEqualToItsApproximationLayout should return * false) */ - Expression approximateOutput = Expression::ParseAndSimplify(buffer, context, angleUnit); + Expression approximateOutput = Expression::ParseAndSimplify(buffer, context, complexFormat, angleUnit); bool equal = isIdenticalTo(approximateOutput); return equal; } @@ -324,12 +386,12 @@ int Expression::serialize(char * buffer, int bufferSize, Preferences::PrintFloat /* Simplification */ -Expression Expression::ParseAndSimplify(const char * text, Context & context, Preferences::AngleUnit angleUnit) { +Expression Expression::ParseAndSimplify(const char * text, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) { Expression exp = Parse(text); if (exp.isUninitialized()) { - return Undefined(); + return Undefined::Builder(); } - exp = exp.simplify(context, angleUnit); + exp = exp.simplify(context, complexFormat, angleUnit); /* simplify might have been interrupted, in which case the resulting * expression is uninitialized, so we need to check that. */ if (exp.isUninitialized()) { @@ -338,15 +400,92 @@ Expression Expression::ParseAndSimplify(const char * text, Context & context, Pr return exp; } -Expression Expression::simplify(Context & context, Preferences::AngleUnit angleUnit) { +void Expression::ParseAndSimplifyAndApproximate(const char * text, Expression * simplifiedExpression, Expression * approximateExpression, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) { + assert(simplifiedExpression); + Expression exp = Parse(text); + if (exp.isUninitialized()) { + *simplifiedExpression = Undefined::Builder(); + *approximateExpression = Undefined::Builder(); + return; + } + exp.simplifyAndApproximate(simplifiedExpression, approximateExpression, context, complexFormat, angleUnit); + /* simplify might have been interrupted, in which case the resulting + * expression is uninitialized, so we need to check that. */ + if (simplifiedExpression->isUninitialized()) { + *simplifiedExpression = Parse(text); + if (approximateExpression) { + *approximateExpression = simplifiedExpression->approximate(context, complexFormat, angleUnit); + } + } +} + +Expression Expression::simplify(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) { sSimplificationHasBeenInterrupted = false; - Expression e = deepReduce(context, angleUnit, ExpressionNode::ReductionTarget::User); + Expression e = deepReduce(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::System); if (!sSimplificationHasBeenInterrupted) { - e = e.deepBeautify(context, angleUnit); + e = e.deepBeautify(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::System); } return sSimplificationHasBeenInterrupted ? Expression() : e; } +void makePositive(Expression * e, bool * isNegative) { + if (e->type() == ExpressionNode::Type::Opposite) { + *isNegative = true; + *e = e->childAtIndex(0); + } +} + +void Expression::simplifyAndApproximate(Expression * simplifiedExpression, Expression * approximateExpression, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) { + assert(simplifiedExpression); + sSimplificationHasBeenInterrupted = false; + // Step 1: we reduce the expression + Expression e = clone().deepReduce(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::User); + if (sSimplificationHasBeenInterrupted) { + sSimplificationHasBeenInterrupted = false; + e = deepReduce(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::System); + } + *simplifiedExpression = Expression(); + if (!sSimplificationHasBeenInterrupted) { + /* Case 1: the reduced expression is ComplexCartesian or pure real, we can + * take into account the complex format to display a+i*b or r*e^(i*th) */ + if (e.type() == ExpressionNode::Type::ComplexCartesian || e.isReal(context)) { + ComplexCartesian ecomplex = e.type() == ExpressionNode::Type::ComplexCartesian ? static_cast(e) : ComplexCartesian::Builder(e, Rational::Builder(0)); + if (approximateExpression) { + /* Step 2: Approximation + * We compute the approximate expression from the Cartesian form to avoid + * unprecision. For example, if the result is the ComplexCartesian(a,b), + * the final expression is goind to be sqrt(a^2+b^2)*exp(i*arctan(b/a)... + * in Polar ComplexFormat. If we approximate this expression instead of + * ComplexCartesian(a,b), we are going to loose precision on the resulting + * complex.*/ + // Clone the ComplexCartesian to use it to compute the approximation + ComplexCartesian ecomplexClone = ecomplex.clone().convert(); + // To minimize the error on the approximation, we reduce the number of nodes in the expression by beautifying + ecomplexClone.real().deepBeautify(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::User); + ecomplexClone.imag().deepBeautify(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::User); + *approximateExpression = ecomplexClone.approximate(context, complexFormat, angleUnit); + } + // Step 3: create the simplied expression with the required complex format + Expression ra = complexFormat == Preferences::ComplexFormat::Polar ? ecomplex.clone().convert().norm(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::User).shallowReduce(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::User) : ecomplex.real(); + Expression tb = complexFormat == Preferences::ComplexFormat::Polar ? ecomplex.argument(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::User).shallowReduce(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::User) : ecomplex.imag(); + ra = ra.deepBeautify(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::User); + tb = tb.deepBeautify(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::User); + bool raIsNegative = false; + bool tbIsNegative = false; + makePositive(&ra, &raIsNegative); + makePositive(&tb, &tbIsNegative); + *simplifiedExpression = CreateComplexExpression(ra, tb, complexFormat, ra.isUndefined() || tb.isUndefined(), IsZero(ra), IsOne(ra), IsZero(tb), IsOne(tb), raIsNegative, tbIsNegative); + } else { + /* Case 2: The reduced expression has a complex component that could not + * be bubbled up. */ + *simplifiedExpression = e.deepBeautify(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::User); + if (approximateExpression) { + *approximateExpression = simplifiedExpression->approximate(context, complexFormat, angleUnit); + } + } + } +} + Expression Expression::ExpressionWithoutSymbols(Expression e, Context & context) { if (e.isUninitialized()) { return e; @@ -378,12 +517,22 @@ Expression Expression::ExpressionWithoutSymbols(Expression e, Context & context) return e; } -Expression Expression::reduce(Context & context, Preferences::AngleUnit angleUnit) { - sSimplificationHasBeenInterrupted = false; - return deepReduce(context, angleUnit, ExpressionNode::ReductionTarget::TopDownComputation); +Expression Expression::radianToDegree() { + // e*180/Pi + return Multiplication::Builder(*this, Rational::Builder(180), Power::Builder(Constant::Builder(Ion::Charset::SmallPi), Rational::Builder(-1))); } -Expression Expression::deepReduce(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { +Expression Expression::degreeToRadian() { + // e*Pi/180 + return Multiplication::Builder(*this, Rational::Builder(1, 180), Constant::Builder(Ion::Charset::SmallPi)); +} + +Expression Expression::reduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) { + sSimplificationHasBeenInterrupted = false; + return deepReduce(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::System); +} + +Expression Expression::deepReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { #if MATRIX_EXACT_REDUCING #else if (IsMatrix(*this, context, true)) { @@ -392,91 +541,175 @@ Expression Expression::deepReduce(Context & context, Preferences::AngleUnit angl } #endif - deepReduceChildren(context, angleUnit, target); + deepReduceChildren(context, complexFormat, angleUnit, target); if (sSimplificationHasBeenInterrupted) { return *this; } - return shallowReduce(context, angleUnit, target); + return shallowReduce(context, complexFormat, angleUnit, target); } -Expression Expression::deepBeautify(Context & context, Preferences::AngleUnit angleUnit) { - Expression e = shallowBeautify(context, angleUnit); +Expression Expression::deepBeautify(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { + Expression e = shallowBeautify(context, complexFormat, angleUnit, target); int nbChildren = e.numberOfChildren(); for (int i = 0; i < nbChildren; i++) { - e.childAtIndex(i).deepBeautify(context, angleUnit); + e.childAtIndex(i).deepBeautify(context, complexFormat, angleUnit, target); } return e; } -Expression Expression::setSign(ExpressionNode::Sign s, Context & context, Preferences::AngleUnit angleUnit) { - return node()->setSign(s, context, angleUnit); +Expression Expression::setSign(ExpressionNode::Sign s, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { + assert(s == ExpressionNode::Sign::Positive || s == ExpressionNode::Sign::Negative); + return node()->setSign(s, context, complexFormat, angleUnit, target); } /* Evaluation */ template -Expression Expression::approximate(Context& context, Preferences::AngleUnit angleUnit, Preferences::ComplexFormat complexFormat) const { - return isUninitialized() ? Undefined() : approximateToEvaluation(context, angleUnit).complexToExpression(complexFormat); +Expression Expression::approximate(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { + return isUninitialized() ? Undefined::Builder() : approximateToEvaluation(context, complexFormat, angleUnit).complexToExpression(complexFormat); } template -U Expression::approximateToScalar(Context& context, Preferences::AngleUnit angleUnit) const { - return approximateToEvaluation(context, angleUnit).toScalar(); +U Expression::approximateToScalar(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { + return approximateToEvaluation(context, complexFormat, angleUnit).toScalar(); } template -U Expression::approximateToScalar(const char * text, Context& context, Preferences::AngleUnit angleUnit) { - Expression exp = ParseAndSimplify(text, context, angleUnit); - return exp.approximateToScalar(context, angleUnit); +U Expression::ApproximateToScalar(const char * text, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) { + Expression exp = ParseAndSimplify(text, context, complexFormat, angleUnit); + assert(!exp.isUninitialized()); + return exp.approximateToScalar(context, complexFormat, angleUnit); } template -U Expression::approximateWithValueForSymbol(const char * symbol, U x, Context & context, Preferences::AngleUnit angleUnit) const { +U Expression::approximateWithValueForSymbol(const char * symbol, U x, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { VariableContext variableContext = VariableContext(symbol, &context); variableContext.setApproximationForVariable(x); - return approximateToScalar(variableContext, angleUnit); + return approximateToScalar(variableContext, complexFormat, angleUnit); } template -U Expression::epsilon() { +U Expression::Epsilon() { static U epsilon = sizeof(U) == sizeof(double) ? 1E-15 : 1E-7f; return epsilon; } -/* Expression roots/extrema solver*/ +/* Builder */ -typename Expression::Coordinate2D Expression::nextMinimum(const char * symbol, double start, double step, double max, Context & context, Preferences::AngleUnit angleUnit) const { - return nextMinimumOfExpression(symbol, start, step, max, [](const char * symbol, double x, Context & context, Preferences::AngleUnit angleUnit, const Expression expression0, const Expression expression1 = Expression()) { - return expression0.approximateWithValueForSymbol(symbol, x, context, angleUnit); - }, context, angleUnit); +bool Expression::IsZero(const Expression e) { + return e.type() == ExpressionNode::Type::Rational && static_cast(e).isZero(); +} +bool Expression::IsOne(const Expression e) { + return e.type() == ExpressionNode::Type::Rational && static_cast(e).isOne(); +} +bool Expression::IsMinusOne(const Expression e) { + return e.type() == ExpressionNode::Type::Rational && static_cast(e).isMinusOne(); } -typename Expression::Coordinate2D Expression::nextMaximum(const char * symbol, double start, double step, double max, Context & context, Preferences::AngleUnit angleUnit) const { - Coordinate2D minimumOfOpposite = nextMinimumOfExpression(symbol, start, step, max, [](const char * symbol, double x, Context & context, Preferences::AngleUnit angleUnit, const Expression expression0, const Expression expression1 = Expression()) { - return -expression0.approximateWithValueForSymbol(symbol, x, context, angleUnit); - }, context, angleUnit); +Expression Expression::CreateComplexExpression(Expression ra, Expression tb, Preferences::ComplexFormat complexFormat, bool undefined, bool isZeroRa, bool isOneRa, bool isZeroTb, bool isOneTb, bool isNegativeRa, bool isNegativeTb) { + if (undefined) { + return Undefined::Builder(); + } + switch (complexFormat) { + case Preferences::ComplexFormat::Real: + case Preferences::ComplexFormat::Cartesian: + { + Expression real; + Expression imag; + if (!isZeroRa || isZeroTb) { + if (isNegativeRa) { + real = Opposite::Builder(ra); + } else { + real = ra; + } + } + if (!isZeroTb) { + if (isOneTb) { + imag = Constant::Builder(Ion::Charset::IComplex); + } else { + imag = Multiplication::Builder(tb , Constant::Builder(Ion::Charset::IComplex)); + } + } + if (imag.isUninitialized()) { + return real; + } else if (real.isUninitialized()) { + if (isNegativeTb) { + return Opposite::Builder(imag); + } else { + return imag; + } + } else if (isNegativeTb) { + return Subtraction::Builder(real, imag); + } else { + return Addition::Builder(real, imag); + } + } + default: + { + assert(complexFormat == Preferences::ComplexFormat::Polar); + Expression norm; + Expression exp; + if (!isOneRa || isZeroTb) { + assert(!isNegativeRa); // norm cannot be negative + norm = ra; + } + if (!isZeroRa && !isZeroTb) { + Expression arg; + if (isOneTb) { + arg = Constant::Builder(Ion::Charset::IComplex); + } else { + arg = Multiplication::Builder(tb, Constant::Builder(Ion::Charset::IComplex)); + } + if (isNegativeTb) { + arg = Opposite::Builder(arg); + } + exp = Power::Builder(Constant::Builder(Ion::Charset::Exponential), arg); + } + if (exp.isUninitialized()) { + return norm; + } else if (norm.isUninitialized()) { + return exp; + } else { + return Multiplication::Builder(norm, exp); + } + } + } +} + +/* Expression roots/extrema solver*/ + +typename Expression::Coordinate2D Expression::nextMinimum(const char * symbol, double start, double step, double max, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { + return nextMinimumOfExpression(symbol, start, step, max, [](const char * symbol, double x, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const Expression expression0, const Expression expression1 = Expression()) { + return expression0.approximateWithValueForSymbol(symbol, x, context, complexFormat, angleUnit); + }, context, complexFormat, angleUnit); +} + +typename Expression::Coordinate2D Expression::nextMaximum(const char * symbol, double start, double step, double max, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { + Coordinate2D minimumOfOpposite = nextMinimumOfExpression(symbol, start, step, max, [](const char * symbol, double x, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const Expression expression0, const Expression expression1 = Expression()) { + return -expression0.approximateWithValueForSymbol(symbol, x, context, complexFormat, angleUnit); + }, context, complexFormat, angleUnit); return {.abscissa = minimumOfOpposite.abscissa, .value = -minimumOfOpposite.value}; } -double Expression::nextRoot(const char * symbol, double start, double step, double max, Context & context, Preferences::AngleUnit angleUnit) const { - return nextIntersectionWithExpression(symbol, start, step, max, [](const char * symbol, double x, Context & context, Preferences::AngleUnit angleUnit, const Expression expression0, const Expression expression1 = Expression()) { - return expression0.approximateWithValueForSymbol(symbol, x, context, angleUnit); - }, context, angleUnit, nullptr); +double Expression::nextRoot(const char * symbol, double start, double step, double max, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { + return nextIntersectionWithExpression(symbol, start, step, max, [](const char * symbol, double x, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const Expression expression0, const Expression expression1 = Expression()) { + return expression0.approximateWithValueForSymbol(symbol, x, context, complexFormat, angleUnit); + }, context, complexFormat, angleUnit, nullptr); } -typename Expression::Coordinate2D Expression::nextIntersection(const char * symbol, double start, double step, double max, Poincare::Context & context, Preferences::AngleUnit angleUnit, const Expression expression) const { - double resultAbscissa = nextIntersectionWithExpression(symbol, start, step, max, [](const char * symbol, double x, Context & context, Preferences::AngleUnit angleUnit, const Expression expression0, const Expression expression1) { - return expression0.approximateWithValueForSymbol(symbol, x, context, angleUnit)-expression1.approximateWithValueForSymbol(symbol, x, context, angleUnit); - }, context, angleUnit, expression); - typename Expression::Coordinate2D result = {.abscissa = resultAbscissa, .value = approximateWithValueForSymbol(symbol, resultAbscissa, context, angleUnit)}; +typename Expression::Coordinate2D Expression::nextIntersection(const char * symbol, double start, double step, double max, Poincare::Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const Expression expression) const { + double resultAbscissa = nextIntersectionWithExpression(symbol, start, step, max, [](const char * symbol, double x, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const Expression expression0, const Expression expression1) { + return expression0.approximateWithValueForSymbol(symbol, x, context, complexFormat, angleUnit)-expression1.approximateWithValueForSymbol(symbol, x, context, complexFormat, angleUnit); + }, context, complexFormat, angleUnit, expression); + typename Expression::Coordinate2D result = {.abscissa = resultAbscissa, .value = approximateWithValueForSymbol(symbol, resultAbscissa, context, complexFormat, angleUnit)}; if (std::fabs(result.value) < step*k_solverPrecision) { result.value = 0.0; } return result; } -typename Expression::Coordinate2D Expression::nextMinimumOfExpression(const char * symbol, double start, double step, double max, EvaluationAtAbscissa evaluate, Context & context, Preferences::AngleUnit angleUnit, const Expression expression, bool lookForRootMinimum) const { +typename Expression::Coordinate2D Expression::nextMinimumOfExpression(const char * symbol, double start, double step, double max, EvaluationAtAbscissa evaluate, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const Expression expression, bool lookForRootMinimum) const { Coordinate2D result = {.abscissa = NAN, .value = NAN}; if (start == max || step == 0.0) { return result; @@ -485,13 +718,13 @@ typename Expression::Coordinate2D Expression::nextMinimumOfExpression(const char double x = start; bool endCondition = false; do { - bracketMinimum(symbol, x, step, max, bracket, evaluate, context, angleUnit, expression); - result = brentMinimum(symbol, bracket[0], bracket[2], evaluate, context, angleUnit, expression); + bracketMinimum(symbol, x, step, max, bracket, evaluate, context, complexFormat, angleUnit, expression); + result = brentMinimum(symbol, bracket[0], bracket[2], evaluate, context, complexFormat, angleUnit, expression); x = bracket[1]; // Because of float approximation, exact zero is never reached if (std::fabs(result.abscissa) < std::fabs(step)*k_solverPrecision) { result.abscissa = 0; - result.value = evaluate(symbol, 0, context, angleUnit, *this, expression); + result.value = evaluate(symbol, 0, context, complexFormat, angleUnit, *this, expression); } /* Ignore extremum whose value is undefined or too big because they are * really unlikely to be local extremum. */ @@ -513,13 +746,13 @@ typename Expression::Coordinate2D Expression::nextMinimumOfExpression(const char return result; } -void Expression::bracketMinimum(const char * symbol, double start, double step, double max, double result[3], EvaluationAtAbscissa evaluate, Context & context, Preferences::AngleUnit angleUnit, const Expression expression) const { +void Expression::bracketMinimum(const char * symbol, double start, double step, double max, double result[3], EvaluationAtAbscissa evaluate, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const Expression expression) const { Coordinate2D p[3]; - p[0] = {.abscissa = start, .value = evaluate(symbol, start, context, angleUnit, *this, expression)}; - p[1] = {.abscissa = start+step, .value = evaluate(symbol, start+step, context, angleUnit, *this, expression)}; + p[0] = {.abscissa = start, .value = evaluate(symbol, start, context, complexFormat, angleUnit, *this, expression)}; + p[1] = {.abscissa = start+step, .value = evaluate(symbol, start+step, context, complexFormat, angleUnit, *this, expression)}; double x = start+2.0*step; while (step > 0.0 ? x <= max : x >= max) { - p[2] = {.abscissa = x, .value = evaluate(symbol, x, context, angleUnit, *this, expression)}; + p[2] = {.abscissa = x, .value = evaluate(symbol, x, context, complexFormat, angleUnit, *this, expression)}; if ((p[0].value > p[1].value || std::isnan(p[0].value)) && (p[2].value > p[1].value || std::isnan(p[2].value)) && (!std::isnan(p[0].value) || !std::isnan(p[2].value))) { result[0] = p[0].abscissa; result[1] = p[1].abscissa; @@ -538,11 +771,11 @@ void Expression::bracketMinimum(const char * symbol, double start, double step, result[2] = NAN; } -typename Expression::Coordinate2D Expression::brentMinimum(const char * symbol, double ax, double bx, EvaluationAtAbscissa evaluate, Context & context, Preferences::AngleUnit angleUnit, const Expression expression) const { +typename Expression::Coordinate2D Expression::brentMinimum(const char * symbol, double ax, double bx, EvaluationAtAbscissa evaluate, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const Expression expression) const { /* Bibliography: R. P. Brent, Algorithms for finding zeros and extrema of * functions without calculating derivatives */ if (ax > bx) { - return brentMinimum(symbol, bx, ax, evaluate, context, angleUnit, expression); + return brentMinimum(symbol, bx, ax, evaluate, context, complexFormat, angleUnit, expression); } double e = 0.0; double a = ax; @@ -550,7 +783,7 @@ typename Expression::Coordinate2D Expression::brentMinimum(const char * symbol, double x = a+k_goldenRatio*(b-a); double v = x; double w = x; - double fx = evaluate(symbol, x, context, angleUnit, *this, expression); + double fx = evaluate(symbol, x, context, complexFormat, angleUnit, *this, expression); double fw = fx; double fv = fw; @@ -562,10 +795,10 @@ typename Expression::Coordinate2D Expression::brentMinimum(const char * symbol, double tol1 = k_sqrtEps*std::fabs(x)+1E-10; double tol2 = 2.0*tol1; if (std::fabs(x-m) <= tol2-0.5*(b-a)) { - double middleFax = evaluate(symbol, (x+a)/2.0, context, angleUnit, *this, expression); - double middleFbx = evaluate(symbol, (x+b)/2.0, context, angleUnit, *this, expression); - double fa = evaluate(symbol, a, context, angleUnit, *this, expression); - double fb = evaluate(symbol, b, context, angleUnit, *this, expression); + double middleFax = evaluate(symbol, (x+a)/2.0, context, complexFormat, angleUnit, *this, expression); + double middleFbx = evaluate(symbol, (x+b)/2.0, context, complexFormat, angleUnit, *this, expression); + double fa = evaluate(symbol, a, context, complexFormat, angleUnit, *this, expression); + double fb = evaluate(symbol, b, context, complexFormat, angleUnit, *this, expression); if (middleFax-fa <= k_sqrtEps && fx-middleFax <= k_sqrtEps && fx-middleFbx <= k_sqrtEps && middleFbx-fb <= k_sqrtEps) { Coordinate2D result = {.abscissa = x, .value = fx}; return result; @@ -598,7 +831,7 @@ typename Expression::Coordinate2D Expression::brentMinimum(const char * symbol, d = k_goldenRatio*e; } u = x + (std::fabs(d) >= tol1 ? d : (d>0 ? tol1 : -tol1)); - fu = evaluate(symbol, u, context, angleUnit, *this, expression); + fu = evaluate(symbol, u, context, complexFormat, angleUnit, *this, expression); if (fu <= fx) { if (u 0.0 ? x <= max : x >= max)); double extremumMax = std::isnan(result) ? max : result; Coordinate2D resultExtremum[2] = { - nextMinimumOfExpression(symbol, start, step, extremumMax, [](const char * symbol, double x, Context & context, Preferences::AngleUnit angleUnit, const Expression expression0, const Expression expression1) { + nextMinimumOfExpression(symbol, start, step, extremumMax, [](const char * symbol, double x, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const Expression expression0, const Expression expression1) { if (expression1.isUninitialized()) { - return expression0.approximateWithValueForSymbol(symbol, x, context, angleUnit); + return expression0.approximateWithValueForSymbol(symbol, x, context, complexFormat, angleUnit); } else { - return expression0.approximateWithValueForSymbol(symbol, x, context, angleUnit)-expression1.approximateWithValueForSymbol(symbol, x, context, angleUnit); + return expression0.approximateWithValueForSymbol(symbol, x, context, complexFormat, angleUnit)-expression1.approximateWithValueForSymbol(symbol, x, context, complexFormat, angleUnit); } - }, context, angleUnit, expression, true), - nextMinimumOfExpression(symbol, start, step, extremumMax, [](const char * symbol, double x, Context & context, Preferences::AngleUnit angleUnit, const Expression expression0, const Expression expression1) { + }, context, complexFormat, angleUnit, expression, true), + nextMinimumOfExpression(symbol, start, step, extremumMax, [](const char * symbol, double x, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const Expression expression0, const Expression expression1) { if (expression1.isUninitialized()) { - return -expression0.approximateWithValueForSymbol(symbol, x, context, angleUnit); + return -expression0.approximateWithValueForSymbol(symbol, x, context, complexFormat, angleUnit); } else { - return expression1.approximateWithValueForSymbol(symbol, x, context, angleUnit)-expression0.approximateWithValueForSymbol(symbol, x, context, angleUnit); + return expression1.approximateWithValueForSymbol(symbol, x, context, complexFormat, angleUnit)-expression0.approximateWithValueForSymbol(symbol, x, context, complexFormat, angleUnit); } - }, context, angleUnit, expression, true)}; + }, context, complexFormat, angleUnit, expression, true)}; for (int i = 0; i < 2; i++) { if (!std::isnan(resultExtremum[i].abscissa) && (std::isnan(result) || std::fabs(result - start) > std::fabs(resultExtremum[i].abscissa - start))) { result = resultExtremum[i].abscissa; @@ -673,12 +906,12 @@ double Expression::nextIntersectionWithExpression(const char * symbol, double st return result; } -void Expression::bracketRoot(const char * symbol, double start, double step, double max, double result[2], EvaluationAtAbscissa evaluation, Context & context, Preferences::AngleUnit angleUnit, const Expression expression) const { +void Expression::bracketRoot(const char * symbol, double start, double step, double max, double result[2], EvaluationAtAbscissa evaluation, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const Expression expression) const { double a = start; double b = start+step; while (step > 0.0 ? b <= max : b >= max) { - double fa = evaluation(symbol, a, context, angleUnit, *this, expression); - double fb = evaluation(symbol, b, context, angleUnit,* this, expression); + double fa = evaluation(symbol, a, context, complexFormat, angleUnit, *this, expression); + double fb = evaluation(symbol, b, context, complexFormat, angleUnit,* this, expression); if (fa*fb <= 0) { result[0] = a; result[1] = b; @@ -691,17 +924,17 @@ void Expression::bracketRoot(const char * symbol, double start, double step, dou result[1] = NAN; } -double Expression::brentRoot(const char * symbol, double ax, double bx, double precision, EvaluationAtAbscissa evaluation, Context & context, Preferences::AngleUnit angleUnit, const Expression expression) const { +double Expression::brentRoot(const char * symbol, double ax, double bx, double precision, EvaluationAtAbscissa evaluation, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, const Expression expression) const { if (ax > bx) { - return brentRoot(symbol, bx, ax, precision, evaluation, context, angleUnit, expression); + return brentRoot(symbol, bx, ax, precision, evaluation, context, complexFormat, angleUnit, expression); } double a = ax; double b = bx; double c = bx; double d = b-a; double e = b-a; - double fa = evaluation(symbol, a, context, angleUnit, *this, expression); - double fb = evaluation(symbol, b, context, angleUnit, *this, expression); + double fa = evaluation(symbol, a, context, complexFormat, angleUnit, *this, expression); + double fb = evaluation(symbol, b, context, complexFormat, angleUnit, *this, expression); double fc = fb; for (int i = 0; i < 100; i++) { if ((fb > 0.0 && fc > 0.0) || (fb < 0.0 && fc < 0.0)) { @@ -721,7 +954,7 @@ double Expression::brentRoot(const char * symbol, double ax, double bx, double p double tol1 = 2.0*DBL_EPSILON*std::fabs(b)+0.5*precision; double xm = 0.5*(c-b); if (std::fabs(xm) <= tol1 || fb == 0.0) { - double fbcMiddle = evaluation(symbol, 0.5*(b+c), context, angleUnit, *this, expression); + double fbcMiddle = evaluation(symbol, 0.5*(b+c), context, complexFormat, angleUnit, *this, expression); double isContinuous = (fb <= fbcMiddle && fbcMiddle <= fc) || (fc <= fbcMiddle && fbcMiddle <= fb); if (isContinuous) { return b; @@ -759,27 +992,27 @@ double Expression::brentRoot(const char * symbol, double ax, double bx, double p } else { b += xm > 0.0 ? tol1 : tol1; } - fb = evaluation(symbol, b, context, angleUnit, *this, expression); + fb = evaluation(symbol, b, context, complexFormat, angleUnit, *this, expression); } return NAN; } -template float Expression::epsilon(); -template double Expression::epsilon(); +template float Expression::Epsilon(); +template double Expression::Epsilon(); -template Expression Expression::approximate(Context& context, Preferences::AngleUnit angleUnit, Preferences::ComplexFormat complexFormat) const; -template Expression Expression::approximate(Context& context, Preferences::AngleUnit angleUnit, Preferences::ComplexFormat complexFormat) const; +template Expression Expression::approximate(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; +template Expression Expression::approximate(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; -template float Expression::approximateToScalar(Context& context, Preferences::AngleUnit angleUnit) const; -template double Expression::approximateToScalar(Context& context, Preferences::AngleUnit angleUnit) const; +template float Expression::approximateToScalar(Context& context, Preferences::ComplexFormat, Preferences::AngleUnit angleUnit) const; +template double Expression::approximateToScalar(Context& context, Preferences::ComplexFormat, Preferences::AngleUnit angleUnit) const; -template float Expression::approximateToScalar(const char * text, Context& context, Preferences::AngleUnit angleUnit); -template double Expression::approximateToScalar(const char * text, Context& context, Preferences::AngleUnit angleUnit); +template float Expression::ApproximateToScalar(const char * text, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit); +template double Expression::ApproximateToScalar(const char * text, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit); -template Evaluation Expression::approximateToEvaluation(Context& context, Preferences::AngleUnit angleUnit) const; -template Evaluation Expression::approximateToEvaluation(Context& context, Preferences::AngleUnit angleUnit) const; +template Evaluation Expression::approximateToEvaluation(Context& context, Preferences::ComplexFormat, Preferences::AngleUnit angleUnit) const; +template Evaluation Expression::approximateToEvaluation(Context& context, Preferences::ComplexFormat, Preferences::AngleUnit angleUnit) const; -template float Expression::approximateWithValueForSymbol(const char * symbol, float x, Context & context, Preferences::AngleUnit angleUnit) const; -template double Expression::approximateWithValueForSymbol(const char * symbol, double x, Context & context, Preferences::AngleUnit angleUnit) const; +template float Expression::approximateWithValueForSymbol(const char * symbol, float x, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; +template double Expression::approximateWithValueForSymbol(const char * symbol, double x, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; } diff --git a/poincare/src/expression_node.cpp b/poincare/src/expression_node.cpp index 446b7caa5..b87fbf347 100644 --- a/poincare/src/expression_node.cpp +++ b/poincare/src/expression_node.cpp @@ -1,6 +1,15 @@ #include #include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include namespace Poincare { @@ -13,7 +22,7 @@ Expression ExpressionNode::replaceUnknown(const Symbol & symbol) { return Expression(this).defaultReplaceUnknown(symbol); } -Expression ExpressionNode::setSign(Sign s, Context & context, Preferences::AngleUnit angleUnit) { +Expression ExpressionNode::setSign(Sign s, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) { assert(false); return Expression(); } @@ -63,30 +72,30 @@ float ExpressionNode::characteristicXRange(Context & context, Preferences::Angle return range; } -int ExpressionNode::SimplificationOrder(const ExpressionNode * e1, const ExpressionNode * e2, bool canBeInterrupted) { +int ExpressionNode::SimplificationOrder(const ExpressionNode * e1, const ExpressionNode * e2, bool ascending, bool canBeInterrupted) { if (e1->type() > e2->type()) { - if (canBeInterrupted && Expression::shouldStopProcessing()) { + if (canBeInterrupted && Expression::ShouldStopProcessing()) { return 1; } - return -(e2->simplificationOrderGreaterType(e1, canBeInterrupted)); + return -(e2->simplificationOrderGreaterType(e1, ascending, canBeInterrupted)); } else if (e1->type() == e2->type()) { - return e1->simplificationOrderSameType(e2, canBeInterrupted); + return e1->simplificationOrderSameType(e2, ascending, canBeInterrupted); } else { - if (canBeInterrupted && Expression::shouldStopProcessing()) { + if (canBeInterrupted && Expression::ShouldStopProcessing()) { return -1; } - return e1->simplificationOrderGreaterType(e2, canBeInterrupted); + return e1->simplificationOrderGreaterType(e2, ascending, canBeInterrupted); } } -int ExpressionNode::simplificationOrderSameType(const ExpressionNode * e, bool canBeInterrupted) const { +int ExpressionNode::simplificationOrderSameType(const ExpressionNode * e, bool ascending, bool canBeInterrupted) const { int index = 0; for (ExpressionNode * c : children()) { // The NULL node is the least node type. if (e->numberOfChildren() <= index) { return 1; } - int childIOrder = SimplificationOrder(c, e->childAtIndex(index), canBeInterrupted); + int childIOrder = SimplificationOrder(c, e->childAtIndex(index), ascending, canBeInterrupted); if (childIOrder != 0) { return childIOrder; } @@ -94,21 +103,21 @@ int ExpressionNode::simplificationOrderSameType(const ExpressionNode * e, bool c } // The NULL node is the least node type. if (e->numberOfChildren() > numberOfChildren()) { - return -1; + return ascending ? -1 : 1; } return 0; } -void ExpressionNode::deepReduceChildren(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { - Expression(this).defaultDeepReduceChildren(context, angleUnit, target); +void ExpressionNode::deepReduceChildren(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { + Expression(this).defaultDeepReduceChildren(context, complexFormat, angleUnit, target); } -Expression ExpressionNode::shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) { - return Expression(this).defaultShallowReduce(context, angleUnit); +Expression ExpressionNode::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) { + return Expression(this).defaultShallowReduce(); } -Expression ExpressionNode::shallowBeautify(Context & context, Preferences::AngleUnit angleUnit) { - return Expression(this).defaultShallowBeautify(context, angleUnit); +Expression ExpressionNode::shallowBeautify(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) { + return Expression(this).defaultShallowBeautify(); } bool ExpressionNode::isOfType(Type * types, int length) const { @@ -124,7 +133,7 @@ void ExpressionNode::setChildrenInPlace(Expression other) { Expression(this).defaultSetChildrenInPlace(other); } -Expression ExpressionNode::denominator(Context & context, Preferences::AngleUnit angleUnit) const { +Expression ExpressionNode::denominator(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { return Expression(); } diff --git a/poincare/src/factor.cpp b/poincare/src/factor.cpp index 1cb6078b3..910ce935c 100644 --- a/poincare/src/factor.cpp +++ b/poincare/src/factor.cpp @@ -26,14 +26,15 @@ int FactorNode::serialize(char * buffer, int bufferSize, Preferences::PrintFloat return SerializationHelper::Prefix(this, buffer, bufferSize, floatDisplayMode, numberOfSignificantDigits, Factor::s_functionHelper.name()); } -Expression FactorNode::shallowBeautify(Context & context, Preferences::AngleUnit angleUnit) { - return Factor(this).shallowBeautify(context, angleUnit); +Expression FactorNode::shallowBeautify(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) { + return Factor(this).shallowBeautify(context, complexFormat, angleUnit); } -Expression Factor::shallowBeautify(Context & context, Preferences::AngleUnit angleUnit) { + +Expression Factor::shallowBeautify(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) { Expression c = childAtIndex(0); if (c.type() != ExpressionNode::Type::Rational) { - Expression result = Undefined(); + Expression result = Undefined::Builder(); replaceWithInPlace(result); return result; } @@ -42,38 +43,38 @@ Expression Factor::shallowBeautify(Context & context, Preferences::AngleUnit ang replaceWithInPlace(r); return r; } - Multiplication numeratorDecomp = createMultiplicationOfIntegerPrimeDecomposition(r.unsignedIntegerNumerator(), context, angleUnit); + Multiplication numeratorDecomp = createMultiplicationOfIntegerPrimeDecomposition(r.unsignedIntegerNumerator(), context, complexFormat, angleUnit); if (numeratorDecomp.numberOfChildren() == 0) { - Expression result = Undefined(); + Expression result = Undefined::Builder(); replaceWithInPlace(result); return result; } Expression result = numeratorDecomp.squashUnaryHierarchyInPlace(); if (!r.integerDenominator().isOne()) { - Multiplication denominatorDecomp = createMultiplicationOfIntegerPrimeDecomposition(r.integerDenominator(), context, angleUnit); + Multiplication denominatorDecomp = createMultiplicationOfIntegerPrimeDecomposition(r.integerDenominator(), context, complexFormat, angleUnit); if (denominatorDecomp.numberOfChildren() == 0) { - Expression result = Undefined(); + Expression result = Undefined::Builder(); replaceWithInPlace(result); return result; } - result = Division(result, denominatorDecomp.squashUnaryHierarchyInPlace()); + result = Division::Builder(result, denominatorDecomp.squashUnaryHierarchyInPlace()); } if (r.sign() == ExpressionNode::Sign::Negative) { - result = Opposite(result); + result = Opposite::Builder(result); } replaceWithInPlace(result); return result; } -Multiplication Factor::createMultiplicationOfIntegerPrimeDecomposition(Integer i, Context & context, Preferences::AngleUnit angleUnit) const { +Multiplication Factor::createMultiplicationOfIntegerPrimeDecomposition(Integer i, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { assert(!i.isZero()); assert(!i.isNegative()); - Multiplication m; + Multiplication m = Multiplication::Builder(); Integer factors[Arithmetic::k_maxNumberOfPrimeFactors]; Integer coefficients[Arithmetic::k_maxNumberOfPrimeFactors]; int numberOfPrimeFactors = Arithmetic::PrimeFactorization(i, factors, coefficients, Arithmetic::k_maxNumberOfPrimeFactors); if (numberOfPrimeFactors == 0) { - m.addChildAtIndexInPlace(Rational(i), 0, 0); + m.addChildAtIndexInPlace(Rational::Builder(i), 0, 0); return m; } if (numberOfPrimeFactors < 0) { @@ -81,9 +82,9 @@ Multiplication Factor::createMultiplicationOfIntegerPrimeDecomposition(Integer i return m; } for (int index = 0; index < numberOfPrimeFactors; index++) { - Expression factor = Rational(factors[index]); + Expression factor = Rational::Builder(factors[index]); if (!coefficients[index].isOne()) { - factor = Power(factor, Rational(coefficients[index])); + factor = Power::Builder(factor, Rational::Builder(coefficients[index])); } m.addChildAtIndexInPlace(factor, m.numberOfChildren(), m.numberOfChildren()); } diff --git a/poincare/src/factorial.cpp b/poincare/src/factorial.cpp index f730a2bef..643d24001 100644 --- a/poincare/src/factorial.cpp +++ b/poincare/src/factorial.cpp @@ -12,10 +12,17 @@ namespace Poincare { +// Property + +Expression FactorialNode::setSign(Sign s, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) { + assert(s == Sign::Positive); + return Factorial(this); +} + // Layout bool FactorialNode::childNeedsParenthesis(const TreeNode * child) const { - if (static_cast(child)->isNumber() && static_cast(child)->sign() == Sign::Negative) { + if (static_cast(child)->isNumber() && Number(static_cast(child)).sign() == Sign::Negative) { return true; } if (static_cast(child)->type() == Type::Rational && !static_cast(child)->denominator().isOne()) { @@ -27,16 +34,16 @@ bool FactorialNode::childNeedsParenthesis(const TreeNode * child) const { // Simplification -Expression FactorialNode::shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) { - return Factorial(this).shallowReduce(context, angleUnit); +Expression FactorialNode::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) { + return Factorial(this).shallowReduce(); } -Expression FactorialNode::shallowBeautify(Context & context, Preferences::AngleUnit angleUnit) { - return Factorial(this).shallowBeautify(context, angleUnit); +Expression FactorialNode::shallowBeautify(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) { + return Factorial(this).shallowBeautify(); } template -Complex FactorialNode::computeOnComplex(const std::complex c, Preferences::AngleUnit angleUnit) { +Complex FactorialNode::computeOnComplex(const std::complex c, Preferences::ComplexFormat, Preferences::AngleUnit angleUnit) { T n = c.real(); if (c.imag() != 0 || std::isnan(n) || n != (int)n || n < 0) { return Complex::Undefined(); @@ -45,17 +52,17 @@ Complex FactorialNode::computeOnComplex(const std::complex c, Preferences: for (int i = 1; i <= (int)n; i++) { result *= (T)i; if (std::isinf(result)) { - return Complex(result); + return Complex::Builder(result); } } - return Complex(std::round(result)); + return Complex::Builder(std::round(result)); } Layout FactorialNode::createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const { - HorizontalLayout result; + HorizontalLayout result = HorizontalLayout::Builder(); result.addOrMergeChildAtIndex(childAtIndex(0)->createLayout(floatDisplayMode, numberOfSignificantDigits), 0, false); int childrenCount = result.numberOfChildren(); - result.addChildAtIndex(CharLayout('!'), childrenCount, childrenCount, nullptr); + result.addChildAtIndex(CharLayout::Builder('!'), childrenCount, childrenCount, nullptr); return result; } @@ -82,11 +89,10 @@ int FactorialNode::serialize(char * buffer, int bufferSize, Preferences::PrintFl return numberOfChar; } -Factorial::Factorial() : Expression(TreePool::sharedPool()->createTreeNode()) {} -Expression Factorial::shallowReduce(Context & context, Preferences::AngleUnit angleUnit) { +Expression Factorial::shallowReduce() { { - Expression e = Expression::defaultShallowReduce(context, angleUnit); + Expression e = Expression::defaultShallowReduce(); if (e.isUndefined()) { return e; } @@ -99,34 +105,34 @@ Expression Factorial::shallowReduce(Context & context, Preferences::AngleUnit an if (childAtIndex(0).type() == ExpressionNode::Type::Rational) { Rational r = childAtIndex(0).convert(); if (!r.integerDenominator().isOne() || r.sign() == ExpressionNode::Sign::Negative) { - Expression result = Undefined(); + Expression result = Undefined::Builder(); replaceWithInPlace(result); return result; } if (Integer(k_maxOperandValue).isLowerThan(r.unsignedIntegerNumerator())) { return *this; } - Rational fact = Rational(Integer::Factorial(r.unsignedIntegerNumerator())); + Rational fact = Rational::Builder(Integer::Factorial(r.unsignedIntegerNumerator())); assert(!fact.numeratorOrDenominatorIsInfinity()); // because fact < k_maxOperandValue! replaceWithInPlace(fact); return fact; } if (childAtIndex(0).type() == ExpressionNode::Type::Constant) { // e! = undef, i! = undef, pi! = undef - Expression result = Undefined(); + Expression result = Undefined::Builder(); replaceWithInPlace(result); return result; } return *this; } -Expression Factorial::shallowBeautify(Context & context, Preferences::AngleUnit angleUnit) { +Expression Factorial::shallowBeautify() { // +(a,b)! ->(+(a,b))! if (childAtIndex(0).type() == ExpressionNode::Type::Addition || childAtIndex(0).type() == ExpressionNode::Type::Multiplication || childAtIndex(0).type() == ExpressionNode::Type::Power) { - Expression result = Factorial(Parenthesis(childAtIndex(0))); + Expression result = Factorial::Builder(Parenthesis::Builder(childAtIndex(0))); replaceWithInPlace(result); return result; } diff --git a/poincare/src/float.cpp b/poincare/src/float.cpp index 0ac32507d..3b5f0c453 100644 --- a/poincare/src/float.cpp +++ b/poincare/src/float.cpp @@ -4,15 +4,20 @@ namespace Poincare { template -Expression FloatNode::setSign(Sign s, Context & context, Preferences::AngleUnit angleUnit) { +Expression FloatNode::setSign(Sign s, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) { + assert(s == ExpressionNode::Sign::Positive || s == ExpressionNode::Sign::Negative); + Sign currentSign = m_value < 0 ? Sign::Negative : Sign::Positive; Expression thisExpr = Number(this); - Expression result = Float(-m_value); + Expression result = Float::Builder(s == currentSign ? m_value : -m_value); thisExpr.replaceWithInPlace(result); return result; } template -int FloatNode::simplificationOrderSameType(const ExpressionNode * e, bool canBeInterrupted) const { +int FloatNode::simplificationOrderSameType(const ExpressionNode * e, bool ascending, bool canBeInterrupted) const { + if (!ascending) { + return e->simplificationOrderSameType(this, true, canBeInterrupted); + } assert(e->type() == ExpressionNode::Type::Float); const FloatNode * other = static_cast *>(e); if (value() < other->value()) { @@ -37,14 +42,17 @@ Layout FloatNode::createLayout(Preferences::PrintFloatMode floatDisplayMode, } template -Float::Float(T value) : Number(TreePool::sharedPool()->createTreeNode>()) { - node()->setFloat(value); +Float Float::Builder(T value) { + void * bufferNode = TreePool::sharedPool()->alloc(sizeof(FloatNode)); + FloatNode * node = new (bufferNode) FloatNode(value); + TreeHandle h = TreeHandle::BuildWithGhostChildren(node); + return static_cast(h); } template class FloatNode; template class FloatNode; -template Float::Float(float value); -template Float::Float(double value); +template Float Float::Builder(float value); +template Float Float::Builder(double value); } diff --git a/poincare/src/floor.cpp b/poincare/src/floor.cpp index 1fa2bc521..12cc0cd91 100644 --- a/poincare/src/floor.cpp +++ b/poincare/src/floor.cpp @@ -16,7 +16,7 @@ constexpr Expression::FunctionHelper Floor::s_functionHelper; int FloorNode::numberOfChildren() const { return Floor::s_functionHelper.numberOfChildren(); } Layout FloorNode::createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const { - return FloorLayout(childAtIndex(0)->createLayout(floatDisplayMode, numberOfSignificantDigits)); + return FloorLayout::Builder(childAtIndex(0)->createLayout(floatDisplayMode, numberOfSignificantDigits)); } int FloorNode::serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const { @@ -24,20 +24,21 @@ int FloorNode::serialize(char * buffer, int bufferSize, Preferences::PrintFloatM } template -Complex FloorNode::computeOnComplex(const std::complex c, Preferences::AngleUnit angleUnit) { +Complex FloorNode::computeOnComplex(const std::complex c, Preferences::ComplexFormat, Preferences::AngleUnit angleUnit) { if (c.imag() != 0) { return Complex::Undefined(); } - return Complex(std::floor(c.real())); + return Complex::Builder(std::floor(c.real())); } -Expression FloorNode::shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) { - return Floor(this).shallowReduce(context, angleUnit); +Expression FloorNode::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) { + return Floor(this).shallowReduce(); } -Expression Floor::shallowReduce(Context & context, Preferences::AngleUnit angleUnit) { + +Expression Floor::shallowReduce() { { - Expression e = Expression::defaultShallowReduce(context, angleUnit); + Expression e = Expression::defaultShallowReduce(); if (e.isUndefined()) { return e; } @@ -52,10 +53,10 @@ Expression Floor::shallowReduce(Context & context, Preferences::AngleUnit angleU Constant s = static_cast(c); Expression result; if (s.isPi()) { - result = Rational(3); + result = Rational::Builder(3); } if (s.isExponential()) { - result = Rational(2); + result = Rational::Builder(2); } if (!result.isUninitialized()) { replaceWithInPlace(result); @@ -68,8 +69,8 @@ Expression Floor::shallowReduce(Context & context, Preferences::AngleUnit angleU } Rational r = static_cast(c); IntegerDivision div = Integer::Division(r.signedIntegerNumerator(), r.integerDenominator()); - assert(!div.quotient.isInfinity()); - Expression result = Rational(div.quotient); + assert(!div.quotient.isOverflow()); + Expression result = Rational::Builder(div.quotient); replaceWithInPlace(result); return result; } diff --git a/poincare/src/frac_part.cpp b/poincare/src/frac_part.cpp index 3cfd30f64..e2877b965 100644 --- a/poincare/src/frac_part.cpp +++ b/poincare/src/frac_part.cpp @@ -19,21 +19,22 @@ int FracPartNode::serialize(char * buffer, int bufferSize, Preferences::PrintFlo return SerializationHelper::Prefix(this, buffer, bufferSize, floatDisplayMode, numberOfSignificantDigits, FracPart::s_functionHelper.name()); } -Expression FracPartNode::shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) { - return FracPart(this).shallowReduce(context, angleUnit); +Expression FracPartNode::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) { + return FracPart(this).shallowReduce(); } template -Complex FracPartNode::computeOnComplex(const std::complex c, Preferences::AngleUnit angleUnit) { +Complex FracPartNode::computeOnComplex(const std::complex c, Preferences::ComplexFormat, Preferences::AngleUnit angleUnit) { if (c.imag() != 0) { return Complex::Undefined(); } - return Complex(c.real()-std::floor(c.real())); + return Complex::Builder(c.real()-std::floor(c.real())); } -Expression FracPart::shallowReduce(Context & context, Preferences::AngleUnit angleUnit) { + +Expression FracPart::shallowReduce() { { - Expression e = Expression::defaultShallowReduce(context, angleUnit); + Expression e = Expression::defaultShallowReduce(); if (e.isUndefined()) { return e; } @@ -49,9 +50,9 @@ Expression FracPart::shallowReduce(Context & context, Preferences::AngleUnit ang } Rational r = static_cast(c); IntegerDivision div = Integer::Division(r.signedIntegerNumerator(), r.integerDenominator()); - assert(!div.remainder.isInfinity()); + assert(!div.remainder.isOverflow()); Integer rDenominator = r.integerDenominator(); - Expression result = Rational(div.remainder, rDenominator); + Expression result = Rational::Builder(div.remainder, rDenominator); replaceWithInPlace(result); return result; } diff --git a/poincare/src/fraction_layout.cpp b/poincare/src/fraction_layout.cpp index da2053c95..2b615d11a 100644 --- a/poincare/src/fraction_layout.cpp +++ b/poincare/src/fraction_layout.cpp @@ -95,7 +95,7 @@ void FractionLayoutNode::deleteBeforeCursor(LayoutCursor * cursor) { if (numeratorLayout()->isEmpty() && denominatorLayout()->isEmpty()) { /* Case: Numerator and denominator are empty. Move the cursor and replace * the fraction with an empty layout. */ - thisRef.replaceWith(EmptyLayout(), cursor); + thisRef.replaceWith(EmptyLayout::Builder(), cursor); // WARNING: Do no use "this" afterwards return; } @@ -216,11 +216,4 @@ void FractionLayoutNode::render(KDContext * ctx, KDPoint p, KDColor expressionCo ctx->fillRect(KDRect(p.x()+Metric::FractionAndConjugateHorizontalMargin, fractionLineY, layoutSize().width()-2*Metric::FractionAndConjugateHorizontalMargin, k_fractionLineHeight), expressionColor); } -FractionLayout::FractionLayout(Layout numerator, Layout denominator) : - Layout(TreePool::sharedPool()->createTreeNode()) -{ - replaceChildAtIndexInPlace(0, numerator); - replaceChildAtIndexInPlace(1, denominator); -} - -} +} \ No newline at end of file diff --git a/poincare/src/function.cpp b/poincare/src/function.cpp index 8c57fdde9..50ed7e0b2 100644 --- a/poincare/src/function.cpp +++ b/poincare/src/function.cpp @@ -9,6 +9,15 @@ namespace Poincare { +FunctionNode::FunctionNode(const char * newName, int length) : SymbolAbstractNode() { + strlcpy(const_cast(name()), newName, length+1); +} + +bool FunctionNode::isReal(Context & context) const { + Function f(this); + return SymbolAbstract::isReal(f, context); +} + Expression FunctionNode::replaceSymbolWithExpression(const SymbolAbstract & symbol, const Expression & expression) { return Function(this).replaceSymbolWithExpression(symbol, expression); } @@ -57,36 +66,48 @@ int FunctionNode::serialize(char * buffer, int bufferSize, Preferences::PrintFlo return SerializationHelper::Prefix(this, buffer, bufferSize, floatDisplayMode, numberOfSignificantDigits, name()); } -Expression FunctionNode::shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) { - return Function(this).shallowReduce(context, angleUnit, target); // This uses Symbol::shallowReduce +Expression FunctionNode::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) { + return Function(this).shallowReduce(context, complexFormat, angleUnit, target); // This uses Symbol::shallowReduce } Expression FunctionNode::shallowReplaceReplaceableSymbols(Context & context) { return Function(this).shallowReplaceReplaceableSymbols(context); } -Evaluation FunctionNode::approximate(SinglePrecision p, Context& context, Preferences::AngleUnit angleUnit) const { - return templatedApproximate(context, angleUnit); +Evaluation FunctionNode::approximate(SinglePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { + return templatedApproximate(context, complexFormat, angleUnit); } -Evaluation FunctionNode::approximate(DoublePrecision p, Context& context, Preferences::AngleUnit angleUnit) const { - return templatedApproximate(context, angleUnit); +Evaluation FunctionNode::approximate(DoublePrecision p, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { + return templatedApproximate(context, complexFormat, angleUnit); } template -Evaluation FunctionNode::templatedApproximate(Context& context, Preferences::AngleUnit angleUnit) const { +Evaluation FunctionNode::templatedApproximate(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { Function f(this); Expression e = SymbolAbstract::Expand(f, context, true); if (e.isUninitialized()) { return Complex::Undefined(); } - return e.approximateToEvaluation(context, angleUnit); + return e.node()->approximate(T(), context, complexFormat, angleUnit); } -Function::Function(const char * name, size_t length) : - Function(TreePool::sharedPool()->createTreeNode(SymbolAbstract::AlignedNodeSize(length, sizeof(FunctionNode)))) -{ - static_cast(Expression::node())->setName(name, length); +Function Function::Builder(const char * name, size_t length, Expression child) { + Function f = SymbolAbstract::Builder(name, length); + if (!child.isUninitialized()) { + f.replaceChildAtIndexInPlace(0, child); + } + return f; +} + +Expression Function::UntypedBuilder(const char * name, size_t length, Expression child, Context * context) { + /* Create an expression only if it is not in the context or defined as a + * function */ + Function f = Function::Builder(name, length, child); + if (SymbolAbstract::ValidInContext(f, context)) { + return f; + } + return Expression(); } Expression Function::replaceSymbolWithExpression(const SymbolAbstract & symbol, const Expression & expression) { @@ -95,12 +116,12 @@ Expression Function::replaceSymbolWithExpression(const SymbolAbstract & symbol, if (symbol.type() == ExpressionNode::Type::Function && strcmp(name(), symbol.name()) == 0) { Expression value = expression.clone(); // Replace the unknown in the new expression by the function's child - Symbol xSymbol = Symbol(Symbol::SpecialSymbols::UnknownX); + Symbol xSymbol = Symbol::Builder(Symbol::SpecialSymbols::UnknownX); Expression xValue = childAtIndex(0); value = value.replaceSymbolWithExpression(xSymbol, xValue); Expression p = parent(); if (!p.isUninitialized() && p.node()->childNeedsParenthesis(value.node())) { - value = Parenthesis(value); + value = Parenthesis::Builder(value); } replaceWithInPlace(value); return value; @@ -108,12 +129,12 @@ Expression Function::replaceSymbolWithExpression(const SymbolAbstract & symbol, return *this; } -Expression Function::shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { +Expression Function::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { Function f(*this); Expression e = SymbolAbstract::Expand(f, context, true); if (!e.isUninitialized()) { replaceWithInPlace(e); - return e.deepReduce(context, angleUnit, target); + return e.deepReduce(context, complexFormat, angleUnit, target); } return *this; } @@ -123,7 +144,7 @@ Expression Function::shallowReplaceReplaceableSymbols(Context & context) { if (e.isUninitialized()) { return *this; } - e.replaceSymbolWithExpression(Symbol(Symbol::SpecialSymbols::UnknownX), childAtIndex(0)); + e.replaceSymbolWithExpression(Symbol::Builder(Symbol::SpecialSymbols::UnknownX), childAtIndex(0)); replaceWithInPlace(e); return e; } @@ -131,7 +152,7 @@ Expression Function::shallowReplaceReplaceableSymbols(Context & context) { // TODO: should we avoid replacing unknown X in-place but use a context instead? #if 0 VariableContext Function::unknownXContext(Context & parentContext) const { - Symbol unknownXSymbol(Symbol::SpecialSymbols::UnknownX); + Symbol unknownXSymbol = Symbol::Builder(Symbol::SpecialSymbols::UnknownX); Expression child = childAtIndex(0); const char x[] = {Symbol::SpecialSymbols::UnknownX, 0}; /* COMMENT */ diff --git a/poincare/src/great_common_divisor.cpp b/poincare/src/great_common_divisor.cpp index 5d1cd2265..7e27cb55c 100644 --- a/poincare/src/great_common_divisor.cpp +++ b/poincare/src/great_common_divisor.cpp @@ -1,4 +1,5 @@ #include + #include #include #include @@ -20,14 +21,14 @@ int GreatCommonDivisorNode::serialize(char * buffer, int bufferSize, Preferences return SerializationHelper::Prefix(this, buffer, bufferSize, floatDisplayMode, numberOfSignificantDigits, GreatCommonDivisor::s_functionHelper.name()); } -Expression GreatCommonDivisorNode::shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) { - return GreatCommonDivisor(this).shallowReduce(context, angleUnit); +Expression GreatCommonDivisorNode::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) { + return GreatCommonDivisor(this).shallowReduce(); } template -Evaluation GreatCommonDivisorNode::templatedApproximate(Context& context, Preferences::AngleUnit angleUnit) const { - Evaluation f1Input = childAtIndex(0)->approximate(T(), context, angleUnit); - Evaluation f2Input = childAtIndex(1)->approximate(T(), context, angleUnit); +Evaluation GreatCommonDivisorNode::templatedApproximate(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { + Evaluation f1Input = childAtIndex(0)->approximate(T(), context, complexFormat, angleUnit); + Evaluation f2Input = childAtIndex(1)->approximate(T(), context, complexFormat, angleUnit); T f1 = f1Input.toScalar(); T f2 = f2Input.toScalar(); if (std::isnan(f1) || std::isnan(f2) || f1 != (int)f1 || f2 != (int)f2) { @@ -45,12 +46,13 @@ Evaluation GreatCommonDivisorNode::templatedApproximate(Context& context, Pre a = b; b = r; } - return Complex(std::round((T)a)); + return Complex::Builder(std::round((T)a)); } -Expression GreatCommonDivisor::shallowReduce(Context & context, Preferences::AngleUnit angleUnit) { + +Expression GreatCommonDivisor::shallowReduce() { { - Expression e = Expression::defaultShallowReduce(context, angleUnit); + Expression e = Expression::defaultShallowReduce(); if (e.isUndefined()) { return e; } @@ -59,13 +61,13 @@ Expression GreatCommonDivisor::shallowReduce(Context & context, Preferences::Ang Expression c1 = childAtIndex(1); #if MATRIX_EXACT_REDUCING if (c0.type() == ExpressionNode::Type::Matrix || c1.type() == ExpressionNode::Type::Matrix) { - return Undefined(); + return Undefined::Builder(); } #endif if (c0.type() == ExpressionNode::Type::Rational) { Rational r0 = static_cast(c0); if (!r0.integerDenominator().isOne()) { - Expression result = Undefined(); + Expression result = Undefined::Builder(); replaceWithInPlace(result); return result; } @@ -73,7 +75,7 @@ Expression GreatCommonDivisor::shallowReduce(Context & context, Preferences::Ang if (c1.type() == ExpressionNode::Type::Rational) { Rational r1 = static_cast(c1); if (!r1.integerDenominator().isOne()) { - Expression result = Undefined(); + Expression result = Undefined::Builder(); replaceWithInPlace(result); return result; } @@ -87,8 +89,8 @@ Expression GreatCommonDivisor::shallowReduce(Context & context, Preferences::Ang Integer a = r0.signedIntegerNumerator(); Integer b = r1.signedIntegerNumerator(); Integer gcd = Arithmetic::GCD(a, b); - assert(!gcd.isInfinity()); - Expression result = Rational(gcd); + assert(!gcd.isOverflow()); + Expression result = Rational::Builder(gcd); replaceWithInPlace(result); return result; } diff --git a/poincare/src/grid_layout.cpp b/poincare/src/grid_layout.cpp index f10bde101..0907208c7 100644 --- a/poincare/src/grid_layout.cpp +++ b/poincare/src/grid_layout.cpp @@ -103,7 +103,7 @@ void GridLayoutNode::addEmptyRow(EmptyLayoutNode::Color color) { int previousRowCount = m_numberOfRows; for (int i = 0; i < columnsCount; i++) { thisRef.addChildAtIndex( - EmptyLayout(color), + EmptyLayout::Builder(color), previousNumberOfChildren, previousNumberOfChildren + i, nullptr); @@ -119,7 +119,7 @@ void GridLayoutNode::addEmptyColumn(EmptyLayoutNode::Color color) { int futureColumnsCount = m_numberOfColumns + 1; for (int i = 0; i < rowsCount; i++) { thisRef.addChildAtIndex( - EmptyLayout(color), + EmptyLayout::Builder(color), i*futureColumnsCount + futureColumnsCount-1, previousNumberOfChildren + i, nullptr); @@ -268,4 +268,4 @@ void GridLayout::setDimensions(int rows, int columns) { setNumberOfColumns(columns); } -} +} \ No newline at end of file diff --git a/poincare/src/horizontal_layout.cpp b/poincare/src/horizontal_layout.cpp index d3ad2c1d6..10dc63766 100644 --- a/poincare/src/horizontal_layout.cpp +++ b/poincare/src/horizontal_layout.cpp @@ -226,7 +226,7 @@ KDPoint HorizontalLayoutNode::positionOfChild(LayoutNode * l) { bool HorizontalLayoutNode::willAddChildAtIndex(LayoutNode * l, int * index, int * currentNumberOfChildren, LayoutCursor * cursor) { if (m_numberOfChildren > 0) { - HorizontalLayout thisRef = HorizontalLayout(this); + HorizontalLayout thisRef(this); thisRef.removeEmptyChildBeforeInsertionAtIndex(index, currentNumberOfChildren, !l->mustHaveLeftSibling(), cursor); *currentNumberOfChildren = thisRef.numberOfChildren(); } @@ -258,7 +258,7 @@ void HorizontalLayoutNode::didRemoveChildAtIndex(int index, LayoutCursor * curso * sibling (e.g. a VerticalOffsetLayout), add an empty layout at index 0 */ if (!force && index == 0 && numberOfChildren() > 0 && childAtIndex(0)->mustHaveLeftSibling()) { - Layout(this).addChildAtIndex(EmptyLayout(), 0, numberOfChildren(), cursor); + Layout(this).addChildAtIndex(EmptyLayout::Builder(), 0, numberOfChildren(), cursor); } } @@ -353,33 +353,6 @@ bool HorizontalLayoutNode::willReplaceChild(LayoutNode * oldChild, LayoutNode * // HorizontalLayout -HorizontalLayout::HorizontalLayout() : Layout(TreePool::sharedPool()->createTreeNode()) {} - -HorizontalLayout::HorizontalLayout(Layout l) : HorizontalLayout() { - addChildAtIndexInPlace(l, 0, 0); -} - -HorizontalLayout::HorizontalLayout(Layout l1, Layout l2) : HorizontalLayout() { - addChildAtIndexInPlace(l1, 0, 0); - addChildAtIndexInPlace(l2, 1, 1); -} -HorizontalLayout::HorizontalLayout(Layout l1, Layout l2, Layout l3) : HorizontalLayout() { - addChildAtIndexInPlace(l1, 0, 0); - addChildAtIndexInPlace(l2, 1, 1); - addChildAtIndexInPlace(l3, 2, 2); -} -HorizontalLayout::HorizontalLayout(Layout l1, Layout l2, Layout l3, Layout l4) : HorizontalLayout() { - addChildAtIndexInPlace(l1, 0, 0); - addChildAtIndexInPlace(l2, 1, 1); - addChildAtIndexInPlace(l3, 2, 2); - addChildAtIndexInPlace(l4, 3, 3); -} -HorizontalLayout::HorizontalLayout(const Layout * children, size_t numberOfChildren) : HorizontalLayout() { - for (size_t i = 0; i < numberOfChildren; i++) { - addChildAtIndexInPlace(children[i], i, i); - } -} - void HorizontalLayout::addOrMergeChildAtIndex(Layout l, int index, bool removeEmptyChildren, LayoutCursor * cursor) { if (l.isHorizontal()) { mergeChildrenAtIndex(HorizontalLayout(static_cast(l.node())), index, removeEmptyChildren, cursor); diff --git a/poincare/src/hyperbolic_arc_cosine.cpp b/poincare/src/hyperbolic_arc_cosine.cpp index 9930c7a51..b52aa2383 100644 --- a/poincare/src/hyperbolic_arc_cosine.cpp +++ b/poincare/src/hyperbolic_arc_cosine.cpp @@ -17,16 +17,17 @@ int HyperbolicArcCosineNode::serialize(char * buffer, int bufferSize, Preference } template -Complex HyperbolicArcCosineNode::computeOnComplex(const std::complex c, Preferences::AngleUnit angleUnit) { +Complex HyperbolicArcCosineNode::computeOnComplex(const std::complex c, Preferences::ComplexFormat, Preferences::AngleUnit angleUnit) { std::complex result = std::acosh(c); /* asinh has a branch cut on ]-inf, 1]: it is then multivalued * on this cut. We followed the convention chosen by the lib c++ of llvm on * ]-inf+0i, 1+0i] (warning: atanh takes the other side of the cut values on * ]-inf-0i, 1-0i[).*/ - return Complex(Trigonometry::RoundToMeaningfulDigits(result, c)); + return Complex::Builder(Trigonometry::RoundToMeaningfulDigits(result, c)); } -template Complex Poincare::HyperbolicArcCosineNode::computeOnComplex(std::complex, Preferences::AngleUnit); -template Complex Poincare::HyperbolicArcCosineNode::computeOnComplex(std::complex, Preferences::AngleUnit); + +template Complex Poincare::HyperbolicArcCosineNode::computeOnComplex(std::complex, Preferences::ComplexFormat, Preferences::AngleUnit); +template Complex Poincare::HyperbolicArcCosineNode::computeOnComplex(std::complex, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit); } diff --git a/poincare/src/hyperbolic_arc_sine.cpp b/poincare/src/hyperbolic_arc_sine.cpp index 86d9f7059..a2c95af34 100644 --- a/poincare/src/hyperbolic_arc_sine.cpp +++ b/poincare/src/hyperbolic_arc_sine.cpp @@ -16,7 +16,7 @@ int HyperbolicArcSineNode::serialize(char * buffer, int bufferSize, Preferences: } template -Complex HyperbolicArcSineNode::computeOnComplex(const std::complex c, Preferences::AngleUnit angleUnit) { +Complex HyperbolicArcSineNode::computeOnComplex(const std::complex c, Preferences::ComplexFormat, Preferences::AngleUnit angleUnit) { std::complex result = std::asinh(c); /* asinh has a branch cut on ]-inf*i, -i[U]i, +inf*i[: it is then multivalued * on this cut. We followed the convention chosen by the lib c++ of llvm on @@ -26,10 +26,11 @@ Complex HyperbolicArcSineNode::computeOnComplex(const std::complex c, Pref if (c.real() == 0 && c.imag() < 1) { result.real(-result.real()); // other side of the cut } - return Complex(Trigonometry::RoundToMeaningfulDigits(result, c)); + return Complex::Builder(Trigonometry::RoundToMeaningfulDigits(result, c)); } -template Complex Poincare::HyperbolicArcSineNode::computeOnComplex(std::complex, Preferences::AngleUnit); -template Complex Poincare::HyperbolicArcSineNode::computeOnComplex(std::complex, Preferences::AngleUnit); + +template Complex Poincare::HyperbolicArcSineNode::computeOnComplex(std::complex, Preferences::ComplexFormat, Preferences::AngleUnit); +template Complex Poincare::HyperbolicArcSineNode::computeOnComplex(std::complex, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit); } diff --git a/poincare/src/hyperbolic_arc_tangent.cpp b/poincare/src/hyperbolic_arc_tangent.cpp index 8c700670c..099835978 100644 --- a/poincare/src/hyperbolic_arc_tangent.cpp +++ b/poincare/src/hyperbolic_arc_tangent.cpp @@ -17,7 +17,7 @@ int HyperbolicArcTangentNode::serialize(char * buffer, int bufferSize, Preferenc } template -Complex HyperbolicArcTangentNode::computeOnComplex(const std::complex c, Preferences::AngleUnit angleUnit) { +Complex HyperbolicArcTangentNode::computeOnComplex(const std::complex c, Preferences::ComplexFormat, Preferences::AngleUnit angleUnit) { std::complex result = std::atanh(c); /* atanh has a branch cut on ]-inf, -1[U]1, +inf[: it is then multivalued on * this cut. We followed the convention chosen by the lib c++ of llvm on @@ -27,10 +27,11 @@ Complex HyperbolicArcTangentNode::computeOnComplex(const std::complex c, P if (c.imag() == 0 && c.real() > 1) { result.imag(-result.imag()); // other side of the cut } - return Complex(Trigonometry::RoundToMeaningfulDigits(result, c)); + return Complex::Builder(Trigonometry::RoundToMeaningfulDigits(result, c)); } -template Complex Poincare::HyperbolicArcTangentNode::computeOnComplex(std::complex, Preferences::AngleUnit); -template Complex Poincare::HyperbolicArcTangentNode::computeOnComplex(std::complex, Preferences::AngleUnit); + +template Complex Poincare::HyperbolicArcTangentNode::computeOnComplex(std::complex, Preferences::ComplexFormat, Preferences::AngleUnit); +template Complex Poincare::HyperbolicArcTangentNode::computeOnComplex(std::complex, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit); } diff --git a/poincare/src/hyperbolic_cosine.cpp b/poincare/src/hyperbolic_cosine.cpp index 0f8fc841d..30f564714 100644 --- a/poincare/src/hyperbolic_cosine.cpp +++ b/poincare/src/hyperbolic_cosine.cpp @@ -14,11 +14,12 @@ int HyperbolicCosineNode::serialize(char * buffer, int bufferSize, Preferences:: } template -Complex HyperbolicCosineNode::computeOnComplex(const std::complex c, Preferences::AngleUnit angleUnit) { - return Complex(Trigonometry::RoundToMeaningfulDigits(std::cosh(c), c)); +Complex HyperbolicCosineNode::computeOnComplex(const std::complex c, Preferences::ComplexFormat, Preferences::AngleUnit angleUnit) { + return Complex::Builder(Trigonometry::RoundToMeaningfulDigits(std::cosh(c), c)); } -template Complex Poincare::HyperbolicCosineNode::computeOnComplex(std::complex, Preferences::AngleUnit); -template Complex Poincare::HyperbolicCosineNode::computeOnComplex(std::complex, Preferences::AngleUnit); + +template Complex Poincare::HyperbolicCosineNode::computeOnComplex(std::complex, Preferences::ComplexFormat, Preferences::AngleUnit); +template Complex Poincare::HyperbolicCosineNode::computeOnComplex(std::complex, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit); } diff --git a/poincare/src/hyperbolic_sine.cpp b/poincare/src/hyperbolic_sine.cpp index cbbf67ca1..d30b18b82 100644 --- a/poincare/src/hyperbolic_sine.cpp +++ b/poincare/src/hyperbolic_sine.cpp @@ -14,11 +14,12 @@ int HyperbolicSineNode::serialize(char * buffer, int bufferSize, Preferences::Pr } template -Complex HyperbolicSineNode::computeOnComplex(const std::complex c, Preferences::AngleUnit angleUnit) { - return Complex(Trigonometry::RoundToMeaningfulDigits(std::sinh(c), c)); +Complex HyperbolicSineNode::computeOnComplex(const std::complex c, Preferences::ComplexFormat, Preferences::AngleUnit angleUnit) { + return Complex::Builder(Trigonometry::RoundToMeaningfulDigits(std::sinh(c), c)); } -template Complex Poincare::HyperbolicSineNode::computeOnComplex(std::complex, Preferences::AngleUnit); -template Complex Poincare::HyperbolicSineNode::computeOnComplex(std::complex, Preferences::AngleUnit); + +template Complex Poincare::HyperbolicSineNode::computeOnComplex(std::complex, Preferences::ComplexFormat, Preferences::AngleUnit); +template Complex Poincare::HyperbolicSineNode::computeOnComplex(std::complex, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit); } diff --git a/poincare/src/hyperbolic_tangent.cpp b/poincare/src/hyperbolic_tangent.cpp index f5557587f..df19fe5e3 100644 --- a/poincare/src/hyperbolic_tangent.cpp +++ b/poincare/src/hyperbolic_tangent.cpp @@ -14,11 +14,12 @@ int HyperbolicTangentNode::serialize(char * buffer, int bufferSize, Preferences: } template -Complex HyperbolicTangentNode::computeOnComplex(const std::complex c, Preferences::AngleUnit angleUnit) { - return Complex(Trigonometry::RoundToMeaningfulDigits(std::tanh(c), c)); +Complex HyperbolicTangentNode::computeOnComplex(const std::complex c, Preferences::ComplexFormat, Preferences::AngleUnit angleUnit) { + return Complex::Builder(Trigonometry::RoundToMeaningfulDigits(std::tanh(c), c)); } -template Complex Poincare::HyperbolicTangentNode::computeOnComplex(std::complex, Preferences::AngleUnit); -template Complex Poincare::HyperbolicTangentNode::computeOnComplex(std::complex, Preferences::AngleUnit); + +template Complex Poincare::HyperbolicTangentNode::computeOnComplex(std::complex, Preferences::ComplexFormat, Preferences::AngleUnit); +template Complex Poincare::HyperbolicTangentNode::computeOnComplex(std::complex, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit); } diff --git a/poincare/src/hyperbolic_trigonometric_function.cpp b/poincare/src/hyperbolic_trigonometric_function.cpp index 812c51d9a..4d48654dc 100644 --- a/poincare/src/hyperbolic_trigonometric_function.cpp +++ b/poincare/src/hyperbolic_trigonometric_function.cpp @@ -2,13 +2,13 @@ namespace Poincare { -Expression HyperbolicTrigonometricFunctionNode::shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) { - return HyperbolicTrigonometricFunction(this).shallowReduce(context, angleUnit); +Expression HyperbolicTrigonometricFunctionNode::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) { + return HyperbolicTrigonometricFunction(this).shallowReduce(); } -Expression HyperbolicTrigonometricFunction::shallowReduce(Context & context, Preferences::AngleUnit angleUnit) { +Expression HyperbolicTrigonometricFunction::shallowReduce() { { - Expression e = Expression::defaultShallowReduce(context, angleUnit); + Expression e = Expression::defaultShallowReduce(); if (e.isUndefined()) { return e; } diff --git a/poincare/src/imaginary_part.cpp b/poincare/src/imaginary_part.cpp index 2a5c3ae43..f50138ddf 100644 --- a/poincare/src/imaginary_part.cpp +++ b/poincare/src/imaginary_part.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include @@ -19,13 +20,14 @@ int ImaginaryPartNode::serialize(char * buffer, int bufferSize, Preferences::Pri return SerializationHelper::Prefix(this, buffer, bufferSize, floatDisplayMode, numberOfSignificantDigits, ImaginaryPart::s_functionHelper.name()); } -Expression ImaginaryPartNode::shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) { - return ImaginaryPart(this).shallowReduce(context, angleUnit); +Expression ImaginaryPartNode::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) { + return ImaginaryPart(this).shallowReduce(context, complexFormat, angleUnit, target); } -Expression ImaginaryPart::shallowReduce(Context & context, Preferences::AngleUnit angleUnit) { + +Expression ImaginaryPart::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { { - Expression e = Expression::defaultShallowReduce(context, angleUnit); + Expression e = Expression::defaultShallowReduce(); if (e.isUndefined()) { return e; } @@ -36,11 +38,17 @@ Expression ImaginaryPart::shallowReduce(Context & context, Preferences::AngleUni return SimplificationHelper::Map(*this, context, angleUnit); } #endif - if (c.type() == ExpressionNode::Type::Rational) { - Expression result = Rational(0); + if (c.isReal(context)) { + Expression result = Rational::Builder(0); replaceWithInPlace(result); return result; } + if (c.type() == ExpressionNode::Type::ComplexCartesian) { + ComplexCartesian complexChild = static_cast(c); + Expression i = complexChild.imag(); + replaceWithInPlace(i); + return i.shallowReduce(context, complexFormat, angleUnit, target); + } return *this; } diff --git a/poincare/src/infinity.cpp b/poincare/src/infinity.cpp index 691fb0f48..a8dc2a552 100644 --- a/poincare/src/infinity.cpp +++ b/poincare/src/infinity.cpp @@ -9,8 +9,9 @@ extern "C" { namespace Poincare { -Expression InfinityNode::setSign(Sign s, Context & context, Preferences::AngleUnit angleUnit) { - return Infinity(this).setSign(s, context, angleUnit); +Expression InfinityNode::setSign(Sign s, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) { + assert(s == ExpressionNode::Sign::Positive || s == ExpressionNode::Sign::Negative); + return Infinity(this).setSign(s, context, complexFormat, angleUnit); } Layout InfinityNode::createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const { @@ -27,11 +28,19 @@ int InfinityNode::serialize(char * buffer, int bufferSize, Preferences::PrintFlo } template Evaluation InfinityNode::templatedApproximate() const { - return Complex(m_negative ? -INFINITY : INFINITY); + return Complex::Builder(m_negative ? -INFINITY : INFINITY); } -Expression Infinity::setSign(ExpressionNode::Sign s, Context & context, Preferences::AngleUnit angleUnit) { - Expression result = Infinity(s == ExpressionNode::Sign::Negative); +Infinity Infinity::Builder(bool negative) { + void * bufferNode = TreePool::sharedPool()->alloc(sizeof(InfinityNode)); + InfinityNode * node = new (bufferNode) InfinityNode(negative); + TreeHandle h = TreeHandle::BuildWithGhostChildren(node); + return static_cast(h); +} + +Expression Infinity::setSign(ExpressionNode::Sign s, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) { + assert(s == ExpressionNode::Sign::Positive || s == ExpressionNode::Sign::Negative); + Expression result = Infinity::Builder(s == ExpressionNode::Sign::Negative); replaceWithInPlace(result); return result; } diff --git a/poincare/src/init.cpp b/poincare/src/init.cpp index 0e4b1bbe7..9c9b6141b 100644 --- a/poincare/src/init.cpp +++ b/poincare/src/init.cpp @@ -12,9 +12,6 @@ void Init() { } void Tidy() { - // Clean Integer - Integer::TidyIntegerBuffer(); - // Clean Expression (reset the SymbolReplacementsLock) Expression::Tidy(); } diff --git a/poincare/src/integer.cpp b/poincare/src/integer.cpp index d69cd9617..51ed772b6 100644 --- a/poincare/src/integer.cpp +++ b/poincare/src/integer.cpp @@ -15,6 +15,17 @@ namespace Poincare { static inline int max(int x, int y) { return (x>y ? x : y); } +/* To compute operations between Integers, we need an array where to store the + * result digits. Instead of allocating it on the stack which would eventually + * lead to a stack overflow, we keep a static working buffer. We actually need + * two of them because division involves inner multiplications and additions + * (which would override the division digits if there were using the same + * buffer). */ +// TODO: we might want to go back to allocating the native_uint_t arrays on the stack once we increase the stack size from 32k to? + +static native_uint_t s_workingBuffer[Integer::k_maxNumberOfDigits + 1]; +static native_uint_t s_workingBufferDivision[Integer::k_maxNumberOfDigits + 1]; + uint8_t log2(native_uint_t v) { constexpr int nativeUnsignedIntegerBitCount = 8*sizeof(native_uint_t); static_assert(nativeUnsignedIntegerBitCount < 256, "uint8_t cannot contain the log2 of a native_uint_t"); @@ -34,119 +45,90 @@ static inline int8_t sign(bool negative) { return 1 - 2*(int8_t)negative; } +IntegerNode::IntegerNode(const native_uint_t * digits, uint8_t numberOfDigits) : + m_numberOfDigits(numberOfDigits) +{ + memcpy(m_digits, digits, numberOfDigits*sizeof(native_uint_t)); +} + +static size_t IntegerSize(uint8_t numberOfDigits) { + return sizeof(IntegerNode) + sizeof(native_uint_t)*(numberOfDigits); +} + +size_t IntegerNode::size() const { + return IntegerSize(m_numberOfDigits); +} + #if POINCARE_TREE_LOG -void Integer::log(std::ostream & stream) const { - if (m_numberOfDigits > k_maxNumberOfDigits) { - stream << "Integer: overflow"; +void IntegerNode::logAttributes(std::ostream & stream) const { + stream << " value=\""; + log(stream); + stream << "\""; +} + +void IntegerNode::log(std::ostream & stream) const { + if (m_numberOfDigits > Integer::k_maxNumberOfDigits) { + stream << "overflow"; return; } double d = 0.0; double base = 1.0; for (int i = 0; i < m_numberOfDigits; i++) { - d += digit(i)*base; + d += m_digits[i]*base; base *= std::pow(2.0,32.0); } - stream << "Integer: " << d; + stream << d; } #endif -/* new operator */ - -// This bit buffer indicates which cases of the sIntegerBuffer are already allocated -static uint16_t sbusyIntegerBuffer = 0; -static native_uint_t sIntegerBuffer[(Integer::k_maxNumberOfDigits+1)*Integer::k_maxNumberOfIntegerSimutaneously]; - -void Integer::TidyIntegerBuffer() { - sbusyIntegerBuffer = 0; -} - -native_uint_t * Integer::allocDigits(int numberOfDigits) { - assert(numberOfDigits <= k_maxNumberOfDigits+1); - uint16_t bitIndex = 1 << (16-1); - int index = 0; - while (sbusyIntegerBuffer & bitIndex) { - bitIndex >>= 1; - index++; - } - if (bitIndex == 0) { // we overflow the sIntegerBuffer - assert(false); - return nullptr; - } - sbusyIntegerBuffer |= bitIndex; - return sIntegerBuffer+index*(Integer::k_maxNumberOfDigits+1); -} - -void Integer::freeDigits(native_uint_t * digits) { - int index = (digits - sIntegerBuffer)/(Integer::k_maxNumberOfDigits+1); - assert(index < 16); - sbusyIntegerBuffer &= ~((uint16_t)1 << (16-1-index)); -} - // Constructor Integer Integer::BuildInteger(native_uint_t * digits, uint16_t numberOfDigits, bool negative, bool enableOverflow) { - if ((!digits || !enableOverflow) && numberOfDigits == k_maxNumberOfDigits+1) { + if ((!digits || !enableOverflow) && numberOfDigits >= k_maxNumberOfDigits+1) { return Overflow(negative); } - native_uint_t * newDigits = allocDigits(numberOfDigits); - for (uint8_t i = 0; i < numberOfDigits; i++) { - newDigits[i] = digits[i]; + // 0 can't be negative + negative = numberOfDigits == 0 ? false : negative; + if (numberOfDigits <= 1) { + Integer i(TreeNode::NoNodeIdentifier, negative); + i.m_digit = numberOfDigits == 0 ? 0 : digits[0]; + return i; } - return Integer(newDigits, numberOfDigits, negative, enableOverflow); + return Integer(digits, numberOfDigits, negative); } -/* WARNING: This constructor takes ownership of the digits array! */ -Integer::Integer(native_uint_t * digits, uint16_t numberOfDigits, bool negative, bool enableOverflow) : - m_negative(numberOfDigits == 0 ? false : negative), - m_numberOfDigits(!enableOverflow && numberOfDigits > k_maxNumberOfDigits ? k_maxNumberOfDigits+1 : numberOfDigits), - m_digits(digits) -{ - if ((m_numberOfDigits <= 1 || (!enableOverflow && m_numberOfDigits > k_maxNumberOfDigits)) && m_digits) { - freeDigits(m_digits); - if (m_numberOfDigits == 1) { - m_digit = digits[0]; - } else { - m_digits = nullptr; - } - } - m_negative = m_numberOfDigits == 0 ? false : m_negative; +// Private constructor + +Integer::Integer(native_uint_t * digits, uint16_t numberOfDigits, bool negative) { + void * bufferNode = TreePool::sharedPool()->alloc(IntegerSize(numberOfDigits)); + IntegerNode * node = new (bufferNode) IntegerNode(digits, numberOfDigits); + TreeHandle h = TreeHandle::BuildWithGhostChildren(node); + /* Integer is a TreeHandle that keeps an extra integer. We cannot just cast + * the TreeHandle in Integer, we have to build a new Integer. To do so, we + * pilfer the TreeHandle identifier. */ + new (this) Integer(h.identifier(), negative); } -Integer::Integer(native_int_t i) { - if (i == 0) { - m_digits = nullptr; - m_numberOfDigits = 0; - m_negative = false; - return; - } - m_numberOfDigits = 1; +Integer::Integer(native_int_t i) : TreeHandle(TreeNode::NoNodeIdentifier) { m_digit = i > 0 ? i : -i; m_negative = i < 0; } Integer::Integer(double_native_int_t i) { - if (i == 0) { - m_digits = nullptr; - m_numberOfDigits = 0; - m_negative = false; - return; - } double_native_uint_t j = i < 0 ? -i : i; native_uint_t * d = (native_uint_t *)&j; native_uint_t leastSignificantDigit = *d; native_uint_t mostSignificantDigit = *(d+1); - m_numberOfDigits = (mostSignificantDigit == 0) ? 1 : 2; - if (m_numberOfDigits == 1) { + uint8_t numberOfDigits = (mostSignificantDigit == 0) ? 1 : 2; + if (numberOfDigits == 1) { + m_identifier = TreeNode::NoNodeIdentifier; + m_negative = i < 0; m_digit = leastSignificantDigit; } else { - native_uint_t * digits = allocDigits(m_numberOfDigits); - digits[0] = leastSignificantDigit; - digits[1] = mostSignificantDigit; - m_digits = digits; + new (this) Integer(d, 2, i < 0); } - m_negative = i < 0; } Integer::Integer(const char * digits, size_t length, bool negative) : @@ -165,90 +147,9 @@ Integer::Integer(const char * digits, size_t length, bool negative) : digits++; } } - setNegative(isZero() ? false : negative); } -void Integer::releaseDynamicIvars() { - if (!usesImmediateDigit() && m_digits) { - freeDigits(m_digits); - } -} - -Integer::~Integer() { - releaseDynamicIvars(); -} - -Integer::Integer(Integer && other) { - // Pilfer other's data - if (other.usesImmediateDigit()) { - m_digit = other.m_digit; - } else { - m_digits = other.m_digits; - } - m_numberOfDigits = other.m_numberOfDigits; - m_negative = other.m_negative; - - // Reset other - other.m_digits = nullptr; - other.m_numberOfDigits = 1; - other.m_negative = 0; -} - -Integer::Integer(const Integer& other) { - // Copy other's data - if (other.usesImmediateDigit() || other.isOverflow()) { - m_digit = other.m_digit; - } else { - native_uint_t * newDigits = allocDigits(other.m_numberOfDigits); - for (uint8_t i = 0; i < other.m_numberOfDigits; i++) { - newDigits[i] = other.m_digits[i]; - } - m_digits = newDigits; - } - m_numberOfDigits = other.m_numberOfDigits; - m_negative = other.m_negative; -} - -Integer& Integer::operator=(Integer && other) { - if (this != &other) { - releaseDynamicIvars(); - // Pilfer other's ivars - if (other.usesImmediateDigit()) { - m_digit = other.m_digit; - } else { - m_digits = other.m_digits; - } - m_numberOfDigits = other.m_numberOfDigits; - m_negative = other.m_negative; - - // Reset other - other.m_digits = nullptr; - other.m_numberOfDigits = 1; - other.m_negative = 0; - } - return *this; -} - -Integer& Integer::operator=(const Integer& other) { - if (this != &other) { - releaseDynamicIvars(); - // Copy other's ivars - if (other.usesImmediateDigit() || other.isOverflow()) { - m_digit = other.m_digit; - } else { - native_uint_t * digits = allocDigits(other.m_numberOfDigits); - for (uint8_t i = 0; i < other.m_numberOfDigits; i++) { - digits[i] = other.m_digits[i]; - } - m_digits = digits; - } - m_numberOfDigits = other.m_numberOfDigits; - m_negative = other.m_negative; - } - return *this; -} - // Serialization int Integer::serialize(char * buffer, int bufferSize) const { @@ -256,7 +157,7 @@ int Integer::serialize(char * buffer, int bufferSize) const { return -1; } buffer[bufferSize-1] = 0; - if (isInfinity()) { + if (isOverflow()) { return PrintFloat::convertFloatToText(m_negative ? -INFINITY : INFINITY, buffer, bufferSize, PrintFloat::k_numberOfStoredSignificantDigits, Preferences::PrintFloatMode::Decimal); } @@ -306,7 +207,7 @@ HorizontalLayout Integer::createLayout() const { template T Integer::approximate() const { - if (m_numberOfDigits == 0) { + if (numberOfDigits() == 0) { /* This special case for 0 is needed, because the current algorithm assumes * that the big integer is non zero, thus puts the exponent to 126 (integer * area), the issue is that when the mantissa is 0, a "shadow bit" is @@ -322,21 +223,22 @@ T Integer::approximate() const { * - the mantissa is the beginning of our BigInt, discarding the first bit */ - if (isInfinity()) { + if (isOverflow()) { return m_negative ? -INFINITY : INFINITY; } - native_uint_t lastDigit = m_numberOfDigits > 0 ? digit(m_numberOfDigits-1) : 0; + assert(numberOfDigits() > 0); + native_uint_t lastDigit = digit(numberOfDigits()-1); uint8_t numberOfBitsInLastDigit = log2(lastDigit); bool sign = m_negative; uint16_t exponent = IEEE754::exponentOffset(); /* Escape case if the exponent is too big to be stored */ - assert(m_numberOfDigits > 0); - if (((int)m_numberOfDigits-1)*32+numberOfBitsInLastDigit-1> IEEE754::maxExponent()-IEEE754::exponentOffset()) { + assert(numberOfDigits() > 0); + if (((int)numberOfDigits()-1)*32+numberOfBitsInLastDigit-1> IEEE754::maxExponent()-IEEE754::exponentOffset()) { return m_negative ? -INFINITY : INFINITY; } - exponent += (m_numberOfDigits-1)*32; + exponent += (numberOfDigits()-1)*32; exponent += numberOfBitsInLastDigit-1; uint64_t mantissa = 0; @@ -352,8 +254,8 @@ T Integer::approximate() const { * the mantissa is complete to avoid undefined right shifting (Shift operator * behavior is undefined if the right operand is negative, or greater than or * equal to the length in bits of the promoted left operand). */ - while (m_numberOfDigits >= digitIndex && numberOfBits < IEEE754::size()) { - lastDigit = digit(m_numberOfDigits-digitIndex); + while (numberOfDigits() >= digitIndex && numberOfBits < IEEE754::size()) { + lastDigit = digit(numberOfDigits()-digitIndex); numberOfBits += 32; if (IEEE754::size() > numberOfBits) { assert(IEEE754::size()-numberOfBits > 0 && IEEE754::size()-numberOfBits < 64); @@ -377,7 +279,7 @@ T Integer::approximate() const { // Properties int Integer::NumberOfBase10DigitsWithoutSign(const Integer & i) { - assert(!i.isInfinity()); + assert(!i.isOverflow()); int numberOfDigits = 1; Integer base(10); IntegerDivision d = udiv(i, base); @@ -473,52 +375,50 @@ Integer Integer::multiplication(const Integer & a, const Integer & b, bool oneDi return Integer::Overflow(a.m_negative != b.m_negative); } - uint8_t size = min(a.m_numberOfDigits + b.m_numberOfDigits, k_maxNumberOfDigits + oneDigitOverflow); // Enable overflowing of 1 digit + uint8_t size = min(a.numberOfDigits() + b.numberOfDigits(), k_maxNumberOfDigits + oneDigitOverflow); // Enable overflowing of 1 digit - native_uint_t * digits = allocDigits(size); - memset(digits, 0, size*sizeof(native_uint_t)); + memset(s_workingBuffer, 0, size*sizeof(native_uint_t)); double_native_uint_t carry = 0; - for (uint8_t i = 0; i < a.m_numberOfDigits; i++) { + for (uint8_t i = 0; i < a.numberOfDigits(); i++) { double_native_uint_t aDigit = a.digit(i); carry = 0; - for (uint8_t j = 0; j < b.m_numberOfDigits; j++) { + for (uint8_t j = 0; j < b.numberOfDigits(); j++) { double_native_uint_t bDigit = b.digit(j); /* The fact that aDigit and bDigit are double_native is very important, * otherwise the product might end up being computed on single_native size * and then zero-padded. */ - double_native_uint_t p = aDigit*bDigit + carry + (double_native_uint_t)(digits[i+j]); // TODO: Prove it cannot overflow double_native type + double_native_uint_t p = aDigit*bDigit + carry + (double_native_uint_t)(s_workingBuffer[i+j]); // TODO: Prove it cannot overflow double_native type native_uint_t * l = (native_uint_t *)&p; if (i+j < (uint8_t) k_maxNumberOfDigits+oneDigitOverflow) { - digits[i+j] = l[0]; + s_workingBuffer[i+j] = l[0]; } else { if (l[0] != 0) { // Overflow the largest Integer - freeDigits(digits); return Integer::Overflow(a.m_negative != b.m_negative); - } } + } + } carry = l[1]; } - if (i+b.m_numberOfDigits < (uint8_t) k_maxNumberOfDigits+oneDigitOverflow) { - digits[i+b.m_numberOfDigits] += carry; + if (i+b.numberOfDigits() < (uint8_t) k_maxNumberOfDigits+oneDigitOverflow) { + s_workingBuffer[i+b.numberOfDigits()] += carry; } else { if (carry != 0) { // Overflow the largest Integer - freeDigits(digits); return Integer::Overflow(a.m_negative != b.m_negative); } } } - while (size>0 && digits[size-1] == 0) { + while (size>0 && s_workingBuffer[size-1] == 0) { size--; } - return Integer(digits, size, a.m_negative != b.m_negative, oneDigitOverflow); + return BuildInteger(s_workingBuffer, size, a.m_negative != b.m_negative, oneDigitOverflow); } int8_t Integer::ucmp(const Integer & a, const Integer & b) { - if (a.m_numberOfDigits < b.m_numberOfDigits) { + if (a.numberOfDigits() < b.numberOfDigits()) { return -1; - } else if (a.m_numberOfDigits > b.m_numberOfDigits) { + } else if (a.numberOfDigits() > b.numberOfDigits()) { return 1; } if (a.isOverflow() && b.isOverflow()) { @@ -526,10 +426,10 @@ int8_t Integer::ucmp(const Integer & a, const Integer & b) { } assert(!a.isOverflow()); assert(!b.isOverflow()); - for (uint16_t i = 0; i < a.m_numberOfDigits; i++) { + for (uint16_t i = 0; i < a.numberOfDigits(); i++) { // Digits are stored most-significant last - native_uint_t aDigit = a.digit(a.m_numberOfDigits-i-1); - native_uint_t bDigit = b.digit(b.m_numberOfDigits-i-1); + native_uint_t aDigit = a.digit(a.numberOfDigits()-i-1); + native_uint_t bDigit = b.digit(b.numberOfDigits()-i-1); if (aDigit < bDigit) { return -1; } else if (aDigit > bDigit) { @@ -541,27 +441,25 @@ int8_t Integer::ucmp(const Integer & a, const Integer & b) { Integer Integer::usum(const Integer & a, const Integer & b, bool subtract, bool oneDigitOverflow) { if (a.isOverflow() || b.isOverflow()) { - return Integer::Overflow(a.m_negative != b.m_negative); + return Overflow(a.m_negative != b.m_negative); } - uint8_t size = max(a.m_numberOfDigits, b.m_numberOfDigits); + uint8_t size = max(a.numberOfDigits(), b.numberOfDigits()); if (!subtract) { // Addition can overflow size++; } - native_uint_t * digits = allocDigits(max(size, k_maxNumberOfDigits+oneDigitOverflow)); bool carry = false; for (uint8_t i = 0; i < size; i++) { - native_uint_t aDigit = (i >= a.m_numberOfDigits ? 0 : a.digit(i)); - native_uint_t bDigit = (i >= b.m_numberOfDigits ? 0 : b.digit(i)); + native_uint_t aDigit = (i >= a.numberOfDigits() ? 0 : a.digit(i)); + native_uint_t bDigit = (i >= b.numberOfDigits() ? 0 : b.digit(i)); native_uint_t result = (subtract ? aDigit - bDigit - carry : aDigit + bDigit + carry); if (i < (uint8_t) (k_maxNumberOfDigits + oneDigitOverflow)) { - digits[i] = result; + s_workingBuffer[i] = result; } else { if (result != 0) { // Overflow the largest Integer - freeDigits(digits); - return Integer::Overflow(false); + return Overflow(false); } } if (subtract) { @@ -571,53 +469,54 @@ Integer Integer::usum(const Integer & a, const Integer & b, bool subtract, bool } } size = min(size, k_maxNumberOfDigits+oneDigitOverflow); - while (size>0 && digits[size-1] == 0) { + while (size>0 && s_workingBuffer[size-1] == 0) { size--; } - return Integer(digits, size, false, oneDigitOverflow); + return BuildInteger(s_workingBuffer, size, false, oneDigitOverflow); } Integer Integer::multiplyByPowerOf2(uint8_t pow) const { assert(pow < 32); - native_uint_t * digits = allocDigits(m_numberOfDigits+1); native_uint_t carry = 0; - for (uint8_t i = 0; i < m_numberOfDigits; i++) { - digits[i] = digit(i) << pow | carry; + for (uint8_t i = 0; i < numberOfDigits(); i++) { + s_workingBuffer[i] = digit(i) << pow | carry; carry = pow == 0 ? 0 : digit(i) >> (32-pow); } - digits[m_numberOfDigits] = carry; - return Integer(digits, carry ? m_numberOfDigits + 1 : m_numberOfDigits, false, true); + s_workingBuffer[numberOfDigits()] = carry; + return BuildInteger(s_workingBuffer, carry ? numberOfDigits() + 1 : numberOfDigits(), false, true); } Integer Integer::divideByPowerOf2(uint8_t pow) const { assert(pow < 32); - native_uint_t * digits = allocDigits(m_numberOfDigits); native_uint_t carry = 0; - for (int i = m_numberOfDigits - 1; i >= 0; i--) { - digits[i] = digit(i) >> pow | carry; + for (int i = numberOfDigits() - 1; i >= 0; i--) { + s_workingBuffer[i] = digit(i) >> pow | carry; carry = pow == 0 ? 0 : digit(i) << (32-pow); } - return Integer(digits, digits[m_numberOfDigits-1] > 0 ? m_numberOfDigits : m_numberOfDigits-1, false, true); + return BuildInteger(s_workingBuffer, s_workingBuffer[numberOfDigits()-1] > 0 ? numberOfDigits() : numberOfDigits()-1, false, true); } // return this*(2^16)^pow Integer Integer::multiplyByPowerOfBase(uint8_t pow) const { int nbOfHalfDigits = numberOfHalfDigits(); - half_native_uint_t * digits = (half_native_uint_t *)allocDigits(m_numberOfDigits+(pow+1)/2); - memset(digits, 0, sizeof(native_uint_t)*(m_numberOfDigits+(pow+1)/2)); + half_native_uint_t * digits = reinterpret_cast(s_workingBuffer); + /* The number of half digits of the built integer is nbOfHalfDigits+pow. + * Still, we set an extra half digit to 0 to easily convert half digits to + * digits. */ + memset(digits, 0, sizeof(half_native_uint_t)*(nbOfHalfDigits+pow+1)); for (uint8_t i = 0; i < nbOfHalfDigits; i++) { digits[i+pow] = halfDigit(i); } nbOfHalfDigits += pow; - return Integer((native_uint_t *)digits, nbOfHalfDigits%2 == 1 ? nbOfHalfDigits/2+1 : nbOfHalfDigits/2, false, true); + return BuildInteger((native_uint_t *)digits, nbOfHalfDigits%2 == 1 ? nbOfHalfDigits/2+1 : nbOfHalfDigits/2, false, true); } IntegerDivision Integer::udiv(const Integer & numerator, const Integer & denominator) { if (denominator.isOverflow()) { - return {.quotient = Integer::Overflow(false), .remainder = Integer::Overflow(false)}; + return {.quotient = Overflow(false), .remainder = Integer::Overflow(false)}; } if (numerator.isOverflow()) { - return {.quotient = Integer::Overflow(false), .remainder = Integer::Overflow(false)}; + return {.quotient = Overflow(false), .remainder = Integer::Overflow(false)}; } /* Modern Computer Arithmetic, Richard P. Brent and Paul Zimmermann * (Algorithm 1.6) */ @@ -646,8 +545,9 @@ IntegerDivision Integer::udiv(const Integer & numerator, const Integer & denomin int n = B.numberOfHalfDigits(); int m = A.numberOfHalfDigits()-n; // qDigits is a half_native_uint_t array and enable one digit overflow - half_native_uint_t * qDigits = (half_native_uint_t *)allocDigits(m/2+1); - memset(qDigits, 0, (m/2+1)*sizeof(native_uint_t)); + half_native_uint_t * qDigits = reinterpret_cast(s_workingBufferDivision); + // The quotient q has at maximum m+1 half digits but we set an extra half digit to 0 to enable to easily convert it from half digits to digits + memset(qDigits, 0, max(m+1+1,2*k_maxNumberOfDigits)*sizeof(half_native_uint_t)); // betaMB = B*beta^m Integer betaMB = B.multiplyByPowerOfBase(m); if (Integer::NaturalOrder(A,betaMB) >= 0) { // A >= B*beta^m @@ -660,10 +560,12 @@ IntegerDivision Integer::udiv(const Integer & numerator, const Integer & denomin half_native_uint_t baseMinus1 = (1 << 16) -1; // beta-1 qDigits[j] = qj2 < (native_uint_t)baseMinus1 ? (half_native_uint_t)qj2 : baseMinus1; // min(qj2, beta -1) A = Integer::addition(A, multiplication(qDigits[j], B.multiplyByPowerOfBase(j), true), true, true); // A-q[j]*beta^j*B - Integer betaJM = B.multiplyByPowerOfBase(j); // betaJM = B*beta^j - while (A.isNegative()) { - qDigits[j] = qDigits[j]-1; // q[j] = q[j]-1 - A = addition(A, betaJM, false, true); // A = B*beta^j+A + if (A.isNegative()) { + Integer betaJM = B.multiplyByPowerOfBase(j); // betaJM = B*beta^j + while (A.isNegative()) { + qDigits[j] = qDigits[j]-1; // q[j] = q[j]-1 + A = addition(A, betaJM, false, true); // A = B*beta^j+A + } } } int qNumberOfDigits = m+1; @@ -671,7 +573,7 @@ IntegerDivision Integer::udiv(const Integer & numerator, const Integer & denomin qNumberOfDigits--; } int qNumberOfDigitsInBase32 = qNumberOfDigits%2 == 1 ? qNumberOfDigits/2+1 : qNumberOfDigits/2; - IntegerDivision div = {.quotient = Integer((native_uint_t *)qDigits, qNumberOfDigitsInBase32, false), .remainder = A}; + IntegerDivision div = {.quotient = BuildInteger((native_uint_t *)qDigits, qNumberOfDigitsInBase32, false), .remainder = A}; if (pow > 0 && !div.remainder.isZero()) { div.remainder = div.remainder.divideByPowerOf2(pow); } diff --git a/poincare/src/integral.cpp b/poincare/src/integral.cpp index 0c2c26fd5..96c565d87 100644 --- a/poincare/src/integral.cpp +++ b/poincare/src/integral.cpp @@ -1,10 +1,12 @@ #include + #include #include #include #include #include #include +#include #include #include #include @@ -32,7 +34,7 @@ Expression IntegralNode::replaceUnknown(const Symbol & symbol) { } Layout IntegralNode::createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const { - return IntegralLayout( + return IntegralLayout::Builder( childAtIndex(0)->createLayout(floatDisplayMode, numberOfSignificantDigits), childAtIndex(1)->createLayout(floatDisplayMode, numberOfSignificantDigits), childAtIndex(2)->createLayout(floatDisplayMode, numberOfSignificantDigits), @@ -43,36 +45,40 @@ int IntegralNode::serialize(char * buffer, int bufferSize, Preferences::PrintFlo return SerializationHelper::Prefix(this, buffer, bufferSize, floatDisplayMode, numberOfSignificantDigits, Integral::s_functionHelper.name()); } -Expression IntegralNode::shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) { - return Integral(this).shallowReduce(context, angleUnit); +Expression IntegralNode::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) { + return Integral(this).shallowReduce(); } template -Evaluation IntegralNode::templatedApproximate(Context & context, Preferences::AngleUnit angleUnit) const { - Evaluation aInput = childAtIndex(2)->approximate(T(), context, angleUnit); - Evaluation bInput = childAtIndex(3)->approximate(T(), context, angleUnit); +Evaluation IntegralNode::templatedApproximate(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { + Evaluation aInput = childAtIndex(2)->approximate(T(), context, complexFormat, angleUnit); + Evaluation bInput = childAtIndex(3)->approximate(T(), context, complexFormat, angleUnit); T a = aInput.toScalar(); T b = bInput.toScalar(); if (std::isnan(a) || std::isnan(b)) { return Complex::Undefined(); } #ifdef LAGRANGE_METHOD - T result = lagrangeGaussQuadrature(a, b, context, angleUnit); + T result = lagrangeGaussQuadrature(a, b, context, complexFormat, angleUnit); #else - T result = adaptiveQuadrature(a, b, 0.1, k_maxNumberOfIterations, context, angleUnit); + T result = adaptiveQuadrature(a, b, 0.1, k_maxNumberOfIterations, context, complexFormat, angleUnit); #endif - return Complex(result); + return Complex::Builder(result); } template -T IntegralNode::functionValueAtAbscissa(T x, Context & context, Preferences::AngleUnit angleUnit) const { - return Expression(childAtIndex(0)).approximateWithValueForSymbol(static_cast(childAtIndex(1))->name(), x, context, angleUnit); +T IntegralNode::functionValueAtAbscissa(T x, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { + // Here we cannot use Expression::approximateWithValueForSymbol which would reset the sApproximationEncounteredComplex flag + assert(childAtIndex(1)->type() == Type::Symbol); + VariableContext variableContext = VariableContext(static_cast(childAtIndex(1))->name(), &context); + variableContext.setApproximationForVariable(x); + return childAtIndex(0)->approximate(T(), variableContext, complexFormat, angleUnit).toScalar(); } #ifdef LAGRANGE_METHOD template -T IntegralNode::lagrangeGaussQuadrature(T a, T b, Context & context, Preferences::AngleUnit angleUnit) const { +T IntegralNode::lagrangeGaussQuadrature(T a, T b, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { /* We here use Gauss-Legendre quadrature with n = 5 * Gauss-Legendre abscissae and weights can be found in * C/C++ library source code. */ @@ -86,11 +92,11 @@ T IntegralNode::lagrangeGaussQuadrature(T a, T b, Context & context, Preferences T result = 0; for (int j = 0; j < 10; j++) { T dx = xr * x[j]; - T evaluationAfterX = functionValueAtAbscissa(xm+dx, context, angleUnit); + T evaluationAfterX = functionValueAtAbscissa(xm+dx, context, complexFormat, angleUnit); if (std::isnan(evaluationAfterX)) { return NAN; } - T evaluationBeforeX = functionValueAtAbscissa(xm-dx, context, angleUnit); + T evaluationBeforeX = functionValueAtAbscissa(xm-dx, context, complexFormat, angleUnit); if (std::isnan(evaluationBeforeX)) { return NAN; } @@ -103,101 +109,119 @@ T IntegralNode::lagrangeGaussQuadrature(T a, T b, Context & context, Preferences #else template -IntegralNode::DetailedResult IntegralNode::kronrodGaussQuadrature(T a, T b, Context & context, Preferences::AngleUnit angleUnit) const { +IntegralNode::DetailedResult IntegralNode::kronrodGaussQuadrature(T a, T b, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { static T epsilon = sizeof(T) == sizeof(double) ? DBL_EPSILON : FLT_EPSILON; static T max = sizeof(T) == sizeof(double) ? DBL_MAX : FLT_MAX; /* We here use Kronrod-Legendre quadrature with n = 21 * The abscissa and weights are taken from QUADPACK library. */ - const static T wg[5]= {0.066671344308688137593568809893332, 0.149451349150580593145776339657697, - 0.219086362515982043995534934228163, 0.269266719309996355091226921569469, 0.295524224714752870173892994651338}; - const static T xgk[11]= {0.995657163025808080735527280689003, 0.973906528517171720077964012084452, + + // Abscissae for the gauss (odd weights) and kronrod rules (all weights) + const static T x[11]= {0.995657163025808080735527280689003, 0.973906528517171720077964012084452, 0.930157491355708226001207180059508, 0.865063366688984510732096688423493, 0.780817726586416897063717578345042, 0.679409568299024406234327365114874, 0.562757134668604683339000099272694, 0.433395394129247190799265943165784, 0.294392862701460198131126603103866, 0.148874338981631210884826001129720, 0.000000000000000000000000000000000}; - const static T wgk[11]= {0.011694638867371874278064396062192, 0.032558162307964727478818972459390, + + // Weights for the gauss integral + const static T wGauss[5]= {0.066671344308688137593568809893332, 0.149451349150580593145776339657697, + 0.219086362515982043995534934228163, 0.269266719309996355091226921569469, 0.295524224714752870173892994651338}; + + // Weights for the kronrod rule + const static T wKronrod[11]= {0.011694638867371874278064396062192, 0.032558162307964727478818972459390, 0.054755896574351996031381300244580, 0.075039674810919952767043140916190, 0.093125454583697605535065465083366, 0.109387158802297641899210590325805, 0.123491976262065851077958109831074, 0.134709217311473325928054001771707, 0.142775938577060080797094273138717, 0.147739104901338491374841515972068, 0.149445554002916905664936468389821}; + T fv1[10]; T fv2[10]; - T centr = 0.5*(a+b); - T hlgth = 0.5*(b-a); - T dhlgth = std::fabs(hlgth); + T center = 0.5 * (a+b); + T halfLength = 0.5 * (b-a); + T absHalfLength = std::fabs(halfLength); DetailedResult errorResult; errorResult.integral = NAN; errorResult.absoluteError = 0; - T resg = 0; - T fc = functionValueAtAbscissa(centr, context, angleUnit); - if (std::isnan(fc)) { + T gaussIntegral = 0; + T fCenter = functionValueAtAbscissa(center, context, complexFormat, angleUnit); + if (std::isnan(fCenter)) { return errorResult; } - T resk = wgk[10]*fc; - T resabs = std::fabs(resk); + T kronrodIntegral = wKronrod[10] * fCenter; + T absKronrodIntegral = std::fabs(kronrodIntegral); for (int j = 0; j < 10; j++) { - T absc = hlgth*xgk[j]; - T fval1 = functionValueAtAbscissa(centr-absc, context, angleUnit); + T xDelta = halfLength * x[j]; + T fval1 = functionValueAtAbscissa(center - xDelta, context, complexFormat, angleUnit); if (std::isnan(fval1)) { return errorResult; } - T fval2 = functionValueAtAbscissa(centr+absc, context, angleUnit); + T fval2 = functionValueAtAbscissa(center + xDelta, context, complexFormat, angleUnit); if (std::isnan(fval2)) { return errorResult; } fv1[j] = fval1; fv2[j] = fval2; - T fsum = fval1+fval2; - if (j%2 == 1) { - resg += wg[j/2]*fsum; + T fsum = fval1 + fval2; + if (j % 2 == 1) { + gaussIntegral += wGauss[j/2] * fsum; } - resk += wgk[j]*fsum; - resabs += wgk[j]*(std::fabs(fval1)+std::fabs(fval2)); + kronrodIntegral += wKronrod[j] * fsum; + absKronrodIntegral += wKronrod[j] * (std::fabs(fval1) + std::fabs(fval2)); } - T reskh = resk*0.5; - T resasc = wgk[10]*std::fabs(fc-reskh); + T halfKronrodIntegral = 0.5 * kronrodIntegral; + T kronrodIntegralDifference = wKronrod[10] * std::fabs(fCenter - halfKronrodIntegral); for (int j = 0; j < 10; j++) { - resasc += wgk[j]*(std::fabs(fv1[j]-reskh)+std::fabs(fv2[j]-reskh)); + kronrodIntegralDifference += wKronrod[j] * (std::fabs(fv1[j] - halfKronrodIntegral) + std::fabs(fv2[j] - halfKronrodIntegral)); } - T integral = resk*hlgth; - resabs = resabs*dhlgth; - resasc = resasc*dhlgth; - T abserr = std::fabs((resk-resg)*hlgth); - if (resasc != 0 && abserr != 0) { - abserr = 1 > std::pow((T)(200*abserr/resasc), (T)1.5)? resasc*std::pow((T)(200*abserr/resasc), (T)1.5) : resasc; + T integral = kronrodIntegral * halfLength; + absKronrodIntegral = absKronrodIntegral * absHalfLength; + kronrodIntegralDifference = kronrodIntegralDifference * absHalfLength; + T absError = std::fabs((kronrodIntegral - gaussIntegral) * halfLength); + if (kronrodIntegralDifference != 0 && absError != 0) { + T errorCoefficient = std::pow((T)(200*absError/kronrodIntegralDifference), (T)1.5); + absError = 1 > errorCoefficient ? kronrodIntegralDifference * errorCoefficient : kronrodIntegralDifference; } - if (resabs > max/(50.0*epsilon)) { - abserr = abserr > epsilon*50*resabs ? abserr : epsilon*50*resabs; + if (absKronrodIntegral > max/(50.0 * epsilon)) { + T minError = epsilon * 50 * absKronrodIntegral; + absError = absError > minError ? absError : minError; } DetailedResult result; result.integral = integral; - result.absoluteError = abserr; + result.absoluteError = absError; return result; } template -T IntegralNode::adaptiveQuadrature(T a, T b, T eps, int numberOfIterations, Context & context, Preferences::AngleUnit angleUnit) const { - if (Integral::shouldStopProcessing()) { +T IntegralNode::adaptiveQuadrature(T a, T b, T eps, int numberOfIterations, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { + if (Expression::ShouldStopProcessing()) { return NAN; } - DetailedResult quadKG = kronrodGaussQuadrature(a, b, context, angleUnit); + DetailedResult quadKG = kronrodGaussQuadrature(a, b, context, complexFormat, angleUnit); T result = quadKG.integral; if (quadKG.absoluteError <= eps) { return result; } else if (--numberOfIterations > 0) { T m = (a+b)/2; - return adaptiveQuadrature(a, m, eps/2, numberOfIterations, context, angleUnit) + adaptiveQuadrature(m, b, eps/2, numberOfIterations, context, angleUnit); + return adaptiveQuadrature(a, m, eps/2, numberOfIterations, context, complexFormat, angleUnit) + adaptiveQuadrature(m, b, eps/2, numberOfIterations, context, complexFormat, angleUnit); } else { return NAN; } } #endif -Expression Integral::shallowReduce(Context & context, Preferences::AngleUnit angleUnit) { +Expression Integral::UntypedBuilder(Expression children) { + assert(children.type() == ExpressionNode::Type::Matrix); + if (children.childAtIndex(1).type() != ExpressionNode::Type::Symbol) { + // Second parameter must be a Symbol. + return Expression(); + } + return Builder(children.childAtIndex(0), children.childAtIndex(1).convert(), children.childAtIndex(2), children.childAtIndex(3)); +} + +Expression Integral::shallowReduce() { { - Expression e = Expression::defaultShallowReduce(context, angleUnit); + Expression e = Expression::defaultShallowReduce(); if (e.isUndefined()) { return e; } @@ -208,7 +232,7 @@ Expression Integral::shallowReduce(Context & context, Preferences::AngleUnit ang || childAtIndex(2).type() == ExpressionNode::Type::Matrix || childAtIndex(3).type() == ExpressionNode::Type::Matrix) { - return Undefined(); + return Undefined::Builder(); } #endif return *this; diff --git a/poincare/src/layout.cpp b/poincare/src/layout.cpp index fb43bfa2e..103f5fa6d 100644 --- a/poincare/src/layout.cpp +++ b/poincare/src/layout.cpp @@ -82,7 +82,7 @@ void Layout::replaceChild(Layout oldChild, Layout newChild, LayoutCursor * curso } void Layout::replaceChildWithEmpty(Layout oldChild, LayoutCursor * cursor) { - replaceChild(oldChild, EmptyLayout(), cursor); + replaceChild(oldChild, EmptyLayout::Builder(), cursor); } void Layout::replaceWith(Layout newChild, LayoutCursor * cursor) { @@ -97,7 +97,7 @@ void Layout::replaceWithJuxtapositionOf(Layout leftChild, Layout rightChild, Lay if (!p.isHorizontal()) { /* One of the children to juxtapose might be "this", so we cannot just call * replaceWith. */ - HorizontalLayout horizontalLayoutR; + HorizontalLayout horizontalLayoutR = HorizontalLayout::Builder(); p.replaceChild(*this, horizontalLayoutR, cursor); horizontalLayoutR.addOrMergeChildAtIndex(leftChild, 0, false); if (putCursorInTheMiddle) { @@ -286,7 +286,7 @@ void Layout::collapseSiblings(LayoutCursor * cursor) { if (node()->shouldCollapseSiblingsOnRight()) { Layout absorbingChild = childAtIndex(rightCollapsingAbsorbingChildIndex()); if (!absorbingChild.isHorizontal()) { - Layout horRef = HorizontalLayout(); + Layout horRef = HorizontalLayout::Builder(); replaceChild(absorbingChild, horRef, cursor, true); horRef.addChildAtIndexInPlace(absorbingChild, 0, 0); } @@ -295,7 +295,7 @@ void Layout::collapseSiblings(LayoutCursor * cursor) { if (node()->shouldCollapseSiblingsOnLeft()) { Layout absorbingChild = childAtIndex(leftCollapsingAbsorbingChildIndex()); if (!absorbingChild.isHorizontal()) { - Layout horRef = HorizontalLayout(); + Layout horRef = HorizontalLayout::Builder(); replaceChild(absorbingChild, horRef, cursor, true); horRef.addChildAtIndexInPlace(absorbingChild, 0, 0); } diff --git a/poincare/src/layout_cursor.cpp b/poincare/src/layout_cursor.cpp index 77a39a976..07b205b4c 100644 --- a/poincare/src/layout_cursor.cpp +++ b/poincare/src/layout_cursor.cpp @@ -73,51 +73,51 @@ void LayoutCursor::move(MoveDirection direction, bool * shouldRecomputeLayout) { /* Layout modification */ void LayoutCursor::addEmptyExponentialLayout() { - EmptyLayout emptyLayout; - HorizontalLayout sibling = HorizontalLayout( - CharLayout(Ion::Charset::Exponential), - VerticalOffsetLayout(emptyLayout, VerticalOffsetLayoutNode::Type::Superscript)); + EmptyLayout emptyLayout = EmptyLayout::Builder(); + HorizontalLayout sibling = HorizontalLayout::Builder( + CharLayout::Builder(Ion::Charset::Exponential), + VerticalOffsetLayout::Builder(emptyLayout, VerticalOffsetLayoutNode::Type::Superscript)); m_layout.addSibling(this, sibling, false); m_layout = emptyLayout; } void LayoutCursor::addEmptyMatrixLayout() { - MatrixLayout matrixLayout = MatrixLayout( - EmptyLayout(EmptyLayoutNode::Color::Yellow), - EmptyLayout(EmptyLayoutNode::Color::Grey), - EmptyLayout(EmptyLayoutNode::Color::Grey), - EmptyLayout(EmptyLayoutNode::Color::Grey)); + MatrixLayout matrixLayout = MatrixLayout::Builder( + EmptyLayout::Builder(EmptyLayoutNode::Color::Yellow), + EmptyLayout::Builder(EmptyLayoutNode::Color::Grey), + EmptyLayout::Builder(EmptyLayoutNode::Color::Grey), + EmptyLayout::Builder(EmptyLayoutNode::Color::Grey)); m_layout.addSibling(this, matrixLayout, false); m_layout = matrixLayout.childAtIndex(0); m_position = Position::Right; } void LayoutCursor::addEmptySquareRootLayout() { - HorizontalLayout child1 = HorizontalLayout(EmptyLayout()); - NthRootLayout newChild = NthRootLayout(child1); + HorizontalLayout child1 = HorizontalLayout::Builder(EmptyLayout::Builder()); + NthRootLayout newChild = NthRootLayout::Builder(child1); m_layout.addSibling(this, newChild, false); m_layout = newChild.childAtIndex(0); ((Layout *)&newChild)->collapseSiblings(this); } void LayoutCursor::addEmptyPowerLayout() { - VerticalOffsetLayout offsetLayout = VerticalOffsetLayout(EmptyLayout(), VerticalOffsetLayoutNode::Type::Superscript); + VerticalOffsetLayout offsetLayout = VerticalOffsetLayout::Builder(EmptyLayout::Builder(), VerticalOffsetLayoutNode::Type::Superscript); privateAddEmptyPowerLayout(offsetLayout); m_layout = offsetLayout.childAtIndex(0); } void LayoutCursor::addEmptySquarePowerLayout() { - VerticalOffsetLayout offsetLayout = VerticalOffsetLayout(CharLayout('2'), VerticalOffsetLayoutNode::Type::Superscript); + VerticalOffsetLayout offsetLayout = VerticalOffsetLayout::Builder(CharLayout::Builder('2'), VerticalOffsetLayoutNode::Type::Superscript); privateAddEmptyPowerLayout(offsetLayout); } void LayoutCursor::addEmptyTenPowerLayout() { - EmptyLayout emptyLayout; - HorizontalLayout sibling = HorizontalLayout( - CharLayout(Ion::Charset::MiddleDot), - CharLayout('1'), - CharLayout('0'), - VerticalOffsetLayout( + EmptyLayout emptyLayout = EmptyLayout::Builder(); + HorizontalLayout sibling = HorizontalLayout::Builder( + CharLayout::Builder(Ion::Charset::MiddleDot), + CharLayout::Builder('1'), + CharLayout::Builder('0'), + VerticalOffsetLayout::Builder( emptyLayout, VerticalOffsetLayoutNode::Type::Superscript)); m_layout.addSibling(this, sibling, false); @@ -125,15 +125,15 @@ void LayoutCursor::addEmptyTenPowerLayout() { } void LayoutCursor::addFractionLayoutAndCollapseSiblings() { - HorizontalLayout child1 = HorizontalLayout(EmptyLayout()); - HorizontalLayout child2 = HorizontalLayout(EmptyLayout()); - FractionLayout newChild = FractionLayout(child1, child2); + HorizontalLayout child1 = HorizontalLayout::Builder(EmptyLayout::Builder()); + HorizontalLayout child2 = HorizontalLayout::Builder(EmptyLayout::Builder()); + FractionLayout newChild = FractionLayout::Builder(child1, child2); m_layout.addSibling(this, newChild, true); Layout(newChild.node()).collapseSiblings(this); } void LayoutCursor::addXNTCharLayout() { - m_layout.addSibling(this, CharLayout(m_layout.XNTChar()), true); + m_layout.addSibling(this, CharLayout::Builder(m_layout.XNTChar()), true); } void LayoutCursor::insertText(const char * text) { @@ -148,14 +148,14 @@ void LayoutCursor::insertText(const char * text) { continue; } if (text[i] == Ion::Charset::MultiplicationSign) { - newChild = CharLayout(Ion::Charset::MiddleDot); + newChild = CharLayout::Builder(Ion::Charset::MiddleDot); } else if (text[i] == '(') { - newChild = LeftParenthesisLayout(); + newChild = LeftParenthesisLayout::Builder(); if (pointedChild.isUninitialized()) { pointedChild = newChild; } } else if (text[i] == ')') { - newChild = RightParenthesisLayout(); + newChild = RightParenthesisLayout::Builder(); } /* We never insert text with brackets for now. Removing this code saves the * binary file 2K. */ @@ -167,7 +167,7 @@ void LayoutCursor::insertText(const char * text) { } #endif else { - newChild = CharLayout(text[i]); + newChild = CharLayout::Builder(text[i]); } m_layout.addSibling(this, newChild, true); } @@ -217,8 +217,8 @@ void LayoutCursor::privateAddEmptyPowerLayout(VerticalOffsetLayout v) { return; } // Else, add an empty base - EmptyLayout e = EmptyLayout(); - HorizontalLayout newChild = HorizontalLayout(e, v); + EmptyLayout e = EmptyLayout::Builder(); + HorizontalLayout newChild = HorizontalLayout::Builder(e, v); m_layout.addSibling(this, newChild, true); } diff --git a/poincare/src/layout_helper.cpp b/poincare/src/layout_helper.cpp index 750bfe098..7a61da0b8 100644 --- a/poincare/src/layout_helper.cpp +++ b/poincare/src/layout_helper.cpp @@ -11,7 +11,7 @@ namespace Poincare { Layout LayoutHelper::Infix(const Expression & expression, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits, const char * operatorName) { int numberOfChildren = expression.numberOfChildren(); assert(numberOfChildren > 1); - HorizontalLayout result; + HorizontalLayout result = HorizontalLayout::Builder(); result.addOrMergeChildAtIndex(expression.childAtIndex(0).createLayout(floatDisplayMode, numberOfSignificantDigits), 0, true); for (int i = 1; i < numberOfChildren; i++) { result.addOrMergeChildAtIndex(String(operatorName, strlen(operatorName)), result.numberOfChildren(), true); @@ -24,17 +24,17 @@ Layout LayoutHelper::Infix(const Expression & expression, Preferences::PrintFloa } Layout LayoutHelper::Prefix(const Expression & expression, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits, const char * operatorName) { - HorizontalLayout result; + HorizontalLayout result = HorizontalLayout::Builder(); // Add the operator name. result.addOrMergeChildAtIndex(String(operatorName, strlen(operatorName)), 0, true); // Create the layout of arguments separated by commas. - HorizontalLayout args; + HorizontalLayout args = HorizontalLayout::Builder(); int numberOfChildren = expression.numberOfChildren(); if (numberOfChildren > 0) { args.addOrMergeChildAtIndex(expression.childAtIndex(0).createLayout(floatDisplayMode, numberOfSignificantDigits), 0, true); for (int i = 1; i < numberOfChildren; i++) { - args.addChildAtIndex(CharLayout(','), args.numberOfChildren(), args.numberOfChildren(), nullptr); + args.addChildAtIndex(CharLayout::Builder(','), args.numberOfChildren(), args.numberOfChildren(), nullptr); args.addOrMergeChildAtIndex(expression.childAtIndex(i).createLayout(floatDisplayMode, numberOfSignificantDigits), args.numberOfChildren(), true); } } @@ -44,27 +44,27 @@ Layout LayoutHelper::Prefix(const Expression & expression, Preferences::PrintFlo } Layout LayoutHelper::Parentheses(Layout layout, bool cloneLayout) { - HorizontalLayout result; - result.addChildAtIndex(LeftParenthesisLayout(), 0, 0, nullptr); + HorizontalLayout result = HorizontalLayout::Builder(); + result.addChildAtIndex(LeftParenthesisLayout::Builder(), 0, 0, nullptr); if (!layout.isUninitialized()) { result.addOrMergeChildAtIndex(cloneLayout ? layout.clone() : layout, 1, true); } - result.addChildAtIndex(RightParenthesisLayout(), result.numberOfChildren(), result.numberOfChildren(), nullptr); + result.addChildAtIndex(RightParenthesisLayout::Builder(), result.numberOfChildren(), result.numberOfChildren(), nullptr); return result; } HorizontalLayout LayoutHelper::String(const char * buffer, int bufferLen, const KDFont * font) { assert(bufferLen > 0); - HorizontalLayout resultLayout; + HorizontalLayout resultLayout = HorizontalLayout::Builder(); for (int i = 0; i < bufferLen; i++) { - resultLayout.addChildAtIndex(CharLayout(buffer[i], font), i, i, nullptr); + resultLayout.addChildAtIndex(CharLayout::Builder(buffer[i], font), i, i, nullptr); } return resultLayout; } Layout LayoutHelper::Logarithm(Layout argument, Layout index) { HorizontalLayout resultLayout = String("log", 3); - VerticalOffsetLayout offsetLayout = VerticalOffsetLayout(index, VerticalOffsetLayoutNode::Type::Subscript); + VerticalOffsetLayout offsetLayout = VerticalOffsetLayout::Builder(index, VerticalOffsetLayoutNode::Type::Subscript); resultLayout.addChildAtIndex(offsetLayout, resultLayout.numberOfChildren(), resultLayout.numberOfChildren(), nullptr); resultLayout.addOrMergeChildAtIndex(Parentheses(argument, false), resultLayout.numberOfChildren(), true); return resultLayout; diff --git a/poincare/src/least_common_multiple.cpp b/poincare/src/least_common_multiple.cpp index e8092e698..cc28523fb 100644 --- a/poincare/src/least_common_multiple.cpp +++ b/poincare/src/least_common_multiple.cpp @@ -21,21 +21,21 @@ int LeastCommonMultipleNode::serialize(char * buffer, int bufferSize, Preference return SerializationHelper::Prefix(this, buffer, bufferSize, floatDisplayMode, numberOfSignificantDigits, LeastCommonMultiple::s_functionHelper.name()); } -Expression LeastCommonMultipleNode::shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) { - return LeastCommonMultiple(this).shallowReduce(context, angleUnit); +Expression LeastCommonMultipleNode::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) { + return LeastCommonMultiple(this).shallowReduce(); } template -Evaluation LeastCommonMultipleNode::templatedApproximate(Context& context, Preferences::AngleUnit angleUnit) const { - Evaluation f1Input = childAtIndex(0)->approximate(T(), context, angleUnit); - Evaluation f2Input = childAtIndex(1)->approximate(T(), context, angleUnit); +Evaluation LeastCommonMultipleNode::templatedApproximate(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { + Evaluation f1Input = childAtIndex(0)->approximate(T(), context, complexFormat, angleUnit); + Evaluation f2Input = childAtIndex(1)->approximate(T(), context, complexFormat, angleUnit); T f1 = f1Input.toScalar(); T f2 = f2Input.toScalar(); if (std::isnan(f1) || std::isnan(f2) || f1 != (int)f1 || f2 != (int)f2) { return Complex::Undefined(); } if (f1 == 0.0f || f2 == 0.0f) { - return Complex(0.0); + return Complex::Builder(0.0); } int a = (int)f2; int b = (int)f1; @@ -50,12 +50,13 @@ Evaluation LeastCommonMultipleNode::templatedApproximate(Context& context, Pr a = b; b = r; } - return Complex(product/a); + return Complex::Builder(product/a); } -Expression LeastCommonMultiple::shallowReduce(Context & context, Preferences::AngleUnit angleUnit) { + +Expression LeastCommonMultiple::shallowReduce() { { - Expression e = Expression::defaultShallowReduce(context, angleUnit); + Expression e = Expression::defaultShallowReduce(); if (e.isUndefined()) { return e; } @@ -64,13 +65,13 @@ Expression LeastCommonMultiple::shallowReduce(Context & context, Preferences::An Expression c1 = childAtIndex(1); #if MATRIX_EXACT_REDUCING if (c0.type() == Type::Matrix || c1.type() == Type::Matrix) { - return Undefined(); + return Undefined::Builder(); } #endif if (c0.type() == ExpressionNode::Type::Rational) { Rational r0 = static_cast(c0); if (!r0.integerDenominator().isOne()) { - Expression result = Undefined(); + Expression result = Undefined::Builder(); replaceWithInPlace(result); return result; } @@ -78,7 +79,7 @@ Expression LeastCommonMultiple::shallowReduce(Context & context, Preferences::An if (c1.type() == ExpressionNode::Type::Rational) { Rational r1 = static_cast(c1); if (!r1.integerDenominator().isOne()) { - Expression result = Undefined(); + Expression result = Undefined::Builder(); replaceWithInPlace(result); return result; } @@ -92,10 +93,10 @@ Expression LeastCommonMultiple::shallowReduce(Context & context, Preferences::An Integer a = r0.signedIntegerNumerator(); Integer b = r1.signedIntegerNumerator(); Integer lcm = Arithmetic::LCM(a, b); - if (lcm.isInfinity()) { + if (lcm.isOverflow()) { return *this; } - Expression result = Rational(lcm); + Expression result = Rational::Builder(lcm); replaceWithInPlace(result); return result; } diff --git a/poincare/src/left_parenthesis_layout.cpp b/poincare/src/left_parenthesis_layout.cpp index 150af8b98..8ddd2dea5 100644 --- a/poincare/src/left_parenthesis_layout.cpp +++ b/poincare/src/left_parenthesis_layout.cpp @@ -59,6 +59,4 @@ void LeftParenthesisLayoutNode::render(KDContext * ctx, KDPoint p, KDColor expre RenderWithChildHeight(ParenthesisLayoutNode::ChildHeightGivenLayoutHeight(layoutSize().height()), ctx, p, expressionColor, backgroundColor); } -LeftParenthesisLayout::LeftParenthesisLayout() : Layout(TreePool::sharedPool()->createTreeNode()) {} - } diff --git a/poincare/src/left_square_bracket_layout.cpp b/poincare/src/left_square_bracket_layout.cpp index b380e9b8c..2a8d85c92 100644 --- a/poincare/src/left_square_bracket_layout.cpp +++ b/poincare/src/left_square_bracket_layout.cpp @@ -8,6 +8,4 @@ void LeftSquareBracketLayoutNode::render(KDContext * ctx, KDPoint p, KDColor exp ctx->fillRect(KDRect(p.x()+k_externWidthMargin, p.y() + childHeight(), k_bracketWidth, k_lineThickness), expressionColor); } -LeftSquareBracketLayout::LeftSquareBracketLayout() : Layout(TreePool::sharedPool()->createTreeNode()) {} - } diff --git a/poincare/src/logarithm.cpp b/poincare/src/logarithm.cpp index 0c7f7cf1f..4301d3c07 100644 --- a/poincare/src/logarithm.cpp +++ b/poincare/src/logarithm.cpp @@ -1,4 +1,5 @@ #include + #include #include #include @@ -48,46 +49,46 @@ int LogarithmNode::serialize(char * buffer, int bufferSize, Preferences::Prin } template<> -Expression LogarithmNode<1>::shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { - return CommonLogarithm(this).shallowReduce(context, angleUnit, target); +Expression LogarithmNode<1>::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { + return CommonLogarithm(this).shallowReduce(context, complexFormat, angleUnit, target); } template<> -Expression LogarithmNode<2>::shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { - return Logarithm(this).shallowReduce(context, angleUnit, target); +Expression LogarithmNode<2>::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { + return Logarithm(this).shallowReduce(context, complexFormat, angleUnit, target); } template<> -Expression LogarithmNode<1>::shallowBeautify(Context & context, Preferences::AngleUnit angleUnit) { +Expression LogarithmNode<1>::shallowBeautify(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { return CommonLogarithm(this); } template<> -Expression LogarithmNode<2>::shallowBeautify(Context & context, Preferences::AngleUnit angleUnit) { - return Logarithm(this).shallowBeautify(context, angleUnit); +Expression LogarithmNode<2>::shallowBeautify(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { + return Logarithm(this).shallowBeautify(); } template<> -template Evaluation LogarithmNode<1>::templatedApproximate(Context& context, Preferences::AngleUnit angleUnit) const { - return ApproximationHelper::Map(this, context, angleUnit, computeOnComplex); +template Evaluation LogarithmNode<1>::templatedApproximate(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { + return ApproximationHelper::Map(this, context, complexFormat, angleUnit, computeOnComplex); } template<> -template Evaluation LogarithmNode<2>::templatedApproximate(Context& context, Preferences::AngleUnit angleUnit) const { - Evaluation x = childAtIndex(0)->approximate(U(), context, angleUnit); - Evaluation n = childAtIndex(1)->approximate(U(), context, angleUnit); +template Evaluation LogarithmNode<2>::templatedApproximate(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { + Evaluation x = childAtIndex(0)->approximate(U(), context, complexFormat, angleUnit); + Evaluation n = childAtIndex(1)->approximate(U(), context, complexFormat, angleUnit); std::complex result = std::complex(NAN, NAN); if (x.type() == EvaluationNode::Type::Complex && n.type() == EvaluationNode::Type::Complex) { std::complex xc = (static_cast&>(x)).stdComplex(); std::complex nc = (static_cast&>(n)).stdComplex(); - result = DivisionNode::compute(computeOnComplex(xc, angleUnit).stdComplex(), computeOnComplex(nc, angleUnit).stdComplex()).stdComplex(); + result = DivisionNode::compute(computeOnComplex(xc, complexFormat, angleUnit).stdComplex(), computeOnComplex(nc, complexFormat, angleUnit).stdComplex(), complexFormat).stdComplex(); } - return Complex(result); + return Complex::Builder(result); } -Expression CommonLogarithm::shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target){ +Expression CommonLogarithm::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target){ { - Expression e = Expression::defaultShallowReduce(context, angleUnit); + Expression e = Expression::defaultShallowReduce(); if (e.isUndefined()) { return e; } @@ -100,14 +101,14 @@ Expression CommonLogarithm::shallowReduce(Context & context, Preferences::AngleU } #endif #endif - Logarithm log = Logarithm::Builder(childAtIndex(0), Rational(10)); + Logarithm log = Logarithm::Builder(childAtIndex(0), Rational::Builder(10)); replaceWithInPlace(log); - return log.shallowReduce(context, angleUnit, target); + return log.shallowReduce(context, complexFormat, angleUnit, target); } -Expression Logarithm::shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target){ +Expression Logarithm::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target){ { - Expression e = Expression::defaultShallowReduce(context, angleUnit); + Expression e = Expression::defaultShallowReduce(); if (e.isUndefined()) { return e; } @@ -116,14 +117,14 @@ Expression Logarithm::shallowReduce(Context & context, Preferences::AngleUnit an #if MATRIX_EXACT_REDUCING #if 0 if (c.type() == ExpressionNode::Type::Matrix || childAtIndex(1).type() == ExpressionNode::Type::Matrix) { - return Undefined(); + return Undefined::Builder(); } #endif #endif - if (c.sign() == ExpressionNode::Sign::Negative || childAtIndex(1).sign() == ExpressionNode::Sign::Negative) { + if (c.sign(&context) == ExpressionNode::Sign::Negative || childAtIndex(1).sign(&context) == ExpressionNode::Sign::Negative) { return *this; } - Expression f = simpleShallowReduce(context, angleUnit); + Expression f = simpleShallowReduce(context, complexFormat, angleUnit); if (f.type() != ExpressionNode::Type::Logarithm) { return f; } @@ -134,105 +135,134 @@ Expression Logarithm::shallowReduce(Context & context, Preferences::AngleUnit an * - the reduction is being BottomUp. In this case, we do not yet have any * information on the parent which could later be a power of b. */ - bool letLogAtRoot = target == ExpressionNode::ReductionTarget::BottomUpComputation || parentIsAPowerOfSameBase(); + bool letLogAtRoot = parentIsAPowerOfSameBase(); + + // log(+inf, a) ? + if (!letLogAtRoot && c.type() == ExpressionNode::Type::Infinity && c.sign(&context) == ExpressionNode::Sign::Positive) { + Expression base = childAtIndex(1); + // log(+inf, a) --> ±inf with a rational and a > 0 + if (base.type() == ExpressionNode::Type::Rational && !static_cast(base).isNegative() && !static_cast(base).isZero()) { + // log(+inf,a) with a < 1 --> -inf + // log(+inf,a) with a > 1 --> inf + if (static_cast(base).signedIntegerNumerator().isLowerThan(static_cast(base).integerDenominator())) { + c = c.setSign(ExpressionNode::Sign::Negative, &context, complexFormat, angleUnit, target); + } + replaceWithInPlace(c); + return c; + } else if (base.type() == ExpressionNode::Type::Constant && (static_cast(base).isExponential() || static_cast(base).isPi())) { + replaceWithInPlace(c); + return c; + } + } + // log(x^y, b)->y*log(x, b) if x>0 - if (!letLogAtRoot && c.type() == ExpressionNode::Type::Power && c.childAtIndex(0).sign() == ExpressionNode::Sign::Positive) { + if (!letLogAtRoot && c.type() == ExpressionNode::Type::Power && c.childAtIndex(0).sign(&context) == ExpressionNode::Sign::Positive) { Power p = static_cast(c); Expression x = p.childAtIndex(0); Expression y = p.childAtIndex(1); replaceChildInPlace(p, x); - Multiplication mult(y); + Multiplication mult = Multiplication::Builder(y); replaceWithInPlace(mult); mult.addChildAtIndexInPlace(*this, 1, 1); // --> y*log(x,b) - shallowReduce(context, angleUnit, target); // reduce log (ie log(e, e) = 1) - return mult.shallowReduce(context, angleUnit, target); + shallowReduce(context, complexFormat, angleUnit, target); // reduce log (ie log(e, e) = 1) + return mult.shallowReduce(context, complexFormat, angleUnit, target); } // log(x*y, b)->log(x,b)+log(y, b) if x,y>0 if (!letLogAtRoot && c.type() == ExpressionNode::Type::Multiplication) { - Addition a = Addition(); + Addition a = Addition::Builder(); for (int i = 0; i < c.numberOfChildren()-1; i++) { Expression factor = c.childAtIndex(i); - if (factor.sign() == ExpressionNode::Sign::Positive) { + if (factor.sign(&context) == ExpressionNode::Sign::Positive) { Expression newLog = clone(); static_cast(c).removeChildInPlace(factor, factor.numberOfChildren()); newLog.replaceChildAtIndexInPlace(0, factor); a.addChildAtIndexInPlace(newLog, a.numberOfChildren(), a.numberOfChildren()); - newLog.shallowReduce(context, angleUnit, target); + newLog.shallowReduce(context, complexFormat, angleUnit, target); } } if (a.numberOfChildren() > 0) { - c.shallowReduce(context, angleUnit, target); - Expression reducedLastLog = shallowReduce(context, angleUnit, target); + c.shallowReduce(context, complexFormat, angleUnit, target); + Expression reducedLastLog = shallowReduce(context, complexFormat, angleUnit, target); reducedLastLog.replaceWithInPlace(a); a.addChildAtIndexInPlace(reducedLastLog, a.numberOfChildren(), a.numberOfChildren()); - return a.shallowReduce(context, angleUnit, target); + return a.shallowReduce(context, complexFormat, angleUnit, target); } } // log(r) with r Rational if (!letLogAtRoot && c.type() == ExpressionNode::Type::Rational) { Rational r = static_cast(c); - Addition a; + Addition a = Addition::Builder(); // if the log base is Integer: log_b(r) = c + log_b(r') with r = b^c*r' if (childAtIndex(1).type() == ExpressionNode::Type::Rational && childAtIndex(1).convert().integerDenominator().isOne()) { Integer b = childAtIndex(1).convert().signedIntegerNumerator(); Integer newNumerator = simplifyLogarithmIntegerBaseInteger(r.signedIntegerNumerator(), b, a, false); Integer newDenomitor = simplifyLogarithmIntegerBaseInteger(r.integerDenominator(), b, a, true); - r = Rational(newNumerator, newDenomitor); + r = Rational::Builder(newNumerator, newDenomitor); } // log(r) = a0log(p0)+a1log(p1)+... with r = p0^a0*p1^a1*... (Prime decomposition) - a.addChildAtIndexInPlace(splitLogarithmInteger(r.signedIntegerNumerator(), false, context, angleUnit, target), a.numberOfChildren(), a.numberOfChildren()); - a.addChildAtIndexInPlace(splitLogarithmInteger(r.integerDenominator(), true, context, angleUnit, target), a.numberOfChildren(), a.numberOfChildren()); + a.addChildAtIndexInPlace(splitLogarithmInteger(r.signedIntegerNumerator(), false, context, complexFormat, angleUnit, target), a.numberOfChildren(), a.numberOfChildren()); + a.addChildAtIndexInPlace(splitLogarithmInteger(r.integerDenominator(), true, context, complexFormat, angleUnit, target), a.numberOfChildren(), a.numberOfChildren()); replaceWithInPlace(a); - return a.shallowReduce(context, angleUnit, target); + return a.shallowReduce(context, complexFormat, angleUnit, target); } return *this; } -Expression Logarithm::simpleShallowReduce(Context & context, Preferences::AngleUnit angleUnit) { +Expression Logarithm::simpleShallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) { Expression c = childAtIndex(0); + Expression b = childAtIndex(1); // log(0,0)->Undefined - if (c.type() == ExpressionNode::Type::Rational && childAtIndex(1).type() == ExpressionNode::Type::Rational && childAtIndex(1).convert().isZero() && static_cast(c).isZero()) { - Expression result = Undefined(); + if (c.type() == ExpressionNode::Type::Rational && b.type() == ExpressionNode::Type::Rational && static_cast(b).isZero() && static_cast(c).isZero()) { + Expression result = Undefined::Builder(); replaceWithInPlace(result); return result; } // log(x,1)->Undefined - if (childAtIndex(1).type() == ExpressionNode::Type::Rational && childAtIndex(1).convert().isOne()) { - Expression result = Undefined(); + if (b.type() == ExpressionNode::Type::Rational && static_cast(b).isOne()) { + Expression result = Undefined::Builder(); replaceWithInPlace(result); return result; } - // log(x,x)->1 - if (c.isIdenticalTo(childAtIndex(1))) { - Expression result = Rational(1); + bool infiniteArg = c.recursivelyMatchesInfinity(context); + // log(x,x)->1 with x != inf and log(inf,inf) = undef + if (c.isIdenticalTo(b)) { + Expression result = infiniteArg ? Undefined::Builder().convert() : Rational::Builder(1).convert(); replaceWithInPlace(result); return result; } - // log(x,0)->0 - if (childAtIndex(1).type() == ExpressionNode::Type::Rational && childAtIndex(1).convert().isZero()) { - Expression result = Rational(0); + // log(x,0)->0 with x != inf and log(inf,0) = undef + if (b.type() == ExpressionNode::Type::Rational && static_cast(b).isZero()) { + Expression result = infiniteArg ? Undefined::Builder().convert() : Rational::Builder(0).convert(); replaceWithInPlace(result); return result; } + if (c.type() == ExpressionNode::Type::Rational) { const Rational r = static_cast(c); - // log(0, x) = -inf if x > 1 || inf x < 1 || undef if x < 0 + // log(0, x) = -inf if x > 1 && x != inf || inf x < 1 || undef if x < 0 if (r.isZero()) { + bool infiniteBase = b.recursivelyMatchesInfinity(context); + // Special case: log(0,inf) -> undef + if (infiniteBase) { + Expression result = Undefined::Builder(); + replaceWithInPlace(result); + return result; + } bool isNegative = true; Expression result; - Evaluation baseApproximation = childAtIndex(1).node()->approximate(1.0f, context, angleUnit); + Evaluation baseApproximation = b.node()->approximate(1.0f, context, complexFormat, angleUnit); std::complex logDenominator = std::log10(static_cast&>(baseApproximation).stdComplex()); if (logDenominator.imag() != 0.0f || logDenominator.real() == 0.0f) { - result = Undefined(); + result = Undefined::Builder(); } isNegative = logDenominator.real() > 0.0; - result = result.isUninitialized() ? Infinity(isNegative) : result; + result = result.isUninitialized() ? Infinity::Builder(isNegative) : result; replaceWithInPlace(result); return result; } // log(1) = 0; if (r.isOne()) { - Expression result = Rational(0); + Expression result = Rational::Builder(0); replaceWithInPlace(result); return result; } @@ -266,58 +296,58 @@ Integer Logarithm::simplifyLogarithmIntegerBaseInteger(Integer i, Integer & base assert(!i.isNegative() && !base.isNegative()); assert(!i.isZero() && !base.isZero() && !base.isOne()); IntegerDivision div = Integer::Division(i, base); - while (!div.quotient.isInfinity() && div.remainder.isZero()) { + while (!div.quotient.isOverflow() && div.remainder.isZero()) { i = div.quotient; - a.addChildAtIndexInPlace(isDenominator ? Rational(-1) : Rational(1), a.numberOfChildren(), a.numberOfChildren()); // a++ + a.addChildAtIndexInPlace(isDenominator ? Rational::Builder(-1) : Rational::Builder(1), a.numberOfChildren(), a.numberOfChildren()); // a++ div = Integer::Division(i, base); } return i; } -Expression Logarithm::splitLogarithmInteger(Integer i, bool isDenominator, Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { +Expression Logarithm::splitLogarithmInteger(Integer i, bool isDenominator, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { assert(!i.isZero()); assert(!i.isNegative()); Integer factors[Arithmetic::k_maxNumberOfPrimeFactors]; Integer coefficients[Arithmetic::k_maxNumberOfPrimeFactors]; int numberOfPrimeFactors = Arithmetic::PrimeFactorization(i, factors, coefficients, Arithmetic::k_maxNumberOfPrimeFactors); if (numberOfPrimeFactors == 0) { - return Rational(0); + return Rational::Builder(0); } if (numberOfPrimeFactors < 0) { /* We could not break i in prime factor (either it might take too many * factors or too much time). */ Expression e = clone(); - e.replaceChildAtIndexInPlace(0, Rational(i)); + e.replaceChildAtIndexInPlace(0, Rational::Builder(i)); if (!isDenominator) { return e; } - Multiplication m = Multiplication(Rational(-1), e); + Multiplication m = Multiplication::Builder(Rational::Builder(-1), e); return m; } - Addition a; + Addition a = Addition::Builder(); for (int index = 0; index < numberOfPrimeFactors; index++) { if (isDenominator) { coefficients[index].setNegative(true); } Logarithm e = clone().convert(); - e.replaceChildAtIndexInPlace(0, Rational(factors[index])); - Multiplication m = Multiplication(Rational(coefficients[index]), e); - e.simpleShallowReduce(context, angleUnit); + e.replaceChildAtIndexInPlace(0, Rational::Builder(factors[index])); + Multiplication m = Multiplication::Builder(Rational::Builder(coefficients[index]), e); + e.simpleShallowReduce(context, complexFormat, angleUnit); a.addChildAtIndexInPlace(m, a.numberOfChildren(), a.numberOfChildren()); - m.shallowReduce(context, angleUnit, target); + m.shallowReduce(context, complexFormat, angleUnit, target); } return a; } -Expression Logarithm::shallowBeautify(Context & context, Preferences::AngleUnit angleUnit) { +Expression Logarithm::shallowBeautify() { assert(numberOfChildren() == 2); - Constant e = Constant(Ion::Charset::Exponential); + Constant e = Constant::Builder(Ion::Charset::Exponential); if (childAtIndex(1).isIdenticalTo(e)) { NaperianLogarithm np = NaperianLogarithm::Builder(childAtIndex(0)); replaceWithInPlace(np); return np; } - Rational ten(10); + Rational ten = Rational::Builder(10); if (childAtIndex(1).isIdenticalTo(ten)) { CommonLogarithm l = CommonLogarithm::Builder(childAtIndex(0)); replaceWithInPlace(l); @@ -326,10 +356,10 @@ Expression Logarithm::shallowBeautify(Context & context, Preferences::AngleUnit return *this; } -template Evaluation LogarithmNode<1>::templatedApproximate(Poincare::Context&, Poincare::Preferences::AngleUnit) const; -template Evaluation LogarithmNode<1>::templatedApproximate(Poincare::Context&, Poincare::Preferences::AngleUnit) const; -template Evaluation LogarithmNode<2>::templatedApproximate(Poincare::Context&, Poincare::Preferences::AngleUnit) const; -template Evaluation LogarithmNode<2>::templatedApproximate(Poincare::Context&, Poincare::Preferences::AngleUnit) const; +template Evaluation LogarithmNode<1>::templatedApproximate(Poincare::Context&, Poincare::Preferences::ComplexFormat, Poincare::Preferences::AngleUnit) const; +template Evaluation LogarithmNode<1>::templatedApproximate(Poincare::Context&, Poincare::Preferences::ComplexFormat, Poincare::Preferences::AngleUnit) const; +template Evaluation LogarithmNode<2>::templatedApproximate(Poincare::Context&, Poincare::Preferences::ComplexFormat, Poincare::Preferences::AngleUnit) const; +template Evaluation LogarithmNode<2>::templatedApproximate(Poincare::Context&, Poincare::Preferences::ComplexFormat, Poincare::Preferences::AngleUnit) const; template int LogarithmNode<1>::serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const; template int LogarithmNode<2>::serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const; diff --git a/poincare/src/matrix.cpp b/poincare/src/matrix.cpp index 5d24c4a9a..f2faeef90 100644 --- a/poincare/src/matrix.cpp +++ b/poincare/src/matrix.cpp @@ -23,7 +23,7 @@ int MatrixNode::polynomialDegree(Context & context, const char * symbolName) con Layout MatrixNode::createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const { assert(numberOfChildren() > 0); - MatrixLayout layout; + MatrixLayout layout = MatrixLayout::Builder(); for (ExpressionNode * c : children()) { layout.addChildAtIndex(c->createLayout(floatDisplayMode, numberOfSignificantDigits), layout.numberOfChildren(), layout.numberOfChildren(), nullptr); } @@ -78,10 +78,10 @@ int MatrixNode::serialize(char * buffer, int bufferSize, Preferences::PrintFloat } template -Evaluation MatrixNode::templatedApproximate(Context& context, Preferences::AngleUnit angleUnit) const { - MatrixComplex matrix; +Evaluation MatrixNode::templatedApproximate(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { + MatrixComplex matrix = MatrixComplex::Builder(); for (ExpressionNode * c : children()) { - matrix.addChildAtIndexInPlace(c->approximate(T(), context, angleUnit), matrix.numberOfChildren(), matrix.numberOfChildren()); + matrix.addChildAtIndexInPlace(c->approximate(T(), context, complexFormat, angleUnit), matrix.numberOfChildren(), matrix.numberOfChildren()); } matrix.setDimensions(numberOfRows(), numberOfColumns()); return matrix; @@ -107,9 +107,9 @@ void Matrix::addChildrenAsRowInPlace(TreeHandle t, int i) { setDimensions(previousNumberOfRows + 1, previousNumberOfColumns == 0 ? t.numberOfChildren() : previousNumberOfColumns); } -int Matrix::rank(Context & context, Preferences::AngleUnit angleUnit, bool inPlace) { +int Matrix::rank(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, bool inPlace) { Matrix m = inPlace ? *this : clone().convert(); - m = m.rowCanonize(context, angleUnit); + m = m.rowCanonize(context, complexFormat, angleUnit); int rank = m.numberOfRows(); int i = rank-1; while (i >= 0) { @@ -147,7 +147,7 @@ int Matrix::ArrayInverse(T * array, int numberOfRows, int numberOfColumns) { ArrayRowCanonize(operands, dim, 2*dim); // Check inversibility for (int i = 0; i < dim; i++) { - if (std::abs(operands[i*2*dim+i] - (T)1.0) > Expression::epsilon()) { + if (std::abs(operands[i*2*dim+i] - (T)1.0) > Expression::Epsilon()) { return -2; } } @@ -159,10 +159,10 @@ int Matrix::ArrayInverse(T * array, int numberOfRows, int numberOfColumns) { return 0; } -Matrix Matrix::rowCanonize(Context & context, Preferences::AngleUnit angleUnit, Multiplication determinant) { - Expression::resetInterruption(); +Matrix Matrix::rowCanonize(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, Multiplication determinant) { + Expression::SetInterruption(false); // The matrix children have to be reduced to be able to spot 0 - deepReduceChildren(context, angleUnit, ExpressionNode::ReductionTarget::TopDownComputation); + deepReduceChildren(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::System); int m = numberOfRows(); int n = numberOfColumns(); @@ -180,7 +180,7 @@ Matrix Matrix::rowCanonize(Context & context, Preferences::AngleUnit angleUnit, // No non-null coefficient in this column, skip k++; // Update determinant: det *= 0 - if (!determinant.isUninitialized()) { determinant.addChildAtIndexInPlace(Rational(0), 0, determinant.numberOfChildren()); } + if (!determinant.isUninitialized()) { determinant.addChildAtIndexInPlace(Rational::Builder(0), 0, determinant.numberOfChildren()); } } else { // Swap row h and iPivot if (iPivot != h) { @@ -188,7 +188,7 @@ Matrix Matrix::rowCanonize(Context & context, Preferences::AngleUnit angleUnit, swapChildrenInPlace(iPivot*n+col, h*n+col); } // Update determinant: det *= -1 - if (!determinant.isUninitialized()) { determinant.addChildAtIndexInPlace(Rational(-1), 0, determinant.numberOfChildren()); } + if (!determinant.isUninitialized()) { determinant.addChildAtIndexInPlace(Rational::Builder(-1), 0, determinant.numberOfChildren()); } } /* Set to 1 M[h][k] by linear combination */ Expression divisor = matrixChild(h, k); @@ -196,11 +196,11 @@ Matrix Matrix::rowCanonize(Context & context, Preferences::AngleUnit angleUnit, if (!determinant.isUninitialized()) { determinant.addChildAtIndexInPlace(divisor.clone(), 0, determinant.numberOfChildren()); } for (int j = k+1; j < n; j++) { Expression opHJ = matrixChild(h, j); - Expression newOpHJ = Division(opHJ, divisor.clone()); + Expression newOpHJ = Division::Builder(opHJ, divisor.clone()); replaceChildAtIndexInPlace(h*n+j, newOpHJ); - newOpHJ = newOpHJ.shallowReduce(context, angleUnit, ExpressionNode::ReductionTarget::TopDownComputation); + newOpHJ = newOpHJ.shallowReduce(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::System); } - replaceChildInPlace(divisor, Rational(1)); + replaceChildInPlace(divisor, Rational::Builder(1)); /* Set to 0 all M[i][j] i != h, j > k by linear combination */ for (int i = 0; i < m; i++) { @@ -208,12 +208,12 @@ Matrix Matrix::rowCanonize(Context & context, Preferences::AngleUnit angleUnit, Expression factor = matrixChild(i, k); for (int j = k+1; j < n; j++) { Expression opIJ = matrixChild(i, j); - Expression newOpIJ = Subtraction(opIJ, Multiplication(matrixChild(h, j).clone(), factor.clone())); + Expression newOpIJ = Subtraction::Builder(opIJ, Multiplication::Builder(matrixChild(h, j).clone(), factor.clone())); replaceChildAtIndexInPlace(i*n+j, newOpIJ); - newOpIJ.childAtIndex(1).shallowReduce(context, angleUnit, ExpressionNode::ReductionTarget::TopDownComputation); - newOpIJ = newOpIJ.shallowReduce(context, angleUnit, ExpressionNode::ReductionTarget::TopDownComputation); + newOpIJ.childAtIndex(1).shallowReduce(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::System); + newOpIJ = newOpIJ.shallowReduce(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::System); } - replaceChildAtIndexInPlace(i*n+k, Rational(0)); + replaceChildAtIndexInPlace(i*n+k, Rational::Builder(0)); } h++; k++; @@ -230,7 +230,7 @@ void Matrix::ArrayRowCanonize(T * array, int numberOfRows, int numberOfColumns, while (h < numberOfRows && k < numberOfColumns) { // Find the first non-null pivot int iPivot = h; - while (iPivot < numberOfRows && std::abs(array[iPivot*numberOfColumns+k]) < Expression::epsilon()) { + while (iPivot < numberOfRows && std::abs(array[iPivot*numberOfColumns+k]) < Expression::Epsilon()) { iPivot++; } if (iPivot == numberOfRows) { @@ -292,34 +292,34 @@ Matrix Matrix::createIdentity(int dim) { Matrix matrix(); for (int i = 0; i < dim; i++) { for (int j = 0; j < dim; j++) { - matrix.addChildAtIndexInPlace(i == j ? Rational(1) : Rational(0), i*dim+j, i*dim+j); + matrix.addChildAtIndexInPlace(i == j ? Rational::Builder(1) : Rational::Builder(0), i*dim+j, i*dim+j); } } matrix.setDimensions(dim, dim); return matrix; } -Expression Matrix::inverse(Context & context, Preferences::AngleUnit angleUnit) const { +Expression Matrix::inverse(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { if (m_numberOfRows != m_numberOfColumns) { - return Undefined(); + return Undefined::Builder(); } int dim = m_numberOfRows; /* Create the matrix inv = (A|I) with A the input matrix and I the dim identity matrix */ - Matrix AI; + Matrix AI = Matrix::Builder(); for (int i = 0; i < dim; i++) { for (int j = 0; j < dim; j++) { AI.addChildAtIndexInPlace(matrixChild(i, j), i*2*dim+j, i*2*dim+j); } for (int j = dim; j < 2*dim; j++) { - AI.addChildAtIndexInPlace(j-dim == i ? Rational(1) : Rational(0), i*2*dim+j, i*2*dim+j); + AI.addChildAtIndexInPlace(j-dim == i ? Rational::Builder(1) : Rational::Builder(0), i*2*dim+j, i*2*dim+j); } } AI.setDimensions(dim, 2*dim); - AI = AI.rowCanonize(context, angleUnit); + AI = AI.rowCanonize(context, complexFormat, angleUnit); // Check inversibility for (int i = 0; i < dim; i++) { if (AI.matrixChild(i, i)->type() != ExpressionNode::Type::Rational || !static_cast(AI.matrixChild(i, i)->node())->isOne()) { - return Undefined(); + return Undefined::Builder(); } } Matrix inverse(); diff --git a/poincare/src/matrix_complex.cpp b/poincare/src/matrix_complex.cpp index 5cabea7a7..76eb28e57 100644 --- a/poincare/src/matrix_complex.cpp +++ b/poincare/src/matrix_complex.cpp @@ -38,13 +38,13 @@ bool MatrixComplexNode::isUndefined() const { template Expression MatrixComplexNode::complexToExpression(Preferences::ComplexFormat complexFormat) const { - Matrix matrix = Matrix::EmptyMatrix(); + Matrix matrix = Matrix::Builder(); int i = 0; for (EvaluationNode * c : this->children()) { if (c->type() == EvaluationNode::Type::Complex) { matrix.addChildAtIndexInPlace(c->complexToExpression(complexFormat), i, i); } else { - matrix.addChildAtIndexInPlace(Undefined(), i, i); + matrix.addChildAtIndexInPlace(Undefined::Builder(), i, i); } i++; } @@ -100,7 +100,7 @@ MatrixComplex MatrixComplexNode::inverse() const { if (result == 0) { /* Intentionally swapping dimensions for inverse, although it doesn't make a * difference because it is square. */ - return MatrixComplex(operandsCopy, m_numberOfColumns, m_numberOfRows); + return MatrixComplex::Builder(operandsCopy, m_numberOfColumns, m_numberOfRows); } return MatrixComplex::Undefined(); } @@ -108,10 +108,10 @@ MatrixComplex MatrixComplexNode::inverse() const { template MatrixComplex MatrixComplexNode::transpose() const { // Intentionally swapping dimensions for transpose - MatrixComplex result; + MatrixComplex result = MatrixComplex::Builder(); for (int j = 0; j < numberOfColumns(); j++) { for (int i = 0; i < numberOfRows(); i++) { - result.addChildAtIndexInPlace(Complex(complexAtIndex(i*numberOfColumns()+j)), result.numberOfChildren(), result.numberOfChildren()); + result.addChildAtIndexInPlace(Complex::Builder(complexAtIndex(i*numberOfColumns()+j)), result.numberOfChildren(), result.numberOfChildren()); } } result.setDimensions(numberOfColumns(), numberOfRows()); @@ -121,30 +121,27 @@ MatrixComplex MatrixComplexNode::transpose() const { // MATRIX COMPLEX REFERENCE template -MatrixComplex::MatrixComplex(std::complex * operands, int numberOfRows, int numberOfColumns) : - MatrixComplex() -{ +MatrixComplex MatrixComplex::Builder(std::complex * operands, int numberOfRows, int numberOfColumns) { + MatrixComplex m = MatrixComplex::Builder(); for (int i=0; i(operands[i]), i, i); + m.addChildAtIndexInPlace(Complex::Builder(operands[i]), i, i); } - setDimensions(numberOfRows, numberOfColumns); + m.setDimensions(numberOfRows, numberOfColumns); + return m; } -template -MatrixComplex::MatrixComplex() : Evaluation(TreePool::sharedPool()->createTreeNode >()) {} - template MatrixComplex MatrixComplex::Undefined() { std::complex undef = std::complex(NAN, NAN); - return MatrixComplex((std::complex *)&undef, 1, 1); + return MatrixComplex::Builder((std::complex *)&undef, 1, 1); } template MatrixComplex MatrixComplex::createIdentity(int dim) { - MatrixComplex result; + MatrixComplex result = MatrixComplex::Builder(); for (int i = 0; i < dim; i++) { for (int j = 0; j < dim; j++) { - Complex c = i == j ? Complex(1.0) : Complex(0.0); + Complex c = i == j ? Complex::Builder(1.0) : Complex::Builder(0.0); result.addChildAtIndexInPlace(c, i*dim+j, i*dim+j); } } diff --git a/poincare/src/matrix_dimension.cpp b/poincare/src/matrix_dimension.cpp index e19409552..5390a43d0 100644 --- a/poincare/src/matrix_dimension.cpp +++ b/poincare/src/matrix_dimension.cpp @@ -11,8 +11,8 @@ constexpr Expression::FunctionHelper MatrixDimension::s_functionHelper; int MatrixDimensionNode::numberOfChildren() const { return MatrixDimension::s_functionHelper.numberOfChildren(); } -Expression MatrixDimensionNode::shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) { - return MatrixDimension(this).shallowReduce(context, angleUnit); +Expression MatrixDimensionNode::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) { + return MatrixDimension(this).shallowReduce(); } Layout MatrixDimensionNode::createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const { @@ -24,8 +24,8 @@ int MatrixDimensionNode::serialize(char * buffer, int bufferSize, Preferences::P } template -Evaluation MatrixDimensionNode::templatedApproximate(Context& context, Preferences::AngleUnit angleUnit) const { - Evaluation input = childAtIndex(0)->approximate(T(), context, angleUnit); +Evaluation MatrixDimensionNode::templatedApproximate(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { + Evaluation input = childAtIndex(0)->approximate(T(), context, complexFormat, angleUnit); std::complex operands[2]; if (input.type() == EvaluationNode::Type::MatrixComplex) { operands[0] = std::complex(static_cast&>(input).numberOfRows()); @@ -34,12 +34,13 @@ Evaluation MatrixDimensionNode::templatedApproximate(Context& context, Prefer operands[0] = std::complex(1.0); operands[1] = std::complex(1.0); } - return MatrixComplex(operands, 1, 2); + return MatrixComplex::Builder(operands, 1, 2); } -Expression MatrixDimension::shallowReduce(Context & context, Preferences::AngleUnit angleUnit) { + +Expression MatrixDimension::shallowReduce() { { - Expression e = Expression::defaultShallowReduce(context, angleUnit); + Expression e = Expression::defaultShallowReduce(); if (e.isUndefined()) { return e; } @@ -48,25 +49,25 @@ Expression MatrixDimension::shallowReduce(Context & context, Preferences::AngleU #if MATRIX_EXACT_REDUCING if (c.type() == ExpressionNode::Type::Matrix) { Matrix m = static_cast(c); - Matrix result; - result.addChildAtIndexInPlace(Rational(m.numberOfRows()), 0, 0); - result.addChildAtIndexInPlace(Rational(m.numberOfColumns()), 1, 1); + Matrix result = Matrix::Builder(); + result.addChildAtIndexInPlace(Rational::Builder(m.numberOfRows()), 0, 0); + result.addChildAtIndexInPlace(Rational::Builder(m.numberOfColumns()), 1, 1); result.setDimensions(1, 2); return result; } if (!c.recursivelyMatches(Expression::IsMatrix)) { - Matrix result; - result.addChildAtIndexInPlace(Rational(1), 0, 0); - result.addChildAtIndexInPlace(Rational(1), 1, 1); + Matrix result = Matrix::Builder(); + result.addChildAtIndexInPlace(Rational::Builder(1), 0, 0); + result.addChildAtIndexInPlace(Rational::Builder(1), 1, 1); result.setDimensions(1, 2); return result; } return *this; #else if (c.type() != ExpressionNode::Type::Matrix) { - Matrix result; - result.addChildAtIndexInPlace(Rational(1), 0, 0); - result.addChildAtIndexInPlace(Rational(1), 1, 1); + Matrix result = Matrix::Builder(); + result.addChildAtIndexInPlace(Rational::Builder(1), 0, 0); + result.addChildAtIndexInPlace(Rational::Builder(1), 1, 1); result.setDimensions(1, 2); replaceWithInPlace(result); return result; diff --git a/poincare/src/matrix_inverse.cpp b/poincare/src/matrix_inverse.cpp index 43890f656..24aba3623 100644 --- a/poincare/src/matrix_inverse.cpp +++ b/poincare/src/matrix_inverse.cpp @@ -14,8 +14,8 @@ constexpr Expression::FunctionHelper MatrixInverse::s_functionHelper; int MatrixInverseNode::numberOfChildren() const { return MatrixInverse::s_functionHelper.numberOfChildren(); } -Expression MatrixInverseNode::shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) { - return MatrixInverse(this).shallowReduce(context, angleUnit, target); +Expression MatrixInverseNode::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) { + return MatrixInverse(this).shallowReduce(context, complexFormat, angleUnit, target); } Layout MatrixInverseNode::createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const { @@ -28,13 +28,13 @@ int MatrixInverseNode::serialize(char * buffer, int bufferSize, Preferences::Pri // TODO: handle this exactly in shallowReduce for small dimensions. template -Evaluation MatrixInverseNode::templatedApproximate(Context& context, Preferences::AngleUnit angleUnit) const { - Evaluation input = childAtIndex(0)->approximate(T(), context, angleUnit); +Evaluation MatrixInverseNode::templatedApproximate(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { + Evaluation input = childAtIndex(0)->approximate(T(), context, complexFormat, angleUnit); Evaluation inverse; if (input.type() == EvaluationNode::Type::MatrixComplex) { inverse = static_cast&>(input).inverse(); } else if (input.type() == EvaluationNode::Type::Complex) { - inverse = Complex(std::complex(1)/(static_cast&>(input).stdComplex())); + inverse = Complex::Builder(std::complex(1)/(static_cast&>(input).stdComplex())); } if (inverse.isUninitialized()) { inverse = Complex::Undefined(); @@ -42,9 +42,10 @@ Evaluation MatrixInverseNode::templatedApproximate(Context& context, Preferen return inverse; } -Expression MatrixInverse::shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { + +Expression MatrixInverse::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { { - Expression e = Expression::defaultShallowReduce(context, angleUnit); + Expression e = Expression::defaultShallowReduce(); if (e.isUndefined()) { return e; } @@ -53,21 +54,21 @@ Expression MatrixInverse::shallowReduce(Context & context, Preferences::AngleUni #if MATRIX_EXACT_REDUCING #if 0 if (!c.recursivelyMatches(Expression::IsMatrix)) { - return Power(c, Rational(-1).shallowReduce(context, angleUnit, target); + return Power::Builder(c, Rational::Builder(-1).shallowReduce(context, complexFormat, angleUnit, target); } if (c.type() == ExpressionNode::Type::Matrix) { Matrix mat = static_cast(c); if (mat.numberOfRows() != mat.numberOfColumns()) { - return Undefined(); + return Undefined::Builder(); } } return *this; #endif #else if (c.type() != ExpressionNode::Type::Matrix) { - Expression result = Power(c, Rational(-1)); + Expression result = Power::Builder(c, Rational::Builder(-1)); replaceWithInPlace(result); - result = result.shallowReduce(context, angleUnit, target); + result = result.shallowReduce(context, complexFormat, angleUnit, target); return result; } return *this; diff --git a/poincare/src/matrix_layout.cpp b/poincare/src/matrix_layout.cpp index 57b085ab0..e7223c3e7 100644 --- a/poincare/src/matrix_layout.cpp +++ b/poincare/src/matrix_layout.cpp @@ -266,16 +266,10 @@ void MatrixLayoutNode::didReplaceChildAtIndex(int index, LayoutCursor * cursor, } } -MatrixLayout::MatrixLayout(const MatrixLayoutNode * n) : GridLayout(n) {} -MatrixLayout::MatrixLayout() : GridLayout(TreePool::sharedPool()->createTreeNode()) {} -MatrixLayout::MatrixLayout(Layout l1, Layout l2, Layout l3, Layout l4) : - MatrixLayout() -{ - addChildAtIndexInPlace(l1, 0, 0); - addChildAtIndexInPlace(l2, 1, 1); - addChildAtIndexInPlace(l3, 2, 2); - addChildAtIndexInPlace(l4, 3, 3); - setDimensions(2, 2); +MatrixLayout MatrixLayout::Builder(Layout l1, Layout l2, Layout l3, Layout l4) { + MatrixLayout m = TreeHandle::NAryBuilder(ArrayBuilder(l1,l2,l3,l4).array(), 4); + m.setDimensions(2, 2); + return m; } } diff --git a/poincare/src/matrix_trace.cpp b/poincare/src/matrix_trace.cpp index aabe59807..dbe915cd7 100644 --- a/poincare/src/matrix_trace.cpp +++ b/poincare/src/matrix_trace.cpp @@ -13,8 +13,8 @@ constexpr Expression::FunctionHelper MatrixTrace::s_functionHelper; int MatrixTraceNode::numberOfChildren() const { return MatrixTrace::s_functionHelper.numberOfChildren(); } -Expression MatrixTraceNode::shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) { - return MatrixTrace(this).shallowReduce(context, angleUnit); +Expression MatrixTraceNode::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) { + return MatrixTrace(this).shallowReduce(); } Layout MatrixTraceNode::createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const { @@ -26,15 +26,16 @@ int MatrixTraceNode::serialize(char * buffer, int bufferSize, Preferences::Print } template -Evaluation MatrixTraceNode::templatedApproximate(Context& context, Preferences::AngleUnit angleUnit) const { - Evaluation input = childAtIndex(0)->approximate(T(), context, angleUnit); - Complex result = Complex(input.trace()); +Evaluation MatrixTraceNode::templatedApproximate(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { + Evaluation input = childAtIndex(0)->approximate(T(), context, complexFormat, angleUnit); + Complex result = Complex::Builder(input.trace()); return result; } -Expression MatrixTrace::shallowReduce(Context & context, Preferences::AngleUnit angleUnit) { + +Expression MatrixTrace::shallowReduce() { { - Expression e = Expression::defaultShallowReduce(context, angleUnit); + Expression e = Expression::defaultShallowReduce(); if (e.isUndefined()) { return e; } @@ -45,14 +46,14 @@ Expression MatrixTrace::shallowReduce(Context & context, Preferences::AngleUnit if (c.type() == ExpressionNode::Type::Matrix) { Matrix m = static_cast(c); if (m.numberOfRows() != m.numberOfColumns()) { - return Undefined(); + return Undefined::Builder(); } int n = m.numberOfRows(); - Addition a = Addition(); + Addition a = Addition::Builder(); for (int i = 0; i < n; i++) { a.addChildAtIndexInPlace(m.childAtIndex(i+n*i), i, a.numberOfChildren()); } - return a.shallowReduce(context, angleUnit); + return a.shallowReduce(context, complexFormat, angleUnit); } if (!c.recursivelyMatches(Expression::IsMatrix)) { return c; diff --git a/poincare/src/matrix_transpose.cpp b/poincare/src/matrix_transpose.cpp index 41ca47975..8e8805d98 100644 --- a/poincare/src/matrix_transpose.cpp +++ b/poincare/src/matrix_transpose.cpp @@ -12,8 +12,8 @@ constexpr Expression::FunctionHelper MatrixTranspose::s_functionHelper; int MatrixTransposeNode::numberOfChildren() const { return MatrixTranspose::s_functionHelper.numberOfChildren(); } -Expression MatrixTransposeNode::shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) { - return MatrixTranspose(this).shallowReduce(context, angleUnit); +Expression MatrixTransposeNode::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) { + return MatrixTranspose(this).shallowReduce(); } Layout MatrixTransposeNode::createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const { @@ -25,8 +25,8 @@ int MatrixTransposeNode::serialize(char * buffer, int bufferSize, Preferences::P } template -Evaluation MatrixTransposeNode::templatedApproximate(Context& context, Preferences::AngleUnit angleUnit) const { - Evaluation input = childAtIndex(0)->approximate(T(), context, angleUnit); +Evaluation MatrixTransposeNode::templatedApproximate(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { + Evaluation input = childAtIndex(0)->approximate(T(), context, complexFormat, angleUnit); Evaluation transpose; if (input.type() == EvaluationNode::Type::MatrixComplex) { transpose = static_cast&>(input).transpose(); @@ -37,9 +37,10 @@ Evaluation MatrixTransposeNode::templatedApproximate(Context& context, Prefer return transpose; } -Expression MatrixTranspose::shallowReduce(Context & context, Preferences::AngleUnit angleUnit) { + +Expression MatrixTranspose::shallowReduce() { { - Expression e = Expression::defaultShallowReduce(context, angleUnit); + Expression e = Expression::defaultShallowReduce(); if (e.isUndefined()) { return e; } diff --git a/poincare/src/multiplication.cpp b/poincare/src/multiplication.cpp index 5973298c7..29aceb262 100644 --- a/poincare/src/multiplication.cpp +++ b/poincare/src/multiplication.cpp @@ -17,13 +17,16 @@ namespace Poincare { -ExpressionNode::Sign MultiplicationNode::sign() const { +ExpressionNode::Sign MultiplicationNode::sign(Context * context) const { if (numberOfChildren() == 0) { return Sign::Unknown; } int sign = 1; for (ExpressionNode * c : children()) { - sign *= (int)(c->sign()); + sign *= (int)(c->sign(context)); + } + if (sign == 0) { + return ExpressionNode::sign(context); } return (Sign)sign; } @@ -45,30 +48,31 @@ int MultiplicationNode::getPolynomialCoefficients(Context & context, const char } template -MatrixComplex MultiplicationNode::computeOnMatrices(const MatrixComplex m, const MatrixComplex n) { +MatrixComplex MultiplicationNode::computeOnMatrices(const MatrixComplex m, const MatrixComplex n, Preferences::ComplexFormat complexFormat) { if (m.numberOfColumns() != n.numberOfRows()) { return MatrixComplex::Undefined(); } - MatrixComplex result; + MatrixComplex result = MatrixComplex::Builder(); for (int i = 0; i < m.numberOfRows(); i++) { for (int j = 0; j < n.numberOfColumns(); j++) { std::complex c(0.0); for (int k = 0; k < m.numberOfColumns(); k++) { c += m.complexAtIndex(i*m.numberOfColumns()+k)*n.complexAtIndex(k*n.numberOfColumns()+j); } - result.addChildAtIndexInPlace(Complex(c), i*n.numberOfColumns()+j, result.numberOfChildren()); + result.addChildAtIndexInPlace(Complex::Builder(c), i*n.numberOfColumns()+j, result.numberOfChildren()); } } result.setDimensions(m.numberOfRows(), n.numberOfColumns()); return result; } -Expression MultiplicationNode::setSign(Sign s, Context & context, Preferences::AngleUnit angleUnit) { - return Multiplication(this).setSign(s, context, angleUnit); +Expression MultiplicationNode::setSign(Sign s, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) { + assert(s == ExpressionNode::Sign::Positive); + return Multiplication(this).setSign(s, context, complexFormat, angleUnit, target); } bool MultiplicationNode::childNeedsParenthesis(const TreeNode * child) const { - if ((static_cast(child)->isNumber() && static_cast(child)->sign() == Sign::Negative) + if ((static_cast(child)->isNumber() && Number(static_cast(child)).sign() == Sign::Negative) || static_cast(child)->type() == ExpressionNode::Type::Opposite) { if (child == childAtIndex(0)) { @@ -90,22 +94,20 @@ int MultiplicationNode::serialize(char * buffer, int bufferSize, Preferences::Pr return SerializationHelper::Infix(this, buffer, bufferSize, floatDisplayMode, numberOfSignificantDigits, multiplicationString); } -Expression MultiplicationNode::shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) { - return Multiplication(this).shallowReduce(context, angleUnit, target); +Expression MultiplicationNode::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) { + return Multiplication(this).shallowReduce(context, complexFormat, angleUnit, target); } -Expression MultiplicationNode::shallowBeautify(Context & context, Preferences::AngleUnit angleUnit) { - return Multiplication(this).shallowBeautify(context, angleUnit); +Expression MultiplicationNode::shallowBeautify(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) { + return Multiplication(this).shallowBeautify(context, complexFormat, angleUnit, target); } -Expression MultiplicationNode::denominator(Context & context, Preferences::AngleUnit angleUnit) const { - return Multiplication(this).denominator(context, angleUnit); +Expression MultiplicationNode::denominator(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { + return Multiplication(this).denominator(context, complexFormat, angleUnit); } /* Multiplication */ -Multiplication::Multiplication() : NAryExpression(TreePool::sharedPool()->createTreeNode()) {} - template void Multiplication::computeOnArrays(T * m, T * n, T * result, int mNumberOfColumns, int mNumberOfRows, int nNumberOfColumns) { for (int i = 0; i < mNumberOfRows; i++) { @@ -119,21 +121,21 @@ void Multiplication::computeOnArrays(T * m, T * n, T * result, int mNumberOfColu } } -Expression Multiplication::setSign(ExpressionNode::Sign s, Context & context, Preferences::AngleUnit angleUnit) { +Expression Multiplication::setSign(ExpressionNode::Sign s, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { assert(s == ExpressionNode::Sign::Positive); for (int i = 0; i < numberOfChildren(); i++) { - if (childAtIndex(i).sign() == ExpressionNode::Sign::Negative) { - replaceChildAtIndexInPlace(i, childAtIndex(i).setSign(s, context, angleUnit)); + if (childAtIndex(i).sign(context) == ExpressionNode::Sign::Negative) { + replaceChildAtIndexInPlace(i, childAtIndex(i).setSign(s, context, complexFormat, angleUnit, target)); } } - return shallowReduce(context, angleUnit, ExpressionNode::ReductionTarget::BottomUpComputation); + return shallowReduce(*context, complexFormat, angleUnit, target); } -Expression Multiplication::shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { - return privateShallowReduce(context, angleUnit, target, true, true); +Expression Multiplication::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { + return privateShallowReduce(context, complexFormat, angleUnit, target, true, true); } -Expression Multiplication::shallowBeautify(Context & context, Preferences::AngleUnit angleUnit) { +Expression Multiplication::shallowBeautify(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { /* Beautifying a Multiplication consists in several possible operations: * - Add Opposite ((-3)*x -> -(3*x), useful when printing fractions) * - Adding parenthesis if needed (a*(b+c) is not a*b+c) @@ -141,25 +143,20 @@ Expression Multiplication::shallowBeautify(Context & context, Preferences::Angle * shall become a/b) or a non-integer rational term (3/2*a -> (3*a)/2). */ // Step 1: Turn -n*A into -(n*A) - Expression child0 = childAtIndex(0); - if (child0.isNumber() && child0.sign() == ExpressionNode::Sign::Negative) { - if (child0.type() == ExpressionNode::Type::Rational && static_cast(child0).isMinusOne()) { - removeChildAtIndexInPlace(0); - } else { - child0.setSign(ExpressionNode::Sign::Positive, context, angleUnit); - } - Expression e = squashUnaryHierarchyInPlace(); - Opposite o = Opposite(); - e.replaceWithInPlace(o); - o.replaceChildAtIndexInPlace(0, e); + Expression noNegativeNumeral = makePositiveAnyNegativeNumeralFactor(context, complexFormat, angleUnit, target); + // If one negative numeral factor was made positive, we turn the expression in an Opposite + if (!noNegativeNumeral.isUninitialized()) { + Opposite o = Opposite::Builder(); + noNegativeNumeral.replaceWithInPlace(o); + o.replaceChildAtIndexInPlace(0, noNegativeNumeral); return o; } /* Step 2: Merge negative powers: a*b^(-1)*c^(-pi)*d = a*(b*c^pi)^(-1) * This also turns 2/3*a into 2*a*3^(-1) */ - Expression thisExp = mergeNegativePower(context, angleUnit); + Expression thisExp = mergeNegativePower(context, complexFormat, angleUnit); if (thisExp.type() == ExpressionNode::Type::Power) { - return thisExp.shallowBeautify(context, angleUnit); + return thisExp.shallowBeautify(context, complexFormat, angleUnit, target); } assert(thisExp.type() == ExpressionNode::Type::Multiplication); @@ -167,7 +164,7 @@ Expression Multiplication::shallowBeautify(Context & context, Preferences::Angle for (int i = 0; i < thisExp.numberOfChildren(); i++) { const Expression o = thisExp.childAtIndex(i); if (o.type() == ExpressionNode::Type::Addition) { - Parenthesis p(o); + Parenthesis p = Parenthesis::Builder(o); thisExp.replaceChildAtIndexInPlace(i, p); } } @@ -183,7 +180,7 @@ Expression Multiplication::shallowBeautify(Context & context, Preferences::Angle Expression denominatorOperand = childI.childAtIndex(0); removeChildInPlace(childI, childI.numberOfChildren()); - Expression numeratorOperand = shallowReduce(context, angleUnit, ExpressionNode::ReductionTarget::User); + Expression numeratorOperand = shallowReduce(context, complexFormat, angleUnit, target); // Delete unnecessary parentheses on numerator if (numeratorOperand.type() == ExpressionNode::Type::Parenthesis) { Expression numeratorChild0 = numeratorOperand.childAtIndex(0); @@ -191,11 +188,11 @@ Expression Multiplication::shallowBeautify(Context & context, Preferences::Angle numeratorOperand = numeratorChild0; } Expression originalParent = numeratorOperand.parent(); - Division d; + Division d = Division::Builder(); numeratorOperand.replaceWithInPlace(d); d.replaceChildAtIndexInPlace(0, numeratorOperand); d.replaceChildAtIndexInPlace(1, denominatorOperand); - return d.shallowBeautify(context, angleUnit); + return d.shallowBeautify(context, complexFormat, angleUnit, target); } return thisExp; } @@ -207,9 +204,9 @@ int Multiplication::getPolynomialCoefficients(Context & context, const char * sy } // Initialization of coefficients for (int i = 1; i <= deg; i++) { - coefficients[i] = Rational(0); + coefficients[i] = Rational::Builder(0); } - coefficients[0] = Rational(1); + coefficients[0] = Rational::Builder(1); Expression intermediateCoefficients[Expression::k_maxNumberOfPolynomialCoefficients]; // Let's note result = a(0)+a(1)*X+a(2)*X^2+a(3)*x^3+.. @@ -219,29 +216,29 @@ int Multiplication::getPolynomialCoefficients(Context & context, const char * sy assert(degI <= Expression::k_maxPolynomialDegree); for (int j = deg; j > 0; j--) { // new coefficients[j] = b(0)*a(j)+b(1)*a(j-1)+b(2)*a(j-2)+... - Addition a; + Addition a = Addition::Builder(); int jbis = j > degI ? degI : j; for (int l = 0; l <= jbis ; l++) { // Always copy the a and b coefficients are they are used multiple times - a.addChildAtIndexInPlace(Multiplication(intermediateCoefficients[l].clone(), coefficients[j-l].clone()), a.numberOfChildren(), a.numberOfChildren()); + a.addChildAtIndexInPlace(Multiplication::Builder(intermediateCoefficients[l].clone(), coefficients[j-l].clone()), a.numberOfChildren(), a.numberOfChildren()); } /* a(j) and b(j) are used only to compute coefficient at rank >= j, we * can delete them as we compute new coefficient by decreasing ranks. */ coefficients[j] = a; } // new coefficients[0] = a(0)*b(0) - coefficients[0] = Multiplication(coefficients[0], intermediateCoefficients[0]); + coefficients[0] = Multiplication::Builder(coefficients[0], intermediateCoefficients[0]); } return deg; } -Expression Multiplication::denominator(Context & context, Preferences::AngleUnit angleUnit) const { +Expression Multiplication::denominator(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { // Merge negative power: a*b^-1*c^(-Pi)*d = a*(b*c^Pi)^-1 // WARNING: we do not want to change the expression but to create a new one. Multiplication thisClone = clone().convert(); - Expression e = thisClone.mergeNegativePower(context, angleUnit); + Expression e = thisClone.mergeNegativePower(context, complexFormat, angleUnit); if (e.type() == ExpressionNode::Type::Power) { - return e.denominator(context, angleUnit); + return e.denominator(context, complexFormat, angleUnit); } else { assert(e.type() == ExpressionNode::Type::Multiplication); for (int i = 0; i < e.numberOfChildren(); i++) { @@ -257,9 +254,9 @@ Expression Multiplication::denominator(Context & context, Preferences::AngleUnit return Expression(); } -Expression Multiplication::privateShallowReduce(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target, bool shouldExpand, bool canBeInterrupted) { +Expression Multiplication::privateShallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target, bool shouldExpand, bool canBeInterrupted) { { - Expression e = Expression::defaultShallowReduce(context, angleUnit);; + Expression e = Expression::defaultShallowReduce();; if (e.isUndefined()) { return e; } @@ -270,7 +267,7 @@ Expression Multiplication::privateShallowReduce(Context & context, Preferences:: mergeMultiplicationChildrenInPlace(); // Step 2: Sort the children - sortChildrenInPlace(ExpressionNode::SimplificationOrder, canBeInterrupted); + sortChildrenInPlace([](const ExpressionNode * e1, const ExpressionNode * e2, bool canBeInterrupted) { return ExpressionNode::SimplificationOrder(e1, e2, true, canBeInterrupted); }, true); #if MATRIX_EXACT_REDUCING #if 0 // OLD CODE @@ -295,7 +292,7 @@ Expression Multiplication::privateShallowReduce(Context & context, Preferences:: int on = currentMatrix->numberOfRows(); int om = currentMatrix->numberOfColumns(); if (om != n) { - return replaceWith(new Undefined(), true); + return replaceWith(new Undefined::Builder(), true); } // Create the matrix resulting of the multiplication of the current matrix and the result matrix /* resultMatrix @@ -319,15 +316,15 @@ Expression Multiplication::privateShallowReduce(Context & context, Preferences:: * */ Expression ** newMatrixOperands = new Expression * [on*m]; for (int e = 0; e < on*m; e++) { - newMatrixOperands[e] = new Addition(); + newMatrixOperands[e] = new Addition::Builder(); int i2 = e%m; int i1 = e/m; for (int j = 0; j < n; j++) { - Expression * mult = new Multiplication(currentMatrix->childAtIndex(j+om*i1), resultMatrix->childAtIndex(j*m+i2), true); + Expression * mult = new Multiplication::Builder(currentMatrix->childAtIndex(j+om*i1), resultMatrix->childAtIndex(j*m+i2), true); static_cast(newMatrixOperands[e])->addOperand(mult); - mult->shallowReduce(context, angleUnit, target); + mult->shallowReduce(context, complexFormat, angleUnit, target); } - Reduce(&newMatrixOperands[e], context, angleUnit, false); + Reduce(&newMatrixOperands[e], context, complexFormat, angleUnit, false); } n = on; removeOperand(currentMatrix, true); @@ -341,9 +338,9 @@ Expression Multiplication::privateShallowReduce(Context & context, Preferences:: Expression * entryI = resultMatrix->childAtIndex(i); resultMatrix->replaceOperand(entryI, m, false); m->addOperand(entryI); - m->shallowReduce(context, angleUnit, target); + m->shallowReduce(context, complexFormat, angleUnit, target); } - return replaceWith(resultMatrix, true)->shallowReduce(context, angleUnit, target); + return replaceWith(resultMatrix, true)->shallowReduce(context, complexFormat, angleUnit, target); } #endif #endif @@ -365,11 +362,11 @@ Expression Multiplication::privateShallowReduce(Context & context, Preferences:: shouldFactorizeBase = oi.type() == ExpressionNode::Type::Power && oi1.type() == ExpressionNode::Type::Power; } if (shouldFactorizeBase) { - factorizeBase(i, i+1, context, angleUnit, target); + factorizeBase(i, i+1, context, complexFormat, angleUnit, target); continue; } } else if (TermHasNumeralBase(oi) && TermHasNumeralBase(oi1) && TermsHaveIdenticalExponent(oi, oi1)) { - factorizeExponent(i, i+1, context, angleUnit, target); + factorizeExponent(i, i+1, context, complexFormat, angleUnit, target); continue; } i++; @@ -389,7 +386,7 @@ Expression Multiplication::privateShallowReduce(Context & context, Preferences:: for (int j = i+1; j < numberOfChildren(); j++) { Expression o2 = childAtIndex(j); if (Base(o2).type() == ExpressionNode::Type::Cosine && TermHasNumeralExponent(o2) && Base(o2).childAtIndex(0).isIdenticalTo(x)) { - factorizeSineAndCosine(i, j, context, angleUnit); + factorizeSineAndCosine(i, j, context, complexFormat, angleUnit); break; } } @@ -398,7 +395,7 @@ Expression Multiplication::privateShallowReduce(Context & context, Preferences:: /* Replacing sin/cos by tan factors may have mixed factors and factors are * guaranteed to be sorted (according ot SimplificationOrder) at the end of * shallowReduce */ - sortChildrenInPlace(ExpressionNode::SimplificationOrder, canBeInterrupted); + sortChildrenInPlace([](const ExpressionNode * e1, const ExpressionNode * e2, bool canBeInterrupted) { return ExpressionNode::SimplificationOrder(e1, e2, true, canBeInterrupted); }, true); } /* Step 5: We remove rational children that appeared in the middle of sorted @@ -435,13 +432,24 @@ Expression Multiplication::privateShallowReduce(Context & context, Preferences:: /* Step 5: If the first child is zero, the multiplication result is zero. We * do this after merging the rational children, because the merge takes care - * of turning 0*inf into undef. + * of turning 0*inf into undef. We still have to check that no other child + * involves an inifity expression to avoid reducing 0*e^(inf) to 0. * If the first child is 1, we remove it if there are other children. */ { const Expression c = childAtIndex(0); if (c.type() == ExpressionNode::Type::Rational && static_cast(c).isZero()) { - replaceWithInPlace(c); - return c; + // Check that other children don't match inf + bool infiniteFactor = false; + for (int i = 1; i < numberOfChildren(); i++) { + infiniteFactor = childAtIndex(i).recursivelyMatchesInfinity(context); + if (infiniteFactor) { + break; + } + } + if (!infiniteFactor) { + replaceWithInPlace(c); + return c; + } } if (c.type() == ExpressionNode::Type::Rational && static_cast(c).isOne() && numberOfChildren() > 1) { removeChildAtIndexInPlace(0); @@ -455,16 +463,60 @@ Expression Multiplication::privateShallowReduce(Context & context, Preferences:: * Note: This step must be done after Step 4, otherwise we wouldn't be able to * reduce expressions such as (x+y)^(-1)*(x+y)(a+b). */ Expression p = parent(); - if (target != ExpressionNode::ReductionTarget::BottomUpComputation && shouldExpand && (p.isUninitialized() || p.type() != ExpressionNode::Type::Multiplication)) { + if (shouldExpand && (p.isUninitialized() || p.type() != ExpressionNode::Type::Multiplication)) { for (int i = 0; i < numberOfChildren(); i++) { if (childAtIndex(i).type() == ExpressionNode::Type::Addition) { - return distributeOnOperandAtIndex(i, context, angleUnit, target); + return distributeOnOperandAtIndex(i, context, complexFormat, angleUnit, target); } } } - // Step 8: Let's remove the multiplication altogether if it has one child + // Step 7: Let's remove the multiplication altogether if it has one child Expression result = squashUnaryHierarchyInPlace(); + if (result != *this) { + return result; + } + + /* Step 8: Let's bubble up the complex operator if possible + * 3 cases: + * - All children are real, we do nothing (allChildrenAreReal == 1) + * - One of the child is non-real and not a ComplexCartesian: it means a + * complex expression could not be resolved as a ComplexCartesian, we cannot + * do anything about it now (allChildrenAreReal == -1) + * - All children are either real or ComplexCartesian (allChildrenAreReal == 0) + * We can bubble up ComplexCartesian nodes. */ + if (allChildrenAreReal(context) == 0) { + int nbChildren = numberOfChildren(); + int i = nbChildren-1; + // Children are sorted so ComplexCartesian nodes are at the end + assert(childAtIndex(i).type() == ExpressionNode::Type::ComplexCartesian); + // First, we merge all ComplexCartesian children into one + ComplexCartesian child = childAtIndex(i).convert(); + removeChildAtIndexInPlace(i); + i--; + while (i >= 0) { + Expression e = childAtIndex(i); + if (e.type() != ExpressionNode::Type::ComplexCartesian) { + // the Multiplication is sorted so ComplexCartesian nodes are the last ones + break; + } + child = child.multiply(static_cast(e), context, complexFormat, angleUnit, target); + removeChildAtIndexInPlace(i); + i--; + } + // The real children are both factors of the real and the imaginary multiplication + Multiplication real = *this; + Multiplication imag = clone().convert(); + real.addChildAtIndexInPlace(child.real(), real.numberOfChildren(), real.numberOfChildren()); + imag.addChildAtIndexInPlace(child.imag(), real.numberOfChildren(), real.numberOfChildren()); + ComplexCartesian newComplexCartesian = ComplexCartesian::Builder(); + replaceWithInPlace(newComplexCartesian); + newComplexCartesian.replaceChildAtIndexInPlace(0, real); + newComplexCartesian.replaceChildAtIndexInPlace(1, imag); + real.shallowReduce(context, complexFormat, angleUnit, target); + imag.shallowReduce(context, complexFormat, angleUnit, target); + return newComplexCartesian.shallowReduce(); + } return result; } @@ -482,30 +534,30 @@ void Multiplication::mergeMultiplicationChildrenInPlace() { } } -void Multiplication::factorizeBase(int i, int j, Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { +void Multiplication::factorizeBase(int i, int j, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { /* This function factorizes two children which have a common base. For example - * if this is Multiplication(pi^2, pi^3), then pi^2 and pi^3 could be merged - * and this turned into Multiplication(pi^5). */ + * if this is Multiplication::Builder(pi^2, pi^3), then pi^2 and pi^3 could be merged + * and this turned into Multiplication::Builder(pi^5). */ Expression e = childAtIndex(j); // Step 1: Get rid of the child j removeChildAtIndexInPlace(j); // Step 2: Merge child j in child i by factorizing base - mergeInChildByFactorizingBase(i, e, context, angleUnit, target); + mergeInChildByFactorizingBase(i, e, context, complexFormat, angleUnit, target); } -void Multiplication::mergeInChildByFactorizingBase(int i, Expression e, Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { +void Multiplication::mergeInChildByFactorizingBase(int i, Expression e, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { /* This function replace the child at index i by its factorization with e. e * and childAtIndex(i) are supposed to have a common base. */ // Step 1: Find the new exponent - Expression s = Addition(CreateExponent(childAtIndex(i)), CreateExponent(e)); // pi^2*pi^3 -> pi^(2+3) -> pi^5 + Expression s = Addition::Builder(CreateExponent(childAtIndex(i)), CreateExponent(e)); // pi^2*pi^3 -> pi^(2+3) -> pi^5 // Step 2: Create the new Power - Expression p = Power(Base(childAtIndex(i)), s); // pi^2*pi^-2 -> pi^0 -> 1 - s.shallowReduce(context, angleUnit, target); + Expression p = Power::Builder(Base(childAtIndex(i)), s); // pi^2*pi^-2 -> pi^0 -> 1 + s.shallowReduce(context, complexFormat, angleUnit, target); // Step 3: Replace one of the child replaceChildAtIndexInPlace(i, p); - p = p.shallowReduce(context, angleUnit, target); + p = p.shallowReduce(context, complexFormat, angleUnit, target); /* Step 4: Reducing the new power might have turned it into a multiplication, * ie: 12^(1/2) -> 2*3^(1/2). In that case, we need to merge the multiplication * node with this. */ @@ -514,19 +566,19 @@ void Multiplication::mergeInChildByFactorizingBase(int i, Expression e, Context } } -void Multiplication::factorizeExponent(int i, int j, Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { +void Multiplication::factorizeExponent(int i, int j, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { /* This function factorizes children which share a common exponent. For - * example, it turns Multiplication(2^x,3^x) into Multiplication(6^x). */ + * example, it turns Multiplication::Builder(2^x,3^x) into Multiplication::Builder(6^x). */ // Step 1: Find the new base - Expression m = Multiplication(Base(childAtIndex(i)), Base(childAtIndex(j))); // 2^x*3^x -> (2*3)^x -> 6^x + Expression m = Multiplication::Builder(Base(childAtIndex(i)), Base(childAtIndex(j))); // 2^x*3^x -> (2*3)^x -> 6^x // Step 2: Get rid of one of the children removeChildAtIndexInPlace(j); // Step 3: Replace the other child childAtIndex(i).replaceChildAtIndexInPlace(0, m); // Step 4: Reduce expressions - m.shallowReduce(context, angleUnit, target); - Expression p = childAtIndex(i).shallowReduce(context, angleUnit, target); // 2^x*(1/2)^x -> (2*1/2)^x -> 1 + m.shallowReduce(context, complexFormat, angleUnit, target); + Expression p = childAtIndex(i).shallowReduce(context, complexFormat, angleUnit, target); // 2^x*(1/2)^x -> (2*1/2)^x -> 1 /* Step 5: Reducing the new power might have turned it into a multiplication, * ie: 12^(1/2) -> 2*3^(1/2). In that case, we need to merge the multiplication * node with this. */ @@ -535,13 +587,13 @@ void Multiplication::factorizeExponent(int i, int j, Context & context, Preferen } } -Expression Multiplication::distributeOnOperandAtIndex(int i, Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { +Expression Multiplication::distributeOnOperandAtIndex(int i, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { /* This method creates a*...*b*y... + a*...*c*y... + ... from * a*...*(b+c+...)*y... */ assert(i >= 0 && i < numberOfChildren()); assert(childAtIndex(i).type() == ExpressionNode::Type::Addition); - Addition a; + Addition a = Addition::Builder(); Expression childI = childAtIndex(i); int numberOfAdditionTerms = childI.numberOfChildren(); for (int j = 0; j < numberOfAdditionTerms; j++) { @@ -549,16 +601,16 @@ Expression Multiplication::distributeOnOperandAtIndex(int i, Context & context, m.replaceChildAtIndexInPlace(i, childI.childAtIndex(j)); // Reduce m: pi^(-1)*(pi + x) -> pi^(-1)*pi + pi^(-1)*x -> 1 + pi^(-1)*x a.addChildAtIndexInPlace(m, a.numberOfChildren(), a.numberOfChildren()); - m.shallowReduce(context, angleUnit, target); + m.shallowReduce(context, complexFormat, angleUnit, target); } replaceWithInPlace(a); - return a.shallowReduce(context, angleUnit, target); // Order terms, put under a common denominator if needed + return a.shallowReduce(context, complexFormat, angleUnit, target); // Order terms, put under a common denominator if needed } -void Multiplication::addMissingFactors(Expression factor, Context & context, Preferences::AngleUnit angleUnit) { +void Multiplication::addMissingFactors(Expression factor, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) { if (factor.type() == ExpressionNode::Type::Multiplication) { for (int j = 0; j < factor.numberOfChildren(); j++) { - addMissingFactors(factor.childAtIndex(j), context, angleUnit); + addMissingFactors(factor.childAtIndex(j), context, complexFormat, angleUnit); } return; } @@ -569,11 +621,11 @@ void Multiplication::addMissingFactors(Expression factor, Context & context, Pre assert(static_cast(factor).integerDenominator().isOne()); assert(childAtIndex(0).convert().integerDenominator().isOne()); Integer lcm = Arithmetic::LCM(static_cast(factor).unsignedIntegerNumerator(), childAtIndex(0).convert().unsignedIntegerNumerator()); - if (lcm.isInfinity()) { - addChildAtIndexInPlace(Rational(static_cast(factor).unsignedIntegerNumerator()), 1, numberOfChildren()); + if (lcm.isOverflow()) { + addChildAtIndexInPlace(Rational::Builder(static_cast(factor).unsignedIntegerNumerator()), 1, numberOfChildren()); return; } - replaceChildAtIndexInPlace(0, Rational(lcm)); + replaceChildAtIndexInPlace(0, Rational::Builder(lcm)); return; } if (factor.type() != ExpressionNode::Type::Rational) { @@ -581,28 +633,28 @@ void Multiplication::addMissingFactors(Expression factor, Context & context, Pre * base if any. Otherwise, we add it as an new child. */ for (int i = 0; i < numberOfChildren(); i++) { if (TermsHaveIdenticalBase(childAtIndex(i), factor)) { - Expression sub = Subtraction(CreateExponent(childAtIndex(i)), CreateExponent(factor)).deepReduce(context, angleUnit, ExpressionNode::ReductionTarget::User); - if (sub.sign() == ExpressionNode::Sign::Negative) { // index[0] < index[1] - sub = Opposite(sub); + Expression sub = Subtraction::Builder(CreateExponent(childAtIndex(i)), CreateExponent(factor)).deepReduce(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::User); + if (sub.sign(&context) == ExpressionNode::Sign::Negative) { // index[0] < index[1] + sub = Opposite::Builder(sub); if (factor.type() == ExpressionNode::Type::Power) { factor.replaceChildAtIndexInPlace(1, sub); } else { - factor = Power(factor, sub); + factor = Power::Builder(factor, sub); } - sub.shallowReduce(context, angleUnit, ExpressionNode::ReductionTarget::User); - mergeInChildByFactorizingBase(i, factor, context, angleUnit, ExpressionNode::ReductionTarget::User); - } else if (sub.sign() == ExpressionNode::Sign::Unknown) { - mergeInChildByFactorizingBase(i, factor, context, angleUnit, ExpressionNode::ReductionTarget::User); + sub.shallowReduce(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::User); + mergeInChildByFactorizingBase(i, factor, context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::User); + } else if (sub.sign(&context) == ExpressionNode::Sign::Unknown) { + mergeInChildByFactorizingBase(i, factor, context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::User); } return; } } } addChildAtIndexInPlace(factor.clone(), 0, numberOfChildren()); - sortChildrenInPlace(ExpressionNode::SimplificationOrder, false); + sortChildrenInPlace([](const ExpressionNode * e1, const ExpressionNode * e2, bool canBeInterrupted) { return ExpressionNode::SimplificationOrder(e1, e2, true, canBeInterrupted); }, true); } -void Multiplication::factorizeSineAndCosine(int i, int j, Context & context, Preferences::AngleUnit angleUnit) { +void Multiplication::factorizeSineAndCosine(int i, int j, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) { /* This function turn sin(x)^p * cos(x)^q into either: * - tan(x)^p*cos(x)^(p+q) if |p|<|q| * - tan(x)^(-q)*sin(x)^(p+q) otherwise */ @@ -615,29 +667,29 @@ void Multiplication::factorizeSineAndCosine(int i, int j, Context & context, Pre return; } Number sumPQ = Number::Addition(p, q); - Number absP = p.clone().convert().setSign(ExpressionNode::Sign::Positive, context, angleUnit); - Number absQ = q.clone().convert().setSign(ExpressionNode::Sign::Positive, context, angleUnit); + Number absP = p.clone().convert().setSign(ExpressionNode::Sign::Positive); + Number absQ = q.clone().convert().setSign(ExpressionNode::Sign::Positive); Expression tan = Tangent::Builder(x.clone()); if (Number::NaturalOrder(absP, absQ) < 0) { // Replace sin(x) by tan(x) or sin(x)^p by tan(x)^p if (p.isRationalOne()) { replaceChildAtIndexInPlace(i, tan); } else { - replaceChildAtIndexInPlace(i, Power(tan, p)); + replaceChildAtIndexInPlace(i, Power::Builder(tan, p)); } - childAtIndex(i).shallowReduce(context, angleUnit, ExpressionNode::ReductionTarget::User); + childAtIndex(i).shallowReduce(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::User); // Replace cos(x)^q by cos(x)^(p+q) - replaceChildAtIndexInPlace(j, Power(Base(childAtIndex(j)), sumPQ)); - childAtIndex(j).shallowReduce(context, angleUnit, ExpressionNode::ReductionTarget::User); + replaceChildAtIndexInPlace(j, Power::Builder(Base(childAtIndex(j)), sumPQ)); + childAtIndex(j).shallowReduce(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::User); } else { // Replace cos(x)^q by tan(x)^(-q) - Expression newPower = Power(tan, Number::Multiplication(q, Rational(-1))); - newPower.childAtIndex(1).shallowReduce(context, angleUnit, ExpressionNode::ReductionTarget::User); + Expression newPower = Power::Builder(tan, Number::Multiplication(q, Rational::Builder(-1))); + newPower.childAtIndex(1).shallowReduce(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::User); replaceChildAtIndexInPlace(j, newPower); - newPower.shallowReduce(context, angleUnit, ExpressionNode::ReductionTarget::User); + newPower.shallowReduce(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::User); // Replace sin(x)^p by sin(x)^(p+q) - replaceChildAtIndexInPlace(i, Power(Base(childAtIndex(i)), sumPQ)); - childAtIndex(i).shallowReduce(context, angleUnit, ExpressionNode::ReductionTarget::User); + replaceChildAtIndexInPlace(i, Power::Builder(Base(childAtIndex(i)), sumPQ)); + childAtIndex(i).shallowReduce(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::User); } } @@ -660,7 +712,7 @@ bool Multiplication::HaveSameNonNumeralFactors(const Expression & e1, const Expr } const Expression Multiplication::CreateExponent(Expression e) { - Expression result = e.type() == ExpressionNode::Type::Power ? e.childAtIndex(1).clone() : Rational(1); + Expression result = e.type() == ExpressionNode::Type::Power ? e.childAtIndex(1).clone() : Rational::Builder(1); return result; } @@ -688,37 +740,47 @@ bool Multiplication::TermHasNumeralExponent(const Expression & e) { return false; } -Expression Multiplication::mergeNegativePower(Context & context, Preferences::AngleUnit angleUnit) { - Multiplication m; +Expression Multiplication::mergeNegativePower(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) { + /* mergeNegativePower groups all factors that are power of form a^(-b) together + * for instance, a^(-1)*b^(-c)*c = c*(a*b^c)^(-1) */ + Multiplication m = Multiplication::Builder(); // Special case for rational p/q: if q != 1, q should be at denominator if (childAtIndex(0).type() == ExpressionNode::Type::Rational && !childAtIndex(0).convert().integerDenominator().isOne()) { Rational r = childAtIndex(0).convert(); - m.addChildAtIndexInPlace(Rational(r.integerDenominator()), 0, m.numberOfChildren()); + m.addChildAtIndexInPlace(Rational::Builder(r.integerDenominator()), 0, m.numberOfChildren()); if (r.signedIntegerNumerator().isOne()) { removeChildAtIndexInPlace(0); } else { - replaceChildAtIndexInPlace(0, Rational(r.signedIntegerNumerator())); + replaceChildAtIndexInPlace(0, Rational::Builder(r.signedIntegerNumerator())); } } int i = 0; + // Look for power of form a^(-b) while (i < numberOfChildren()) { - if (childAtIndex(i).type() == ExpressionNode::Type::Power && childAtIndex(i).childAtIndex(1).sign() == ExpressionNode::Sign::Negative) { - Expression e = childAtIndex(i); - e.childAtIndex(1).setSign(ExpressionNode::Sign::Positive, context, angleUnit); - removeChildAtIndexInPlace(i); - m.addChildAtIndexInPlace(e, m.numberOfChildren(), m.numberOfChildren()); - e.shallowReduce(context, angleUnit, ExpressionNode::ReductionTarget::User); - } else { - i++; + if (childAtIndex(i).type() == ExpressionNode::Type::Power) { + Expression p = childAtIndex(i); + Expression positivePIndex = p.childAtIndex(1).makePositiveAnyNegativeNumeralFactor(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::User); + if (!positivePIndex.isUninitialized()) { + // Remove a^(-b) from the Multiplication + removeChildAtIndexInPlace(i); + // Add a^b to m + m.addChildAtIndexInPlace(p, m.numberOfChildren(), m.numberOfChildren()); + if (p.childAtIndex(1).isRationalOne()) { + p.replaceWithInPlace(p.childAtIndex(0)); + } + // We do not increment i because we removed one child from the Multiplication + continue; + } } + i++; } if (m.numberOfChildren() == 0) { return *this; } - m.sortChildrenInPlace(ExpressionNode::SimplificationOrder, true); - Power p(m.squashUnaryHierarchyInPlace(), Rational(-1)); + m.sortChildrenInPlace([](const ExpressionNode * e1, const ExpressionNode * e2, bool canBeInterrupted) { return ExpressionNode::SimplificationOrder(e1, e2, true, canBeInterrupted); }, true); + Power p = Power::Builder(m.squashUnaryHierarchyInPlace(), Rational::Builder(-1)); addChildAtIndexInPlace(p, 0, numberOfChildren()); - sortChildrenInPlace(ExpressionNode::SimplificationOrder, true); + sortChildrenInPlace([](const ExpressionNode * e1, const ExpressionNode * e2, bool canBeInterrupted) { return ExpressionNode::SimplificationOrder(e1, e2, true, canBeInterrupted); }, true); return squashUnaryHierarchyInPlace(); } @@ -729,10 +791,10 @@ const Expression Multiplication::Base(const Expression e) { return e; } -template MatrixComplex MultiplicationNode::computeOnComplexAndMatrix(std::complex const, const MatrixComplex); -template MatrixComplex MultiplicationNode::computeOnComplexAndMatrix(std::complex const, const MatrixComplex); -template Complex MultiplicationNode::compute(const std::complex, const std::complex); -template Complex MultiplicationNode::compute(const std::complex, const std::complex); +template MatrixComplex MultiplicationNode::computeOnComplexAndMatrix(std::complex const, const MatrixComplex, Preferences::ComplexFormat); +template MatrixComplex MultiplicationNode::computeOnComplexAndMatrix(std::complex const, const MatrixComplex, Preferences::ComplexFormat); +template Complex MultiplicationNode::compute(const std::complex, const std::complex, Preferences::ComplexFormat); +template Complex MultiplicationNode::compute(const std::complex, const std::complex, Preferences::ComplexFormat); template void Multiplication::computeOnArrays(double * m, double * n, double * result, int mNumberOfColumns, int mNumberOfRows, int nNumberOfColumns); } diff --git a/poincare/src/n_ary_expression_node.cpp b/poincare/src/n_ary_expression_node.cpp index b6db830f5..2b6ffd44e 100644 --- a/poincare/src/n_ary_expression_node.cpp +++ b/poincare/src/n_ary_expression_node.cpp @@ -28,6 +28,10 @@ void NAryExpressionNode::sortChildrenInPlace(ExpressionOrder order, bool canBeIn } } +bool NAryExpressionNode::isReal(Context & context) const { + return NAryExpression(this).allChildrenAreReal(context) == 1; +} + Expression NAryExpressionNode::squashUnaryHierarchyInPlace() { NAryExpression reference = NAryExpression(this); if (reference.numberOfChildren() == 1) { @@ -40,7 +44,7 @@ Expression NAryExpressionNode::squashUnaryHierarchyInPlace() { // Private -int NAryExpressionNode::simplificationOrderSameType(const ExpressionNode * e, bool canBeInterrupted) const { +int NAryExpressionNode::simplificationOrderSameType(const ExpressionNode * e, bool ascending, bool canBeInterrupted) const { int m = numberOfChildren(); int n = e->numberOfChildren(); for (int i = 1; i <= m; i++) { @@ -48,32 +52,47 @@ int NAryExpressionNode::simplificationOrderSameType(const ExpressionNode * e, bo if (n < i) { return 1; } - int order = SimplificationOrder(childAtIndex(m-i), e->childAtIndex(n-i), canBeInterrupted); + int order = SimplificationOrder(childAtIndex(m-i), e->childAtIndex(n-i), ascending, canBeInterrupted); if (order != 0) { return order; } } // The NULL node is the least node type. if (n > m) { - return -1; + return ascending ? -1 : 1; } return 0; } -int NAryExpressionNode::simplificationOrderGreaterType(const ExpressionNode * e, bool canBeInterrupted) const { +int NAryExpressionNode::simplificationOrderGreaterType(const ExpressionNode * e, bool ascending, bool canBeInterrupted) const { int m = numberOfChildren(); if (m == 0) { return -1; } /* Compare e to last term of hierarchy. */ - int order = SimplificationOrder(childAtIndex(m-1), e, canBeInterrupted); + int order = SimplificationOrder(childAtIndex(m-1), e, ascending, canBeInterrupted); if (order != 0) { return order; } if (m > 1) { - return 1; + return ascending ? 1 : -1; } return 0; } +int NAryExpression::allChildrenAreReal(Context & context) const { + int i = 0; + int result = 1; + while (i < numberOfChildren()) { + Expression c = childAtIndex(i); + if (c.type() == ExpressionNode::Type::ComplexCartesian) { + result *= 0; + } else if (!c.isReal(context)) { + return -1; + } + i++; + } + return result; +} + } diff --git a/poincare/src/naperian_logarithm.cpp b/poincare/src/naperian_logarithm.cpp index 852476d02..29ea6cb9b 100644 --- a/poincare/src/naperian_logarithm.cpp +++ b/poincare/src/naperian_logarithm.cpp @@ -18,13 +18,14 @@ int NaperianLogarithmNode::serialize(char * buffer, int bufferSize, Preferences: return SerializationHelper::Prefix(this, buffer, bufferSize, floatDisplayMode, numberOfSignificantDigits, NaperianLogarithm::s_functionHelper.name()); } -Expression NaperianLogarithmNode::shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) { - return NaperianLogarithm(this).shallowReduce(context, angleUnit, target); +Expression NaperianLogarithmNode::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) { + return NaperianLogarithm(this).shallowReduce(context, complexFormat, angleUnit, target); } -Expression NaperianLogarithm::shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { + +Expression NaperianLogarithm::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { { - Expression e = Expression::defaultShallowReduce(context, angleUnit); + Expression e = Expression::defaultShallowReduce(); if (e.isUndefined()) { return e; } @@ -34,9 +35,9 @@ Expression NaperianLogarithm::shallowReduce(Context & context, Preferences::Angl return SimplificationHelper::Map(*this, context, angleUnit); } #endif - Logarithm l = Logarithm::Builder(childAtIndex(0), Constant(Ion::Charset::Exponential)); + Logarithm l = Logarithm::Builder(childAtIndex(0), Constant::Builder(Ion::Charset::Exponential)); replaceWithInPlace(l); - return l.shallowReduce(context, angleUnit, target); + return l.shallowReduce(context, complexFormat, angleUnit, target); } } diff --git a/poincare/src/nth_root.cpp b/poincare/src/nth_root.cpp index 0624a2b70..b7cbe9566 100644 --- a/poincare/src/nth_root.cpp +++ b/poincare/src/nth_root.cpp @@ -1,8 +1,12 @@ #include +#include +#include #include +#include #include #include #include +#include #include #include #include @@ -15,7 +19,7 @@ constexpr Expression::FunctionHelper NthRoot::s_functionHelper; int NthRootNode::numberOfChildren() const { return NthRoot::s_functionHelper.numberOfChildren(); } Layout NthRootNode::createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const { - return NthRootLayout( + return NthRootLayout::Builder( childAtIndex(0)->createLayout(floatDisplayMode, numberOfSignificantDigits), childAtIndex(1)->createLayout(floatDisplayMode, numberOfSignificantDigits)); } @@ -24,42 +28,59 @@ int NthRootNode::serialize(char * buffer, int bufferSize, Preferences::PrintFloa return SerializationHelper::Prefix(this, buffer, bufferSize, floatDisplayMode, numberOfSignificantDigits, NthRoot::s_functionHelper.name()); } -Expression NthRootNode::shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) { - return NthRoot(this).shallowReduce(context, angleUnit, target); +Expression NthRootNode::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) { + return NthRoot(this).shallowReduce(context, complexFormat, angleUnit, target); } template -Evaluation NthRootNode::templatedApproximate(Context& context, Preferences::AngleUnit angleUnit) const { - Evaluation base = childAtIndex(0)->approximate(T(), context, angleUnit); - Evaluation index = childAtIndex(1)->approximate(T(), context, angleUnit); +Evaluation NthRootNode::templatedApproximate(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { + Evaluation base = childAtIndex(0)->approximate(T(), context, complexFormat, angleUnit); + Evaluation index = childAtIndex(1)->approximate(T(), context, complexFormat, angleUnit); Complex result = Complex::Undefined(); if (base.type() == EvaluationNode::Type::Complex && index.type() == EvaluationNode::Type::Complex) { - Complex basec = static_cast &>(base); - Complex indexc = static_cast &>(index); - result = PowerNode::compute(basec.stdComplex(), std::complex(1)/(indexc.stdComplex())); + std::complex basec = static_cast &>(base).stdComplex(); + std::complex indexc = static_cast &>(index).stdComplex(); + /* If the complexFormat is Real, we look for nthroot of form root(x,q) with + * x real and q integer because they might have a real form which does not + * correspond to the principale angle. */ + if (complexFormat == Preferences::ComplexFormat::Real) { + // root(x, q) with q integer and x real + if (basec.imag() == 0.0 && indexc.imag() == 0.0 && std::round(indexc.real()) == indexc.real()) { + std::complex absBasec = basec; + absBasec.real(std::fabs(absBasec.real())); + // compute root(|x|, q) + Complex absBasePowIndex = PowerNode::compute(absBasec, std::complex(1.0)/(indexc), complexFormat); + // q odd if (-1)^q = -1 + if (std::pow((T)-1.0, (T)indexc.real()) < 0.0) { + return basec.real() < 0 ? Complex::Builder(-absBasePowIndex.stdComplex()) : absBasePowIndex; + } + } + } + result = PowerNode::compute(basec, std::complex(1.0)/(indexc), complexFormat); } return result; } -Expression NthRoot::shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { + +Expression NthRoot::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { { - Expression e = Expression::defaultShallowReduce(context, angleUnit); + Expression e = Expression::defaultShallowReduce(); if (e.isUndefined()) { return e; } } #if MATRIX_EXACT_REDUCING if (childAtIndex(0).type() == ExpressionNode::Type::Matrix || childAtIndex(1).type() == ExpressionNode:Type::Matrix) { - return Undefined(); + return Undefined::Builder(); } #endif - Expression invIndex = Power(childAtIndex(1), Rational(-1)); - Power p = Power(childAtIndex(0), invIndex); - invIndex.shallowReduce(context, angleUnit, target); + Expression invIndex = Power::Builder(childAtIndex(1), Rational::Builder(-1)); + Power p = Power::Builder(childAtIndex(0), invIndex); + invIndex.shallowReduce(context, complexFormat, angleUnit, target); replaceWithInPlace(p); - return p.shallowReduce(context, angleUnit, target); + return p.shallowReduce(context, complexFormat, angleUnit, target); } } diff --git a/poincare/src/nth_root_layout.cpp b/poincare/src/nth_root_layout.cpp index 208952e46..4e7dd4573 100644 --- a/poincare/src/nth_root_layout.cpp +++ b/poincare/src/nth_root_layout.cpp @@ -270,16 +270,21 @@ void NthRootLayoutNode::render(KDContext * ctx, KDPoint p, KDColor expressionCol } } -NthRootLayout::NthRootLayout(Layout radicand) : NthRootLayout() { - replaceChildAtIndexInPlace(0, radicand); +NthRootLayout NthRootLayout::Builder(Layout child) { + void * bufferNode = TreePool::sharedPool()->alloc(sizeof(NthRootLayoutNode)); + NthRootLayoutNode * node = new (bufferNode) NthRootLayoutNode(false); + TreeHandle h = TreeHandle::BuildWithGhostChildren(node); + h.replaceChildAtIndexInPlace(0, child); + return static_cast(h); } -NthRootLayout::NthRootLayout(Layout radicand, Layout index) : NthRootLayout() { - replaceChildAtIndexInPlace(0, radicand); - addChildAtIndexInPlace(index, 1, 1); - static_cast(node())->setNumberOfChildren(2); +NthRootLayout NthRootLayout::Builder(Layout child, Layout index) { + void * bufferNode = TreePool::sharedPool()->alloc(sizeof(NthRootLayoutNode)); + NthRootLayoutNode * node = new (bufferNode) NthRootLayoutNode(true); + TreeHandle h = TreeHandle::BuildWithGhostChildren(node); + h.replaceChildAtIndexInPlace(0, child); + h.replaceChildAtIndexInPlace(1, index); + return static_cast(h); } -NthRootLayout::NthRootLayout() : Layout(TreePool::sharedPool()->createTreeNode()) {} - } diff --git a/poincare/src/number.cpp b/poincare/src/number.cpp index 0b0138c40..bd5c613af 100644 --- a/poincare/src/number.cpp +++ b/poincare/src/number.cpp @@ -17,9 +17,11 @@ namespace Poincare { double NumberNode::doubleApproximation() const { switch (type()) { case Type::Undefined: + case Type::Unreal: return NAN; case Type::Infinity: - return sign() == Sign::Negative ? -INFINITY : INFINITY; + assert(Number(this).sign() == Sign::Negative || Number(this).sign() == Sign::Positive); + return Number(this).sign() == Sign::Negative ? -INFINITY : INFINITY; case Type::Float: if (size() == sizeof(FloatNode)) { return static_cast *>(this)->value(); @@ -39,8 +41,8 @@ Number Number::ParseNumber(const char * integralPart, size_t integralLength, con // Integer if (exponentLength == 0 && decimalLenght == 0) { Integer i(integralPart, integralLength, false); - if (!i.isInfinity()) { - return Rational(i); + if (!i.isOverflow()) { + return Rational::Builder(i); } } int exp; @@ -53,32 +55,32 @@ Number Number::ParseNumber(const char * integralPart, size_t integralLength, con // Avoid Decimal with exponent > k_maxExponentLength if (exponentLength >= Decimal::k_maxExponentLength || exp > Decimal::k_maxExponent || exp < -Decimal::k_maxExponent) { if (exp < 0) { - return Decimal(0.0); + return Decimal::Builder(0.0); } else { - return Infinity(false); + return Infinity::Builder(false); } } - return Decimal(integralPart, integralLength, decimalPart, decimalLenght, exp); + return Decimal::Builder(integralPart, integralLength, decimalPart, decimalLenght, exp); } template Number Number::DecimalNumber(T f) { if (std::isnan(f)) { - return Undefined(); + return Undefined::Builder(); } if (std::isinf(f)) { - return Infinity(f < 0.0); + return Infinity::Builder(f < 0.0); } - return Decimal(f); + return Decimal::Builder(f); } Number Number::FloatNumber(double d) { if (std::isnan(d)) { - return Undefined(); + return Undefined::Builder(); } else if (std::isinf(d)) { - return Infinity(d < 0.0); + return Infinity::Builder(d < 0.0); } else { - return Float(d); + return Float::Builder(d); } } @@ -90,7 +92,8 @@ Number Number::BinaryOperation(const Number & i, const Number & j, RationalBinar return a; } } - // one of the operand is Undefined/Infinity/Float or the Rational addition overflowed + /* At least one of the operands is Undefined/Infinity/Float, or the Rational + * addition overflowed */ double a = doubleOp(i.node()->doubleApproximation(), j.node()->doubleApproximation()); return FloatNumber(a); } @@ -109,7 +112,7 @@ Number Number::Power(const Number & i, const Number & j) { [](const Rational & i, const Rational & j) { if (!j.integerDenominator().isOne()) { // We return an overflown result to reach the escape case Float+Float - return Rational(Integer::Overflow(false)); + return Rational::Builder(Integer::Overflow(false)); } return Rational::IntegerPower(i, j.signedIntegerNumerator()); }, diff --git a/poincare/src/opposite.cpp b/poincare/src/opposite.cpp index e3bff0c12..ec4665c05 100644 --- a/poincare/src/opposite.cpp +++ b/poincare/src/opposite.cpp @@ -1,5 +1,7 @@ #include +#include #include +#include #include #include #include @@ -17,20 +19,21 @@ int OppositeNode::polynomialDegree(Context & context, const char * symbolName) c return childAtIndex(0)->polynomialDegree(context, symbolName); } -ExpressionNode::Sign OppositeNode::sign() const { - if (childAtIndex(0)->sign() == Sign::Positive) { +ExpressionNode::Sign OppositeNode::sign(Context * context) const { + Sign child0Sign = childAtIndex(0)->sign(context); + if (child0Sign == Sign::Positive) { return Sign::Negative; } - if (childAtIndex(0)->sign() == Sign::Negative) { + if (child0Sign == Sign::Negative) { return Sign::Positive; } - return Sign::Unknown; + return ExpressionNode::sign(context); } /* Layout */ bool OppositeNode::childNeedsParenthesis(const TreeNode * child) const { - if (static_cast(child)->isNumber() && static_cast(child)->sign() == Sign::Negative) { + if (static_cast(child)->isNumber() && Number(static_cast(child)).sign() == Sign::Negative) { return true; } Type types[] = {Type::Addition, Type::Subtraction, Type::Opposite}; @@ -38,7 +41,7 @@ bool OppositeNode::childNeedsParenthesis(const TreeNode * child) const { } Layout OppositeNode::createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const { - HorizontalLayout result = HorizontalLayout(CharLayout('-')); + HorizontalLayout result = HorizontalLayout::Builder(CharLayout::Builder('-')); if (childAtIndex(0)->type() == Type::Opposite) { result.addOrMergeChildAtIndex(LayoutHelper::Parentheses(childAtIndex(0)->createLayout(floatDisplayMode, numberOfSignificantDigits), false), 1, false); } else { @@ -60,25 +63,23 @@ int OppositeNode::serialize(char * buffer, int bufferSize, Preferences::PrintFlo return numberOfChar; } -Expression OppositeNode::shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) { - return Opposite(this).shallowReduce(context, angleUnit, target); +Expression OppositeNode::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) { + return Opposite(this).shallowReduce(context, complexFormat, angleUnit, target); } /* Simplification */ -Opposite::Opposite() : Expression(TreePool::sharedPool()->createTreeNode()) {} - -Expression Opposite::shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { - Expression result = Expression::defaultShallowReduce(context, angleUnit); +Expression Opposite::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { + Expression result = Expression::defaultShallowReduce(); if (result.isUndefined()) { return result; } Expression child = result.childAtIndex(0); #if MATRIX_EXACT_REDUCING #endif - result = Multiplication(Rational(-1), child); + result = Multiplication::Builder(Rational::Builder(-1), child); replaceWithInPlace(result); - return result.shallowReduce(context, angleUnit, target); + return result.shallowReduce(context, complexFormat, angleUnit, target); } } diff --git a/poincare/src/parenthesis.cpp b/poincare/src/parenthesis.cpp index 32b5152b5..3ad088ca9 100644 --- a/poincare/src/parenthesis.cpp +++ b/poincare/src/parenthesis.cpp @@ -16,17 +16,18 @@ int ParenthesisNode::serialize(char * buffer, int bufferSize, Preferences::Print return SerializationHelper::Prefix(this, buffer, bufferSize, floatDisplayMode, numberOfSignificantDigits, ""); } -Expression ParenthesisNode::shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) { - return Parenthesis(this).shallowReduce(context, angleUnit); +Expression ParenthesisNode::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) { + return Parenthesis(this).shallowReduce(); } template -Evaluation ParenthesisNode::templatedApproximate(Context& context, Preferences::AngleUnit angleUnit) const { - return childAtIndex(0)->approximate(T(), context, angleUnit); +Evaluation ParenthesisNode::templatedApproximate(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { + return childAtIndex(0)->approximate(T(), context, complexFormat, angleUnit); } -Expression Parenthesis::shallowReduce(Context & context, Preferences::AngleUnit angleUnit) { - Expression e = Expression::defaultShallowReduce(context, angleUnit); + +Expression Parenthesis::shallowReduce() { + Expression e = Expression::defaultShallowReduce(); if (e.isUndefined()) { return e; } diff --git a/poincare/src/parsing/parser.cpp b/poincare/src/parsing/parser.cpp index 5274fec93..0925f4f4a 100644 --- a/poincare/src/parsing/parser.cpp +++ b/poincare/src/parsing/parser.cpp @@ -2,6 +2,8 @@ namespace Poincare { +static inline Token::Type maxToken(Token::Type x, Token::Type y) { return ((int)x > (int) y ? x : y); } + constexpr const Expression::FunctionHelper * Parser::s_reservedFunctions[]; Expression Parser::parse() { @@ -40,6 +42,7 @@ bool Parser::IsSpecialIdentifierName(const char * name, size_t nameLength) { Token::CompareNonNullTerminatedName(name, nameLength, Symbol::k_ans) == 0 || Token::CompareNonNullTerminatedName(name, nameLength, Infinity::Name()) == 0 || Token::CompareNonNullTerminatedName(name, nameLength, Undefined::Name()) == 0 || + Token::CompareNonNullTerminatedName(name, nameLength, Unreal::Name()) == 0 || Token::CompareNonNullTerminatedName(name, nameLength, "u_") == 0 || Token::CompareNonNullTerminatedName(name, nameLength, "v_") == 0 || Token::CompareNonNullTerminatedName(name, nameLength, "u") == 0 || @@ -49,7 +52,7 @@ bool Parser::IsSpecialIdentifierName(const char * name, size_t nameLength) { } Expression Parser::parseUntil(Token::Type stoppingType) { - typedef void (Parser::*TokenParser)(Expression & leftHandSide); + typedef void (Parser::*TokenParser)(Expression & leftHandSide, Token::Type stoppingType); static constexpr TokenParser tokenParsers[] = { &Parser::parseUnexpected, // Token::EndOfStream &Parser::parseStore, // Token::Store @@ -79,7 +82,7 @@ Expression Parser::parseUntil(Token::Type stoppingType) { Expression leftHandSide; do { popToken(); - (this->*(tokenParsers[m_currentToken.type()]))(leftHandSide); + (this->*(tokenParsers[m_currentToken.type()]))(leftHandSide, stoppingType); } while (m_status == Status::Progress && nextTokenHasPrecedenceOver(stoppingType)); return leftHandSide; } @@ -132,11 +135,11 @@ void Parser::isThereImplicitMultiplication() { ); } -void Parser::parseUnexpected(Expression & leftHandSide) { +void Parser::parseUnexpected(Expression & leftHandSide, Token::Type stoppingType) { m_status = Status::Error; // Unexpected Token } -void Parser::parseNumber(Expression & leftHandSide) { +void Parser::parseNumber(Expression & leftHandSide, Token::Type stoppingType) { if (!leftHandSide.isUninitialized()) { m_status = Status::Error; //FIXME return; @@ -149,66 +152,66 @@ void Parser::parseNumber(Expression & leftHandSide) { isThereImplicitMultiplication(); } -void Parser::parsePlus(Expression & leftHandSide) { +void Parser::parsePlus(Expression & leftHandSide, Token::Type stoppingType) { Expression rightHandSide; if (parseBinaryOperator(leftHandSide, rightHandSide, Token::Plus)) { - leftHandSide = Addition(leftHandSide, rightHandSide); + leftHandSide = Addition::Builder(leftHandSide, rightHandSide); } } -void Parser::parseEmpty(Expression & leftHandSide) { +void Parser::parseEmpty(Expression & leftHandSide, Token::Type stoppingType) { if (!leftHandSide.isUninitialized()) { m_status = Status::Error; //FIXME return; } - leftHandSide = EmptyExpression(); + leftHandSide = EmptyExpression::Builder(); } -void Parser::parseMinus(Expression & leftHandSide) { +void Parser::parseMinus(Expression & leftHandSide, Token::Type stoppingType) { if (leftHandSide.isUninitialized()) { - Expression rightHandSide = parseUntil(Token::Slash); + Expression rightHandSide = parseUntil(maxToken(stoppingType, Token::Minus)); if (m_status != Status::Progress) { return; } - leftHandSide = Opposite(rightHandSide); + leftHandSide = Opposite::Builder(rightHandSide); } else { Expression rightHandSide = parseUntil(Token::Minus); // Subtraction is left-associative if (m_status != Status::Progress) { return; } - leftHandSide = Subtraction(leftHandSide, rightHandSide); + leftHandSide = Subtraction::Builder(leftHandSide, rightHandSide); } } -void Parser::parseTimes(Expression & leftHandSide) { +void Parser::parseTimes(Expression & leftHandSide, Token::Type stoppingType) { Expression rightHandSide; if (parseBinaryOperator(leftHandSide, rightHandSide, Token::Times)) { - leftHandSide = Multiplication(leftHandSide, rightHandSide); + leftHandSide = Multiplication::Builder(leftHandSide, rightHandSide); } } -void Parser::parseSlash(Expression & leftHandSide) { +void Parser::parseSlash(Expression & leftHandSide, Token::Type stoppingType) { Expression rightHandSide; if (parseBinaryOperator(leftHandSide, rightHandSide, Token::Slash)) { - leftHandSide = Division(leftHandSide, rightHandSide); + leftHandSide = Division::Builder(leftHandSide, rightHandSide); } } -void Parser::parseImplicitTimes(Expression & leftHandSide) { +void Parser::parseImplicitTimes(Expression & leftHandSide, Token::Type stoppingType) { Expression rightHandSide; if (parseBinaryOperator(leftHandSide, rightHandSide, Token::Slash)) { - leftHandSide = Multiplication(leftHandSide, rightHandSide); + leftHandSide = Multiplication::Builder(leftHandSide, rightHandSide); } } -void Parser::parseCaret(Expression & leftHandSide) { +void Parser::parseCaret(Expression & leftHandSide, Token::Type stoppingType) { Expression rightHandSide; if (parseBinaryOperator(leftHandSide, rightHandSide, Token::ImplicitTimes)) { - leftHandSide = Power(leftHandSide, rightHandSide); + leftHandSide = Power::Builder(leftHandSide, rightHandSide); } } -void Parser::parseEqual(Expression & leftHandSide) { +void Parser::parseEqual(Expression & leftHandSide, Token::Type stoppingType) { if (leftHandSide.isUninitialized()) { m_status = Status::Error; // Equal must have a left operand return; @@ -217,7 +220,7 @@ void Parser::parseEqual(Expression & leftHandSide) { if (parseBinaryOperator(leftHandSide, rightHandSide, Token::Equal)) { /* We parse until finding a token of lesser precedence than Equal. The next * token is thus either EndOfStream or Store. */ - leftHandSide = Equal(leftHandSide, rightHandSide); + leftHandSide = Equal::Builder(leftHandSide, rightHandSide); } if (!m_nextToken.is(Token::EndOfStream)) { m_status = Status::Error; // Equal should be top-most expression in Tree @@ -225,7 +228,7 @@ void Parser::parseEqual(Expression & leftHandSide) { } } -void Parser::parseStore(Expression & leftHandSide) { +void Parser::parseStore(Expression & leftHandSide, Token::Type stoppingType) { if (leftHandSide.isUninitialized()) { m_status = Status::Error; // Left-hand side missing. return; @@ -249,10 +252,10 @@ void Parser::parseStore(Expression & leftHandSide) { m_status = Status::Error; // Store expects a single symbol or function. return; } - leftHandSide = Store(leftHandSide, static_cast(rightHandSide)); + leftHandSide = Store::Builder(leftHandSide, static_cast(rightHandSide)); } -void Parser::parseLeftSuperscript(Expression & leftHandSide) { +void Parser::parseLeftSuperscript(Expression & leftHandSide, Token::Type stoppingType) { if (leftHandSide.isUninitialized()) { m_status = Status::Error; // Power must have a left operand return; @@ -265,7 +268,7 @@ void Parser::parseLeftSuperscript(Expression & leftHandSide) { m_status = Status::Error; // Right superscript marker missing. return; } - leftHandSide = Power(leftHandSide, rightHandSide); + leftHandSide = Power::Builder(leftHandSide, rightHandSide); isThereImplicitMultiplication(); } @@ -285,7 +288,7 @@ bool Parser::parseBinaryOperator(const Expression & leftHandSide, Expression & r return true; } -void Parser::parseLeftParenthesis(Expression & leftHandSide) { +void Parser::parseLeftParenthesis(Expression & leftHandSide, Token::Type stoppingType) { if (!leftHandSide.isUninitialized()) { m_status = Status::Error; //FIXME return; @@ -298,15 +301,15 @@ void Parser::parseLeftParenthesis(Expression & leftHandSide) { m_status = Status::Error; // Right parenthesis missing. return; } - leftHandSide = Parenthesis(leftHandSide); + leftHandSide = Parenthesis::Builder(leftHandSide); isThereImplicitMultiplication(); } -void Parser::parseBang(Expression & leftHandSide) { +void Parser::parseBang(Expression & leftHandSide, Token::Type stoppingType) { if (leftHandSide.isUninitialized()) { m_status = Status::Error; // Left-hand side missing } else { - leftHandSide = Factorial(leftHandSide); + leftHandSide = Factorial::Builder(leftHandSide); } isThereImplicitMultiplication(); } @@ -319,8 +322,8 @@ bool Parser::currentTokenIsSpecialIdentifier() const { return IsSpecialIdentifierName(m_currentToken.text(), m_currentToken.length()); } -void Parser::parseConstant(Expression & leftHandSide) { - leftHandSide = Constant(m_currentToken.text()[0]); +void Parser::parseConstant(Expression & leftHandSide, Token::Type stoppingType) { + leftHandSide = Constant::Builder(m_currentToken.text()[0]); isThereImplicitMultiplication(); } @@ -357,12 +360,12 @@ void Parser::parseSequence(Expression & leftHandSide, const char name, Token::Ty if (m_status != Status::Progress) { } else if (!popTokenIfType(rightDelimiter)) { m_status = Status::Error; // Right delimiter missing. - } else if (rank.isIdenticalTo(Symbol("n",1))) { - char sym[4] = {name, '(', 'n', ')'}; - leftHandSide = Symbol(sym, 4); - } else if (rank.isIdenticalTo(Addition(Symbol("n",1),Rational("1")))) { - char sym[6] = {name, '(', 'n', '+', '1', ')'}; - leftHandSide = Symbol(sym, 6); + } else if (rank.isIdenticalTo(Symbol::Builder("n",1))) { + char sym[5] = {name, '(', 'n', ')', 0}; + leftHandSide = Symbol::Builder(sym, 4); + } else if (rank.isIdenticalTo(Addition::Builder(Symbol::Builder("n",1),Rational::Builder("1")))) { + char sym[7] = {name, '(', 'n', '+', '1', ')', 0}; + leftHandSide = Symbol::Builder(sym, 6); } else { m_status = Status::Error; // Unexpected parameter. } @@ -373,9 +376,11 @@ void Parser::parseSpecialIdentifier(Expression & leftHandSide) { if (m_currentToken.compareTo(Symbol::k_ans) == 0) { leftHandSide = Symbol::Ans(); } else if (m_currentToken.compareTo(Infinity::Name()) == 0) { - leftHandSide = Infinity(false); + leftHandSide = Infinity::Builder(false); } else if (m_currentToken.compareTo(Undefined::Name()) == 0) { - leftHandSide = Undefined(); + leftHandSide = Undefined::Builder(); + } else if (m_currentToken.compareTo(Unreal::Name()) == 0) { + leftHandSide = Unreal::Builder(); } else if (m_currentToken.compareTo("u_") == 0 || m_currentToken.compareTo("v_") == 0) { // Special case for sequences (e.g. "u_{n}") parseSequence(leftHandSide, m_currentToken.text()[0], Token::LeftBrace, Token::RightBrace); } else if (m_currentToken.compareTo("u") == 0 || m_currentToken.compareTo("v") == 0) { // Special case for sequences (e.g. "u(n)") @@ -407,7 +412,7 @@ void Parser::parseCustomIdentifier(Expression & leftHandSide, const char * name, return; } if (!popTokenIfType(Token::LeftParenthesis)) { - leftHandSide = Symbol(name, length); + leftHandSide = Symbol::Builder(name, length); return; } Expression parameter = parseCommaSeparatedList(); @@ -425,11 +430,11 @@ void Parser::parseCustomIdentifier(Expression & leftHandSide, const char * name, } else if (!popTokenIfType(Token::RightParenthesis)) { m_status = Status::Error; // Right parenthesis missing. } else { - leftHandSide = Function(name, length, parameter); + leftHandSide = Function::Builder(name, length, parameter); } } -void Parser::parseIdentifier(Expression & leftHandSide) { +void Parser::parseIdentifier(Expression & leftHandSide, Token::Type stoppingType) { if (!leftHandSide.isUninitialized()) { m_status = Status::Error; //FIXME return; @@ -454,7 +459,7 @@ Expression Parser::parseFunctionParameters() { return Expression(); } if (popTokenIfType(Token::RightParenthesis)) { - return Matrix(); // The function has no parameter. + return Matrix::Builder(); // The function has no parameter. } Expression commaSeparatedList = parseCommaSeparatedList(); if (m_status != Status::Progress) { @@ -467,12 +472,12 @@ Expression Parser::parseFunctionParameters() { return commaSeparatedList; } -void Parser::parseMatrix(Expression & leftHandSide) { +void Parser::parseMatrix(Expression & leftHandSide, Token::Type stoppingType) { if (!leftHandSide.isUninitialized()) { m_status = Status::Error; //FIXME return; } - Matrix matrix; + Matrix matrix = Matrix::Builder(); int numberOfRows = 0; int numberOfColumns = 0; while (!popTokenIfType(Token::RightBracket)) { @@ -515,7 +520,7 @@ Expression Parser::parseVector() { } Expression Parser::parseCommaSeparatedList() { - Matrix commaSeparatedList; + Matrix commaSeparatedList = Matrix::Builder(); int length = 0; do { Expression item = parseUntil(Token::Comma); diff --git a/poincare/src/parsing/parser.h b/poincare/src/parsing/parser.h index f85d00171..bd591b4dc 100644 --- a/poincare/src/parsing/parser.h +++ b/poincare/src/parsing/parser.h @@ -45,23 +45,23 @@ private: void isThereImplicitMultiplication(); // Specific Token parsers - void parseUnexpected(Expression & leftHandSide); - void parseNumber(Expression & leftHandSide); - void parseConstant(Expression & leftHandSide); - void parseIdentifier(Expression & leftHandSide); - void parseEmpty(Expression & leftHandSide); - void parseMatrix(Expression & leftHandSide); - void parseLeftParenthesis(Expression & leftHandSide); - void parseBang(Expression & leftHandSide); - void parsePlus(Expression & leftHandSide); - void parseMinus(Expression & leftHandSide); - void parseTimes(Expression & leftHandSide); - void parseSlash(Expression & leftHandSide); - void parseImplicitTimes(Expression & leftHandSide); - void parseCaret(Expression & leftHandSide); - void parseEqual(Expression & leftHandSide); - void parseStore(Expression & leftHandSide); - void parseLeftSuperscript(Expression & leftHandSide); + void parseUnexpected(Expression & leftHandSide, Token::Type stoppingType = (Token::Type)0); + void parseNumber(Expression & leftHandSide, Token::Type stoppingType = (Token::Type)0); + void parseConstant(Expression & leftHandSide, Token::Type stoppingType = (Token::Type)0); + void parseIdentifier(Expression & leftHandSide, Token::Type stoppingType = (Token::Type)0); + void parseEmpty(Expression & leftHandSide, Token::Type stoppingType = (Token::Type)0); + void parseMatrix(Expression & leftHandSide, Token::Type stoppingType = (Token::Type)0); + void parseLeftParenthesis(Expression & leftHandSide, Token::Type stoppingType = (Token::Type)0); + void parseBang(Expression & leftHandSide, Token::Type stoppingType = (Token::Type)0); + void parsePlus(Expression & leftHandSide, Token::Type stoppingType = (Token::Type)0); + void parseMinus(Expression & leftHandSide, Token::Type stoppingType = (Token::Type)0); + void parseTimes(Expression & leftHandSide, Token::Type stoppingType = (Token::Type)0); + void parseSlash(Expression & leftHandSide, Token::Type stoppingType = (Token::Type)0); + void parseImplicitTimes(Expression & leftHandSide, Token::Type stoppingType = (Token::Type)0); + void parseCaret(Expression & leftHandSide, Token::Type stoppingType = (Token::Type)0); + void parseEqual(Expression & leftHandSide, Token::Type stoppingType = (Token::Type)0); + void parseStore(Expression & leftHandSide, Token::Type stoppingType = (Token::Type)0); + void parseLeftSuperscript(Expression & leftHandSide, Token::Type stoppingType = (Token::Type)0); // Parsing helpers bool parseBinaryOperator(const Expression & leftHandSide, Expression & rightHandSide, Token::Type stoppingType); @@ -127,6 +127,7 @@ private: &DivisionRemainder::s_functionHelper, &NthRoot::s_functionHelper, &Round::s_functionHelper, + &SignFunction::s_functionHelper, &Sine::s_functionHelper, &HyperbolicSine::s_functionHelper, &Sum::s_functionHelper, diff --git a/poincare/src/permute_coefficient.cpp b/poincare/src/permute_coefficient.cpp index 8970f23a0..2e01cd395 100644 --- a/poincare/src/permute_coefficient.cpp +++ b/poincare/src/permute_coefficient.cpp @@ -23,35 +23,36 @@ int PermuteCoefficientNode::serialize(char * buffer, int bufferSize, Preferences return SerializationHelper::Prefix(this, buffer, bufferSize, floatDisplayMode, numberOfSignificantDigits, PermuteCoefficient::s_functionHelper.name()); } -Expression PermuteCoefficientNode::shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) { - return PermuteCoefficient(this).shallowReduce(context, angleUnit); +Expression PermuteCoefficientNode::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) { + return PermuteCoefficient(this).shallowReduce(); } template -Evaluation PermuteCoefficientNode::templatedApproximate(Context& context, Preferences::AngleUnit angleUnit) const { - Evaluation nInput = childAtIndex(0)->approximate(T(), context, angleUnit); - Evaluation kInput = childAtIndex(1)->approximate(T(), context, angleUnit); +Evaluation PermuteCoefficientNode::templatedApproximate(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { + Evaluation nInput = childAtIndex(0)->approximate(T(), context, complexFormat, angleUnit); + Evaluation kInput = childAtIndex(1)->approximate(T(), context, complexFormat, angleUnit); T n = nInput.toScalar(); T k = kInput.toScalar(); if (std::isnan(n) || std::isnan(k) || n != std::round(n) || k != std::round(k) || n < 0.0f || k < 0.0f) { return Complex::Undefined(); } if (k > n) { - return Complex(0.0); + return Complex::Builder(0.0); } T result = 1; for (int i = (int)n-(int)k+1; i <= (int)n; i++) { result *= i; if (std::isinf(result) || std::isnan(result)) { - return Complex(result); + return Complex::Builder(result); } } - return Complex(std::round(result)); + return Complex::Builder(std::round(result)); } -Expression PermuteCoefficient::shallowReduce(Context & context, Preferences::AngleUnit angleUnit) { + +Expression PermuteCoefficient::shallowReduce() { { - Expression e = Expression::defaultShallowReduce(context, angleUnit); + Expression e = Expression::defaultShallowReduce(); if (e.isUndefined()) { return e; } @@ -60,13 +61,13 @@ Expression PermuteCoefficient::shallowReduce(Context & context, Preferences::Ang Expression c1 = childAtIndex(1); #if MATRIX_EXACT_REDUCING if (c0.type() == ExpressionNode::Type::Matrix || c1.type() == ExpressionNode::Type::Matrix) { - return replaceWith(new Undefined(), true); + return replaceWith(new Undefined::Builder(), true); } #endif if (c0.type() == ExpressionNode::Type::Rational) { Rational r0 = static_cast(c0); if (!r0.integerDenominator().isOne() || r0.sign() == ExpressionNode::Sign::Negative) { - Expression result = Undefined(); + Expression result = Undefined::Builder(); replaceWithInPlace(result); return result; } @@ -74,7 +75,7 @@ Expression PermuteCoefficient::shallowReduce(Context & context, Preferences::Ang if (c1.type() == ExpressionNode::Type::Rational) { Rational r1 = static_cast(c1); if (!r1.integerDenominator().isOne() || r1.sign() == ExpressionNode::Sign::Negative) { - Expression result = Undefined(); + Expression result = Undefined::Builder(); replaceWithInPlace(result); return result; } @@ -88,7 +89,7 @@ Expression PermuteCoefficient::shallowReduce(Context & context, Preferences::Ang Integer n = r0.unsignedIntegerNumerator(); Integer k = r1.unsignedIntegerNumerator(); if (n.isLowerThan(k)) { - Expression result = Rational(0); + Expression result = Rational::Builder(0); replaceWithInPlace(result); return result; } @@ -103,8 +104,8 @@ Expression PermuteCoefficient::shallowReduce(Context & context, Preferences::Ang Integer factor = Integer::Subtraction(n, Integer(i)); result = Integer::Multiplication(result, factor); } - assert(!result.isInfinity()); // < permute(k_maxNValue, k_maxNValue-1)~10^158 - Expression rationalResult = Rational(result); + assert(!result.isOverflow()); // < permute(k_maxNValue, k_maxNValue-1)~10^158 + Expression rationalResult = Rational::Builder(result); replaceWithInPlace(rationalResult); return rationalResult; diff --git a/poincare/src/power.cpp b/poincare/src/power.cpp index 4ad731d1f..91302c26b 100644 --- a/poincare/src/power.cpp +++ b/poincare/src/power.cpp @@ -8,12 +8,14 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include #include #include @@ -25,17 +27,17 @@ namespace Poincare { // Properties -ExpressionNode::Sign PowerNode::sign() const { - if (Expression::shouldStopProcessing()) { +ExpressionNode::Sign PowerNode::sign(Context * context) const { + if (Expression::ShouldStopProcessing()) { return Sign::Unknown; } - if (childAtIndex(0)->sign() == Sign::Positive && childAtIndex(1)->sign() != Sign::Unknown) { + if (childAtIndex(0)->sign(context) == Sign::Positive && childAtIndex(1)->sign(context) != Sign::Unknown) { return Sign::Positive; } - if (childAtIndex(0)->sign() == Sign::Negative && childAtIndex(1)->type() == ExpressionNode::Type::Rational) { + if (childAtIndex(0)->sign(context) == Sign::Negative && childAtIndex(1)->type() == ExpressionNode::Type::Rational) { RationalNode * r = static_cast(childAtIndex(1)); if (r->denominator().isOne()) { - assert(!Integer::Division(r->signedNumerator(), Integer(2)).remainder.isInfinity()); + assert(!Integer::Division(r->signedNumerator(), Integer(2)).remainder.isOverflow()); if (Integer::Division(r->signedNumerator(), Integer(2)).remainder.isZero()) { return Sign::Positive; } else { @@ -46,8 +48,9 @@ ExpressionNode::Sign PowerNode::sign() const { return Sign::Unknown; } -Expression PowerNode::setSign(Sign s, Context & context, Preferences::AngleUnit angleUnit) { - return Power(this).setSign(s, context, angleUnit); +Expression PowerNode::setSign(Sign s, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) { + assert(s == ExpressionNode::Sign::Positive); + return Power(this).setSign(s, context, complexFormat, angleUnit, target); } int PowerNode::polynomialDegree(Context & context, const char * symbolName) const { @@ -61,7 +64,7 @@ int PowerNode::polynomialDegree(Context & context, const char * symbolName) cons } if (childAtIndex(1)->type() == ExpressionNode::Type::Rational) { RationalNode * r = static_cast(childAtIndex(1)); - if (!r->denominator().isOne() || r->sign() == Sign::Negative) { + if (!r->denominator().isOne() || Number(r).sign() == Sign::Negative) { return -1; } Integer numeratorInt = r->signedNumerator(); @@ -78,12 +81,27 @@ int PowerNode::getPolynomialCoefficients(Context & context, const char * symbolN return Power(this).getPolynomialCoefficients(context, symbolName, coefficients); } +bool PowerNode::isReal(Context & context) const { + ExpressionNode * base = childAtIndex(0); + ExpressionNode * index = childAtIndex(1); + // Both base and index are real and: + // - either base > 0 + // - or index is an integer + if (base->isReal(context) && + index->isReal(context) && + (base->sign(&context) == Sign::Positive || + (index->type() == ExpressionNode::Type::Rational && static_cast(index)->denominator().isOne()))) { + return true; + } + return false; +} + // Private template -Complex PowerNode::compute(const std::complex c, const std::complex d) { +Complex PowerNode::compute(const std::complex c, const std::complex d, Preferences::ComplexFormat complexFormat) { std::complex result; - if (c.imag() == 0.0 && d.imag() == 0.0 && (c.real() > 0.0 || std::round(d.real()) == d.real())) { + if (c.imag() == 0.0 && d.imag() == 0.0 && c.real() != 0.0 && (c.real() > 0.0 || std::round(d.real()) == d.real())) { /* pow: (R+, R) -> R+ (2^1.3 ~ 2.46) * pow: (R-, N) -> R+ ((-2)^3 = -8) * In these cases we rather use std::pow(double, double) because: @@ -105,7 +123,7 @@ Complex PowerNode::compute(const std::complex c, const std::complex d) * avoid weird results as e(i*pi) = -1+6E-17*i, we compute the argument of * the result of c^d and if arg ~ 0 [Pi], we discard the residual imaginary * part and if arg ~ Pi/2 [Pi], we discard the residual real part. */ - return Complex(ApproximationHelper::TruncateRealOrImaginaryPartAccordingToArgument(result)); + return Complex::Builder(ApproximationHelper::TruncateRealOrImaginaryPartAccordingToArgument(result)); } // Layout @@ -116,9 +134,9 @@ Layout PowerNode::createLayout(Preferences::PrintFloatMode floatDisplayMode, int if (indiceOperand->type() == ExpressionNode::Type::Parenthesis) { indiceOperand = indiceOperand->childAtIndex(0); } - HorizontalLayout result = HorizontalLayout(); + HorizontalLayout result = HorizontalLayout::Builder(); result.addOrMergeChildAtIndex(childAtIndex(0)->createLayout(floatDisplayMode, numberOfSignificantDigits), 0, false); - result.addChildAtIndex(VerticalOffsetLayout( + result.addChildAtIndex(VerticalOffsetLayout::Builder( indiceOperand->createLayout(floatDisplayMode, numberOfSignificantDigits), VerticalOffsetLayoutNode::Type::Superscript), result.numberOfChildren(), @@ -130,7 +148,7 @@ Layout PowerNode::createLayout(Preferences::PrintFloatMode floatDisplayMode, int // Serialize bool PowerNode::childNeedsParenthesis(const TreeNode * child) const { - if (static_cast(child)->isNumber() && static_cast(child)->sign() == Sign::Negative) { + if (static_cast(child)->isNumber() && Number(static_cast(child)).sign() == Sign::Negative) { return true; } if (static_cast(child)->type() == Type::Rational && !static_cast(child)->denominator().isOne()) { @@ -144,51 +162,49 @@ int PowerNode::serialize(char * buffer, int bufferSize, Preferences::PrintFloatM return SerializationHelper::Infix(this, buffer, bufferSize, floatDisplayMode, numberOfSignificantDigits, "^"); } - - // Simplify -Expression PowerNode::shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) { - return Power(this).shallowReduce(context, angleUnit, target); +Expression PowerNode::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) { + return Power(this).shallowReduce(context, complexFormat, angleUnit, target); } -Expression PowerNode::shallowBeautify(Context & context, Preferences::AngleUnit angleUnit) { - return Power(this).shallowBeautify(context, angleUnit); +Expression PowerNode::shallowBeautify(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { + return Power(this).shallowBeautify(context, complexFormat, angleUnit, target); } -int PowerNode::simplificationOrderGreaterType(const ExpressionNode * e, bool canBeInterrupted) const { - int baseComparison = SimplificationOrder(childAtIndex(0), e, canBeInterrupted); +int PowerNode::simplificationOrderGreaterType(const ExpressionNode * e, bool ascending, bool canBeInterrupted) const { + int baseComparison = SimplificationOrder(childAtIndex(0), e, ascending, canBeInterrupted); if (baseComparison != 0) { return baseComparison; } - Rational one(1); - return SimplificationOrder(childAtIndex(1), one.node(), canBeInterrupted); + Rational one = Rational::Builder(1); + return SimplificationOrder(childAtIndex(1), one.node(), ascending, canBeInterrupted); } -int PowerNode::simplificationOrderSameType(const ExpressionNode * e, bool canBeInterrupted) const { +int PowerNode::simplificationOrderSameType(const ExpressionNode * e, bool ascending, bool canBeInterrupted) const { assert(e->numberOfChildren() > 0); - int baseComparison = SimplificationOrder(childAtIndex(0), e->childAtIndex(0), canBeInterrupted); + int baseComparison = SimplificationOrder(childAtIndex(0), e->childAtIndex(0), ascending, canBeInterrupted); if (baseComparison != 0) { return baseComparison; } assert(e->numberOfChildren() > 1); - return SimplificationOrder(childAtIndex(1), e->childAtIndex(1), canBeInterrupted); + return SimplificationOrder(childAtIndex(1), e->childAtIndex(1), ascending, canBeInterrupted); } -Expression PowerNode::denominator(Context & context, Preferences::AngleUnit angleUnit) const { - return Power(this).denominator(context, angleUnit); +Expression PowerNode::denominator(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { + return Power(this).denominator(context, complexFormat, angleUnit); } // Evaluation -template MatrixComplex PowerNode::computeOnComplexAndMatrix(const std::complex c, const MatrixComplex n) { +template MatrixComplex PowerNode::computeOnComplexAndMatrix(const std::complex c, const MatrixComplex n, Preferences::ComplexFormat complexFormat) { return MatrixComplex::Undefined(); } -template MatrixComplex PowerNode::computeOnMatrixAndComplex(const MatrixComplex m, const std::complex d) { +template MatrixComplex PowerNode::computeOnMatrixAndComplex(const MatrixComplex m, const std::complex d, Preferences::ComplexFormat complexFormat) { if (m.numberOfRows() != m.numberOfColumns()) { return MatrixComplex::Undefined(); } - T power = Complex(d).toScalar(); + T power = Complex::Builder(d).toScalar(); if (std::isnan(power) || std::isinf(power) || power != (int)power || std::fabs(power) > k_maxApproximatePowerMatrix) { return MatrixComplex::Undefined(); } @@ -197,37 +213,35 @@ template MatrixComplex PowerNode::computeOnMatrixAndComplex(const if (inverse.isUninitialized()) { return MatrixComplex::Undefined(); } - Complex minusC = Complex(-d); - MatrixComplex result = PowerNode::computeOnMatrixAndComplex(inverse, minusC.stdComplex()); + Complex minusC = Complex::Builder(-d); + MatrixComplex result = PowerNode::computeOnMatrixAndComplex(inverse, minusC.stdComplex(), complexFormat); return result; } MatrixComplex result = MatrixComplex::createIdentity(m.numberOfRows()); // TODO: implement a quick exponentiation for (int k = 0; k < (int)power; k++) { - if (Expression::shouldStopProcessing()) { + if (Expression::ShouldStopProcessing()) { return MatrixComplex::Undefined(); } - result = MultiplicationNode::computeOnMatrices(result, m); + result = MultiplicationNode::computeOnMatrices(result, m, complexFormat); } return result; } -template MatrixComplex PowerNode::computeOnMatrices(const MatrixComplex m, const MatrixComplex n) { +template MatrixComplex PowerNode::computeOnMatrices(const MatrixComplex m, const MatrixComplex n, Preferences::ComplexFormat complexFormat) { return MatrixComplex::Undefined(); } // Power -Power::Power(Expression base, Expression exponent) : Expression(TreePool::sharedPool()->createTreeNode()) { - replaceChildAtIndexInPlace(0, base); - replaceChildAtIndexInPlace(1, exponent); -} -Expression Power::setSign(ExpressionNode::Sign s, Context & context, Preferences::AngleUnit angleUnit) { +Expression Power::setSign(ExpressionNode::Sign s, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { assert(s == ExpressionNode::Sign::Positive); - assert(childAtIndex(0).sign() == ExpressionNode::Sign::Negative); - Expression result = Power(childAtIndex(0).setSign(ExpressionNode::Sign::Positive, context, angleUnit), childAtIndex(1)); - replaceWithInPlace(result); - return result; + if (childAtIndex(0).sign(context) == ExpressionNode::Sign::Negative) { + Expression result = Power::Builder(childAtIndex(0).setSign(ExpressionNode::Sign::Positive, context, complexFormat, angleUnit, target), childAtIndex(1)); + replaceWithInPlace(result); + return result.shallowReduce(*context, complexFormat, angleUnit, target); + } + return *this; } int Power::getPolynomialCoefficients(Context & context, const char * symbolName, Expression coefficients[]) const { @@ -252,19 +266,19 @@ int Power::getPolynomialCoefficients(Context & context, const char * symbolName, int n = num.extractedInt(); if (n <= k_maxPolynomialDegree) { for (int i = 0; i < n; i++) { - coefficients[i] = Rational(0); + coefficients[i] = Rational::Builder(0); } - coefficients[n] = Rational(1); + coefficients[n] = Rational::Builder(1); return n; } } return -1; } -Expression Power::shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { +Expression Power::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { { - Expression e = Expression::defaultShallowReduce(context, angleUnit); + Expression e = Expression::defaultShallowReduce(); if (e.isUndefined()) { return e; } @@ -274,20 +288,20 @@ Expression Power::shallowReduce(Context & context, Preferences::AngleUnit angleU #if 0 /* Step 0: get rid of matrix */ if (childAtIndex(1)->type() == ExpressionNode::Type::Matrix) { - return replaceWith(new Undefined(), true); + return replaceWith(new Undefined::Builder(), true); } if (childAtIndex(0)->type() == ExpressionNode::Type::Matrix) { Matrix * mat = static_cast(childAtIndex(0)); if (childAtIndex(1)->type() != ExpressionNode::Type::Rational || !static_cast(childAtIndex(1))->denominator().isOne()) { - return replaceWith(new Undefined(), true); + return replaceWith(new Undefined::Builder(), true); } Integer exponent = static_cast(childAtIndex(1))->numerator(); if (mat->numberOfRows() != mat->numberOfColumns()) { - return replaceWith(new Undefined(), true); + return replaceWith(new Undefined::Builder(), true); } if (exponent.isNegative()) { - childAtIndex(1)->setSign(Sign::Positive, context, angleUnit); - Expression * newMatrix = shallowReduce(context, angleUnit, target); + childAtIndex(1)->setSign(Sign::Positive, context, complexFormat, angleUnit); + Expression * newMatrix = shallowReduce(context, complexFormat, angleUnit, target); Expression * parent = newMatrix->parent(); MatrixInverse * inv = new MatrixInverse(newMatrix, false); parent->replaceOperand(newMatrix, inv, false); @@ -301,52 +315,46 @@ Expression Power::shallowReduce(Context & context, Preferences::AngleUnit angleU if (exp == 0) { return replaceWith(id, true); } - Multiplication * result = new Multiplication(id, mat->clone()); + Multiplication * result = new Multiplication::Builder(id, mat->clone()); // TODO: implement a quick exponentiation for (int k = 1; k < exp; k++) { result->addOperand(mat->clone()); } replaceWith(result, true); - return result->shallowReduce(context, angleUnit, target); + return result->shallowReduce(context, complexFormat, angleUnit, target); } #endif #endif - /* Step 0: if both children are true complexes, the result is undefined. We - * can assert that evaluations are Complex, as matrix are not simplified */ - - Evaluation c0Approximated = childAtIndex(0).node()->approximate(1.0f, context, angleUnit); - Evaluation c1Approximated = childAtIndex(1).node()->approximate(1.0f, context, angleUnit); - Complex c0 = static_cast&>(c0Approximated); - Complex c1 = static_cast&>(c1Approximated); - bool bothChildrenComplexes = c0.imag() != 0 && c1.imag() != 0 && !std::isnan(c0.imag()) && !std::isnan(c1.imag()); - bool nonComplexNegativeChild0 = c0.imag() == 0 && c0.real() < 0; - bool nonNullChild0 = !std::isnan(c0.real()) && !std::isnan(c0.imag()) && (c0.real() > Expression::epsilon() || c0.imag() > Expression::epsilon()); - if (bothChildrenComplexes) { + Expression power = *this; + Expression base = childAtIndex(0); + Expression index = childAtIndex(1); + /* Step 0: if both children are true unresolved complexes, the result is not simplified. TODO? */ + if (!base.isReal(context) && base.type() != ExpressionNode::Type::ComplexCartesian && !index.isReal(context) && index.type() != ExpressionNode::Type::ComplexCartesian) { return *this; } /* Step 1: We handle simple cases as x^0, x^1, 0^x and 1^x first for 2 reasons: - * - we can assert this step that there is no division by 0: + * - we can assert after this step that there is no division by 0: * for instance, 0^(-2)->undefined * - we save computational time by early escaping for these cases. */ if (childAtIndex(1).type() == ExpressionNode::Type::Rational) { const Rational b = childAtIndex(1).convert(); // x^0 if (b.isZero()) { - // 0^0 = undef - if (childAtIndex(0).type() == ExpressionNode::Type::Rational && childAtIndex(0).convert().isZero()) { - Expression result = Undefined(); + // 0^0 = undef or (±inf)^0 = undef + if ((childAtIndex(0).type() == ExpressionNode::Type::Rational && childAtIndex(0).convert().isZero()) || childAtIndex(0).type() == ExpressionNode::Type::Infinity) { + Expression result = Undefined::Builder(); replaceWithInPlace(result); return result; } // x^0 - if (target == ExpressionNode::ReductionTarget::User || nonNullChild0) { + if (target == ExpressionNode::ReductionTarget::User || childAtIndex(0).isNumber()) { /* Warning: if the ReductionTarget is User, in all other cases but 0^0, * we replace x^0 by one. This is almost always true except when x = 0. * However, not substituting x^0 by one would prevent from simplifying * many expressions like x/x->1. */ - Expression result = Rational(1); + Expression result = Rational::Builder(1); replaceWithInPlace(result); return result; } @@ -362,75 +370,128 @@ Expression Power::shallowReduce(Context & context, Preferences::AngleUnit angleU Rational a = childAtIndex(0).convert(); // 0^x if (a.isZero()) { - if (childAtIndex(1).sign() == ExpressionNode::Sign::Positive) { - Expression result = Rational(0); + // 0^x with x > 0 = 0 + if (childAtIndex(1).sign(&context) == ExpressionNode::Sign::Positive) { + Expression result = Rational::Builder(0); replaceWithInPlace(result); return result; } - if (childAtIndex(1).sign() == ExpressionNode::Sign::Negative) { - Expression result = Undefined(); + // 0^x with x < 0 = undef + if (childAtIndex(1).sign(&context) == ExpressionNode::Sign::Negative) { + Expression result = Undefined::Builder(); replaceWithInPlace(result); return result; } } - // 1^x - if (a.isOne()) { - Expression result = Rational(1); + // 1^x = 1 if x != ±inf + if (a.isOne() && !childAtIndex(1).recursivelyMatchesInfinity(context)) { + Expression result = Rational::Builder(1); replaceWithInPlace(result); return result; } } - /* Step 2: We look for square root and sum of square roots (two terms maximum + /* We do not apply some rules to a^b if the parent node is a logarithm of same + * base a. In this case there is a simplication of form ln(e^(3^(1/2))->3^(1/2). + */ + bool letPowerAtRoot = parentIsALogarithmOfSameBase(); + + /* Step 2: we now bubble up ComplexCartesian, we handle different cases: + * At least, one child is a ComplexCartesian and the other is either a + * ComplexCartesian or real. */ + + ComplexCartesian complexBase; + ComplexCartesian complexIndex; + ComplexCartesian result; + /* First, (x+iy)^q with q special values + * For q = -1, 1/2, -1/2, n with n integer < 10, we avoid introducing arctangent + * by using the formula (r*e^(i*th))^(a+ib) = r^a*e(-th*b)*e^(b*ln(r)+th*a). + * Instead, we rather use the cartesian form of the base and the index. */ + if (!letPowerAtRoot && base.type() == ExpressionNode::Type::ComplexCartesian) { + complexBase = static_cast(base); + Integer ten(10); + if (index.type() == ExpressionNode::Type::Rational) { + Rational r = static_cast(index); + if (r.isMinusOne()) { + // (x+iy)^(-1) + result = complexBase.inverse(context, complexFormat, angleUnit, target); + } else if (r.isHalf()) { + // (x+iy)^(1/2) + result = complexBase.squareRoot(context, complexFormat, angleUnit, target); + } else if (r.isMinusHalf()) { + // (x+iy)^(-1/2) + result = complexBase.squareRoot(context, complexFormat, angleUnit, target).inverse(context, complexFormat, angleUnit, target); + } else if (r.integerDenominator().isOne() && r.unsignedIntegerNumerator().isLowerThan(ten)) { + if (r.sign() == ExpressionNode::Sign::Positive) { + // (x+iy)^n, n integer positive n < 10 + result = complexBase.powerInteger(r.unsignedIntegerNumerator().extractedInt(), context, complexFormat, angleUnit, target); + } else { + // (x+iy)^(-n), n integer positive n < 10 + assert(r.sign() == ExpressionNode::Sign::Negative); + result = complexBase.powerInteger(r.unsignedIntegerNumerator().extractedInt(), context, complexFormat, angleUnit, target).inverse(context, complexFormat, angleUnit, target); + } + } + if (!result.isUninitialized()) { + replaceWithInPlace(result); + return result.shallowReduce(); + } + } + } + // All other cases where one child at least is a ComplexCartesian + if ((!letPowerAtRoot && base.isReal(context) && index.type() == ExpressionNode::Type::ComplexCartesian) || + (!letPowerAtRoot && base.type() == ExpressionNode::Type::ComplexCartesian && index.isReal(context)) || + (!letPowerAtRoot && base.type() == ExpressionNode::Type::ComplexCartesian && index.type() == ExpressionNode::Type::ComplexCartesian)) { + complexBase = base.type() == ExpressionNode::Type::ComplexCartesian ? static_cast(base) : ComplexCartesian::Builder(base, Rational::Builder(0)); + complexIndex = index.type() == ExpressionNode::Type::ComplexCartesian ? static_cast(index) : ComplexCartesian::Builder(index, Rational::Builder(0)); + result = complexBase.power(complexIndex, context, complexFormat, angleUnit, target); + replaceWithInPlace(result); + return result.shallowReduce(); + } + + /* Step 3: We look for square root and sum of square roots (two terms maximum * so far) at the denominator and move them to the numerator. */ if (target == ExpressionNode::ReductionTarget::User) { - Expression r = removeSquareRootsFromDenominator(context, angleUnit); + Expression r = removeSquareRootsFromDenominator(context, complexFormat, angleUnit); if (!r.isUninitialized()) { return r; } } - if (target == ExpressionNode::ReductionTarget::User && childAtIndex(1).type() == ExpressionNode::Type::Rational) { + /* Step 4: we simplify i^(p/q) = e^(i*Pi*p/2q) */ + if (childAtIndex(1).type() == ExpressionNode::Type::Rational) { const Rational b = childAtIndex(1).convert(); // i^(p/q) if (childAtIndex(0).type() == ExpressionNode::Type::Constant && childAtIndex(0).convert().isIComplex()) { - Number r = Number::Multiplication(b, Rational(1, 2)); - Expression result = CreateComplexExponent(r); + Number r = Number::Multiplication(b, Rational::Builder(1, 2)); + Expression result = CreateComplexExponent(r, context, complexFormat, angleUnit, target); replaceWithInPlace(result); - return result.shallowReduce(context, angleUnit, target); + return result.shallowReduce(context, complexFormat, angleUnit, target); } } - // (±inf)^x + // Step 5: (±inf)^x = 0 or ±inf if (childAtIndex(0).type() == ExpressionNode::Type::Infinity) { Expression result; - if (childAtIndex(1).sign() == ExpressionNode::Sign::Negative) { + if (childAtIndex(1).sign(&context) == ExpressionNode::Sign::Negative) { // --> 0 if x < 0 - result = Rational(0); - } else if (childAtIndex(1).sign() == ExpressionNode::Sign::Positive) { + result = Rational::Builder(0); + } else if (childAtIndex(1).sign(&context) == ExpressionNode::Sign::Positive) { // --> (±inf) if x > 0 - result = Infinity(false); - if (childAtIndex(0).sign() == ExpressionNode::Sign::Negative) { + result = Infinity::Builder(false); + if (childAtIndex(0).sign(&context) == ExpressionNode::Sign::Negative) { // (-inf)^x --> (-1)^x*inf - Power p(Rational(-1), childAtIndex(1)); - result = Multiplication(p, result); - p.shallowReduce(context, angleUnit, target); + Power p = Power::Builder(Rational::Builder(-1), childAtIndex(1)); + result = Multiplication::Builder(p, result); + p.shallowReduce(context, complexFormat, angleUnit, target); } } if (!result.isUninitialized()) { replaceWithInPlace(result); - return result.shallowReduce(context, angleUnit, target); + return result.shallowReduce(context, complexFormat, angleUnit, target); } } - /* We do not apply some rules to a^b if: - * - the parent node is a logarithm of same base a. In this case there is a - * simplication of form ln(e^(3^(1/2))->3^(1/2). - * - the reduction is being BottomUp. In this case, we do not yet have any - * information on the parent which could later be a logarithm of the same - * base. - */ - bool letPowerAtRoot = target == ExpressionNode::ReductionTarget::BottomUpComputation || parentIsALogarithmOfSameBase(); + // Step 6: p^q with p, q rationals --> a*b^c*exp(i*pi*d) with a, b, c, d rationals if (!letPowerAtRoot && childAtIndex(0).type() == ExpressionNode::Type::Rational) { Rational a = childAtIndex(0).convert(); // p^q with p, q rationals @@ -440,49 +501,58 @@ Expression Power::shallowReduce(Context & context, Preferences::AngleUnit angleU if (RationalExponentShouldNotBeReduced(a, exp)) { return *this; } - return simplifyRationalRationalPower(context, angleUnit, target); + return simplifyRationalRationalPower(context, complexFormat, angleUnit, target); } } - // (a)^(1/2) with a < 0 --> i*(-a)^(1/2) + // Step 7: (a)^(1/2) --> i*(-a)^(1/2) + // WARNING: this rule true only if: + // - a real: (-1*i)^(1/2) != i*i^(1/2) + // - a is negative: (-(-2))^(1/2) != -2^(1/2) + // We apply this rule only when a is a negative numeral if (!letPowerAtRoot - && nonComplexNegativeChild0 + && childAtIndex(0).isNumber() && childAtIndex(1).type() == ExpressionNode::Type::Rational && childAtIndex(1).convert().isHalf()) { - Expression m0 = Multiplication(Rational(-1), childAtIndex(0)); - replaceChildAtIndexInPlace(0, m0); - m0.shallowReduce(context, angleUnit, target); - Multiplication m1 = Multiplication(); - replaceWithInPlace(m1); - m1.addChildAtIndexInPlace(Constant(Ion::Charset::IComplex), 0, 0); - m1.addChildAtIndexInPlace(*this, 1, 1); - shallowReduce(context, angleUnit, target); - return m1.shallowReduce(context, angleUnit, target); + Expression m0 = childAtIndex(0).makePositiveAnyNegativeNumeralFactor(context, complexFormat, angleUnit, target); + if (!m0.isUninitialized()) { + replaceChildAtIndexInPlace(0, m0); + // m0 doest not need to be shallowReduce as makePositiveAnyNegativeNumeralFactor returns a reduced expression + Multiplication m1 = Multiplication::Builder(); + replaceWithInPlace(m1); + // Multiply m1 by i complex + Constant i = Constant::Builder(Ion::Charset::IComplex); + m1.addChildAtIndexInPlace(i, 0, 0); + i.shallowReduce(context, complexFormat, angleUnit, target); + m1.addChildAtIndexInPlace(*this, 1, 1); + shallowReduce(context, complexFormat, angleUnit, target); + return m1.shallowReduce(context, complexFormat, angleUnit, target); + } } - // e^(i*Pi*r) with r rational + // Step 8: e^(r*i*Pi) with r rational --> cos(pi*r) + i*sin(pi*r) if (!letPowerAtRoot && isNthRootOfUnity()) { Expression m = childAtIndex(1); - Expression i = m.childAtIndex(m.numberOfChildren()-1); - static_cast(m).removeChildAtIndexInPlace(m.numberOfChildren()-1); + Expression i = m.childAtIndex(m.numberOfChildren()-2); + static_cast(m).removeChildAtIndexInPlace(m.numberOfChildren()-2); if (angleUnit == Preferences::AngleUnit::Degree) { - m.replaceChildAtIndexInPlace(m.numberOfChildren()-1, Rational(180)); + m.replaceChildAtIndexInPlace(m.numberOfChildren()-1, Rational::Builder(180)); } Expression cos = Cosine::Builder(m); - m = m.shallowReduce(context, angleUnit, target); + m = m.shallowReduce(context, complexFormat, angleUnit, target); Expression sin = Sine::Builder(m.clone()); - Expression complexPart = Multiplication(sin, i); - sin.shallowReduce(context, angleUnit, target); - Expression a = Addition(cos, complexPart); - cos.shallowReduce(context, angleUnit, target); - complexPart.shallowReduce(context, angleUnit, target); + Expression complexPart = Multiplication::Builder(sin, i); + sin.shallowReduce(context, complexFormat, angleUnit, target); + Expression a = Addition::Builder(cos, complexPart); + cos.shallowReduce(context, complexFormat, angleUnit, target); + complexPart.shallowReduce(context, complexFormat, angleUnit, target); replaceWithInPlace(a); - return a.shallowReduce(context, angleUnit, target); + return a.shallowReduce(context, complexFormat, angleUnit, target); } - // x^log(y,x)->y if y > 0 + // Step 9: x^log(y,x)->y if y > 0 if (childAtIndex(1).type() == ExpressionNode::Type::Logarithm) { if (childAtIndex(1).numberOfChildren() == 2 && childAtIndex(0).isIdenticalTo(childAtIndex(1).childAtIndex(1))) { // y > 0 - if (childAtIndex(1).childAtIndex(0).sign() == ExpressionNode::Sign::Positive) { + if (childAtIndex(1).childAtIndex(0).sign(&context) == ExpressionNode::Sign::Positive) { Expression result = childAtIndex(1).childAtIndex(0); replaceWithInPlace(result); return result; @@ -498,28 +568,40 @@ Expression Power::shallowReduce(Context & context, Preferences::AngleUnit angleU return result; } } - // (a^b)^c -> a^(b*c) if a > 0 or c is integer + /* Step 10: (a^b)^c -> a^(b*c) + * This rule is not generally true: ((-2)^2)^(1/2) != (-2)^(2*1/2) = -2 + * This rule is true if: + * - a > 0 + * - in Real: when b and c are integers + * - in other modes: when c is integer + * (Warning: in real mode only c integer is not enough: + * ex: ((-2)^(1/2))^2 = unreal != -2) + */ if (childAtIndex(0).type() == ExpressionNode::Type::Power) { Power p = childAtIndex(0).convert(); // Check if a > 0 or c is Integer - if (p.childAtIndex(0).sign() == ExpressionNode::Sign::Positive - || (childAtIndex(1).type() == ExpressionNode::Type::Rational - && childAtIndex(1).convert().integerDenominator().isOne())) - { - return simplifyPowerPower(context, angleUnit, target); + bool aPositive = p.childAtIndex(0).sign(&context) == ExpressionNode::Sign::Positive; + bool cInteger = (childAtIndex(1).type() == ExpressionNode::Type::Rational + && childAtIndex(1).convert().integerDenominator().isOne()); + if (aPositive || cInteger) { + // Check that the complex format is not Real or that b is an integer + bool bInteger = (p.childAtIndex(1).type() == ExpressionNode::Type::Rational && p.childAtIndex(1).convert().integerDenominator().isOne()); + if (aPositive || complexFormat != Preferences::ComplexFormat::Real || bInteger) { + return simplifyPowerPower(context, complexFormat, angleUnit, target); + } } } - // (a*b*c*...)^r ? + // Step 11: (a*b*c*...)^r ? if (!letPowerAtRoot && childAtIndex(0).type() == ExpressionNode::Type::Multiplication) { Multiplication m = childAtIndex(0).convert(); - // (a*b*c*...)^n = a^n*b^n*c^n*... if n integer + // Case 1: (a*b*c*...)^n = a^n*b^n*c^n*... if n integer if (childAtIndex(1).type() == ExpressionNode::Type::Rational && childAtIndex(1).convert().integerDenominator().isOne()) { - return simplifyPowerMultiplication(context, angleUnit, target); + return simplifyPowerMultiplication(context, complexFormat, angleUnit, target); } - // (a*b*...)^r -> |a|^r*(sign(a)*b*...)^r if a not -1 + // Case 2: (a*b*...)^r -> |a|^r*(sign(a)*b*...)^r if a not -1 for (int i = 0; i < m.numberOfChildren(); i++) { // a is signed and a != -1 - if (m.childAtIndex(i).sign() != ExpressionNode::Sign::Unknown + if (m.childAtIndex(i).sign(&context) != ExpressionNode::Sign::Unknown && (m.childAtIndex(i).type() != ExpressionNode::Type::Rational || !m.childAtIndex(i).convert().isMinusOne())) { @@ -528,29 +610,29 @@ Expression Power::shallowReduce(Context & context, Preferences::AngleUnit angleU Expression factor = m.childAtIndex(i); // (sign(a)*b*...)^r - if (factor.sign() == ExpressionNode::Sign::Negative) { - m.replaceChildAtIndexInPlace(i, Rational(-1)); - factor = factor.setSign(ExpressionNode::Sign::Positive, context, angleUnit); + if (factor.sign(&context) == ExpressionNode::Sign::Negative) { + m.replaceChildAtIndexInPlace(i, Rational::Builder(-1)); + factor = factor.setSign(ExpressionNode::Sign::Positive, &context, complexFormat, angleUnit, target); } else { m.removeChildAtIndexInPlace(i); } - m.shallowReduce(context, angleUnit, target); + m.shallowReduce(context, complexFormat, angleUnit, target); // |a|^r - Power p = Power(factor, rCopy); + Power p = Power::Builder(factor, rCopy); // |a|^r*(sign(a)*b*...)^r Power thisRef = *this; - Multiplication root = Multiplication(p); + Multiplication root = Multiplication::Builder(p); replaceWithInPlace(root); root.addChildAtIndexInPlace(thisRef, 1, 1); - p.shallowReduce(context, angleUnit, target); - thisRef.shallowReduce(context, angleUnit, target); - return root.shallowReduce(context, angleUnit, target); + p.shallowReduce(context, complexFormat, angleUnit, target); + thisRef.shallowReduce(context, complexFormat, angleUnit, target); + return root.shallowReduce(context, complexFormat, angleUnit, target); } } } - // a^(b+c+...) -> Rational(a^b)*a^c with a and b rational and a != 0 + // Step 12: a^(p/q+c+...) -> Rational::Builder(a^p)*a^(1/q+c+...) with a rational and a != 0 and p, q integers if (!letPowerAtRoot && childAtIndex(0).type() == ExpressionNode::Type::Rational && !childAtIndex(0).convert().isZero() @@ -559,27 +641,42 @@ Expression Power::shallowReduce(Context & context, Preferences::AngleUnit angleU Addition a = childAtIndex(1).convert(); // Check is b is rational if (a.childAtIndex(0).type() == ExpressionNode::Type::Rational) { - const Rational rationalBase = childAtIndex(0).convert(); const Rational rationalIndex = a.childAtIndex(0).convert(); + if (rationalIndex.unsignedIntegerNumerator().isOne() && !rationalIndex.integerDenominator().isOne()) { + /* Do not reduce a^(1/q+c+...) to avoid potential infinite loop: + * a^(1/q+c+...) --> a^(1/q)*a^(c+...) --> a^(1/q+c+...)*/ + /* TODO: do something more sensible here: + * - add rule (-rational)^x --> (-1)^x*rational^x so we only consider + * positive rational or (-1) + * - change simplifyRationalRationalPower to be able to detect when no + * rational was extracted (ie 2^(1/2) --> 2^(1/2)) to avoid applying + * this rule in that case + * Once this is done, we can reduce 4^(1/2+a) --> 2*4^a which is not + * done so far to avoir the infinite loop: + * 2^(1/2+s) --> 2^(1/2)*2^s --> 2^(1/2+s)... */ + return *this; + } + const Rational rationalBase = childAtIndex(0).convert(); if (RationalExponentShouldNotBeReduced(rationalBase, rationalIndex)) { return *this; } - Power p1 = Power(childAtIndex(0).clone(), a.childAtIndex(0).clone()); + Power p1 = Power::Builder(childAtIndex(0).clone(), a.childAtIndex(0)); Power thisRef = *this; childAtIndex(1).convert().removeChildAtIndexInPlace(0); // p2 = a^(c+...) // if addition had only 2 children childAtIndex(1).convert().squashUnaryHierarchyInPlace(); - Multiplication m = Multiplication(p1); + Multiplication m = Multiplication::Builder(p1); replaceWithInPlace(m); m.addChildAtIndexInPlace(thisRef, 1, 1); - p1.simplifyRationalRationalPower(context, angleUnit, target); - return m.shallowReduce(context, angleUnit, target); + p1.simplifyRationalRationalPower(context, complexFormat, angleUnit, target); + return m.shallowReduce(context, complexFormat, angleUnit, target); } } - // (a0+a1+...am)^n with n integer -> a^n+?a^(n-1)*b+?a^(n-2)*b^2+...+b^n (Multinome) + // Step 13: (a0+a1+...am)^n with n integer -> a^n+?a^(n-1)*b+?a^(n-2)*b^2+...+b^n (Multinome) if (!letPowerAtRoot && childAtIndex(1).type() == ExpressionNode::Type::Rational + && !childAtIndex(1).convert().signedIntegerNumerator().isZero() && childAtIndex(1).convert().integerDenominator().isOne() && childAtIndex(0).type() == ExpressionNode::Type::Addition) { @@ -592,6 +689,8 @@ Expression Power::shallowReduce(Context & context, Preferences::AngleUnit angleU return *this; } int clippedN = n.extractedInt(); // Authorized because n < k_maxNumberOfTermsInExpandedMultinome + assert(clippedN > 0); + // Number of terms in addition m int m = childAtIndex(0).numberOfChildren(); /* The multinome (a0+a2+...+a(m-1))^n has BinomialCoefficient(n+m-1,n) terms; @@ -607,30 +706,30 @@ Expression Power::shallowReduce(Context & context, Preferences::AngleUnit angleU // result = result * (a0+a1+...+a(m-1) in its expanded form if (result.type() == ExpressionNode::Type::Addition) { // We need a 'double' distribution and newA will hold the new expanded form - Expression newA = Addition(); + Expression newA = Addition::Builder(); for (int j = 0; j < a.numberOfChildren(); j++) { - Expression m = Multiplication(result.clone(), a.childAtIndex(j).clone()).distributeOnOperandAtIndex(0, context, angleUnit, target); + Expression m = Multiplication::Builder(result.clone(), a.childAtIndex(j).clone()).distributeOnOperandAtIndex(0, context, complexFormat, angleUnit, target); if (newA.type() == ExpressionNode::Type::Addition) { static_cast(newA).addChildAtIndexInPlace(m, newA.numberOfChildren(), newA.numberOfChildren()); } else { - newA = Addition(newA, m); + newA = Addition::Builder(newA, m); } - newA = newA.shallowReduce(context, angleUnit, target); + newA = newA.shallowReduce(context, complexFormat, angleUnit, target); } result.replaceWithInPlace(newA); result = newA; } else { // Just distribute result on a - Multiplication m = Multiplication(a.clone(), result.clone()); - Expression distributedM = m.distributeOnOperandAtIndex(0, context, angleUnit, target); + Multiplication m = Multiplication::Builder(a.clone(), result.clone()); + Expression distributedM = m.distributeOnOperandAtIndex(0, context, complexFormat, angleUnit, target); result.replaceWithInPlace(distributedM); result = distributedM; - result = result.shallowReduce(context, angleUnit, target); + result = result.shallowReduce(context, complexFormat, angleUnit, target); } } if (nr.sign() == ExpressionNode::Sign::Negative) { - nr.replaceWithInPlace(Rational(-1)); - return shallowReduce(context, angleUnit, target); + nr.replaceWithInPlace(Rational::Builder(-1)); + return shallowReduce(context, complexFormat, angleUnit, target); } else { replaceWithInPlace(result); return result; @@ -650,56 +749,73 @@ Expression Power::shallowReduce(Context & context, Preferences::AngleUnit angleU int clippedN = n.extractedInt(); // Authorized because n < k_maxExpandedBinome < k_maxNValue Expression * x0 = childAtIndex(0)->childAtIndex(0); Expression * x1 = childAtIndex(0)->childAtIndex(1); - Addition * a = new Addition(); + Addition * a = new Addition::Builder(); for (int i = 0; i <= clippedN; i++) { - Rational * r = new Rational(static_cast(BinomialCoefficient::compute(static_cast(i), static_cast(clippedN)))); - Power * p0 = new Power(x0->clone(), new Rational(i), false); - Power * p1 = new Power(x1->clone(), new Rational(clippedN-i), false); + Rational * r = new Rational::Builder(static_cast(BinomialCoefficient::compute(static_cast(i), static_cast(clippedN)))); + Power * p0 = new Power::Builder(x0->clone(), new Rational::Builder(i), false); + Power * p1 = new Power::Builder(x1->clone(), new Rational::Builder(clippedN-i), false); const Expression * operands[3] = {r, p0, p1}; - Multiplication * m = new Multiplication(operands, 3, false); - p0->shallowReduce(context, angleUnit, target); - p1->shallowReduce(context, angleUnit, target); + Multiplication * m = new Multiplication::Builder(operands, 3, false); + p0->shallowReduce(context, complexFormat, angleUnit, target); + p1->shallowReduce(context, complexFormat, angleUnit, target); a->addOperand(m); - m->shallowReduce(context, angleUnit, target); + m->shallowReduce(context, complexFormat, angleUnit, target); } - if (nr->sign() == Sign::Negative) { - nr->replaceWith(new Rational(-1), true); - childAtIndex(0)->replaceWith(a, true)->shallowReduce(context, angleUnit, target); - return shallowReduce(context, angleUnit, target); + if (nr->sign(&context) == Sign::Negative) { + nr->replaceWith(new Rational::Builder(-1), true); + childAtIndex(0)->replaceWith(a, true)->shallowReduce(context, complexFormat, angleUnit, target); + return shallowReduce(context, complexFormat, angleUnit, target); } else { - return replaceWith(a, true)->shallowReduce(context, angleUnit, target); + return replaceWith(a, true)->shallowReduce(context, complexFormat, angleUnit, target); } } #endif return *this; } -Expression Power::shallowBeautify(Context & context, Preferences::AngleUnit angleUnit) { - // X^-y -> 1/(X->shallowBeautify)^y - if (childAtIndex(1).sign() == ExpressionNode::Sign::Negative) { - Expression p = denominator(context, angleUnit); - Division d = Division(Rational(1), p); - p.shallowReduce(context, angleUnit, ExpressionNode::ReductionTarget::User); +Expression Power::shallowBeautify(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { + // Step 1: X^-y -> 1/(X->shallowBeautify)^y + Expression p = denominator(context, complexFormat, angleUnit); + // If the denominator is initialized, the index of the power is of form -y + if (!p.isUninitialized()) { + Division d = Division::Builder(Rational::Builder(1), p); + p.shallowReduce(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::User); replaceWithInPlace(d); - return d.shallowBeautify(context, angleUnit); + return d.shallowBeautify(context, complexFormat, angleUnit, target); } + // Step 2: Turn a^(1/n) into root(a, n) if (childAtIndex(1).type() == ExpressionNode::Type::Rational && childAtIndex(1).convert().signedIntegerNumerator().isOne()) { Integer index = childAtIndex(1).convert().integerDenominator(); + // Special case: a^(1/2) --> sqrt(a) if (index.isEqualTo(Integer(2))) { Expression result = SquareRoot::Builder(childAtIndex(0)); replaceWithInPlace(result); return result; } - Expression result = NthRoot::Builder(childAtIndex(0), Rational(index)); + Expression result = NthRoot::Builder(childAtIndex(0), Rational::Builder(index)); replaceWithInPlace(result); return result; } - // +(a,b)^c ->(+(a,b))^c and *(a,b)^c ->(*(a,b))^c + /* Optional Step 3: if the ReductionTarget is the System, turn a^(p/q) into + * (root(a, q))^p + * Indeed, root(a, q) can have a real root which is not the principale angle + * but that we want to return in real complex format. This special case is + * handled in NthRoot approximation but not in Power approximation. */ + if (target == ExpressionNode::ReductionTarget::System && childAtIndex(1).type() == ExpressionNode::Type::Rational) { + Integer p = childAtIndex(1).convert().signedIntegerNumerator(); + Integer q = childAtIndex(1).convert().integerDenominator(); + Expression nthRoot = q.isOne() ? childAtIndex(0) : NthRoot::Builder(childAtIndex(0), Rational::Builder(q)); + Expression result = p.isOne() ? nthRoot : Power::Builder(nthRoot, Rational::Builder(p)); + replaceWithInPlace(result); + return result; + } + + // Step 4: +(a,b)^c ->(+(a,b))^c and *(a,b)^c ->(*(a,b))^c if (childAtIndex(0).type() == ExpressionNode::Type::Addition || childAtIndex(0).type() == ExpressionNode::Type::Multiplication) { - Parenthesis p = Parenthesis(childAtIndex(0)); + Parenthesis p = Parenthesis::Builder(childAtIndex(0)); replaceChildAtIndexInPlace(0, p); } return *this; @@ -708,48 +824,53 @@ Expression Power::shallowBeautify(Context & context, Preferences::AngleUnit angl // Private // Simplification -Expression Power::denominator(Context & context, Preferences::AngleUnit angleUnit) const { - if (childAtIndex(1).sign() == ExpressionNode::Sign::Negative) { - Expression positivePowerClone = Power(childAtIndex(0).clone(), childAtIndex(1).clone().setSign(ExpressionNode::Sign::Positive, context, angleUnit)); - if (positivePowerClone.childAtIndex(1).type() == ExpressionNode::Type::Rational && positivePowerClone.childAtIndex(1).convert().isOne()) { - return positivePowerClone.childAtIndex(0); +Expression Power::denominator(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { + // Clone the power + Expression clone = Power::Builder(childAtIndex(0).clone(), childAtIndex(1).clone()); + // If the power is of form x^(-y), denominator should be x^y + Expression positiveIndex = clone.childAtIndex(1).makePositiveAnyNegativeNumeralFactor(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::User); + if (!positiveIndex.isUninitialized()) { + // if y was -1, clone is now x^1, denominator is then only x + // we cannot shallowReduce the clone as it is not attached to its parent yet + if (positiveIndex.isRationalOne()) { + return clone.childAtIndex(0); } - return positivePowerClone; + return clone; } return Expression(); } -Expression Power::simplifyPowerPower(Context& context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { +Expression Power::simplifyPowerPower(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { // this is p^e = (a^b)^e, we want a^(b*e) Expression p = childAtIndex(0); - Multiplication m(p.childAtIndex(1), childAtIndex(1)); + Multiplication m = Multiplication::Builder(p.childAtIndex(1), childAtIndex(1)); replaceChildAtIndexInPlace(0, p.childAtIndex(0)); replaceChildAtIndexInPlace(1, m); - m.shallowReduce(context, angleUnit, target); - return shallowReduce(context, angleUnit, target); + m.shallowReduce(context, complexFormat, angleUnit, target); + return shallowReduce(context, complexFormat, angleUnit, target); } -Expression Power::simplifyPowerMultiplication(Context& context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { +Expression Power::simplifyPowerMultiplication(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { // this is m^r= (a*b*c*...)^r, we want a^r * b^r *c^r * ... Expression m = childAtIndex(0); Expression r = childAtIndex(1); for (int index = 0; index < m.numberOfChildren(); index++) { - Power p = Power(m.childAtIndex(index).clone(), r.clone()); // We copy r and factor to avoid inheritance issues + Power p = Power::Builder(m.childAtIndex(index).clone(), r.clone()); // We copy r and factor to avoid inheritance issues m.replaceChildAtIndexInPlace(index, p); - p.shallowReduce(context, angleUnit, target); + p.shallowReduce(context, complexFormat, angleUnit, target); } replaceWithInPlace(m); - return m.shallowReduce(context, angleUnit, target); + return m.shallowReduce(context, complexFormat, angleUnit, target); } -Expression Power::simplifyRationalRationalPower(Context& context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { +Expression Power::simplifyRationalRationalPower(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { // this is a^b with a, b rationals Rational a = childAtIndex(0).convert(); Rational b = childAtIndex(1).convert(); if (b.integerDenominator().isOne()) { Rational r = Rational::IntegerPower(a, b.signedIntegerNumerator()); if (r.numeratorOrDenominatorIsInfinity()) { - return Power(a, b); + return Power::Builder(a, b); } replaceWithInPlace(r); return r; @@ -758,22 +879,22 @@ Expression Power::simplifyRationalRationalPower(Context& context, Preferences::A Expression d; if (b.sign() == ExpressionNode::Sign::Negative) { b.setSign(ExpressionNode::Sign::Positive); - n = CreateSimplifiedIntegerRationalPower(a.integerDenominator(), b, false, context, angleUnit, target); - d = CreateSimplifiedIntegerRationalPower(a.signedIntegerNumerator(), b, true, context, angleUnit, target); + n = CreateSimplifiedIntegerRationalPower(a.integerDenominator(), b, false, context, complexFormat, angleUnit, target); + d = CreateSimplifiedIntegerRationalPower(a.signedIntegerNumerator(), b, true, context, complexFormat, angleUnit, target); } else { - n = CreateSimplifiedIntegerRationalPower(a.signedIntegerNumerator(), b, false, context, angleUnit, target); - d = CreateSimplifiedIntegerRationalPower(a.integerDenominator(), b, true, context, angleUnit, target); + n = CreateSimplifiedIntegerRationalPower(a.signedIntegerNumerator(), b, false, context, complexFormat, angleUnit, target); + d = CreateSimplifiedIntegerRationalPower(a.integerDenominator(), b, true, context, complexFormat, angleUnit, target); } - Multiplication m = Multiplication(n, d); + Multiplication m = Multiplication::Builder(n, d); replaceWithInPlace(m); - return m.shallowReduce(context, angleUnit, target); + return m.shallowReduce(context, complexFormat, angleUnit, target); } -Expression Power::CreateSimplifiedIntegerRationalPower(Integer i, Rational r, bool isDenominator, Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { +Expression Power::CreateSimplifiedIntegerRationalPower(Integer i, Rational r, bool isDenominator, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { assert(!i.isZero()); assert(r.sign() == ExpressionNode::Sign::Positive); if (i.isOne()) { - return Rational(1); + return Rational::Builder(1); } Integer factors[Arithmetic::k_maxNumberOfPrimeFactors]; Integer coefficients[Arithmetic::k_maxNumberOfPrimeFactors]; @@ -781,8 +902,8 @@ Expression Power::CreateSimplifiedIntegerRationalPower(Integer i, Rational r, bo if (numberOfPrimeFactors < 0) { /* We could not break i in prime factors (it might take either too many * factors or too much time). */ - Expression rClone = r.clone().setSign(isDenominator ? ExpressionNode::Sign::Negative : ExpressionNode::Sign::Positive, context, angleUnit); - return Power(Rational(i), rClone); + Expression rClone = r.clone().setSign(isDenominator ? ExpressionNode::Sign::Negative : ExpressionNode::Sign::Positive, &context, complexFormat, angleUnit, target); + return Power::Builder(Rational::Builder(i), rClone); } Integer r1(1); @@ -793,35 +914,49 @@ Expression Power::CreateSimplifiedIntegerRationalPower(Integer i, Rational r, bo r1 = Integer::Multiplication(r1, Integer::Power(factors[index], div.quotient)); r2 = Integer::Multiplication(r2, Integer::Power(factors[index], div.remainder)); } - if (r2.isInfinity() || r1.isInfinity()) { + if (r2.isOverflow() || r1.isOverflow()) { // we overflow Integer at one point: we abort - return Power(Rational(i), r.clone()); + return Power::Builder(Rational::Builder(i), r.clone()); } - Rational p1 = Rational(r2); + Rational p1 = Rational::Builder(r2); Integer oneExponent = isDenominator ? Integer(-1) : Integer(1); Integer rDenominator = r.integerDenominator(); - Rational p2 = Rational(oneExponent, rDenominator); - Power p = Power(p1, p2); + Rational p2 = Rational::Builder(oneExponent, rDenominator); + Power p = Power::Builder(p1, p2); if (r1.isEqualTo(Integer(1)) && !i.isNegative()) { return p; } Integer one(1); - Rational r3 = isDenominator ? Rational(one, r1) : Rational(r1); - Multiplication m; + Rational r3 = isDenominator ? Rational::Builder(one, r1) : Rational::Builder(r1); + Multiplication m = Multiplication::Builder(); m.addChildAtIndexInPlace(r3, 0, 0); if (!r2.isOne()) { m.addChildAtIndexInPlace(p, 1, 1); } if (i.isNegative()) { - Expression exp = CreateComplexExponent(r); - m.addChildAtIndexInPlace(exp, m.numberOfChildren(), m.numberOfChildren()); - exp.shallowReduce(context, angleUnit, target); + if (complexFormat == Preferences::ComplexFormat::Real) { + /* On real numbers (-1)^(p/q) = + * - 1 if p is even + * - -1 if p and q are odd + * - has no real solution otherwise */ + if (!r.unsignedIntegerNumerator().isEven()) { + if (r.integerDenominator().isEven()) { + return Unreal::Builder(); + } else { + m.addChildAtIndexInPlace(Rational::Builder(-1), 0, m.numberOfChildren()); + } + } + } else { + /* On complex numbers, we pick the first root (-1)^(p/q) = e^(i*pi*p/q) */ + Expression exp = CreateComplexExponent(r, context, complexFormat, angleUnit, target); + m.addChildAtIndexInPlace(exp, m.numberOfChildren(), m.numberOfChildren()); + exp.shallowReduce(context, complexFormat, angleUnit, target); + } } - m.sortChildrenInPlace(PowerNode::SimplificationOrder, false); - return m; + return m.shallowReduce(context, complexFormat, angleUnit, target); } -Expression Power::removeSquareRootsFromDenominator(Context & context, Preferences::AngleUnit angleUnit) { +Expression Power::removeSquareRootsFromDenominator(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) { Expression result; if (childAtIndex(0).type() == ExpressionNode::Type::Rational && childAtIndex(1).type() == ExpressionNode::Type::Rational @@ -838,17 +973,17 @@ Expression Power::removeSquareRootsFromDenominator(Context & context, Preference // We do nothing for terms of the form sqrt(p) if (!q.isOne() || castedChild1.isMinusHalf()) { Integer pq = Integer::Multiplication(p, q); - if (pq.isInfinity()) { + if (pq.isOverflow()) { return result; } - Power sqrt = Power(Rational(pq), Rational(1, 2)); + Power sqrt = Power::Builder(Rational::Builder(pq), Rational::Builder(1, 2)); Integer one(1); if (castedChild1.isHalf()) { - result = Multiplication(Rational(one, q), sqrt); + result = Multiplication::Builder(Rational::Builder(one, q), sqrt); } else { - result = Multiplication(Rational(one, p), sqrt); // We use here the assertion that p != 0 + result = Multiplication::Builder(Rational::Builder(one, p), sqrt); // We use here the assertion that p != 0 } - sqrt.shallowReduce(context, angleUnit, ExpressionNode::ReductionTarget::User); + sqrt.shallowReduce(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::User); } } else if (childAtIndex(1).type() == ExpressionNode::Type::Rational && childAtIndex(1).convert().isMinusOne() @@ -896,34 +1031,34 @@ Expression Power::removeSquareRootsFromDenominator(Context & context, Preference // Compute the numerator Integer pq1 = Integer::Multiplication(p1, q1); Integer pq2 = Integer::Multiplication(p2, q2); - Power sqrt1 = Power(Rational(pq1), Rational(1, 2)); - Power sqrt2 = Power(Rational(pq2), Rational(1, 2)); + Power sqrt1 = Power::Builder(Rational::Builder(pq1), Rational::Builder(1, 2)); + Power sqrt2 = Power::Builder(Rational::Builder(pq2), Rational::Builder(1, 2)); Integer factor1 = Integer::Multiplication( Integer::Multiplication(n1, d1), Integer::Multiplication(Integer::Power(d2, Integer(2)), q2)); - Multiplication m1 = Multiplication(Rational(factor1), sqrt1); + Multiplication m1 = Multiplication::Builder(Rational::Builder(factor1), sqrt1); Integer factor2 = Integer::Multiplication( Integer::Multiplication(n2, d2), Integer::Multiplication(Integer::Power(d1, Integer(2)), q1)); - Multiplication m2 = Multiplication(Rational(factor2), sqrt2); + Multiplication m2 = Multiplication::Builder(Rational::Builder(factor2), sqrt2); Expression numerator; if (denominator.isNegative()) { - numerator = Subtraction(m2, m1); + numerator = Subtraction::Builder(m2, m1); denominator.setNegative(false); } else { - numerator = Subtraction(m1, m2); + numerator = Subtraction::Builder(m1, m2); } - if (denominator.isInfinity() || factor1.isInfinity() || factor2.isInfinity() || pq1.isInfinity() || pq2.isInfinity()) { + if (denominator.isOverflow() || factor1.isOverflow() || factor2.isOverflow() || pq1.isOverflow() || pq2.isOverflow()) { return result; // Escape } - numerator = numerator.deepReduce(context, angleUnit, ExpressionNode::ReductionTarget::User); + numerator = numerator.deepReduce(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::User); Integer one(1); - result = Multiplication(numerator, Rational(one, denominator)); + result = Multiplication::Builder(numerator, Rational::Builder(one, denominator)); } if (!result.isUninitialized()) { replaceWithInPlace(result); - result = result.shallowReduce(context, angleUnit, ExpressionNode::ReductionTarget::User); + result = result.shallowReduce(context, complexFormat, angleUnit, ExpressionNode::ReductionTarget::User); } return result; } @@ -967,11 +1102,11 @@ bool Power::isNthRootOfUnity() const { if (childAtIndex(1).numberOfChildren() < 2 || childAtIndex(1).numberOfChildren() > 3) { return false; } - const Expression i = childAtIndex(1).childAtIndex(childAtIndex(1).numberOfChildren()-1); + const Expression i = childAtIndex(1).childAtIndex(childAtIndex(1).numberOfChildren()-2); if (i.type() != ExpressionNode::Type::Constant || !static_cast(i).isIComplex()) { return false; } - const Expression pi = childAtIndex(1).childAtIndex(childAtIndex(1).numberOfChildren()-2); + const Expression pi = childAtIndex(1).childAtIndex(childAtIndex(1).numberOfChildren()-1); if (pi.type() != ExpressionNode::Type::Constant || !static_cast(pi).isPi()) { return false; } @@ -984,22 +1119,39 @@ bool Power::isNthRootOfUnity() const { return false; } -Expression Power::CreateComplexExponent(const Expression & r) { +Expression Power::equivalentExpressionUsingStandardExpression() const { + if (childAtIndex(1).type() == ExpressionNode::Type::Rational) { + if (childAtIndex(1).convert().isMinusOne()) { + return Division::Builder(Rational::Builder(1), childAtIndex(0).clone()); + } + if (childAtIndex(1).convert().isHalf()) { + return SquareRoot::Builder(childAtIndex(0).clone()); + } + if (childAtIndex(1).convert().isMinusHalf()) { + return Division::Builder(Rational::Builder(1), SquareRoot::Builder(childAtIndex(0).clone())); + } + } + return Expression(); +} + +Expression Power::CreateComplexExponent(const Expression & r, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { // Returns e^(i*pi*r) - const Constant exp = Constant(Ion::Charset::Exponential); - const Constant iComplex = Constant(Ion::Charset::IComplex); - const Constant pi = Constant(Ion::Charset::SmallPi); - Multiplication mExp = Multiplication(iComplex, pi, r.clone()); - mExp.sortChildrenInPlace(PowerNode::SimplificationOrder, false); - return Power(exp, mExp); + const Constant exp = Constant::Builder(Ion::Charset::Exponential); + Constant iComplex = Constant::Builder(Ion::Charset::IComplex); + const Constant pi = Constant::Builder(Ion::Charset::SmallPi); + Multiplication mExp = Multiplication::Builder(iComplex, pi, r.clone()); + iComplex.shallowReduce(context, complexFormat, angleUnit, target); + Power p = Power::Builder(exp, mExp); + mExp.shallowReduce(context, complexFormat, angleUnit, target); + return p; #if 0 - const Constant iComplex = Constant(Ion::Charset::IComplex); - const Constant pi = Constant(Ion::Charset::SmallPi); - Expression op = Multiplication(pi, r).shallowReduce(context, angleUnit, false); - Cosine cos = Cosine(op).shallowReduce(context, angleUnit, false);; - Sine sin = Sine(op).shallowReduce(context, angleUnit, false); - Expression m = Multiplication(iComplex, sin); - Expression a = Addition(cos, m); + const Constant iComplex = Constant::Builder(Ion::Charset::IComplex); + const Constant pi = Constant::Builder(Ion::Charset::SmallPi); + Expression op = Multiplication::Builder(pi, r).shallowReduce(context, complexFormat, angleUnit, false); + Cosine cos = Cosine(op).shallowReduce(context, complexFormat, angleUnit, false);; + Sine sin = Sine(op).shallowReduce(context, complexFormat, angleUnit, false); + Expression m = Multiplication::Builder(iComplex, sin); + Expression a = Addition::Builder(cos, m); const Expression * multExpOperands[3] = {pi, r->clone()}; #endif } @@ -1030,7 +1182,7 @@ bool Power::TermIsARationalSquareRootOrRational(const Expression & e) { const Rational Power::RadicandInExpression(const Expression & e) { if (e.type() == ExpressionNode::Type::Rational) { - return Rational(1); + return Rational::Builder(1); } else if (e.type() == ExpressionNode::Type::Power) { assert(e.type() == ExpressionNode::Type::Power); assert(e.childAtIndex(0).type() == ExpressionNode::Type::Rational); @@ -1047,7 +1199,7 @@ const Rational Power::RationalFactorInExpression(const Expression & e) { if (e.type() == ExpressionNode::Type::Rational) { return static_cast(e); } else if (e.type() == ExpressionNode::Type::Power) { - return Rational(1); + return Rational::Builder(1); } else { assert(e.type() == ExpressionNode::Type::Multiplication); assert(e.childAtIndex(0).type() == ExpressionNode::Type::Rational); @@ -1083,7 +1235,7 @@ bool Power::RationalExponentShouldNotBeReduced(const Rational & b, const Rationa } -template Complex PowerNode::compute(std::complex, std::complex); -template Complex PowerNode::compute(std::complex, std::complex); +template Complex PowerNode::compute(std::complex, std::complex, Preferences::ComplexFormat); +template Complex PowerNode::compute(std::complex, std::complex, Preferences::ComplexFormat); } diff --git a/poincare/src/prediction_interval.cpp b/poincare/src/prediction_interval.cpp index 8f0a659b3..ac894f92b 100644 --- a/poincare/src/prediction_interval.cpp +++ b/poincare/src/prediction_interval.cpp @@ -27,14 +27,14 @@ int PredictionIntervalNode::serialize(char * buffer, int bufferSize, Preferences } -Expression PredictionIntervalNode::shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) { - return PredictionInterval(this).shallowReduce(context, angleUnit, target); +Expression PredictionIntervalNode::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) { + return PredictionInterval(this).shallowReduce(context, complexFormat, angleUnit, target); } template -Evaluation PredictionIntervalNode::templatedApproximate(Context& context, Preferences::AngleUnit angleUnit) const { - Evaluation pInput = childAtIndex(0)->approximate(T(), context, angleUnit); - Evaluation nInput = childAtIndex(1)->approximate(T(), context, angleUnit); +Evaluation PredictionIntervalNode::templatedApproximate(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { + Evaluation pInput = childAtIndex(0)->approximate(T(), context, complexFormat, angleUnit); + Evaluation nInput = childAtIndex(1)->approximate(T(), context, complexFormat, angleUnit); T p = static_cast &>(pInput).toScalar(); T n = static_cast &>(nInput).toScalar(); if (std::isnan(p) || std::isnan(n) || n != (int)n || n < 0 || p < 0 || p > 1) { @@ -43,12 +43,13 @@ Evaluation PredictionIntervalNode::templatedApproximate(Context& context, Pre std::complex operands[2]; operands[0] = std::complex(p - 1.96*std::sqrt(p*(1.0-p))/std::sqrt(n)); operands[1] = std::complex(p + 1.96*std::sqrt(p*(1.0-p))/std::sqrt(n)); - return MatrixComplex(operands, 1, 2); + return MatrixComplex::Builder(operands, 1, 2); } -Expression PredictionInterval::shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { + +Expression PredictionInterval::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { { - Expression e = Expression::defaultShallowReduce(context, angleUnit); + Expression e = Expression::defaultShallowReduce(); if (e.isUndefined()) { return e; } @@ -57,13 +58,13 @@ Expression PredictionInterval::shallowReduce(Context & context, Preferences::Ang Expression c1 = childAtIndex(1); #if MATRIX_EXACT_REDUCING if (c0.type() == ExpressionNode::Type::Matrix || c1.type() == ExpressionNode::Type::Matrix) { - return Undefined(); + return Undefined::Builder(); } #endif if (c0.type() == ExpressionNode::Type::Rational) { Rational r0 = static_cast(c0); if (r0.sign() == ExpressionNode::Sign::Negative || Integer::NaturalOrder(r0.unsignedIntegerNumerator(), r0.integerDenominator()) > 0) { - Expression result = Undefined(); + Expression result = Undefined::Builder(); replaceWithInPlace(result); return result; } @@ -71,7 +72,7 @@ Expression PredictionInterval::shallowReduce(Context & context, Preferences::Ang if (c1.type() == ExpressionNode::Type::Rational) { Rational r1 = static_cast(c1); if (!r1.integerDenominator().isOne() || r1.sign() == ExpressionNode::Sign::Negative) { - Expression result = Undefined(); + Expression result = Undefined::Builder(); replaceWithInPlace(result); return result; } @@ -82,7 +83,7 @@ Expression PredictionInterval::shallowReduce(Context & context, Preferences::Ang Rational r0 = static_cast(c0); Rational r1 = static_cast(c1); if (!r1.integerDenominator().isOne() || r1.sign() == ExpressionNode::Sign::Negative || r0.sign() == ExpressionNode::Sign::Negative || Integer::NaturalOrder(r0.unsignedIntegerNumerator(), r0.integerDenominator()) > 0) { - Expression result = Undefined(); + Expression result = Undefined::Builder(); replaceWithInPlace(result); return result; } @@ -90,19 +91,19 @@ Expression PredictionInterval::shallowReduce(Context & context, Preferences::Ang // Compute numerator = r0*(1-r0) Integer factorNumerator = Integer::Subtraction(r0.integerDenominator(), r0.unsignedIntegerNumerator()); Integer factorDenominator = r0.integerDenominator(); - Rational numerator = Rational::Multiplication(r0, Rational(factorNumerator, factorDenominator)); + Rational numerator = Rational::Multiplication(r0, Rational::Builder(factorNumerator, factorDenominator)); if (numerator.numeratorOrDenominatorIsInfinity()) { return *this; } // Compute sqr = sqrt(r0*(1-r0)/r1) - Expression sqr = Power(Division(numerator, r1), Rational(1, 2)); - Expression m = Multiplication(Rational(196, 100), sqr); - Matrix matrix; - matrix.addChildAtIndexInPlace(Addition(r0.clone(), Multiplication(Rational(-1), m.clone())), 0, 0); - matrix.addChildAtIndexInPlace(Addition(r0.clone(), m), 1, 1); + Expression sqr = Power::Builder(Division::Builder(numerator, r1), Rational::Builder(1, 2)); + Expression m = Multiplication::Builder(Rational::Builder(196, 100), sqr); + Matrix matrix = Matrix::Builder(); + matrix.addChildAtIndexInPlace(Addition::Builder(r0.clone(), Multiplication::Builder(Rational::Builder(-1), m.clone())), 0, 0); + matrix.addChildAtIndexInPlace(Addition::Builder(r0.clone(), m), 1, 1); matrix.setDimensions(1, 2); replaceWithInPlace(matrix); - matrix.deepReduceChildren(context, angleUnit, target); + matrix.deepReduceChildren(context, complexFormat, angleUnit, target); return matrix; } diff --git a/poincare/src/preferences.cpp b/poincare/src/preferences.cpp index 467f0065c..207dea71d 100644 --- a/poincare/src/preferences.cpp +++ b/poincare/src/preferences.cpp @@ -7,7 +7,7 @@ Preferences::Preferences() : m_angleUnit(AngleUnit::Degree), m_displayMode(Preferences::PrintFloatMode::Decimal), m_editionMode(EditionMode::Edition2D), - m_complexFormat(Preferences::ComplexFormat::Cartesian), + m_complexFormat(Preferences::ComplexFormat::Real), m_numberOfSignificantDigits(PrintFloat::k_numberOfPrintedSignificantDigits) { } diff --git a/poincare/src/print_float.cpp b/poincare/src/print_float.cpp index 369d8a1ee..10171fe93 100644 --- a/poincare/src/print_float.cpp +++ b/poincare/src/print_float.cpp @@ -41,22 +41,31 @@ void PrintFloat::printBase10IntegerWithDecimalMarker(char * buffer, int bufferLe template int PrintFloat::convertFloatToText(T f, char * buffer, int bufferSize, - int numberOfSignificantDigits, Preferences::PrintFloatMode mode) { + int numberOfSignificantDigits, Preferences::PrintFloatMode mode, bool allowRounding) +{ assert(numberOfSignificantDigits > 0); + assert(bufferSize > 0); + char tempBuffer[PrintFloat::k_maxFloatBufferLength]; - int requiredLength = convertFloatToTextPrivate(f, tempBuffer, numberOfSignificantDigits, mode); - /* if the required buffer size overflows the buffer size, we first force the + int numberOfZerosRemoved = 0; + int requiredLength = convertFloatToTextPrivate(f, tempBuffer, numberOfSignificantDigits, mode, &numberOfZerosRemoved); + /* If the required buffer size overflows the buffer size, we first force the * display mode to scientific and decrease the number of significant digits to - * fit the buffer size. If the buffer size is still to small, we only write - * the beginning of the float and truncate it (which can result in a non sense - * text) */ + * fit the buffer size. */ if (mode == Preferences::PrintFloatMode::Decimal && requiredLength >= bufferSize) { - requiredLength = convertFloatToTextPrivate(f, tempBuffer, numberOfSignificantDigits, Preferences::PrintFloatMode::Scientific); + requiredLength = convertFloatToTextPrivate(f, tempBuffer, numberOfSignificantDigits, Preferences::PrintFloatMode::Scientific, &numberOfZerosRemoved); } if (requiredLength >= bufferSize) { - int adjustedNumberOfSignificantDigits = numberOfSignificantDigits - requiredLength + bufferSize - 1; + /* If the buffer size is still too small and rounding is allowed, we only + * write the beginning of the float and truncate it (which can result in a + * non sense text). If no rounding is allowed, we set the text to null. */ + if (!allowRounding) { + buffer[0] = 0; + return requiredLength; + } + int adjustedNumberOfSignificantDigits = numberOfSignificantDigits - numberOfZerosRemoved - requiredLength + bufferSize - 1; adjustedNumberOfSignificantDigits = adjustedNumberOfSignificantDigits < 1 ? 1 : adjustedNumberOfSignificantDigits; - requiredLength = convertFloatToTextPrivate(f, tempBuffer, adjustedNumberOfSignificantDigits, Preferences::PrintFloatMode::Scientific); + requiredLength = convertFloatToTextPrivate(f, tempBuffer, adjustedNumberOfSignificantDigits, Preferences::PrintFloatMode::Scientific, &numberOfZerosRemoved); } requiredLength = requiredLength < bufferSize ? requiredLength : bufferSize-1; strlcpy(buffer, tempBuffer, bufferSize); @@ -64,7 +73,7 @@ int PrintFloat::convertFloatToText(T f, char * buffer, int bufferSize, } template -int PrintFloat::convertFloatToTextPrivate(T f, char * buffer, int numberOfSignificantDigits, Preferences::PrintFloatMode mode) { +int PrintFloat::convertFloatToTextPrivate(T f, char * buffer, int numberOfSignificantDigits, Preferences::PrintFloatMode mode, int * numberOfRemovedZeros) { assert(numberOfSignificantDigits > 0); if (std::isinf(f)) { assert(Infinity::NameSize()+1 < PrintFloat::k_maxFloatBufferLength); @@ -129,17 +138,22 @@ int PrintFloat::convertFloatToTextPrivate(T f, char * buffer, int numberOfSignif * that we stay beyond this threshold during computation. */ assert(numberOfSignificantDigits < std::log10(std::pow(2.0f, 63.0f))); - // Supress the 0 on the right side of the mantissa + // Remove the 0 on the right side of the mantissa Integer dividend = Integer((int64_t)mantissa); Integer quotient = Integer::Division(dividend, Integer(10)).quotient; Integer digit = Integer::Subtraction(dividend, Integer::Multiplication(quotient, Integer(10))); int minimumNumberOfCharsInMantissa = 1; + int numberOfZerosRemoved = 0; while (digit.isZero() && numberOfCharsForMantissaWithoutSign > minimumNumberOfCharsInMantissa && (numberOfCharsForMantissaWithoutSign > exponentInBase10+1 || mode == Preferences::PrintFloatMode::Scientific)) { numberOfCharsForMantissaWithoutSign--; dividend = quotient; quotient = Integer::Division(dividend, Integer(10)).quotient; digit = Integer::Subtraction(dividend, Integer::Multiplication(quotient, Integer(10))); + numberOfZerosRemoved++; + } + if (numberOfRemovedZeros != nullptr) { + *numberOfRemovedZeros = numberOfZerosRemoved; } /* Part II: Decimal marker */ @@ -165,7 +179,7 @@ int PrintFloat::convertFloatToTextPrivate(T f, char * buffer, int numberOfSignif /* Part III: print mantissa*10^exponent*/ int numberOfCharsForMantissaWithSign = f >= 0 ? numberOfCharsForMantissaWithoutSign : numberOfCharsForMantissaWithoutSign + 1; // Print mantissa - assert(!dividend.isInfinity()); + assert(!dividend.isOverflow()); if (numberOfCharsForMantissaWithSign >= PrintFloat::k_maxFloatBufferLength) { /* Exception 3: if we are about to overflow the buffer, we escape by * returning a big int. This will be caught by 'convertFloatToText' which @@ -188,8 +202,7 @@ int PrintFloat::convertFloatToTextPrivate(T f, char * buffer, int numberOfSignif return (numberOfCharsForMantissaWithSign+1+numberOfCharExponent); } -template int PrintFloat::convertFloatToText(float, char*, int, int, Preferences::Preferences::PrintFloatMode); -template int PrintFloat::convertFloatToText(double, char*, int, int, Preferences::Preferences::PrintFloatMode); +template int PrintFloat::convertFloatToText(float, char*, int, int, Preferences::Preferences::PrintFloatMode, bool); +template int PrintFloat::convertFloatToText(double, char*, int, int, Preferences::Preferences::PrintFloatMode, bool); } - diff --git a/poincare/src/product.cpp b/poincare/src/product.cpp index 721c64648..ff6d7a8e0 100644 --- a/poincare/src/product.cpp +++ b/poincare/src/product.cpp @@ -19,7 +19,7 @@ Expression ProductNode::replaceUnknown(const Symbol & symbol) { } Layout ProductNode::createSequenceLayout(Layout argumentLayout, Layout symbolLayout, Layout subscriptLayout, Layout superscriptLayout) const { - return ProductLayout(argumentLayout, symbolLayout, subscriptLayout, superscriptLayout); + return ProductLayout::Builder(argumentLayout, symbolLayout, subscriptLayout, superscriptLayout); } int ProductNode::serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const { @@ -27,23 +27,32 @@ int ProductNode::serialize(char * buffer, int bufferSize, Preferences::PrintFloa } template -Evaluation ProductNode::templatedApproximateWithNextTerm(Evaluation a, Evaluation b) const { +Evaluation ProductNode::templatedApproximateWithNextTerm(Evaluation a, Evaluation b, Preferences::ComplexFormat complexFormat) const { if (a.type() == EvaluationNode::Type::Complex && b.type() == EvaluationNode::Type::Complex) { Complex c = static_cast&>(a); Complex d = static_cast&>(b); - return Complex(c.stdComplex()*d.stdComplex()); + return Complex::Builder(c.stdComplex()*d.stdComplex()); } if (a.type() == EvaluationNode::Type::Complex) { Complex c = static_cast &>(a); assert(b.type() == EvaluationNode::Type::MatrixComplex); MatrixComplex m = static_cast &>(b); - return MultiplicationNode::computeOnComplexAndMatrix(c.stdComplex(), m); + return MultiplicationNode::computeOnComplexAndMatrix(c.stdComplex(), m, complexFormat); } assert(a.type() == EvaluationNode::Type::MatrixComplex); assert(b.type() == EvaluationNode::Type::MatrixComplex); MatrixComplex m = static_cast&>(a); MatrixComplex n = static_cast&>(b); - return MultiplicationNode::computeOnMatrices(m, n); + return MultiplicationNode::computeOnMatrices(m, n, complexFormat); +} + +Expression Product::UntypedBuilder(Expression children) { + assert(children.type() == ExpressionNode::Type::Matrix); + if (children.childAtIndex(1).type() != ExpressionNode::Type::Symbol) { + // Second parameter must be a Symbol. + return Expression(); + } + return Builder(children.childAtIndex(0), children.childAtIndex(1).convert(), children.childAtIndex(2), children.childAtIndex(3)); } } diff --git a/poincare/src/randint.cpp b/poincare/src/randint.cpp index cd826e725..47d20058b 100644 --- a/poincare/src/randint.cpp +++ b/poincare/src/randint.cpp @@ -24,9 +24,9 @@ int RandintNode::serialize(char * buffer, int bufferSize, Preferences::PrintFloa return SerializationHelper::Prefix(this, buffer, bufferSize, floatDisplayMode, numberOfSignificantDigits, Randint::s_functionHelper.name()); } -template Evaluation RandintNode::templateApproximate(Context & context, Preferences::AngleUnit angleUnit) const { - Evaluation aInput = childAtIndex(0)->approximate(T(), context, angleUnit); - Evaluation bInput = childAtIndex(1)->approximate(T(), context, angleUnit); +template Evaluation RandintNode::templateApproximate(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { + Evaluation aInput = childAtIndex(0)->approximate(T(), context, complexFormat, angleUnit); + Evaluation bInput = childAtIndex(1)->approximate(T(), context, complexFormat, angleUnit); T a = aInput.toScalar(); T b = bInput.toScalar(); if (std::isnan(a) || std::isnan(b) || a != std::round(a) || b != std::round(b) || a > b) { @@ -34,7 +34,8 @@ template Evaluation RandintNode::templateApproximate(Context & c } T result = std::floor(Random::random()*(b+1.0-a)+a); - return Complex(result); + return Complex::Builder(result); } + } diff --git a/poincare/src/random.cpp b/poincare/src/random.cpp index 66474af83..cd5e309a0 100644 --- a/poincare/src/random.cpp +++ b/poincare/src/random.cpp @@ -12,8 +12,9 @@ constexpr Expression::FunctionHelper Random::s_functionHelper; int RandomNode::numberOfChildren() const { return Random::s_functionHelper.numberOfChildren(); } -Expression RandomNode::setSign(Sign s, Context & context, Preferences::AngleUnit angleUnit) { - return Random(this).setSign(s, context, angleUnit); +Expression RandomNode::setSign(Sign s, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) { + assert(s == ExpressionNode::Sign::Positive); + return Random(this).setSign(s, context, complexFormat, angleUnit); } Layout RandomNode::createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const { @@ -25,10 +26,10 @@ int RandomNode::serialize(char * buffer, int bufferSize, Preferences::PrintFloat } template Evaluation RandomNode::templateApproximate() const { - return Complex(Random::random()); + return Complex::Builder(Random::random()); } -Expression Random::setSign(ExpressionNode::Sign s, Context & context, Preferences::AngleUnit angleUnit) { +Expression Random::setSign(ExpressionNode::Sign s, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) { assert(s == ExpressionNode::Sign::Positive); return *this; } diff --git a/poincare/src/rational.cpp b/poincare/src/rational.cpp index 886465c1f..fa86aba96 100644 --- a/poincare/src/rational.cpp +++ b/poincare/src/rational.cpp @@ -15,10 +15,11 @@ namespace Poincare { /* Rational Node */ -void RationalNode::setDigits(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; +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)); } @@ -27,20 +28,6 @@ void RationalNode::setDigits(const native_uint_t * numeratorDigits, uint8_t nume } } -void RationalNode::initToMatchSize(size_t goalSize) { - assert(goalSize != sizeof(RationalNode)); - int digitsSize = goalSize - sizeof(RationalNode); - assert(digitsSize%sizeof(native_uint_t) == 0); - /* We are initing the Rational to match a specific size. The built rational - * is dummy. However, we cannot assign to m_numberOfDigitsNumerator (or - * m_numberOfDigitsDenominator) values that are aboce k_maxNumberOfDigits. - * To prevent that, we evenly separe digits between numerator and denominator. */ - size_t numberOfDigits = digitsSize/sizeof(native_uint_t); - m_numberOfDigitsNumerator = numberOfDigits/2; - m_numberOfDigitsDenominator = numberOfDigits-m_numberOfDigitsNumerator; - assert(size() == goalSize); -} - Integer RationalNode::signedNumerator() const { return Integer::BuildInteger((native_uint_t *)m_digits, m_numberOfDigitsNumerator, m_negative); } @@ -71,10 +58,10 @@ size_t RationalNode::size() const { void RationalNode::logAttributes(std::ostream & stream) const { stream << " negative=\"" << m_negative << "\""; stream << " numerator=\""; - this->signedNumerator().log(stream); + this->signedNumerator().logInteger(stream); stream << "\""; stream << " denominator=\""; - this->denominator().log(stream); + this->denominator().logInteger(stream); stream << "\""; } #endif @@ -98,7 +85,8 @@ int RationalNode::serialize(char * buffer, int bufferSize, Preferences::PrintFlo // Expression subclassing -Expression RationalNode::setSign(Sign s, Context & context, Preferences::AngleUnit angleUnit) { +Expression RationalNode::setSign(Sign s, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) { + assert(s == ExpressionNode::Sign::Positive || s == ExpressionNode::Sign::Negative); return Rational(this).setSign(s); } @@ -110,7 +98,7 @@ Layout RationalNode::createLayout(Preferences::PrintFloatMode floatDisplayMode, return numeratorLayout; } HorizontalLayout denominatorLayout = denominator().createLayout(); - return FractionLayout(numeratorLayout, denominatorLayout); + return FractionLayout::Builder(numeratorLayout, denominatorLayout); } // Approximation @@ -124,10 +112,10 @@ template T RationalNode::templatedApproximate() const { // Comparison int RationalNode::NaturalOrder(const RationalNode * i, const RationalNode * j) { - if (i->sign() == Sign::Negative && j->sign() == Sign::Positive) { + if (Number(i).sign() == Sign::Negative && Number(j).sign() == Sign::Positive) { return -1; } - if (i->sign() == Sign::Positive && j->sign() == Sign::Negative) { + if (Number(i).sign() == Sign::Positive && Number(j).sign() == Sign::Negative) { return 1; } Integer i1 = Integer::Multiplication(i->signedNumerator(), j->denominator()); @@ -135,7 +123,10 @@ int RationalNode::NaturalOrder(const RationalNode * i, const RationalNode * j) { return Integer::NaturalOrder(i1, i2); } -int RationalNode::simplificationOrderSameType(const ExpressionNode * e, bool canBeInterrupted) const { +int RationalNode::simplificationOrderSameType(const ExpressionNode * e, bool ascending, bool canBeInterrupted) const { + if (!ascending) { + return e->simplificationOrderSameType(this, true, canBeInterrupted); + } assert(e->type() == ExpressionNode::Type::Rational); const RationalNode * other = static_cast(e); return NaturalOrder(this, other); @@ -143,23 +134,23 @@ int RationalNode::simplificationOrderSameType(const ExpressionNode * e, bool can // Simplification -Expression RationalNode::shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) { - return Rational(this).shallowReduce(context, angleUnit); +Expression RationalNode::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) { + return Rational(this).shallowReduce(); } -Expression RationalNode::shallowBeautify(Context & context, Preferences::AngleUnit angleUnit) { - return Rational(this).shallowBeautify(context, angleUnit); +Expression RationalNode::shallowBeautify(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) { + return Rational(this).shallowBeautify(); } -Expression RationalNode::denominator(Context & context, Preferences::AngleUnit angleUnit) const { - return Rational(this).denominator(context, angleUnit); +Expression RationalNode::denominator(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { + return Rational(this).denominator(context, complexFormat, angleUnit); } /* Rational */ // Constructors -Rational::Rational(Integer & num, Integer & den) : Number() { +Rational Rational::Builder(Integer & num, Integer & den) { assert(!den.isZero()); if (!num.isOne() && !den.isOne()) { // Avoid computing GCD if possible @@ -168,38 +159,37 @@ Rational::Rational(Integer & num, Integer & den) : Number() { den = Integer::Division(den, gcd).quotient; } bool negative = (!num.isNegative() && den.isNegative()) || (!den.isNegative() && num.isNegative()); - new (this) Rational(num.digits(), num.numberOfDigits(), den.digits(), den.numberOfDigits(), negative); + return Rational::Builder(num.digits(), num.numberOfDigits(), den.digits(), den.numberOfDigits(), negative); } -Rational::Rational(const Integer & numerator) : Number() { +Rational Rational::Builder(const Integer & numerator) { native_uint_t one = 1; - new (this) Rational(numerator.digits(), numerator.numberOfDigits(), &one, 1, numerator.isNegative()); + return Rational::Builder(numerator.digits(), numerator.numberOfDigits(), &one, 1, numerator.isNegative()); } -Rational::Rational(native_int_t i) : Number() { +Rational Rational::Builder(native_int_t i) { native_uint_t one = 1; if (i == 0) { - new (this) Rational(nullptr, 0, &one, 1, false); - return; + return Rational::Builder(nullptr, 0, &one, 1, false); } native_uint_t absI = i < 0 ? -i : i; - new (this) Rational(&absI, 1, &one, 1, i < 0); + return Rational::Builder(&absI, 1, &one, 1, i < 0); } -Rational::Rational(native_int_t i, native_int_t j) : Number() { +Rational Rational::Builder(native_int_t i, native_int_t j) { Integer iInteger(i); Integer jInteger(j); - new (this) Rational(iInteger, jInteger); + return Rational::Builder(iInteger, jInteger); } -Rational::Rational(const char * iString, const char * jString) : Number() { +Rational Rational::Builder(const char * iString, const char * jString) { Integer iInteger(iString); Integer jInteger(jString); - new (this) Rational(iInteger, jInteger); + return Rational::Builder(iInteger, jInteger); } bool Rational::numeratorOrDenominatorIsInfinity() const { - return signedIntegerNumerator().isInfinity() || integerDenominator().isInfinity(); + return signedIntegerNumerator().isOverflow() || integerDenominator().isOverflow(); } // Basic operations @@ -207,13 +197,13 @@ bool Rational::numeratorOrDenominatorIsInfinity() const { 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(newNumerator, newDenominator); + 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(newNumerator, newDenominator); + return Rational::Builder(newNumerator, newDenominator); } Rational Rational::IntegerPower(const Rational & i, const Integer & j) { @@ -222,47 +212,48 @@ Rational Rational::IntegerPower(const Rational & i, const Integer & j) { Integer newNumerator = Integer::Power(i.signedIntegerNumerator(), absJ); Integer newDenominator = Integer::Power(i.integerDenominator(), absJ); if (j.isNegative()) { - return Rational(newDenominator, newNumerator); + return Rational::Builder(newDenominator, newNumerator); } - return Rational(newNumerator, newDenominator); + return Rational::Builder(newNumerator, newDenominator); } -Rational::Rational(const native_uint_t * i, uint8_t numeratorSize, const native_uint_t * j, uint8_t denominatorSize, bool negative) : - Number(TreePool::sharedPool()->createTreeNode(RationalSize(numeratorSize, denominatorSize))) -{ - static_cast(node())->setDigits(i, numeratorSize, j, denominatorSize, negative); +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(h); } -Expression Rational::shallowReduce(Context & context, Preferences::AngleUnit angleUnit) { +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().isInfinity() && integerDenominator().isInfinity()) { + if (unsignedIntegerNumerator().isOverflow() && integerDenominator().isOverflow()) { assert(false); - return Undefined(); + return Undefined::Builder(); } // Turn into Infinite if the numerator is too big. - if (unsignedIntegerNumerator().isInfinity()) { + if (unsignedIntegerNumerator().isOverflow()) { assert(false); - return Infinity(sign() == ExpressionNode::Sign::Negative); + return Infinity::Builder(sign(&context) == ExpressionNode::Sign::Negative); } // Turn into 0 if the denominator is too big. - if (integerDenominator().isInfinity()) { + if (integerDenominator().isOverflow()) { assert(false); - return Rational(0); + return Rational::Builder(0); } #endif assert(!numeratorOrDenominatorIsInfinity()); return *this; } -Expression Rational::shallowBeautify(Context & context, Preferences::AngleUnit angleUnit) { +Expression Rational::shallowBeautify() { if (sign() == ExpressionNode::Sign::Negative) { Expression abs = setSign(ExpressionNode::Sign::Positive); - Opposite o; + Opposite o = Opposite::Builder(); replaceWithInPlace(o); o.replaceChildAtIndexInPlace(0, abs); return o; @@ -270,23 +261,24 @@ Expression Rational::shallowBeautify(Context & context, Preferences::AngleUnit a return *this; } -Expression Rational::denominator(Context & context, Preferences::AngleUnit angleUnit) const { +Expression Rational::denominator(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { Integer d = integerDenominator(); if (d.isOne()) { return Expression(); } - if (d.isInfinity()) { - return Infinity(false); + if (d.isOverflow()) { + return Infinity::Builder(false); } - return Rational(d); + return Rational::Builder(d); } Expression Rational::setSign(ExpressionNode::Sign s) { - assert(s != ExpressionNode::Sign::Unknown); + 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; } diff --git a/poincare/src/real_part.cpp b/poincare/src/real_part.cpp index b4fdf9e88..3fc75d22f 100644 --- a/poincare/src/real_part.cpp +++ b/poincare/src/real_part.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -19,13 +20,14 @@ int RealPartNode::serialize(char * buffer, int bufferSize, Preferences::PrintFlo return SerializationHelper::Prefix(this, buffer, bufferSize, floatDisplayMode, numberOfSignificantDigits, RealPart::s_functionHelper.name()); } -Expression RealPartNode::shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) { - return RealPart(this).shallowReduce(context, angleUnit); +Expression RealPartNode::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) { + return RealPart(this).shallowReduce(context, complexFormat, angleUnit, target); } -Expression RealPart::shallowReduce(Context & context, Preferences::AngleUnit angleUnit) { + +Expression RealPart::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { { - Expression e = Expression::defaultShallowReduce(context, angleUnit); + Expression e = Expression::defaultShallowReduce(); if (e.isUndefined()) { return e; } @@ -36,10 +38,16 @@ Expression RealPart::shallowReduce(Context & context, Preferences::AngleUnit ang return SimplificationHelper::Map(*this, context, angleUnit); } #endif - if (c.type() == ExpressionNode::Type::Rational) { + if (c.isReal(context)) { replaceWithInPlace(c); return c; } + if (c.type() == ExpressionNode::Type::ComplexCartesian) { + ComplexCartesian complexChild = static_cast(c); + Expression r = complexChild.real(); + replaceWithInPlace(r); + return r.shallowReduce(context, complexFormat, angleUnit, target); + } return *this; } diff --git a/poincare/src/right_parenthesis_layout.cpp b/poincare/src/right_parenthesis_layout.cpp index e6a305113..d38345f20 100644 --- a/poincare/src/right_parenthesis_layout.cpp +++ b/poincare/src/right_parenthesis_layout.cpp @@ -59,6 +59,4 @@ void RightParenthesisLayoutNode::render(KDContext * ctx, KDPoint p, KDColor expr RenderWithChildHeight(ParenthesisLayoutNode::ChildHeightGivenLayoutHeight(layoutSize().height()), ctx, p, expressionColor, backgroundColor); } -RightParenthesisLayout::RightParenthesisLayout() : Layout(TreePool::sharedPool()->createTreeNode()) {} - } diff --git a/poincare/src/right_square_bracket_layout.cpp b/poincare/src/right_square_bracket_layout.cpp index f5b6aecc4..cd1608a02 100644 --- a/poincare/src/right_square_bracket_layout.cpp +++ b/poincare/src/right_square_bracket_layout.cpp @@ -8,6 +8,4 @@ void RightSquareBracketLayoutNode::render(KDContext * ctx, KDPoint p, KDColor ex ctx->fillRect(KDRect(p.x()+k_widthMargin-k_bracketWidth+1, p.y() + childHeight(), k_bracketWidth, k_lineThickness), expressionColor); } -RightSquareBracketLayout::RightSquareBracketLayout() : Layout(TreePool::sharedPool()->createTreeNode()) {} - } diff --git a/poincare/src/round.cpp b/poincare/src/round.cpp index eea75450a..416bd2037 100644 --- a/poincare/src/round.cpp +++ b/poincare/src/round.cpp @@ -21,33 +21,34 @@ int RoundNode::serialize(char * buffer, int bufferSize, Preferences::PrintFloatM return SerializationHelper::Prefix(this, buffer, bufferSize, floatDisplayMode, numberOfSignificantDigits, Round::s_functionHelper.name()); } -Expression RoundNode::shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) { - return Round(this).shallowReduce(context, angleUnit); +Expression RoundNode::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) { + return Round(this).shallowReduce(); } template -Evaluation RoundNode::templatedApproximate(Context& context, Preferences::AngleUnit angleUnit) const { - Evaluation f1Input = childAtIndex(0)->approximate(T(), context, angleUnit); - Evaluation f2Input = childAtIndex(1)->approximate(T(), context, angleUnit); +Evaluation RoundNode::templatedApproximate(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { + Evaluation f1Input = childAtIndex(0)->approximate(T(), context, complexFormat, angleUnit); + Evaluation f2Input = childAtIndex(1)->approximate(T(), context, complexFormat, angleUnit); T f1 = f1Input.toScalar(); T f2 = f2Input.toScalar(); if (std::isnan(f2) || f2 != std::round(f2)) { return Complex::Undefined(); } T err = std::pow(10, std::floor(f2)); - return Complex(std::round(f1*err)/err); + return Complex::Builder(std::round(f1*err)/err); } -Expression Round::shallowReduce(Context & context, Preferences::AngleUnit angleUnit) { + +Expression Round::shallowReduce() { { - Expression e = Expression::defaultShallowReduce(context, angleUnit); + Expression e = Expression::defaultShallowReduce(); if (e.isUndefined()) { return e; } } #if MATRIX_EXACT_REDUCING if (childAtIndex(0).type() == ExpressionNode::Type::Matrix || childAtIndex(1).type() == ExpressionNode::Type::Matrix) { - return Undefined(); + return Undefined::Builder(); } #endif /* We reduce only round(Rational, Rational). We do not reduce @@ -56,11 +57,11 @@ Expression Round::shallowReduce(Context & context, Preferences::AngleUnit angleU Rational r1 = childAtIndex(0).convert(); Rational r2 = childAtIndex(1).convert(); if (!r2.integerDenominator().isOne()) { - Expression result = Undefined(); + Expression result = Undefined::Builder(); replaceWithInPlace(result); return result; } - const Rational ten(10); + const Rational ten = Rational::Builder(10); if (Power::RationalExponentShouldNotBeReduced(ten, r2)) { return *this; } @@ -69,10 +70,10 @@ Expression Round::shallowReduce(Context & context, Preferences::AngleUnit angleU IntegerDivision d = Integer::Division(mult.signedIntegerNumerator(), mult.integerDenominator()); Integer rounding = d.quotient; Integer multDenominator = mult.integerDenominator(); - if (Rational::NaturalOrder(Rational(d.remainder, multDenominator), Rational(1,2)) >= 0) { + if (Rational::NaturalOrder(Rational::Builder(d.remainder, multDenominator), Rational::Builder(1,2)) >= 0) { rounding = Integer::Addition(rounding, Integer(1)); } - Rational result = Rational::Multiplication(rounding, Rational::IntegerPower(Rational(1,10), r2.signedIntegerNumerator())); + Rational result = Rational::Multiplication(Rational::Builder(rounding), Rational::IntegerPower(Rational::Builder(1,10), r2.signedIntegerNumerator())); if (result.numeratorOrDenominatorIsInfinity()) { return *this; } diff --git a/poincare/src/sequence.cpp b/poincare/src/sequence.cpp index 5e3748ac6..5e09b9732 100644 --- a/poincare/src/sequence.cpp +++ b/poincare/src/sequence.cpp @@ -20,22 +20,22 @@ Layout SequenceNode::createLayout(Preferences::PrintFloatMode floatDisplayMode, } template -Evaluation SequenceNode::templatedApproximate(Context& context, Preferences::AngleUnit angleUnit) const { - Evaluation aInput = childAtIndex(2)->approximate(T(), context, angleUnit); - Evaluation bInput = childAtIndex(3)->approximate(T(), context, angleUnit); +Evaluation SequenceNode::templatedApproximate(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { + Evaluation aInput = childAtIndex(2)->approximate(T(), context, complexFormat, angleUnit); + Evaluation bInput = childAtIndex(3)->approximate(T(), context, complexFormat, angleUnit); T start = aInput.toScalar(); T end = bInput.toScalar(); if (std::isnan(start) || std::isnan(end) || start != (int)start || end != (int)end || end - start > k_maxNumberOfSteps) { return Complex::Undefined(); } VariableContext nContext = VariableContext(static_cast(childAtIndex(1))->name(), &context); - Evaluation result = Complex((T)emptySequenceValue()); + Evaluation result = Complex::Builder((T)emptySequenceValue()); for (int i = (int)start; i <= (int)end; i++) { - if (Expression::shouldStopProcessing()) { + if (Expression::ShouldStopProcessing()) { return Complex::Undefined(); } nContext.setApproximationForVariable((T)i); - result = evaluateWithNextTerm(T(), result, childAtIndex(0)->approximate(T(), nContext, angleUnit)); + result = evaluateWithNextTerm(T(), result, childAtIndex(0)->approximate(T(), nContext, complexFormat, angleUnit), complexFormat); if (result.isUndefined()) { return Complex::Undefined(); } @@ -43,7 +43,7 @@ Evaluation SequenceNode::templatedApproximate(Context& context, Preferences:: return result; } -template Evaluation SequenceNode::templatedApproximate(Context& context, Preferences::AngleUnit angleUnit) const; -template Evaluation SequenceNode::templatedApproximate(Context& context, Preferences::AngleUnit angleUnit) const; +template Evaluation SequenceNode::templatedApproximate(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; +template Evaluation SequenceNode::templatedApproximate(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const; } diff --git a/poincare/src/sign_function.cpp b/poincare/src/sign_function.cpp new file mode 100644 index 000000000..cc8a8bf09 --- /dev/null +++ b/poincare/src/sign_function.cpp @@ -0,0 +1,102 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Poincare { + +constexpr Expression::FunctionHelper SignFunction::s_functionHelper; + +int SignFunctionNode::numberOfChildren() const { return SignFunction::s_functionHelper.numberOfChildren(); } + +ExpressionNode::Sign SignFunctionNode::sign(Context * context) const { + return childAtIndex(0)->sign(context); +} + +Expression SignFunctionNode::setSign(Sign s, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) { + assert(s == ExpressionNode::Sign::Positive || s == ExpressionNode::Sign::Negative); + SignFunction sign(this); + Rational r = Rational::Builder(s == ExpressionNode::Sign::Positive ? 1 : -1); + sign.replaceWithInPlace(r); + return r; +} + +Layout SignFunctionNode::createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const { + return LayoutHelper::Prefix(SignFunction(this), floatDisplayMode, numberOfSignificantDigits, SignFunction::s_functionHelper.name()); +} + +int SignFunctionNode::serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const { + return SerializationHelper::Prefix(this, buffer, bufferSize, floatDisplayMode, numberOfSignificantDigits, SignFunction::s_functionHelper.name()); +} + +Expression SignFunctionNode::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) { + return SignFunction(this).shallowReduce(context, complexFormat, angleUnit, target); +} + +template +Complex SignFunctionNode::computeOnComplex(const std::complex c, Preferences::ComplexFormat, Preferences::AngleUnit angleUnit) { + if (c.imag() != 0 || std::isnan(c.real())) { + return Complex::Undefined(); + } + if (c.real() == 0) { + return Complex::Builder(0.0); + } + if (c.real() < 0) { + return Complex::Builder(-1.0); + } + return Complex::Builder(1.0); +} + + +Expression SignFunction::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { + { + Expression e = Expression::defaultShallowReduce(); + if (e.isUndefined()) { + return e; + } + } +#if MATRIX_EXACT_REDUCING + if (c.type() == ExpressionNode::Type::Matrix) { + return SimplificationHelper::Map(*this, context, angleUnit); + } +#endif + Rational resultSign = Rational::Builder(1); + Expression child = childAtIndex(0); + ExpressionNode::Sign s = child.sign(&context); + if (s == ExpressionNode::Sign::Negative) { + resultSign = Rational::Builder(-1); + } else { + Evaluation childApproximated = child.node()->approximate(1.0f, context, complexFormat, angleUnit); + assert(childApproximated.type() == EvaluationNode::Type::Complex); + Complex c = static_cast&>(childApproximated); + if (std::isnan(c.imag()) || std::isnan(c.real()) || c.imag() != 0) { + // c's approximation has no sign (c is complex or NAN) + if (target == ExpressionNode::ReductionTarget::User && s == ExpressionNode::Sign::Positive) { + // For the user, we want sign(abs(x)) = 1 + } else { + // sign(-x) = -sign(x) + Expression oppChild = child.makePositiveAnyNegativeNumeralFactor(context, complexFormat, angleUnit, target); + if (oppChild.isUninitialized()) { + return *this; + } + Expression sign = *this; + Multiplication m = Multiplication::Builder(Rational::Builder(-1)); + replaceWithInPlace(m); + m.addChildAtIndexInPlace(sign, 1, 1); // sign does not need to be shallowReduced because -x = NAN --> x = NAN + return m; // m does not need to be shallowReduced, -1*sign cannot be reduced + } + } else if (c.real() < 0) { + resultSign = Rational::Builder(-1); + } else if (c.real() == 0) { + resultSign = Rational::Builder(0); + } + } + replaceWithInPlace(resultSign); + return resultSign; +} + +} diff --git a/poincare/src/simplification_helper.cpp b/poincare/src/simplification_helper.cpp index 3afadd465..4b3220212 100644 --- a/poincare/src/simplification_helper.cpp +++ b/poincare/src/simplification_helper.cpp @@ -4,17 +4,17 @@ namespace Poincare { // TODO Use clones -Expression SimplificationHelper::Map(const Expression & e, Context & context, Preferences::AngleUnit angleUnit) { +Expression SimplificationHelper::Map(const Expression & e, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) { assert(e->numberOfChildren() == 1 && e->childAtIndex(0)->type() == ExpressionNode::Type::Matrix); Expression c = e.childAtIndex(0); - Matrix matrix; + Matrix matrix = Matrix::Builder(); for (int i = 0; i < c->numberOfChildren(); i++) { Expression f = e.replaceChildAtIndexInPlace(0, e.childAtIndex(0).childAtIndex(i)); matrix.addChildAtIndexInPlace(f, i, i); - f.shallowReduce(context, angleUnit); + f.shallowReduce(context, complexFormat, angleUnit); } replaceWithInPlace(matrix); - return matrix.shallowReduce(context, angleUnit); + return matrix.shallowReduce(context, complexFormat, angleUnit); } } diff --git a/poincare/src/sine.cpp b/poincare/src/sine.cpp index e34607158..7241efff2 100644 --- a/poincare/src/sine.cpp +++ b/poincare/src/sine.cpp @@ -16,10 +16,10 @@ float SineNode::characteristicXRange(Context & context, Preferences::AngleUnit a } template -Complex SineNode::computeOnComplex(const std::complex c, Preferences::AngleUnit angleUnit) { +Complex SineNode::computeOnComplex(const std::complex c, Preferences::ComplexFormat, Preferences::AngleUnit angleUnit) { std::complex angleInput = Trigonometry::ConvertToRadian(c, angleUnit); std::complex res = std::sin(angleInput); - return Complex(Trigonometry::RoundToMeaningfulDigits(res, angleInput)); + return Complex::Builder(Trigonometry::RoundToMeaningfulDigits(res, angleInput)); } Layout SineNode::createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const { @@ -30,13 +30,14 @@ int SineNode::serialize(char * buffer, int bufferSize, Preferences::PrintFloatMo return SerializationHelper::Prefix(this, buffer, bufferSize, floatDisplayMode, numberOfSignificantDigits, Sine::s_functionHelper.name()); } -Expression SineNode::shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) { - return Sine(this).shallowReduce(context, angleUnit, target); +Expression SineNode::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) { + return Sine(this).shallowReduce(context, complexFormat, angleUnit, target); } -Expression Sine::shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { + +Expression Sine::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { { - Expression e = Expression::defaultShallowReduce(context, angleUnit); + Expression e = Expression::defaultShallowReduce(); if (e.isUndefined()) { return e; } @@ -47,7 +48,7 @@ Expression Sine::shallowReduce(Context & context, Preferences::AngleUnit angleUn return SimplificationHelper::Map(*this, context, angleUnit); } #endif - return Trigonometry::shallowReduceDirectFunction(*this, context, angleUnit, target); + return Trigonometry::shallowReduceDirectFunction(*this, context, complexFormat, angleUnit, target); } } diff --git a/poincare/src/square_root.cpp b/poincare/src/square_root.cpp index 9fd0ee7cc..243bbc37a 100644 --- a/poincare/src/square_root.cpp +++ b/poincare/src/square_root.cpp @@ -1,9 +1,13 @@ #include #include +#include +#include #include #include #include #include +#include +#include #include #include #include @@ -15,7 +19,7 @@ constexpr Expression::FunctionHelper SquareRoot::s_functionHelper; int SquareRootNode::numberOfChildren() const { return SquareRoot::s_functionHelper.numberOfChildren(); } Layout SquareRootNode::createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const { - return NthRootLayout(childAtIndex(0)->createLayout(floatDisplayMode, numberOfSignificantDigits)); + return NthRootLayout::Builder(childAtIndex(0)->createLayout(floatDisplayMode, numberOfSignificantDigits)); } int SquareRootNode::serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const { @@ -23,7 +27,7 @@ int SquareRootNode::serialize(char * buffer, int bufferSize, Preferences::PrintF } template -Complex SquareRootNode::computeOnComplex(const std::complex c, Preferences::AngleUnit angleUnit) { +Complex SquareRootNode::computeOnComplex(const std::complex c, Preferences::ComplexFormat, Preferences::AngleUnit angleUnit) { std::complex result = std::sqrt(c); /* Openbsd trigonometric functions are numerical implementation and thus are * approximative. @@ -31,16 +35,17 @@ Complex SquareRootNode::computeOnComplex(const std::complex c, Preferences * weird results as sqrt(-1) = 6E-16+i, we compute the argument of the result * of sqrt(c) and if arg ~ 0 [Pi], we discard the residual imaginary part and * if arg ~ Pi/2 [Pi], we discard the residual real part.*/ - return Complex(ApproximationHelper::TruncateRealOrImaginaryPartAccordingToArgument(result)); + return Complex::Builder(ApproximationHelper::TruncateRealOrImaginaryPartAccordingToArgument(result)); } -Expression SquareRootNode::shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) { - return SquareRoot(this).shallowReduce(context, angleUnit, target); +Expression SquareRootNode::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) { + return SquareRoot(this).shallowReduce(context, complexFormat, angleUnit, target); } -Expression SquareRoot::shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { + +Expression SquareRoot::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { { - Expression e = Expression::defaultShallowReduce(context, angleUnit); + Expression e = Expression::defaultShallowReduce(); if (e.isUndefined()) { return e; } @@ -50,9 +55,9 @@ Expression SquareRoot::shallowReduce(Context & context, Preferences::AngleUnit a return SimplificationHelper::Map(this, context, angleUnit); } #endif - Power p = Power(childAtIndex(0), Rational(1, 2)); + Power p = Power::Builder(childAtIndex(0), Rational::Builder(1, 2)); replaceWithInPlace(p); - return p.shallowReduce(context, angleUnit, target); + return p.shallowReduce(context, complexFormat, angleUnit, target); } } diff --git a/poincare/src/store.cpp b/poincare/src/store.cpp index 3aa3f341d..0c085733d 100644 --- a/poincare/src/store.cpp +++ b/poincare/src/store.cpp @@ -15,12 +15,16 @@ extern "C" { namespace Poincare { -void StoreNode::deepReduceChildren(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { +void StoreNode::deepReduceChildren(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { + // Interrupt simplification if the expression stored contains a matrix + if (Expression(childAtIndex(0)).recursivelyMatches([](const Expression e, Context & context, bool replaceSymbols) { return Expression::IsMatrix(e, context, replaceSymbols); }, context, true)) { + Expression::SetInterruption(true); + } return; } -Expression StoreNode::shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) { - return Store(this).shallowReduce(context, angleUnit); +Expression StoreNode::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) { + return Store(this).shallowReduce(context, complexFormat, angleUnit, target); } int StoreNode::serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const { @@ -28,35 +32,50 @@ int StoreNode::serialize(char * buffer, int bufferSize, Preferences::PrintFloatM } Layout StoreNode::createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const { - HorizontalLayout result = HorizontalLayout(); + HorizontalLayout result = HorizontalLayout::Builder(); result.addOrMergeChildAtIndex(childAtIndex(0)->createLayout(floatDisplayMode, numberOfSignificantDigits), 0, false); - result.addChildAtIndex(CharLayout(Ion::Charset::Sto), result.numberOfChildren(), result.numberOfChildren(), nullptr); + result.addChildAtIndex(CharLayout::Builder(Ion::Charset::Sto), result.numberOfChildren(), result.numberOfChildren(), nullptr); result.addOrMergeChildAtIndex(childAtIndex(1)->createLayout(floatDisplayMode, numberOfSignificantDigits), result.numberOfChildren(), false); return result; } template -Evaluation StoreNode::templatedApproximate(Context& context, Preferences::AngleUnit angleUnit) const { +Evaluation StoreNode::templatedApproximate(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { /* If we are here, it means that the store node was not shallowReduced. * Otherwise, it would have been replaced by its symbol. We thus have to * setExpressionForSymbol. */ - Store s(this); - assert(!s.value().isUninitialized()); - context.setExpressionForSymbol(s.value(), s.symbol(), context); - Expression e = context.expressionForSymbol(s.symbol(), false); - if (e.isUninitialized()) { - return Complex::Undefined(); - } - return e.approximateToEvaluation(context, angleUnit); + Expression storedExpression = Store(this).storeValueForSymbol(context, complexFormat, angleUnit); + assert(!storedExpression.isUninitialized()); + return storedExpression.node()->approximate(T(), context, complexFormat, angleUnit); } -Expression Store::shallowReduce(Context & context, Preferences::AngleUnit angleUnit) { +Expression Store::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { + Expression storedExpression = storeValueForSymbol(context, complexFormat, angleUnit); + + /* We want to replace the store with its reduced left side. If the + * simplification of the left side failed, just replace with the left side of + * the store without simplifying it. + * The simplification fails for [x]->d(x) for instance, because we do not + * have exact simplification of matrices yet. */ + bool interruptedSimplification = SimplificationHasBeenInterrupted(); + Expression reducedE = storedExpression.clone().deepReduce(context, complexFormat, angleUnit, target); + if (!reducedE.isUninitialized() && !SimplificationHasBeenInterrupted()) { + storedExpression = reducedE; + } + // Restore the previous interruption flag + SetInterruption(interruptedSimplification); + + replaceWithInPlace(storedExpression); + return storedExpression; +} + +Expression Store::storeValueForSymbol(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { Expression finalValue; if (symbol().type() == ExpressionNode::Type::Function) { // In tata + 2 ->f(tata), replace tata with xUnknown symbol assert(symbol().childAtIndex(0).type() == ExpressionNode::Type::Symbol); Expression userDefinedUnknown = symbol().childAtIndex(0); - Symbol xUnknown = Symbol(Symbol::SpecialSymbols::UnknownX); + Symbol xUnknown = Symbol::Builder(Symbol::SpecialSymbols::UnknownX); finalValue = childAtIndex(0).replaceSymbolWithExpression(static_cast(userDefinedUnknown), xUnknown); } else { assert(symbol().type() == ExpressionNode::Type::Symbol); @@ -64,19 +83,19 @@ Expression Store::shallowReduce(Context & context, Preferences::AngleUnit angleU } assert(!finalValue.isUninitialized()); context.setExpressionForSymbol(finalValue, symbol(), context); - Expression e = context.expressionForSymbol(symbol(), true); - if (e.isUninitialized()) { - return Undefined(); + Expression storedExpression = context.expressionForSymbol(symbol(), true); + + if (storedExpression.isUninitialized()) { + return Undefined::Builder(); } if (symbol().type() == ExpressionNode::Type::Function) { // Replace the xUnknown symbol with the variable initially used assert(symbol().childAtIndex(0).type() == ExpressionNode::Type::Symbol); Expression userDefinedUnknown = symbol().childAtIndex(0); - Symbol xUnknown = Symbol(Symbol::SpecialSymbols::UnknownX); - e = e.replaceSymbolWithExpression(xUnknown, static_cast(userDefinedUnknown)); + Symbol xUnknown = Symbol::Builder(Symbol::SpecialSymbols::UnknownX); + storedExpression = storedExpression.replaceSymbolWithExpression(xUnknown, static_cast(userDefinedUnknown)); } - replaceWithInPlace(e); - return e; + return storedExpression; } } diff --git a/poincare/src/subtraction.cpp b/poincare/src/subtraction.cpp index cebd5e29a..44f670cf2 100644 --- a/poincare/src/subtraction.cpp +++ b/poincare/src/subtraction.cpp @@ -27,7 +27,7 @@ bool SubtractionNode::childNeedsParenthesis(const TreeNode * child) const { if (child == childAtIndex(0)) { return false; } - if (static_cast(child)->isNumber() && static_cast(child)->sign() == Sign::Negative) { + if (static_cast(child)->isNumber() && Number(static_cast(child)).sign() == Sign::Negative) { return true; } Type types[] = {Type::Subtraction, Type::Opposite, Type::Addition}; @@ -42,32 +42,30 @@ int SubtractionNode::serialize(char * buffer, int bufferSize, Preferences::Print return SerializationHelper::Infix(this, buffer, bufferSize, floatDisplayMode, numberOfSignificantDigits, "-"); } -template MatrixComplex SubtractionNode::computeOnComplexAndMatrix(const std::complex c, const MatrixComplex m) { - MatrixComplex opposite = computeOnMatrixAndComplex(m, c); - MatrixComplex result; +template MatrixComplex SubtractionNode::computeOnComplexAndMatrix(const std::complex c, const MatrixComplex m, Preferences::ComplexFormat complexFormat) { + MatrixComplex opposite = computeOnMatrixAndComplex(m, c, complexFormat); + MatrixComplex result = MatrixComplex::Builder(); for (int i = 0; i < opposite.numberOfChildren(); i++) { - result.addChildAtIndexInPlace(OppositeNode::compute(opposite.complexAtIndex(i)), i, i); + result.addChildAtIndexInPlace(OppositeNode::compute(opposite.complexAtIndex(i), complexFormat), i, i); } result.setDimensions(opposite.numberOfRows(), opposite.numberOfColumns()); return result; } -Expression SubtractionNode::shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) { - return Subtraction(this).shallowReduce(context, angleUnit, target); +Expression SubtractionNode::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) { + return Subtraction(this).shallowReduce(context, complexFormat, angleUnit, target); } -Subtraction::Subtraction() : Expression(TreePool::sharedPool()->createTreeNode()) {} - -Expression Subtraction::shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { - Expression e = Expression::defaultShallowReduce(context, angleUnit); +Expression Subtraction::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { + Expression e = Expression::defaultShallowReduce(); if (e.isUndefined()) { return e; } - Expression m = Multiplication(Rational(-1), childAtIndex(1)); - Addition a(childAtIndex(0), m); - m = m.shallowReduce(context, angleUnit, target); + Expression m = Multiplication::Builder(Rational::Builder(-1), childAtIndex(1)); + Addition a = Addition::Builder(childAtIndex(0), m); + m = m.shallowReduce(context, complexFormat, angleUnit, target); replaceWithInPlace(a); - return a.shallowReduce(context, angleUnit, target); + return a.shallowReduce(context, complexFormat, angleUnit, target); } } diff --git a/poincare/src/sum.cpp b/poincare/src/sum.cpp index 9f35641bd..549cfdba9 100644 --- a/poincare/src/sum.cpp +++ b/poincare/src/sum.cpp @@ -19,7 +19,7 @@ Expression SumNode::replaceUnknown(const Symbol & symbol) { } Layout SumNode::createSequenceLayout(Layout argumentLayout, Layout symbolLayout, Layout subscriptLayout, Layout superscriptLayout) const { - return SumLayout(argumentLayout, symbolLayout, subscriptLayout, superscriptLayout); + return SumLayout::Builder(argumentLayout, symbolLayout, subscriptLayout, superscriptLayout); } int SumNode::serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const { @@ -27,23 +27,32 @@ int SumNode::serialize(char * buffer, int bufferSize, Preferences::PrintFloatMod } template -Evaluation SumNode::templatedApproximateWithNextTerm(Evaluation a, Evaluation b) const { +Evaluation SumNode::templatedApproximateWithNextTerm(Evaluation a, Evaluation b, Preferences::ComplexFormat complexFormat) const { if (a.type() == EvaluationNode::Type::Complex && b.type() == EvaluationNode::Type::Complex) { Complex c = static_cast&>(a); Complex d = static_cast&>(b); - return Complex(c.stdComplex()+d.stdComplex()); + return Complex::Builder(c.stdComplex()+d.stdComplex()); } if (a.type() == EvaluationNode::Type::Complex) { Complex c = static_cast &>(a); assert(b.type() == EvaluationNode::Type::MatrixComplex); MatrixComplex m = static_cast &>(b); - return AdditionNode::computeOnComplexAndMatrix(c.stdComplex(), m); + return AdditionNode::computeOnComplexAndMatrix(c.stdComplex(), m, complexFormat); } assert(a.type() == EvaluationNode::Type::MatrixComplex); assert(b.type() == EvaluationNode::Type::MatrixComplex); MatrixComplex m = static_cast&>(a); MatrixComplex n = static_cast&>(b); - return AdditionNode::computeOnMatrices(m, n); + return AdditionNode::computeOnMatrices(m, n, complexFormat); +} + +Expression Sum::UntypedBuilder(Expression children) { + assert(children.type() == ExpressionNode::Type::Matrix); + if (children.childAtIndex(1).type() != ExpressionNode::Type::Symbol) { + // Second parameter must be a Symbol. + return Expression(); + } + return Builder(children.childAtIndex(0), children.childAtIndex(1).convert(), children.childAtIndex(2), children.childAtIndex(3)); } } diff --git a/poincare/src/symbol.cpp b/poincare/src/symbol.cpp index dc9ecf035..0428218eb 100644 --- a/poincare/src/symbol.cpp +++ b/poincare/src/symbol.cpp @@ -15,13 +15,9 @@ namespace Poincare { constexpr char Symbol::k_ans[]; -/*ExpressionNode::Sign SymbolNode::sign() const { - TODO: Maybe, we will want to know that from a context given in parameter: - if (context.expressionForSymbol(this, false) != nullptr) { - return context.expressionForSymbol(this, false)->sign(context); - } +SymbolNode::SymbolNode(const char * newName, int length) : SymbolAbstractNode() { + strlcpy(const_cast(name()), newName, length+1); } -*/ Expression SymbolNode::replaceSymbolWithExpression(const SymbolAbstract & symbol, const Expression & expression) { return Symbol(this).replaceSymbolWithExpression(symbol, expression); @@ -77,36 +73,41 @@ float SymbolNode::characteristicXRange(Context & context, Preferences::AngleUnit return 0.0f; } +bool SymbolNode::isReal(Context & context) const { + Symbol s(this); + return SymbolAbstract::isReal(s, context); +} + Layout SymbolNode::createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const { if (m_name[0] == Symbol::SpecialSymbols::UnknownX) { assert(m_name[1] == 0); - return CharLayout(Symbol::k_unknownXReadableChar); + return CharLayout::Builder(Symbol::k_unknownXReadableChar); } if (strcmp(m_name, "u(n)") == 0) { - return HorizontalLayout( - CharLayout('u'), - VerticalOffsetLayout( - CharLayout('n'), + return HorizontalLayout::Builder( + CharLayout::Builder('u'), + VerticalOffsetLayout::Builder( + CharLayout::Builder('n'), VerticalOffsetLayoutNode::Type::Subscript)); } if (strcmp(m_name, "u(n+1)") == 0) { - return HorizontalLayout( - CharLayout('u'), - VerticalOffsetLayout( + return HorizontalLayout::Builder( + CharLayout::Builder('u'), + VerticalOffsetLayout::Builder( LayoutHelper::String("n+1", 3), VerticalOffsetLayoutNode::Type::Subscript)); } if (strcmp(m_name, "v(n)") == 0) { - return HorizontalLayout( - CharLayout('v'), - VerticalOffsetLayout( - CharLayout('n'), + return HorizontalLayout::Builder( + CharLayout::Builder('v'), + VerticalOffsetLayout::Builder( + CharLayout::Builder('n'), VerticalOffsetLayoutNode::Type::Subscript)); } if (strcmp(m_name, "v(n+1)") == 0) { - return HorizontalLayout( - CharLayout('v'), - VerticalOffsetLayout( + return HorizontalLayout::Builder( + CharLayout::Builder('v'), + VerticalOffsetLayout::Builder( LayoutHelper::String("n+1", 3), VerticalOffsetLayoutNode::Type::Subscript)); } @@ -120,8 +121,8 @@ int SymbolNode::serialize(char * buffer, int bufferSize, Preferences::PrintFloat return strlcpy(buffer, m_name, bufferSize); } -Expression SymbolNode::shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) { - return Symbol(this).shallowReduce(context, angleUnit, target); +Expression SymbolNode::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) { + return Symbol(this).shallowReduce(context, complexFormat, angleUnit, target); } Expression SymbolNode::shallowReplaceReplaceableSymbols(Context & context) { @@ -129,21 +130,24 @@ Expression SymbolNode::shallowReplaceReplaceableSymbols(Context & context) { } template -Evaluation SymbolNode::templatedApproximate(Context& context, Preferences::AngleUnit angleUnit) const { +Evaluation SymbolNode::templatedApproximate(Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) const { Symbol s(this); Expression e = SymbolAbstract::Expand(s, context, false); if (e.isUninitialized()) { return Complex::Undefined(); } - return e.approximateToEvaluation(context, angleUnit); + return e.node()->approximate(T(), context, complexFormat, angleUnit); } -Symbol::Symbol(const char * name, int length) : SymbolAbstract(TreePool::sharedPool()->createTreeNode(SymbolAbstract::AlignedNodeSize(length, sizeof(SymbolNode)))) { - node()->setName(name, length); +Expression Symbol::UntypedBuilder(const char * name, size_t length, Context * context) { + // create an expression only if it is not in the context or defined as a symbol + Symbol s = Symbol::Builder(name, length); + if (SymbolAbstract::ValidInContext(s, context)) { + return s; + } + return Expression(); } -Symbol::Symbol(char name) : Symbol(&name, 1) {} - bool Symbol::isSeriesSymbol(const char * c) { // [NV][1-3] if (c[2] == 0 && (c[0] == 'N' || c[0] == 'V') && c[1] >= '1' && c[1] <= '3') { @@ -160,7 +164,7 @@ bool Symbol::isRegressionSymbol(const char * c) { return false; } -Expression Symbol::shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { +Expression Symbol::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { Symbol s = *this; Expression result = SymbolAbstract::Expand(s, context, true); if (result.isUninitialized()) { @@ -168,7 +172,7 @@ Expression Symbol::shallowReduce(Context & context, Preferences::AngleUnit angle } replaceWithInPlace(result); // The stored expression is as entered by the user, so we need to call reduce - return result.deepReduce(context, angleUnit, target); + return result.deepReduce(context, complexFormat, angleUnit, target); } Expression Symbol::replaceSymbolWithExpression(const SymbolAbstract & symbol, const Expression & expression) { @@ -176,7 +180,7 @@ Expression Symbol::replaceSymbolWithExpression(const SymbolAbstract & symbol, co Expression value = expression.clone(); Expression p = parent(); if (!p.isUninitialized() && p.node()->childNeedsParenthesis(value.node())) { - value = Parenthesis(value); + value = Parenthesis::Builder(value); } replaceWithInPlace(value); return value; @@ -187,13 +191,13 @@ Expression Symbol::replaceSymbolWithExpression(const SymbolAbstract & symbol, co Expression Symbol::replaceUnknown(const Symbol & symbol) { assert(!symbol.isUninitialized()); assert(symbol.type() == ExpressionNode::Type::Symbol); - return replaceSymbolWithExpression(symbol, Symbol(SpecialSymbols::UnknownX)); + return replaceSymbolWithExpression(symbol, Symbol::Builder(SpecialSymbols::UnknownX)); } int Symbol::getPolynomialCoefficients(Context & context, const char * symbolName, Expression coefficients[]) const { if (strcmp(name(), symbolName) == 0) { - coefficients[0] = Rational(0); - coefficients[1] = Rational(1); + coefficients[0] = Rational::Builder(0); + coefficients[1] = Rational::Builder(1); return 1; } coefficients[0] = clone(); diff --git a/poincare/src/symbol_abstract.cpp b/poincare/src/symbol_abstract.cpp index 3bc67899a..9e9a518ad 100644 --- a/poincare/src/symbol_abstract.cpp +++ b/poincare/src/symbol_abstract.cpp @@ -1,4 +1,8 @@ #include +#include +#include +#include +#include #include #include #include @@ -6,31 +10,41 @@ namespace Poincare { -void SymbolAbstractNode::setName(const char * newName, int length) { - strlcpy(const_cast(name()), newName, length+1); -} - size_t SymbolAbstractNode::size() const { - return SymbolAbstract::AlignedNodeSize(strlen(name()), nodeSize()); + return nodeSize() + strlen(name()) + 1; } -void SymbolAbstractNode::initToMatchSize(size_t goalSize) { - assert(goalSize != nodeSize()); - assert(goalSize > nodeSize()); - size_t nameSize = goalSize - nodeSize(); - char * modifiableName = const_cast(name()); - for (size_t i = 0; i < nameSize - 1; i++) { - modifiableName[i] = 'a'; +ExpressionNode::Sign SymbolAbstractNode::sign(Context * context) const { + SymbolAbstract s(this); + Expression e = SymbolAbstract::Expand(s, *context, false); + if (e.isUninitialized()) { + return Sign::Unknown; } - modifiableName[nameSize-1] = 0; - assert(size() == goalSize); + return e.sign(context); } -int SymbolAbstractNode::simplificationOrderSameType(const ExpressionNode * e, bool canBeInterrupted) const { +Expression SymbolAbstractNode::setSign(ExpressionNode::Sign s, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) { + SymbolAbstract sa(this); + Expression e = SymbolAbstract::Expand(sa, *context, true); + assert(!e.isUninitialized()); + sa.replaceWithInPlace(e); + return e.setSign(s, context, complexFormat, angleUnit, target); +} + +int SymbolAbstractNode::simplificationOrderSameType(const ExpressionNode * e, bool ascending, bool canBeInterrupted) const { assert(type() == e->type()); return strcmp(name(), static_cast(e)->name()); } +template +T SymbolAbstract::Builder(const char * name, int length) { + size_t size = sizeof(U) + length + 1; + void * bufferNode = TreePool::sharedPool()->alloc(size); + U * node = new (bufferNode) U(name, length); + TreeHandle h = TreeHandle::BuildWithGhostChildren(node); + return static_cast(h); +} + size_t SymbolAbstract::TruncateExtension(char * dst, const char * src, size_t len) { const char * cur = src; const char * end = src+len-1; @@ -55,15 +69,21 @@ Expression SymbolAbstract::Expand(const SymbolAbstract & symbol, Context & conte * symbols are defined circularly. */ e = Expression::ExpressionWithoutSymbols(e, context); if (!e.isUninitialized() && isFunction) { - e = e.replaceSymbolWithExpression(Symbol(Symbol::SpecialSymbols::UnknownX), symbol.childAtIndex(0)); + e = e.replaceSymbolWithExpression(Symbol::Builder(Symbol::SpecialSymbols::UnknownX), symbol.childAtIndex(0)); } return e; } -/* TreePool uses adresses and sizes that are multiples of 4 in order to make - * node moves faster.*/ -size_t SymbolAbstract::AlignedNodeSize(size_t nameLength, size_t nodeSize) { - return Helpers::AlignedSize(nodeSize+nameLength+1, 4); +bool SymbolAbstract::isReal(const SymbolAbstract & symbol, Context & context) { + Expression e = SymbolAbstract::Expand(symbol, context, false); + if (e.isUninitialized()) { + return true; + } + return e.isReal(context); } +template Constant SymbolAbstract::Builder(char const*, int); +template Function SymbolAbstract::Builder(char const*, int); +template Symbol SymbolAbstract::Builder(char const*, int); + } diff --git a/poincare/src/tangent.cpp b/poincare/src/tangent.cpp index 7c3b496b1..afb372d38 100644 --- a/poincare/src/tangent.cpp +++ b/poincare/src/tangent.cpp @@ -27,19 +27,20 @@ int TangentNode::serialize(char * buffer, int bufferSize, Preferences::PrintFloa } template -Complex TangentNode::computeOnComplex(const std::complex c, Preferences::AngleUnit angleUnit) { +Complex TangentNode::computeOnComplex(const std::complex c, Preferences::ComplexFormat, Preferences::AngleUnit angleUnit) { std::complex angleInput = Trigonometry::ConvertToRadian(c, angleUnit); std::complex res = std::tan(angleInput); - return Complex(Trigonometry::RoundToMeaningfulDigits(res, angleInput)); + return Complex::Builder(Trigonometry::RoundToMeaningfulDigits(res, angleInput)); } -Expression TangentNode::shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ReductionTarget target) { - return Tangent(this).shallowReduce(context, angleUnit, target); +Expression TangentNode::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ReductionTarget target) { + return Tangent(this).shallowReduce(context, complexFormat, angleUnit, target); } -Expression Tangent::shallowReduce(Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { + +Expression Tangent::shallowReduce(Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { { - Expression e = Expression::defaultShallowReduce(context, angleUnit); + Expression e = Expression::defaultShallowReduce(); if (e.isUndefined()) { return e; } @@ -50,13 +51,15 @@ Expression Tangent::shallowReduce(Context & context, Preferences::AngleUnit angl return SimplificationHelper::Map(*this, context, angleUnit); } #endif - Expression newExpression = Trigonometry::shallowReduceDirectFunction(*this, context, angleUnit, target); + Expression newExpression = Trigonometry::shallowReduceDirectFunction(*this, context, complexFormat, angleUnit, target); if (newExpression.type() == ExpressionNode::Type::Tangent) { Sine s = Sine::Builder(newExpression.childAtIndex(0).clone()); Cosine c = Cosine::Builder(newExpression.childAtIndex(0)); - Division d = Division(s, c); + Division d = Division::Builder(s, c); + s.shallowReduce(context, complexFormat, angleUnit, target); + c.shallowReduce(context, complexFormat, angleUnit, target); newExpression.replaceWithInPlace(d); - return d.shallowReduce(context, angleUnit, target); + return d.shallowReduce(context, complexFormat, angleUnit, target); } return newExpression; } diff --git a/poincare/src/tree_handle.cpp b/poincare/src/tree_handle.cpp index e4eaba5ed..25b2aef27 100644 --- a/poincare/src/tree_handle.cpp +++ b/poincare/src/tree_handle.cpp @@ -1,4 +1,6 @@ #include +#include +#include #include #if POINCARE_TREE_LOG #include @@ -16,7 +18,7 @@ TreeHandle TreeHandle::clone() const { } /* Hierarchy operations */ -TreeNode * TreeHandle::node() const { assert(m_identifier != TreeNode::NoNodeIdentifier); return TreePool::sharedPool()->node(m_identifier); } +TreeNode * TreeHandle::node() const { assert(hasNode(m_identifier)); return TreePool::sharedPool()->node(m_identifier); } size_t TreeHandle::size() const { return node()->deepSize(node()->numberOfChildren()); } @@ -69,7 +71,7 @@ void TreeHandle::replaceChildAtIndexInPlace(int oldChildIndex, TreeHandle newChi } void TreeHandle::replaceChildWithGhostInPlace(TreeHandle t) { - Ghost ghost; + Ghost ghost = Ghost::Builder(); return replaceChildInPlace(t, ghost); } @@ -185,6 +187,47 @@ TreeHandle::TreeHandle(const TreeNode * node) : TreeHandle() { } } +template +TreeHandle TreeHandle::Builder() { + void * bufferNode = TreePool::sharedPool()->alloc(sizeof(U)); + U * node = new (bufferNode) U(); + return TreeHandle::BuildWithGhostChildren(node); +} + +template +T TreeHandle::NAryBuilder(TreeHandle * children, size_t numberOfChildren) { + TreeHandle h = Builder(); + for (size_t i = 0; i < numberOfChildren; i++) { + h.addChildAtIndexInPlace(children[i], i, i); + } + return static_cast(h); +} + +template +T TreeHandle::FixedArityBuilder(TreeHandle * children, size_t numberOfChildren) { + TreeHandle h = Builder(); + for (size_t i = 0; i < numberOfChildren; i++) { + h.replaceChildAtIndexInPlace(i, children[i]); + } + return static_cast(h); +} + +TreeHandle TreeHandle::BuildWithGhostChildren(TreeNode * node) { + assert(node != nullptr); + TreePool * pool = TreePool::sharedPool(); + int expectedNumberOfChildren = node->numberOfChildren(); + /* Ensure the pool is syntaxically correct by creating ghost children for + * nodes that have a fixed, non-zero number of children. */ + for (int i = 0; i < expectedNumberOfChildren; i++) { + GhostNode * ghost = new (pool->alloc(sizeof(GhostNode))) GhostNode(); + ghost->rename(pool->generateIdentifier(), false); + ghost->retain(); + assert((char *)ghost == (char *)node->next() + i*sizeof(GhostNode)); + } + node->rename(pool->generateIdentifier(), false); + return TreeHandle(node); +} + void TreeHandle::setIdentifierAndRetain(int newId) { m_identifier = newId; if (!isUninitialized()) { @@ -204,7 +247,7 @@ void TreeHandle::setTo(const TreeHandle & tr) { } void TreeHandle::release(int identifier) { - if (identifier == TreeNode::NoNodeIdentifier) { + if (!hasNode(identifier)) { return; } TreeNode * node = TreePool::sharedPool()->node(identifier); @@ -217,4 +260,86 @@ void TreeHandle::release(int identifier) { node->release(node->numberOfChildren()); } +template AbsoluteValue TreeHandle::FixedArityBuilder(TreeHandle*, size_t); +template AbsoluteValueLayout TreeHandle::FixedArityBuilder(TreeHandle*, size_t); +template Addition TreeHandle::NAryBuilder(TreeHandle*, size_t); +template ArcCosine TreeHandle::FixedArityBuilder(TreeHandle*, size_t); +template ArcSine TreeHandle::FixedArityBuilder(TreeHandle*, size_t); +template ArcTangent TreeHandle::FixedArityBuilder(TreeHandle*, size_t); +template BinomialCoefficient TreeHandle::FixedArityBuilder(TreeHandle*, size_t); +template BinomialCoefficientLayout TreeHandle::FixedArityBuilder(TreeHandle*, size_t); +template Ceiling TreeHandle::FixedArityBuilder(TreeHandle*, size_t); +template CeilingLayout TreeHandle::FixedArityBuilder(TreeHandle*, size_t); +template CommonLogarithm TreeHandle::FixedArityBuilder >(TreeHandle*, size_t); +template ComplexArgument TreeHandle::FixedArityBuilder(TreeHandle*, size_t); +template ComplexCartesian TreeHandle::FixedArityBuilder(TreeHandle*, size_t); +template CondensedSumLayout TreeHandle::FixedArityBuilder(TreeHandle*, size_t); +template ConfidenceInterval TreeHandle::FixedArityBuilder(TreeHandle*, size_t); +template Conjugate TreeHandle::FixedArityBuilder(TreeHandle*, size_t); +template ConjugateLayout TreeHandle::FixedArityBuilder(TreeHandle*, size_t); +template Cosine TreeHandle::FixedArityBuilder(TreeHandle*, size_t); +template Derivative TreeHandle::FixedArityBuilder(TreeHandle*, size_t); +template Determinant TreeHandle::FixedArityBuilder(TreeHandle*, size_t); +template Division TreeHandle::FixedArityBuilder(TreeHandle*, size_t); +template DivisionQuotient TreeHandle::FixedArityBuilder(TreeHandle*, size_t); +template DivisionRemainder TreeHandle::FixedArityBuilder(TreeHandle*, size_t); +template EmptyExpression TreeHandle::FixedArityBuilder(TreeHandle*, size_t); +template Equal TreeHandle::FixedArityBuilder(TreeHandle*, size_t); +template Factor TreeHandle::FixedArityBuilder(TreeHandle*, size_t); +template Factorial TreeHandle::FixedArityBuilder(TreeHandle*, size_t); +template Floor TreeHandle::FixedArityBuilder(TreeHandle*, size_t); +template FloorLayout TreeHandle::FixedArityBuilder(TreeHandle*, size_t); +template FracPart TreeHandle::FixedArityBuilder(TreeHandle*, size_t); +template FractionLayout TreeHandle::FixedArityBuilder(TreeHandle*, size_t); +template GreatCommonDivisor TreeHandle::FixedArityBuilder(TreeHandle*, size_t); +template HorizontalLayout TreeHandle::NAryBuilder(TreeHandle*, size_t); +template HyperbolicArcCosine TreeHandle::FixedArityBuilder(TreeHandle*, size_t); +template HyperbolicArcSine TreeHandle::FixedArityBuilder(TreeHandle*, size_t); +template HyperbolicArcTangent TreeHandle::FixedArityBuilder(TreeHandle*, size_t); +template HyperbolicCosine TreeHandle::FixedArityBuilder(TreeHandle*, size_t); +template HyperbolicSine TreeHandle::FixedArityBuilder(TreeHandle*, size_t); +template HyperbolicTangent TreeHandle::FixedArityBuilder(TreeHandle*, size_t); +template ImaginaryPart TreeHandle::FixedArityBuilder(TreeHandle*, size_t); +template Integral TreeHandle::FixedArityBuilder(TreeHandle*, size_t); +template IntegralLayout TreeHandle::FixedArityBuilder(TreeHandle*, size_t); +template LeastCommonMultiple TreeHandle::FixedArityBuilder(TreeHandle*, size_t); +template LeftParenthesisLayout TreeHandle::FixedArityBuilder(TreeHandle*, size_t); +template LeftSquareBracketLayout TreeHandle::FixedArityBuilder(TreeHandle*, size_t); +template Logarithm TreeHandle::FixedArityBuilder >(TreeHandle*, size_t); +template Matrix TreeHandle::NAryBuilder(TreeHandle*, size_t); +template MatrixComplex TreeHandle::NAryBuilder, MatrixComplexNode >(TreeHandle*, size_t); +template MatrixComplex TreeHandle::NAryBuilder, MatrixComplexNode >(TreeHandle*, size_t); +template MatrixDimension TreeHandle::FixedArityBuilder(TreeHandle*, size_t); +template MatrixInverse TreeHandle::FixedArityBuilder(TreeHandle*, size_t); +template MatrixTrace TreeHandle::FixedArityBuilder(TreeHandle*, size_t); +template MatrixTranspose TreeHandle::FixedArityBuilder(TreeHandle*, size_t); +template Multiplication TreeHandle::NAryBuilder(TreeHandle*, size_t); +template NaperianLogarithm TreeHandle::FixedArityBuilder(TreeHandle*, size_t); +template NthRoot TreeHandle::FixedArityBuilder(TreeHandle*, size_t); +template Opposite TreeHandle::FixedArityBuilder(TreeHandle*, size_t); +template Parenthesis TreeHandle::FixedArityBuilder(TreeHandle*, size_t); +template PermuteCoefficient TreeHandle::FixedArityBuilder(TreeHandle*, size_t); +template Power TreeHandle::FixedArityBuilder(TreeHandle*, size_t); +template PredictionInterval TreeHandle::FixedArityBuilder(TreeHandle*, size_t); +template Product TreeHandle::FixedArityBuilder(TreeHandle*, size_t); +template ProductLayout TreeHandle::FixedArityBuilder(TreeHandle*, size_t); +template Randint TreeHandle::FixedArityBuilder(TreeHandle*, size_t); +template Random TreeHandle::FixedArityBuilder(TreeHandle*, size_t); +template RealPart TreeHandle::FixedArityBuilder(TreeHandle*, size_t); +template RightParenthesisLayout TreeHandle::FixedArityBuilder(TreeHandle*, size_t); +template RightSquareBracketLayout TreeHandle::FixedArityBuilder(TreeHandle*, size_t); +template Round TreeHandle::FixedArityBuilder(TreeHandle*, size_t); +template SignFunction TreeHandle::FixedArityBuilder(TreeHandle*, size_t); +template SimplePredictionInterval TreeHandle::FixedArityBuilder(TreeHandle*, size_t); +template Sine TreeHandle::FixedArityBuilder(TreeHandle*, size_t); +template SquareRoot TreeHandle::FixedArityBuilder(TreeHandle*, size_t); +template Store TreeHandle::FixedArityBuilder(TreeHandle*, size_t); +template Subtraction TreeHandle::FixedArityBuilder(TreeHandle*, size_t); +template Sum TreeHandle::FixedArityBuilder(TreeHandle*, size_t); +template SumLayout TreeHandle::FixedArityBuilder(TreeHandle*, size_t); +template Tangent TreeHandle::FixedArityBuilder(TreeHandle*, size_t); +template Undefined TreeHandle::FixedArityBuilder(TreeHandle*, size_t); +template Unreal TreeHandle::FixedArityBuilder(TreeHandle*, size_t); +template MatrixLayout TreeHandle::NAryBuilder(TreeHandle*, size_t); + } diff --git a/poincare/src/tree_node.cpp b/poincare/src/tree_node.cpp index 1c8df84ee..3036690b6 100644 --- a/poincare/src/tree_node.cpp +++ b/poincare/src/tree_node.cpp @@ -31,7 +31,7 @@ void TreeNode::rename(int identifier, bool unregisterPreviousIdentifier) { // Hierarchy TreeNode * TreeNode::parent() const { - return m_parentIdentifier == NoNodeIdentifier ? nullptr : TreePool::sharedPool()->node(m_parentIdentifier); + return TreeHandle::hasNode(m_parentIdentifier) ? TreePool::sharedPool()->node(m_parentIdentifier) : nullptr; } TreeNode * TreeNode::root() { diff --git a/poincare/src/tree_pool.cpp b/poincare/src/tree_pool.cpp index 17b476f53..9769f299a 100644 --- a/poincare/src/tree_pool.cpp +++ b/poincare/src/tree_pool.cpp @@ -20,19 +20,6 @@ void TreePool::freeIdentifier(int identifier) { } } -template -T * TreePool::createTreeNode(size_t size) { - T * node = new(alloc(size)) T(); - if (size != sizeof(T)) { - /* If the node does not have a standard size, it should init itself so that - * T::size gives the right result, otherwise TreeNode::next() does not work - * and addGhostChildrenAndRename might not work. */ - node->initToMatchSize(size); - } - addGhostChildrenAndRename(node); - return node; -} - void TreePool::move(TreeNode * destination, TreeNode * source, int realNumberOfSourceChildren) { size_t moveSize = source->deepSize(realNumberOfSourceChildren); moveNodes(destination, source, moveSize); @@ -152,17 +139,6 @@ void TreePool::dealloc(TreeNode * node, size_t size) { updateNodeForIdentifierFromNode(node); } -void TreePool::addGhostChildrenAndRename(TreeNode * node) { - /* Ensure the pool is syntaxically correct by creating ghost children for - * nodes that have a fixed, non-zero number of children. */ - for (int i = 0; i < node->numberOfChildren(); i++) { - TreeNode * ghost = createTreeNode(); - ghost->retain(); - move(node->next(), ghost, 0); - } - node->rename(generateIdentifier(), false); -} - void TreePool::discardTreeNode(TreeNode * node) { int nodeIdentifier = node->identifier(); size_t size = node->size(); @@ -202,103 +178,4 @@ void TreePool::freePoolFromNode(TreeNode * firstNodeToDiscard) { m_cursor = reinterpret_cast(firstNodeToDiscard); } -template HorizontalLayoutNode * Poincare::TreePool::createTreeNode(size_t size); -template EmptyLayoutNode * Poincare::TreePool::createTreeNode(size_t size); -template CharLayoutNode * Poincare::TreePool::createTreeNode(size_t size); -template VerticalOffsetLayoutNode * Poincare::TreePool::createTreeNode(size_t size); -template NthRootLayoutNode * Poincare::TreePool::createTreeNode(size_t size); -template FractionLayoutNode * Poincare::TreePool::createTreeNode(size_t size); -template LeftParenthesisLayoutNode * Poincare::TreePool::createTreeNode(size_t size); -template RightParenthesisLayoutNode * Poincare::TreePool::createTreeNode(size_t size); -template AbsoluteValueNode * Poincare::TreePool::createTreeNode(size_t size); -template AdditionNode * Poincare::TreePool::createTreeNode(size_t size); -template ArcCosineNode * Poincare::TreePool::createTreeNode(size_t size); -template ArcSineNode * Poincare::TreePool::createTreeNode(size_t size); -template ArcTangentNode * Poincare::TreePool::createTreeNode(size_t size); -template UndefinedNode * Poincare::TreePool::createTreeNode(size_t size); -template BinomialCoefficientNode * Poincare::TreePool::createTreeNode(size_t size); -template CeilingNode * Poincare::TreePool::createTreeNode(size_t size); -template OppositeNode * Poincare::TreePool::createTreeNode(size_t size); -template PowerNode * Poincare::TreePool::createTreeNode(size_t size); -template ComplexArgumentNode * Poincare::TreePool::createTreeNode(size_t size); -template ConfidenceIntervalNode * Poincare::TreePool::createTreeNode(size_t size); -template ConjugateNode * Poincare::TreePool::createTreeNode(size_t size); -template ConstantNode * Poincare::TreePool::createTreeNode(size_t size); -template CosineNode * Poincare::TreePool::createTreeNode(size_t size); -template DecimalNode * Poincare::TreePool::createTreeNode(size_t size); -template DerivativeNode * Poincare::TreePool::createTreeNode(size_t size); -template DeterminantNode * Poincare::TreePool::createTreeNode(size_t size); -template DivisionNode * Poincare::TreePool::createTreeNode(size_t size); -template DivisionQuotientNode * Poincare::TreePool::createTreeNode(size_t size); -template DivisionRemainderNode * Poincare::TreePool::createTreeNode(size_t size); -template EmptyExpressionNode * Poincare::TreePool::createTreeNode(size_t size); -template FunctionNode * Poincare::TreePool::createTreeNode(size_t size); -template HyperbolicArcCosineNode * Poincare::TreePool::createTreeNode(size_t size); -template HyperbolicArcSineNode * Poincare::TreePool::createTreeNode(size_t size); -template HyperbolicArcTangentNode * Poincare::TreePool::createTreeNode(size_t size); -template HyperbolicCosineNode * Poincare::TreePool::createTreeNode(size_t size); -template SimplePredictionIntervalNode * Poincare::TreePool::createTreeNode(size_t size); -template HyperbolicSineNode * Poincare::TreePool::createTreeNode(size_t size); -template HyperbolicTangentNode * Poincare::TreePool::createTreeNode(size_t size); -template LogarithmNode<2> * Poincare::TreePool::createTreeNode >(size_t size); -template SymbolNode * Poincare::TreePool::createTreeNode(size_t size); -template LogarithmNode<1> * Poincare::TreePool::createTreeNode >(size_t size); -template ParenthesisNode * Poincare::TreePool::createTreeNode(size_t size); -template StoreNode * Poincare::TreePool::createTreeNode(size_t size); -template EqualNode * Poincare::TreePool::createTreeNode(size_t size); -template FactorNode * Poincare::TreePool::createTreeNode(size_t size); -template FactorialNode * Poincare::TreePool::createTreeNode(size_t size); -template FloorNode * Poincare::TreePool::createTreeNode(size_t size); -template FracPartNode * Poincare::TreePool::createTreeNode(size_t size); -template MatrixNode * Poincare::TreePool::createTreeNode(size_t size); -template FloatNode * Poincare::TreePool::createTreeNode >(size_t size); -template GreatCommonDivisorNode * Poincare::TreePool::createTreeNode(size_t size); -template ImaginaryPartNode * Poincare::TreePool::createTreeNode(size_t size); -template IntegralNode * Poincare::TreePool::createTreeNode(size_t size); -template LeastCommonMultipleNode * Poincare::TreePool::createTreeNode(size_t size); -template MatrixDimensionNode * Poincare::TreePool::createTreeNode(size_t size); -template MatrixInverseNode * Poincare::TreePool::createTreeNode(size_t size); -template MatrixTraceNode * Poincare::TreePool::createTreeNode(size_t size); -template MatrixTransposeNode * Poincare::TreePool::createTreeNode(size_t size); -template MultiplicationNode * Poincare::TreePool::createTreeNode(size_t size); -template NaperianLogarithmNode * Poincare::TreePool::createTreeNode(size_t size); -template NthRootNode * Poincare::TreePool::createTreeNode(size_t size); -template InfinityNode * Poincare::TreePool::createTreeNode(size_t size); -template PermuteCoefficientNode * Poincare::TreePool::createTreeNode(size_t size); -template PredictionIntervalNode * Poincare::TreePool::createTreeNode(size_t size); -template ProductNode * Poincare::TreePool::createTreeNode(size_t size); -template RandintNode * Poincare::TreePool::createTreeNode(size_t size); -template RandomNode * Poincare::TreePool::createTreeNode(size_t size); -template RationalNode * Poincare::TreePool::createTreeNode(size_t size); -template RealPartNode * Poincare::TreePool::createTreeNode(size_t size); -template RoundNode * Poincare::TreePool::createTreeNode(size_t size); -template SineNode * Poincare::TreePool::createTreeNode(size_t size); -template SquareRootNode * Poincare::TreePool::createTreeNode(size_t size); -template SubtractionNode * Poincare::TreePool::createTreeNode(size_t size); -template SumNode * Poincare::TreePool::createTreeNode(size_t size); -template TangentNode * Poincare::TreePool::createTreeNode(size_t size); -template GhostNode * Poincare::TreePool::createTreeNode(size_t size); - -template MatrixLayoutNode* TreePool::createTreeNode(size_t size); -template AbsoluteValueLayoutNode* TreePool::createTreeNode(size_t size); -template ComplexNode* TreePool::createTreeNode >(size_t size); -template ComplexNode* TreePool::createTreeNode >(size_t size); -template MatrixComplexNode* TreePool::createTreeNode >(size_t size); -template MatrixComplexNode* TreePool::createTreeNode >(size_t size); -template BinomialCoefficientLayoutNode* TreePool::createTreeNode(size_t size); -template CeilingLayoutNode* TreePool::createTreeNode(size_t size); -template CondensedSumLayoutNode* TreePool::createTreeNode(size_t size); -template ConjugateLayoutNode* TreePool::createTreeNode(size_t size); -template FloorLayoutNode* TreePool::createTreeNode(size_t size); -template IntegralLayoutNode* TreePool::createTreeNode(size_t size); -template ProductLayoutNode* TreePool::createTreeNode(size_t size); -template SumLayoutNode* TreePool::createTreeNode(size_t size); -template FloatNode* TreePool::createTreeNode >(size_t size); - -template LeftSquareBracketLayoutNode* TreePool::createTreeNode(size_t size); -template RightSquareBracketLayoutNode* TreePool::createTreeNode(size_t size); - -template BlobNode* TreePool::createTreeNode(size_t size); -template PairNode* TreePool::createTreeNode(size_t size); - } diff --git a/poincare/src/trigonometry.cpp b/poincare/src/trigonometry.cpp index 5aac41b0d..71e174ca0 100644 --- a/poincare/src/trigonometry.cpp +++ b/poincare/src/trigonometry.cpp @@ -1,14 +1,18 @@ #include +#include #include -#include -#include -#include -#include -#include -#include -#include #include +#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include #include @@ -32,48 +36,146 @@ float Trigonometry::characteristicXRange(const Expression & e, Context & context assert(d == 1); /* To compute a, the slope of the expression child(0), we compute the * derivative of child(0) for any x value. */ - Poincare::Derivative derivative = Poincare::Derivative::Builder(e.childAtIndex(0).clone(), Symbol(x, 1), Float(1.0f)); - float a = derivative.approximateToScalar(context, angleUnit); + Poincare::Derivative derivative = Poincare::Derivative::Builder(e.childAtIndex(0).clone(), Symbol::Builder(x, 1), Float::Builder(1.0f)); + float a = derivative.node()->approximate(float(), context, Preferences::ComplexFormat::Real, angleUnit).toScalar(); float pi = angleUnit == Preferences::AngleUnit::Radian ? M_PI : 180.0f; return 2.0f*pi/std::fabs(a); } -Expression Trigonometry::shallowReduceDirectFunction(Expression & e, Context& context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { - assert(e.type() == ExpressionNode::Type::Sine - || e.type() == ExpressionNode::Type::Cosine - || e.type() == ExpressionNode::Type::Tangent); +bool Trigonometry::isDirectTrigonometryFunction(const Expression & e) { + return e.type() == ExpressionNode::Type::Cosine || e.type() == ExpressionNode::Type::Sine || e.type() == ExpressionNode::Type::Tangent; +} + +bool Trigonometry::isInverseTrigonometryFunction(const Expression & e) { + return e.type() == ExpressionNode::Type::ArcCosine || e.type() == ExpressionNode::Type::ArcSine || e.type() == ExpressionNode::Type::ArcTangent; +} + +bool Trigonometry::AreInverseFunctions(const Expression & directFunction, const Expression & inverseFunction) { + if (!isDirectTrigonometryFunction(directFunction)) { + return false; + } + ExpressionNode::Type correspondingType; + switch (directFunction.type()) { + case ExpressionNode::Type::Cosine: + correspondingType = ExpressionNode::Type::ArcCosine; + break; + case ExpressionNode::Type::Sine: + correspondingType = ExpressionNode::Type::ArcSine; + break; + default: + assert(directFunction.type() == ExpressionNode::Type::Tangent); + correspondingType = ExpressionNode::Type::ArcTangent; + break; + } + return inverseFunction.type() == correspondingType; +} + +bool Trigonometry::ExpressionIsEquivalentToTangent(const Expression & e) { + // We look for (cos^-1 * sin) + assert(ExpressionNode::Type::Power < ExpressionNode::Type::Sine); + if (e.type() == ExpressionNode::Type::Multiplication + && e.childAtIndex(1).type() == ExpressionNode::Type::Sine + && e.childAtIndex(0).type() == ExpressionNode::Type::Power + && e.childAtIndex(0).childAtIndex(0).type() == ExpressionNode::Type::Cosine + && e.childAtIndex(0).childAtIndex(1).type() == ExpressionNode::Type::Rational + && e.childAtIndex(0).childAtIndex(1).convert().isMinusOne()) { + return true; + } + return false; +} + +Expression Trigonometry::shallowReduceDirectFunction(Expression & e, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { + assert(isDirectTrigonometryFunction(e)); // Step 1. Try finding an easy standard calculation reduction - Expression lookup = Trigonometry::table(e.childAtIndex(0), e.type(), context, angleUnit, target); + Expression lookup = TrigonometryCheatTable::Table()->simplify(e.childAtIndex(0), e.type(), context, complexFormat, angleUnit, target); if (!lookup.isUninitialized()) { e.replaceWithInPlace(lookup); return lookup; } // Step 2. Look for an expression of type "cos(arccos(x))", return x - ExpressionNode::Type correspondingType = e.type() == ExpressionNode::Type::Cosine ? ExpressionNode::Type::ArcCosine : - (e.type() == ExpressionNode::Type::Sine ? ExpressionNode::Type::ArcSine : ExpressionNode::Type::ArcTangent); - if (e.childAtIndex(0).type() == correspondingType) { + if (AreInverseFunctions(e, e.childAtIndex(0))) { Expression result = e.childAtIndex(0).childAtIndex(0); e.replaceWithInPlace(result); return result; } - // Step 3. Look for an expression of type "cos(-a)", return "+/-cos(a)" - if (e.childAtIndex(0).sign() == ExpressionNode::Sign::Negative) { - e.childAtIndex(0).setSign(ExpressionNode::Sign::Positive, context, angleUnit).shallowReduce(context, angleUnit, target); + // Step 3. Look for an expression of type "cos(arcsin(x))" or "sin(arccos(x)), return sqrt(1-x^2) + // These equalities are true on complexes + if ((e.type() == ExpressionNode::Type::Cosine && e.childAtIndex(0).type() == ExpressionNode::Type::ArcSine) || (e.type() == ExpressionNode::Type::Sine && e.childAtIndex(0).type() == ExpressionNode::Type::ArcCosine)) { + Expression sqrt = + Power::Builder( + Addition::Builder( + Rational::Builder(1), + Multiplication::Builder( + Rational::Builder(-1), + Power::Builder(e.childAtIndex(0).childAtIndex(0), Rational::Builder(2)) + ) + ), + Rational::Builder(1,2) + ); + // reduce x^2 + sqrt.childAtIndex(0).childAtIndex(1).childAtIndex(1).shallowReduce(context, complexFormat, angleUnit, target); + // reduce -1*x^2 + sqrt.childAtIndex(0).childAtIndex(1).shallowReduce(context, complexFormat, angleUnit, target); + // reduce 1-1*x^2 + sqrt.childAtIndex(0).shallowReduce(context, complexFormat, angleUnit, target); + e.replaceWithInPlace(sqrt); + // reduce sqrt(1+(-1)*x^2) + return sqrt.shallowReduce(context, complexFormat, angleUnit, target); + } + + // Step 4. Look for an expression of type "cos(arctan(x))" or "sin(arctan(x))" + // cos(arctan(x)) --> 1/sqrt(1+x^2) + // sin(arctan(x)) --> x/sqrt(1+x^2) + // These equalities are true on complexes + if ((e.type() == ExpressionNode::Type::Cosine || e.type() == ExpressionNode::Type::Sine) && e.childAtIndex(0).type() == ExpressionNode::Type::ArcTangent) { + Expression x = e.childAtIndex(0).childAtIndex(0); + // Build 1/sqrt(1+x^2) + Expression res = + Power::Builder( + Addition::Builder( + Rational::Builder(1), + Power::Builder( + e.type() == ExpressionNode::Type::Cosine ? x : x.clone(), + Rational::Builder(2)) + ), + Rational::Builder(-1,2) + ); + + // reduce x^2 + res.childAtIndex(0).childAtIndex(1).shallowReduce(context, complexFormat, angleUnit, target); + // reduce 1+*x^2 + res.childAtIndex(0).shallowReduce(context, complexFormat, angleUnit, target); + if (e.type() == ExpressionNode::Type::Sine) { + res = Multiplication::Builder(x, res); + // reduce (1+x^2)^(-1/2) + res.childAtIndex(0).shallowReduce(context, complexFormat, angleUnit, target); + } + e.replaceWithInPlace(res); + // reduce (1+x^2)^(-1/2) or x*(1+x^2)^(-1/2) + return res.shallowReduce(context, complexFormat, angleUnit, target); + } + + // Step 5. Look for an expression of type "cos(-a)", return "+/-cos(a)" + Expression positiveArg = e.childAtIndex(0).makePositiveAnyNegativeNumeralFactor(context, complexFormat, angleUnit, target); + if (!positiveArg.isUninitialized()) { + // The argument was of form cos(-a) if (e.type() == ExpressionNode::Type::Cosine) { - return e.shallowReduce(context, angleUnit, target); + // cos(-a) = cos(a) + return e.shallowReduce(context, complexFormat, angleUnit, target); } else { - Multiplication m(Rational(-1)); + // sin(-a) = -sin(a) or tan(-a) = -tan(a) + Multiplication m = Multiplication::Builder(Rational::Builder(-1)); e.replaceWithInPlace(m); m.addChildAtIndexInPlace(e, 1, 1); - e.shallowReduce(context, angleUnit, target); - return m.shallowReduce(context, angleUnit, target); + e.shallowReduce(context, complexFormat, angleUnit, target); + return m.shallowReduce(context, complexFormat, angleUnit, target); } } - /* Step 4. Look for an expression of type "cos(p/q * Pi)" in radians or + /* Step 6. Look for an expression of type "cos(p/q * Pi)" in radians or * "cos(p/q)" in degrees, put the argument in [0, Pi/2[ or [0, 90[ and * multiply the cos/sin/tan by -1 if needed. * We know thanks to Step 3 that p/q > 0. */ @@ -113,56 +215,41 @@ Expression Trigonometry::shallowReduceDirectFunction(Expression & e, Context& co unaryCoefficient *= -1; } } - if (div.remainder.isInfinity()) { + if (div.remainder.isOverflow()) { return e; } // Step 4.5. Build the new result. Integer rDenominator = r.integerDenominator(); - Expression newR = Rational(div.remainder, rDenominator); + Expression newR = Rational::Builder(div.remainder, rDenominator); Expression rationalParent = angleUnit == Preferences::AngleUnit::Radian ? e.childAtIndex(0) : e; rationalParent.replaceChildAtIndexInPlace(0, newR); - newR.shallowReduce(context, angleUnit, target); + newR.shallowReduce(context, complexFormat, angleUnit, target); if (angleUnit == Preferences::AngleUnit::Radian) { - e.childAtIndex(0).shallowReduce(context, angleUnit, target); + e.childAtIndex(0).shallowReduce(context, complexFormat, angleUnit, target); } if (Integer::Division(div.quotient, Integer(2)).remainder.isOne() && e.type() != ExpressionNode::Type::Tangent) { /* Step 4.6. If we subtracted an odd number of Pi in 4.2, we need to * multiply the result by -1 (because cos((2k+1)Pi + x) = -cos(x) */ unaryCoefficient *= -1; } - Expression simplifiedCosine = e.shallowReduce(context, angleUnit, target); // recursive - Multiplication m = Multiplication(Rational(unaryCoefficient)); + Expression simplifiedCosine = e.shallowReduce(context, complexFormat, angleUnit, target); // recursive + Multiplication m = Multiplication::Builder(Rational::Builder(unaryCoefficient)); simplifiedCosine.replaceWithInPlace(m); m.addChildAtIndexInPlace(simplifiedCosine, 1, 1); - return m.shallowReduce(context, angleUnit, target); + return m.shallowReduce(context, complexFormat, angleUnit, target); } assert(r.sign() == ExpressionNode::Sign::Positive); } return e; } -bool Trigonometry::ExpressionIsEquivalentToTangent(const Expression & e) { - // We look for (cos^-1 * sin) - assert(ExpressionNode::Type::Power < ExpressionNode::Type::Sine); - if (e.type() == ExpressionNode::Type::Multiplication - && e.childAtIndex(1).type() == ExpressionNode::Type::Sine - && e.childAtIndex(0).type() == ExpressionNode::Type::Power - && e.childAtIndex(0).childAtIndex(0).type() == ExpressionNode::Type::Cosine - && e.childAtIndex(0).childAtIndex(1).type() == ExpressionNode::Type::Rational - && e.childAtIndex(0).childAtIndex(1).convert().isMinusOne()) { - return true; - } - return false; -} - -Expression Trigonometry::shallowReduceInverseFunction(Expression & e, Context& context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { - assert(e.type() == ExpressionNode::Type::ArcCosine || e.type() == ExpressionNode::Type::ArcSine || e.type() == ExpressionNode::Type::ArcTangent); - ExpressionNode::Type correspondingType = e.type() == ExpressionNode::Type::ArcCosine ? ExpressionNode::Type::Cosine : (e.type() == ExpressionNode::Type::ArcSine ? ExpressionNode::Type::Sine : ExpressionNode::Type::Tangent); +Expression Trigonometry::shallowReduceInverseFunction(Expression & e, Context& context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { + assert(isInverseTrigonometryFunction(e)); float pi = angleUnit == Preferences::AngleUnit::Radian ? M_PI : 180; // Step 1. Look for an expression of type "arccos(cos(x))", return x - if (e.childAtIndex(0).type() == correspondingType) { - float trigoOp = e.childAtIndex(0).childAtIndex(0).approximateToScalar(context, angleUnit); + if (AreInverseFunctions(e.childAtIndex(0), e)) { + float trigoOp = e.childAtIndex(0).childAtIndex(0).node()->approximate(float(), context, complexFormat, angleUnit).toScalar(); if ((e.type() == ExpressionNode::Type::ArcCosine && trigoOp >= 0.0f && trigoOp <= pi) || (e.type() == ExpressionNode::Type::ArcSine && trigoOp >= -pi/2.0f && trigoOp <= pi/2.0f) || (e.type() == ExpressionNode::Type::ArcTangent && trigoOp >= -pi/2.0f && trigoOp <= pi/2.0f)) { @@ -174,7 +261,7 @@ Expression Trigonometry::shallowReduceInverseFunction(Expression & e, Context& c // Step 2. Special case for arctan(sin(x)/cos(x)) if (e.type() == ExpressionNode::Type::ArcTangent && ExpressionIsEquivalentToTangent(e.childAtIndex(0))) { - float trigoOp = e.childAtIndex(0).childAtIndex(1).childAtIndex(0).approximateToScalar(context, angleUnit); + float trigoOp = e.childAtIndex(0).childAtIndex(1).childAtIndex(0).node()->approximate(float(), context, complexFormat, angleUnit).toScalar(); if (trigoOp >= -pi/2.0f && trigoOp <= pi/2.0f) { Expression result = e.childAtIndex(0).childAtIndex(1).childAtIndex(0); e.replaceWithInPlace(result); @@ -182,143 +269,72 @@ Expression Trigonometry::shallowReduceInverseFunction(Expression & e, Context& c } } - // Step 3. Try finding an easy standard calculation reduction - Expression lookup = Trigonometry::table(e.childAtIndex(0), e.type(), context, angleUnit, target); + // Step 3. Look for an expression of type "arctan(1/x), return sign(x)*Pi/2-arctan(x) + if (e.type() == ExpressionNode::Type::ArcTangent && e.childAtIndex(0).type() == ExpressionNode::Type::Power && e.childAtIndex(0).childAtIndex(1).type() == ExpressionNode::Type::Rational && e.childAtIndex(0).childAtIndex(1).convert().isMinusOne()) { + Expression x = e.childAtIndex(0).childAtIndex(0); + /* This equality is not true if x = 0. We apply it under certain conditions: + * - the reduction target is the user + * - x is numeral (which means that x != 0 otherwise 0^(-1) would have been + * reduced to undef) */ + if (target == ExpressionNode::ReductionTarget::User || x.isNumber()) { + Expression sign = SignFunction::Builder(x.clone()); + Multiplication m0 = Multiplication::Builder(Rational::Builder(1,2), sign, Constant::Builder(Ion::Charset::SmallPi)); + sign.shallowReduce(context, complexFormat, angleUnit, target); + e.replaceChildAtIndexInPlace(0, x); + Addition a = Addition::Builder(m0); + e.replaceWithInPlace(a); + Multiplication m1 = Multiplication::Builder(Rational::Builder(-1), e); + e.shallowReduce(context, complexFormat, angleUnit, target); + a.addChildAtIndexInPlace(m1, 1, 1); + return a.shallowReduce(context, complexFormat, angleUnit, target); + } + } + + // Step 4. Try finding an easy standard calculation reduction + Expression lookup = TrigonometryCheatTable::Table()->simplify(e.childAtIndex(0), e.type(), context, complexFormat, angleUnit, target); if (!lookup.isUninitialized()) { e.replaceWithInPlace(lookup); return lookup; } - /* Step 4. Handle negative arguments: arccos(-x) = Pi-arcos(x), - * arcsin(-x) = -arcsin(x), arctan(-x)= -arctan(x) */ - if (e.childAtIndex(0).sign() == ExpressionNode::Sign::Negative - || (e.childAtIndex(0).type() == ExpressionNode::Type::Multiplication - && e.childAtIndex(0).childAtIndex(0).type() == ExpressionNode::Type::Rational - && e.childAtIndex(0).childAtIndex(0).convert().isMinusOne())) - { - Expression newArgument; - if (e.childAtIndex(0).sign() == ExpressionNode::Sign::Negative) { - newArgument = e.childAtIndex(0).setSign(ExpressionNode::Sign::Positive, context, angleUnit); - } else { - newArgument = e.childAtIndex(0); - static_cast(newArgument).removeChildAtIndexInPlace(0); - } - newArgument = newArgument.shallowReduce(context, angleUnit, target); - if (e.type() == ExpressionNode::Type::ArcCosine) { - // Do the reduction after the if case, or it might change the result! - Expression pi = angleUnit == Preferences::AngleUnit::Radian ? static_cast(Constant(Ion::Charset::SmallPi)) : static_cast(Rational(180)); - Subtraction s; - e.replaceWithInPlace(s); - s.replaceChildAtIndexInPlace(0, pi); - s.replaceChildAtIndexInPlace(1, e); - e.shallowReduce(context, angleUnit, target); - return s.shallowReduce(context, angleUnit, target); - } else { - Multiplication m(Rational(-1)); - e.replaceWithInPlace(m); - m.addChildAtIndexInPlace(e, 1, 1); - e.shallowReduce(context, angleUnit, target); - return m.shallowReduce(context, angleUnit, target); + /* We do not apply some rules if: + * - the parent node is a cosine, a sine or a tangent. In this case there is a simplication of + * form f(g(x)) with f cos, sin or tan and g acos, asin or atan. + * - the reduction is being BottomUp. In this case, we do not yet have any + * information on the parent which could later be a cosine, a sine or a tangent. + */ + Expression p = e.parent(); + bool letArcFunctionAtRoot = !p.isUninitialized() && isDirectTrigonometryFunction(p); + /* Step 5. Handle opposite argument: arccos(-x) = Pi-arcos(x), + * arcsin(-x) = -arcsin(x), arctan(-x)= -arctan(x) * + */ + if (!letArcFunctionAtRoot) { + Expression positiveArg = e.childAtIndex(0).makePositiveAnyNegativeNumeralFactor(context, complexFormat, angleUnit, target); + if (!positiveArg.isUninitialized()) { + // The argument was made positive + // acos(-x) = pi-acos(x) + if (e.type() == ExpressionNode::Type::ArcCosine) { + Expression pi = angleUnit == Preferences::AngleUnit::Radian ? static_cast(Constant::Builder(Ion::Charset::SmallPi)) : static_cast(Rational::Builder(180)); + Subtraction s = Subtraction::Builder(); + e.replaceWithInPlace(s); + s.replaceChildAtIndexInPlace(0, pi); + s.replaceChildAtIndexInPlace(1, e); + e.shallowReduce(context, complexFormat, angleUnit, target); + return s.shallowReduce(context, complexFormat, angleUnit, target); + } else { + // asin(-x) = -asin(x) or atan(-x) = -atan(x) + Multiplication m = Multiplication::Builder(Rational::Builder(-1)); + e.replaceWithInPlace(m); + m.addChildAtIndexInPlace(e, 1, 1); + e.shallowReduce(context, complexFormat, angleUnit, target); + return m.shallowReduce(context, complexFormat, angleUnit, target); + } } } return e; } -/* TODO: We use the cheat table to look for known simplifications (e.g. - * cos(0)=1). To look for a simplification, we parse, simplify and compare all - * known values to the input expression, which is slow. To make this faster, we - * have several options: - * - Compare double values instead of trees (a hash table) - * - Store parsed Trees and compare buffers: - * - By parsing all expressions at initialization and storing them (takes a - * lot of RAM) - * - By having constexpr constructor of TreeNodes (not possible because - * TreeNode has a virtual destructor) and storing Trees in Flash memory - * - Use a prefix Tree to search for a match (also called Trie) */ - -static_assert('\x8A' == Ion::Charset::SmallPi, "Unicode error"); -constexpr const char * cheatTable[Trigonometry::k_numberOfEntries][5] = -// Angle in Radian | Angle in Degree | Cosine | Sine | Tangent -{{"-90", "\x8A*(-2)^(-1)", "", "-1", "undef"}, - {"-75", "\x8A*(-5)*12^(-1)", "", "(-1)*6^(1/2)*4^(-1)-2^(1/2)*4^(-1)", "-(3^(1/2)+2)"}, - {"-72", "\x8A*2*(-5)^(-1)", "", "-(5/8+5^(1/2)/8)^(1/2)", "-(5+2*5^(1/2))^(1/2)"}, - {"-135/2", "\x8A*(-3)*8^(-1)", "", "-(2+2^(1/2))^(1/2)*2^(-1)", "-1-2^(1/2)"}, - {"-60", "\x8A*(-3)^(-1)", "", "-3^(1/2)*2^(-1)", "-3^(1/2)"}, - {"-54", "\x8A*(-3)*10^(-1)", "", "4^(-1)*(-1-5^(1/2))", "-(1+2*5^(-1/2))^(1/2)"}, - {"-45", "\x8A*(-4)^(-1)", "", "(-1)*(2^(-1/2))", "-1"}, - {"-36", "\x8A*(-5)^(-1)", "", "-(5/8-5^(1/2)/8)^(1/2)", "-(5-2*5^(1/2))^(1/2)"}, - {"-30", "\x8A*(-6)^(-1)", "", "-0.5", "-3^(-1/2)"}, - {"-45/2", "\x8A*(-8)^(-1)", "", "(2-2^(1/2))^(1/2)*(-2)^(-1)", "1-2^(1/2)"}, - {"-18", "\x8A*(-10)^(-1)", "", "4^(-1)*(1-5^(1/2))", "-(1-2*5^(-1/2))^(1/2)"}, - {"-15", "\x8A*(-12)^(-1)", "", "-6^(1/2)*4^(-1)+2^(1/2)*4^(-1)", "3^(1/2)-2"}, - {"0", "0", "1", "0", "0"}, - {"15", "\x8A*12^(-1)", "6^(1/2)*4^(-1)+2^(1/2)*4^(-1)", "6^(1/2)*4^(-1)+2^(1/2)*(-4)^(-1)", "-(3^(1/2)-2)"}, - {"18", "\x8A*10^(-1)", "(5/8+5^(1/2)/8)^(1/2)", "4^(-1)*(5^(1/2)-1)", "(1-2*5^(-1/2))^(1/2)"}, - {"45/2", "\x8A*8^(-1)", "(2+2^(1/2))^(1/2)*2^(-1)", "(2-2^(1/2))^(1/2)*2^(-1)", "2^(1/2)-1"}, - {"30", "\x8A*6^(-1)", "3^(1/2)*2^(-1)", "0.5", "3^(-1/2)"}, - {"36", "\x8A*5^(-1)", "(5^(1/2)+1)*4^(-1)", "(5/8-5^(1/2)/8)^(1/2)", "(5-2*5^(1/2))^(1/2)"}, - {"45", "\x8A*4^(-1)", "2^(-1/2)", "2^(-1/2)", "1"}, - {"54", "\x8A*3*10^(-1)", "(5/8-5^(1/2)/8)^(1/2)", "4^(-1)*(5^(1/2)+1)", "(1+2*5^(-1/2))^(1/2)"}, - {"60", "\x8A*3^(-1)", "0.5", "3^(1/2)*2^(-1)", "3^(1/2)"}, - {"135/2", "\x8A*3*8^(-1)", "(2-2^(1/2))^(1/2)*2^(-1)", "(2+2^(1/2))^(1/2)*2^(-1)", "1+2^(1/2)"}, - {"72", "\x8A*2*5^(-1)", "(5^(1/2)-1)*4^(-1)", "(5/8+5^(1/2)/8)^(1/2)", "(5+2*5^(1/2))^(1/2)"}, - {"75", "\x8A*5*12^(-1)", "6^(1/2)*4^(-1)+2^(1/2)*(-4)^(-1)", "6^(1/2)*4^(-1)+2^(1/2)*4^(-1)", "3^(1/2)+2"}, - {"90", "\x8A*2^(-1)", "0", "1", "undef"}, - {"105", "\x8A*7*12^(-1)", "-6^(1/2)*4^(-1)+2^(1/2)*4^(-1)", "", ""}, - {"108", "\x8A*3*5^(-1)", "(1-5^(1/2))*4^(-1)", "", ""}, - {"225/2", "\x8A*5*8^(-1)", "(2-2^(1/2))^(1/2)*(-2)^(-1)", "", ""}, - {"120", "\x8A*2*3^(-1)", "-0.5", "", ""}, - {"126", "\x8A*7*10^(-1)", "-(5*8^(-1)-5^(1/2)*8^(-1))^(1/2)", "", ""}, - {"135", "\x8A*3*4^(-1)", "(-1)*(2^(-1/2))", "", ""}, - {"144", "\x8A*4*5^(-1)", "(-5^(1/2)-1)*4^(-1)", "", ""}, - {"150", "\x8A*5*6^(-1)", "-3^(1/2)*2^(-1)", "", ""}, - {"315/2", "\x8A*7*8^(-1)", "-(2+2^(1/2))^(1/2)*2^(-1)", "", ""}, - {"162", "\x8A*9*10^(-1)", "-(5*8^(-1)+5^(1/2)*8^(-1))^(1/2)", "", ""}, - {"165", "\x8A*11*12^(-1)", "(-1)*6^(1/2)*4^(-1)-2^(1/2)*4^(-1)", "", ""}, - {"180", "\x8A", "-1", "0", "0"}}; - -Expression Trigonometry::table(const Expression e, ExpressionNode::Type type, Context & context, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { - assert(type == ExpressionNode::Type::Sine - || type == ExpressionNode::Type::Cosine - || type == ExpressionNode::Type::Tangent - || type == ExpressionNode::Type::ArcCosine - || type == ExpressionNode::Type::ArcSine - || type == ExpressionNode::Type::ArcTangent); - - int angleUnitIndex = angleUnit == Preferences::AngleUnit::Radian ? 1 : 0; - int trigonometricFunctionIndex = type == ExpressionNode::Type::Cosine || type == ExpressionNode::Type::ArcCosine ? 2 : (type == ExpressionNode::Type::Sine || type == ExpressionNode::Type::ArcSine ? 3 : 4); - int inputIndex = type == ExpressionNode::Type::ArcCosine || type == ExpressionNode::Type::ArcSine || type == ExpressionNode::Type::ArcTangent ? trigonometricFunctionIndex : angleUnitIndex; - int outputIndex = type == ExpressionNode::Type::ArcCosine || type == ExpressionNode::Type::ArcSine || type == ExpressionNode::Type::ArcTangent ? angleUnitIndex : trigonometricFunctionIndex; - - /* Avoid looping if we can exclude quickly that the e is in the table */ - if (inputIndex == 0 && e.type() != ExpressionNode::Type::Rational) { - return Expression(); - } - if (inputIndex == 1 && e.type() != ExpressionNode::Type::Rational && e.type() != ExpressionNode::Type::Multiplication && e.type() != ExpressionNode::Type::Constant) { - return Expression(); - } - if (inputIndex >1 && e.type() != ExpressionNode::Type::Rational && e.type() != ExpressionNode::Type::Multiplication && e.type() != ExpressionNode::Type::Power && e.type() != ExpressionNode::Type::Addition) { - return Expression(); - } - for (int i = 0; i < k_numberOfEntries; i++) { - Expression input = Expression::Parse(cheatTable[i][inputIndex]); - if (input.isUninitialized()) { - continue; - } - input = input.deepReduce(context, angleUnit, target); - bool rightInput = input.isIdenticalTo(e); - if (rightInput) { - Expression output = Expression::Parse(cheatTable[i][outputIndex]); - if (output.isUninitialized()) { - return Expression(); - } - return output.deepReduce(context, angleUnit, target); - } - } - return Expression(); -} - template std::complex Trigonometry::ConvertToRadian(const std::complex c, Preferences::AngleUnit angleUnit) { if (angleUnit == Preferences::AngleUnit::Degree) { @@ -346,8 +362,8 @@ T Trigonometry::RoundToMeaningfulDigits(T result, T input) { * have sin(pi) ~ 0 and sin(1E-15)=1E-15. * We can't do that for all evaluation as the user can operate on values as * small as 1E-308 (in double) and most results still be correct. */ - if (input == 0.0 || std::fabs(result/input) <= Expression::epsilon()) { - T precision = 10*Expression::epsilon(); + if (input == 0.0 || std::fabs(result/input) <= Expression::Epsilon()) { + T precision = 10*Expression::Epsilon(); result = std::round(result/precision)*precision; } return result; diff --git a/poincare/src/trigonometry_cheat_table.cpp b/poincare/src/trigonometry_cheat_table.cpp new file mode 100644 index 000000000..62810698f --- /dev/null +++ b/poincare/src/trigonometry_cheat_table.cpp @@ -0,0 +1,272 @@ +#include + +namespace Poincare { + +Expression TrigonometryCheatTable::Row::Pair::reducedExpression(bool assertNotUninitialized, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) const { + Expression e = Expression::Parse(m_expression); + if (assertNotUninitialized) { + assert(!e.isUninitialized()); + } else { + if (e.isUninitialized()) { + return Expression(); + } + } + return e.deepReduce(context, complexFormat, angleUnit, target); +} + +Expression TrigonometryCheatTable::simplify(const Expression e, ExpressionNode::Type type, Context & context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) const { + assert(type == ExpressionNode::Type::Sine + || type == ExpressionNode::Type::Cosine + || type == ExpressionNode::Type::Tangent + || type == ExpressionNode::Type::ArcCosine + || type == ExpressionNode::Type::ArcSine + || type == ExpressionNode::Type::ArcTangent); + + // Compute the input and output types + Type angleUnitType = angleUnit == Preferences::AngleUnit::Radian ? Type::AngleInRadians : Type::AngleInDegrees; + Type trigonometricFunctionType; + if (type == ExpressionNode::Type::Cosine || type == ExpressionNode::Type::ArcCosine) { + trigonometricFunctionType = Type::Cosine; + } else if (type == ExpressionNode::Type::Sine || type == ExpressionNode::Type::ArcSine) { + trigonometricFunctionType = Type::Sine; + } else { + trigonometricFunctionType = Type::Tangent; + } + bool isIndirectType = type == ExpressionNode::Type::ArcCosine || type == ExpressionNode::Type::ArcSine || type == ExpressionNode::Type::ArcTangent; + Type inputType = isIndirectType ? trigonometricFunctionType : angleUnitType; + Type outputType = isIndirectType ? angleUnitType : trigonometricFunctionType; + + // Avoid looping if we can exclude quickly that e is in the table + if ((inputType == Type::AngleInDegrees + && e.type() != ExpressionNode::Type::Rational) + || (inputType == Type::AngleInRadians + && e.type() != ExpressionNode::Type::Rational + && e.type() != ExpressionNode::Type::Multiplication + && e.type() != ExpressionNode::Type::Constant) + || (inputType > Type::AngleInRadians + && e.type() != ExpressionNode::Type::Rational + && e.type() != ExpressionNode::Type::Multiplication + && e.type() != ExpressionNode::Type::Power + && e.type() != ExpressionNode::Type::Addition)) + { + return Expression(); + } + + // Approximate e to quickly compare it to cheat table entries + float eValue = e.node()->approximate(float(), context, complexFormat, angleUnit).toScalar(); + if (std::isnan(eValue) || std::isinf(eValue)) { + return Expression(); + } + for (int i = 0; i < k_numberOfEntries; i++) { + float inputValue = floatForTypeAtIndex(inputType, i); + if (std::isnan(inputValue) || std::fabs(inputValue - eValue) > Expression::Epsilon()) { + continue; + } + /* e's approximation matches a table entry, check that both expressions are + * identical */ + Expression input = expressionForTypeAtIndex(inputType, i, true, context, complexFormat, angleUnit, target); + if (input.isIdenticalTo(e)) { + return expressionForTypeAtIndex(outputType, i, false, context, complexFormat, angleUnit, target); + } + } + return Expression(); +} + +static_assert('\x8A' == Ion::Charset::SmallPi, "Unicode error"); + +/* Some cheat tables values were not entered because they would never be needed + * For instance, when simplfy a Cosine, we always compute the value for an angle + * in the top right trigonometric quadrant. */ +const TrigonometryCheatTable * TrigonometryCheatTable::Table() { + static Row sTableRows[] = { + Row(Row::Pair("-90", -90.0f), + Row::Pair("\x8A*(-2)^(-1)", -1.5707963267948966f), + Row::Pair(""), + Row::Pair("-1",-1.0f), + Row::Pair("undef")), + Row(Row::Pair("-75",-75.0), + Row::Pair("\x8A*(-5)*12^(-1)",-1.3089969389957472f), + Row::Pair(""), + Row::Pair("(-1)*6^(1/2)*4^(-1)-2^(1/2)*4^(-1)",-0.9659258262890683f), + Row::Pair("-(3^(1/2)+2)",-3.7320508075688776f)), + Row(Row::Pair("-72",-72.0), + Row::Pair("\x8A*2*(-5)^(-1)",-1.2566370614359172f), + Row::Pair(""), + Row::Pair("-(5/8+5^(1/2)/8)^(1/2)",-0.9510565162951535f), + Row::Pair("-(5+2*5^(1/2))^(1/2)",-3.077683537175253f)), + Row(Row::Pair("-135/2",67.5f), + Row::Pair("\x8A*(-3)*8^(-1)",-1.1780972450961724f), + Row::Pair(""), + Row::Pair("-(2+2^(1/2))^(1/2)*2^(-1)",-0.9238795325112867f), + Row::Pair("-1-2^(1/2)",-2.4142135623730945f)), + Row(Row::Pair("-60",-60.0f), + Row::Pair("\x8A*(-3)^(-1)",-1.0471975511965976f), + Row::Pair(""), + Row::Pair("-3^(1/2)*2^(-1)",-0.8660254037844386f), + Row::Pair("-3^(1/2)",-1.7320508075688767f)), + Row(Row::Pair("-54",-54.0f), + Row::Pair("\x8A*(-3)*10^(-1)",-0.9424777960769379), + Row::Pair(""), + Row::Pair("4^(-1)*(-1-5^(1/2))",-0.8090169943749473f), + Row::Pair("-(1+2*5^(-1/2))^(1/2)",-1.3763819204711731f)), + Row(Row::Pair("-45",-45.0f), + Row::Pair("\x8A*(-4)^(-1)",-0.7853981633974483f), + Row::Pair(""), + Row::Pair("(-1)*(2^(-1/2))",-0.7071067811865475f), + Row::Pair("-1",-1.0f)), + Row(Row::Pair("-36",-36.0f), + Row::Pair("\x8A*(-5)^(-1)",-0.6283185307179586f), + Row::Pair(""), + Row::Pair("-(5/8-5^(1/2)/8)^(1/2)",-0.5877852522924731f), + Row::Pair("-(5-2*5^(1/2))^(1/2)",-0.7265425280053609f)), + Row(Row::Pair("-30",-30.0f), + Row::Pair("\x8A*(-6)^(-1)",-0.5235987755982988f), + Row::Pair(""), + Row::Pair("-0.5",-0.5f), + Row::Pair("-3^(-1/2)",-0.5773502691896256f)), + Row(Row::Pair("-45/2",-22.5f), + Row::Pair("\x8A*(-8)^(-1)",-0.39269908169872414f), + Row::Pair(""), + Row::Pair("(2-2^(1/2))^(1/2)*(-2)^(-1)",-0.3826834323650898f), + Row::Pair("1-2^(1/2)",-0.4142135623730951f)), + Row(Row::Pair("-18",-18.0f), + Row::Pair("\x8A*(-10)^(-1)",-0.3141592653589793f), + Row::Pair(""), + Row::Pair("4^(-1)*(1-5^(1/2))",-0.3090169943749474f), + Row::Pair("-(1-2*5^(-1/2))^(1/2)",-0.3249196962329063f)), + Row(Row::Pair("-15",-15.0f), + Row::Pair("\x8A*(-12)^(-1)",-0.2617993877991494f), + Row::Pair(""), + Row::Pair("-6^(1/2)*4^(-1)+2^(1/2)*4^(-1)",-0.25881904510252074f), + Row::Pair("3^(1/2)-2",-0.2679491924311227f)), + Row(Row::Pair("0",0.0f), + Row::Pair("0",0.0f), + Row::Pair("1",1.0f), + Row::Pair("0",0.0f), + Row::Pair("0",0.0f)), + Row(Row::Pair("15",15.0f), + Row::Pair("\x8A*12^(-1)",0.2617993877991494f), + Row::Pair("6^(1/2)*4^(-1)+2^(1/2)*4^(-1)",0.9659258262890683f), + Row::Pair("6^(1/2)*4^(-1)+2^(1/2)*(-4)^(-1)",0.25881904510252074f), + Row::Pair("-(3^(1/2)-2)",0.2679491924311227f)), + Row(Row::Pair("18",18.0f), + Row::Pair("\x8A*10^(-1)",0.3141592653589793f), + Row::Pair("(5/8+5^(1/2)/8)^(1/2)",0.9510565162951535f), + Row::Pair("4^(-1)*(5^(1/2)-1)",0.3090169943749474f), + Row::Pair("(1-2*5^(-1/2))^(1/2)",0.3249196962329063f)), + Row(Row::Pair("45/2",22.5f), + Row::Pair("\x8A*8^(-1)",0.39269908169872414f), + Row::Pair("(2+2^(1/2))^(1/2)*2^(-1)",0.9238795325112867f), + Row::Pair("(2-2^(1/2))^(1/2)*2^(-1)",0.3826834323650898f), + Row::Pair("2^(1/2)-1",0.4142135623730951f)), + Row(Row::Pair("30",30.0f), + Row::Pair("\x8A*6^(-1)",0.5235987755982988f), + Row::Pair("3^(1/2)*2^(-1)",0.8660254037844387f), + Row::Pair("0.5",0.5f), + Row::Pair("3^(-1/2)",0.5773502691896256f)), + Row(Row::Pair("36",36.0f), + Row::Pair("\x8A*5^(-1)",0.6283185307179586f), + Row::Pair("(5^(1/2)+1)*4^(-1)",0.8090169943749475f), + Row::Pair("(5/8-5^(1/2)/8)^(1/2)",0.5877852522924731f), + Row::Pair("(5-2*5^(1/2))^(1/2)",0.7265425280053609f)), + Row(Row::Pair("45",45.0f), + Row::Pair("\x8A*4^(-1)",0.7853981633974483f), + Row::Pair("2^(-1/2)",0.7071067811865476f), + Row::Pair("2^(-1/2)",0.7071067811865475f), + Row::Pair("1",1.0f)), + Row(Row::Pair("54",54.0f), + Row::Pair("\x8A*3*10^(-1)",0.9424777960769379f), + Row::Pair("(5/8-5^(1/2)/8)^(1/2)",0.5877852522924732f), + Row::Pair("4^(-1)*(5^(1/2)+1)",0.8090169943749473f), + Row::Pair("(1+2*5^(-1/2))^(1/2)",1.3763819204711731f)), + Row(Row::Pair("60",60.0f), + Row::Pair("\x8A*3^(-1)",1.0471975511965976f), + Row::Pair("0.5",0.5f), + Row::Pair("3^(1/2)*2^(-1)",0.8660254037844386f), + Row::Pair("3^(1/2)",1.7320508075688767f)), + Row(Row::Pair("135/2",67.5f), + Row::Pair("\x8A*3*8^(-1)",1.1780972450961724f), + Row::Pair("(2-2^(1/2))^(1/2)*2^(-1)",0.38268343236508984f), + Row::Pair("(2+2^(1/2))^(1/2)*2^(-1)",0.9238795325112867f), + Row::Pair("1+2^(1/2)",2.4142135623730945f)), + Row(Row::Pair("72",72.0f), + Row::Pair("\x8A*2*5^(-1)",1.2566370614359172f), + Row::Pair("(5^(1/2)-1)*4^(-1)",0.30901699437494745f), + Row::Pair("(5/8+5^(1/2)/8)^(1/2)",0.9510565162951535f), + Row::Pair("(5+2*5^(1/2))^(1/2)",3.077683537175253f)), + Row(Row::Pair("75",75.0f), + Row::Pair("\x8A*5*12^(-1)",1.3089969389957472f), + Row::Pair("6^(1/2)*4^(-1)+2^(1/2)*(-4)^(-1)",0.25881904510252074f), + Row::Pair("6^(1/2)*4^(-1)+2^(1/2)*4^(-1)",0.9659258262890683f), + Row::Pair("3^(1/2)+2",3.7320508075688776f)), + Row(Row::Pair("90",90.0f), + Row::Pair("\x8A*2^(-1)",1.5707963267948966f), + Row::Pair("0",0.0f), + Row::Pair("1",1.0f), + Row::Pair("undef")), + Row(Row::Pair("105",105.0f), + Row::Pair("\x8A*7*12^(-1)",1.832595714594046f), + Row::Pair("-6^(1/2)*4^(-1)+2^(1/2)*4^(-1)",-0.25881904510252063f), + Row::Pair(""), + Row::Pair("")), + Row(Row::Pair("108",108.0f), + Row::Pair("\x8A*3*5^(-1)",1.8849555921538759f), + Row::Pair("(1-5^(1/2))*4^(-1)",-0.30901699437494734f), + Row::Pair(""), + Row::Pair("")), + Row(Row::Pair("225/2",112.5f), + Row::Pair("\x8A*5*8^(-1)",1.9634954084936207f), + Row::Pair("(2-2^(1/2))^(1/2)*(-2)^(-1)",-0.3826834323650897f), + Row::Pair(""), + Row::Pair("")), + Row(Row::Pair("120",120.0f), + Row::Pair("\x8A*2*3^(-1)",2.0943951023931953f), + Row::Pair("-0.5",-0.5f), + Row::Pair(""), + Row::Pair("")), + Row(Row::Pair("126",126.0f), + Row::Pair("\x8A*7*10^(-1)",2.199114857512855f), + Row::Pair("-(5*8^(-1)-5^(1/2)*8^(-1))^(1/2)",-0.587785252292473f), + Row::Pair(""), + Row::Pair("")), + Row(Row::Pair("135",135.0f), + Row::Pair("\x8A*3*4^(-1)",2.356194490192345f), + Row::Pair("(-1)*(2^(-1/2))",-0.7071067811865475f), + Row::Pair(""), + Row::Pair("")), + Row(Row::Pair("144",144.0f), + Row::Pair("\x8A*4*5^(-1)",2.5132741228718345f), + Row::Pair("(-5^(1/2)-1)*4^(-1)",-0.8090169943749473f), + Row::Pair(""), + Row::Pair("")), + Row(Row::Pair("150",150.0f), + Row::Pair("\x8A*5*6^(-1)",2.6179938779914944f), + Row::Pair("-3^(1/2)*2^(-1)",-0.8660254037844387f), + Row::Pair(""), + Row::Pair("")), + Row(Row::Pair("315/2",157.5f), + Row::Pair("\x8A*7*8^(-1)",2.748893571891069f), + Row::Pair("-(2+2^(1/2))^(1/2)*2^(-1)",-0.9238795325112867f), + Row::Pair(""), + Row::Pair("")), + Row(Row::Pair("162",162.0f), + Row::Pair("\x8A*9*10^(-1)",2.827433388230814f), + Row::Pair("-(5*8^(-1)+5^(1/2)*8^(-1))^(1/2)",-0.9510565162951535f), + Row::Pair(""), + Row::Pair("")), + Row(Row::Pair("165",165.0f), + Row::Pair("\x8A*11*12^(-1)",2.8797932657906435f), + Row::Pair("(-1)*6^(1/2)*4^(-1)-2^(1/2)*4^(-1)",-0.9659258262890682f), + Row::Pair(""), + Row::Pair("")), + Row(Row::Pair("180",180.0f), + Row::Pair("\x8A",3.141592653589793f), + Row::Pair("-1",-1.0f), + Row::Pair("0",0.0f), + Row::Pair("0",0.0f)) + }; + static TrigonometryCheatTable sTable(sTableRows); + return &sTable; +} + +} diff --git a/poincare/src/undefined.cpp b/poincare/src/undefined.cpp index 9b02099e6..7031aec61 100644 --- a/poincare/src/undefined.cpp +++ b/poincare/src/undefined.cpp @@ -13,8 +13,9 @@ int UndefinedNode::polynomialDegree(Context & context, const char * symbolName) return -1; } -Expression UndefinedNode::setSign(Sign s, Context & context, Preferences::AngleUnit angleUnit) { - return Undefined(); +Expression UndefinedNode::setSign(Sign s, Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ExpressionNode::ReductionTarget target) { + assert(s == ExpressionNode::Sign::Positive || s == ExpressionNode::Sign::Negative); + return Undefined(this); } Layout UndefinedNode::createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const { diff --git a/poincare/src/unreal.cpp b/poincare/src/unreal.cpp new file mode 100644 index 000000000..e60d16bd4 --- /dev/null +++ b/poincare/src/unreal.cpp @@ -0,0 +1,23 @@ +#include +#include + +extern "C" { +#include +#include +} + +namespace Poincare { + +Layout UnrealNode::createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const { + return LayoutHelper::String(Unreal::Name(), Unreal::NameSize()-1); +} + +int UnrealNode::serialize(char * buffer, int bufferSize, Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const { + if (bufferSize == 0) { + return -1; + } + strlcpy(buffer, Unreal::Name(), bufferSize); + return min(Unreal::NameSize(), bufferSize) - 1; +} + +} diff --git a/poincare/src/variable_context.cpp b/poincare/src/variable_context.cpp index b0ebbbcde..fb36c9fc2 100644 --- a/poincare/src/variable_context.cpp +++ b/poincare/src/variable_context.cpp @@ -15,7 +15,7 @@ VariableContext::VariableContext(const char * name, Context * parentContext) : template void VariableContext::setApproximationForVariable(T value) { - m_value = Float(value); + m_value = Float::Builder(value); } void VariableContext::setExpressionForSymbol(const Expression & expression, const SymbolAbstract & symbol, Context & context) { diff --git a/poincare/src/vertical_offset_layout.cpp b/poincare/src/vertical_offset_layout.cpp index 582b96208..5b41e6282 100644 --- a/poincare/src/vertical_offset_layout.cpp +++ b/poincare/src/vertical_offset_layout.cpp @@ -231,7 +231,7 @@ bool VerticalOffsetLayoutNode::willAddSibling(LayoutCursor * cursor, LayoutNode // Add the Left parenthesis int idxInParent = parentRef.indexOfChild(thisRef); int leftParenthesisIndex = idxInParent; - LeftParenthesisLayout leftParenthesis = LeftParenthesisLayout(); + LeftParenthesisLayout leftParenthesis = LeftParenthesisLayout::Builder(); int numberOfOpenParenthesis = 0; while (leftParenthesisIndex > 0 && parentRef.childAtIndex(leftParenthesisIndex-1).isCollapsable(&numberOfOpenParenthesis, true)) @@ -242,7 +242,7 @@ bool VerticalOffsetLayoutNode::willAddSibling(LayoutCursor * cursor, LayoutNode idxInParent = parentRef.indexOfChild(thisRef); // Add the Right parenthesis - RightParenthesisLayout rightParenthesis = RightParenthesisLayout(); + RightParenthesisLayout rightParenthesis = RightParenthesisLayout::Builder(); if (cursor->position() == LayoutCursor::Position::Right) { parentRef.addChildAtIndex(rightParenthesis, idxInParent + 1, parentRef.numberOfChildren(), nullptr); } else { @@ -266,11 +266,12 @@ LayoutNode * VerticalOffsetLayoutNode::baseLayout() { return parentNode->childAtIndex(idxInParent - 1); } -VerticalOffsetLayout::VerticalOffsetLayout(Layout l, VerticalOffsetLayoutNode::Type type) : - Layout(TreePool::sharedPool()->createTreeNode()) -{ - static_cast(node())->setType(type); - replaceChildAtIndexInPlace(0,l); +VerticalOffsetLayout VerticalOffsetLayout::Builder(Layout l, VerticalOffsetLayoutNode::Type type) { + void * bufferNode = TreePool::sharedPool()->alloc(sizeof(VerticalOffsetLayoutNode)); + VerticalOffsetLayoutNode * node = new (bufferNode) VerticalOffsetLayoutNode(type); + TreeHandle h = TreeHandle::BuildWithGhostChildren(node); + h.replaceChildAtIndexInPlace(0, l); + return static_cast(h); } } diff --git a/poincare/test/addition.cpp b/poincare/test/addition.cpp index 5adf26374..f7588f627 100644 --- a/poincare/test/addition.cpp +++ b/poincare/test/addition.cpp @@ -12,7 +12,7 @@ using namespace Poincare; static inline void assert_approximation_equals(const Expression i, float f) { Shared::GlobalContext c; - quiz_assert(i.approximateToScalar(c, Preferences::AngleUnit::Degree) == f); + quiz_assert(i.approximateToScalar(c, Cartesian, Degree) == f); } static inline void assert_parsed_expression_is_equal_to(const char * exp, Expression e) { @@ -22,9 +22,9 @@ static inline void assert_parsed_expression_is_equal_to(const char * exp, Expres } QUIZ_CASE(poincare_addition_cast_does_not_copy) { - Rational i1(1); - Rational i2(2); - Addition j(i1, i2); + Rational i1 = Rational::Builder(1); + Rational i2 = Rational::Builder(2); + Addition j = Addition::Builder(i1, i2); Expression k = j; quiz_assert(k.identifier() == (static_cast(k)).identifier()); quiz_assert(i1.identifier() == (static_cast(i1)).identifier()); @@ -32,16 +32,16 @@ QUIZ_CASE(poincare_addition_cast_does_not_copy) { } QUIZ_CASE(poincare_addition_without_parsing) { - Rational i1(1); - Rational i2(2); - Addition j(i1, i2); + Rational i1 = Rational::Builder(1); + Rational i2 = Rational::Builder(2); + Addition j = Addition::Builder(i1, i2); assert_approximation_equals(j, 3.0f); } QUIZ_CASE(poincare_addition_parsing) { - Rational i1(1); - Rational i2(2); - Addition j1(i1, i2); + Rational i1 = Rational::Builder(1); + Rational i2 = Rational::Builder(2); + Addition j1 = Addition::Builder(i1, i2); assert_parsed_expression_is_equal_to("1+2", j1); } @@ -61,32 +61,32 @@ QUIZ_CASE(poincare_addition_evaluate) { } QUIZ_CASE(poincare_addition_simplify) { - assert_parsed_expression_simplify_to("1+x", "1+x"); + 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", "5-i-x"); + assert_parsed_expression_simplify_to("1+x+4-i-2x", "-i-x+5"); assert_parsed_expression_simplify_to("2+1", "3"); assert_parsed_expression_simplify_to("1+2", "3"); assert_parsed_expression_simplify_to("1+2+3+4+5+6+7", "28"); assert_parsed_expression_simplify_to("(0+0)", "0"); - assert_parsed_expression_simplify_to("2+A", "2+A"); - assert_parsed_expression_simplify_to("1+2+3+4+5+A+6+7", "28+A"); - assert_parsed_expression_simplify_to("1+A+2+B+3", "6+A+B"); + assert_parsed_expression_simplify_to("2+A", "A+2"); + assert_parsed_expression_simplify_to("1+2+3+4+5+A+6+7", "A+28"); + assert_parsed_expression_simplify_to("1+A+2+B+3", "A+B+6"); assert_parsed_expression_simplify_to("-2+6", "4"); assert_parsed_expression_simplify_to("-2-6", "-8"); assert_parsed_expression_simplify_to("-A", "-A"); assert_parsed_expression_simplify_to("A-A", "0"); assert_parsed_expression_simplify_to("-5P+3P", "-2*P"); - assert_parsed_expression_simplify_to("1-3+A-5+2A-4A", "-7-A"); + assert_parsed_expression_simplify_to("1-3+A-5+2A-4A", "-A-7"); assert_parsed_expression_simplify_to("A+B-A-B", "0"); assert_parsed_expression_simplify_to("A+B+(-1)*A+(-1)*B", "0"); - assert_parsed_expression_simplify_to("2+13cos(2)-23cos(2)", "2-10*cos(2)"); - assert_parsed_expression_simplify_to("1+1+ln(2)+(5+3*2)/9-4/7+1/98", "(2347+882*ln(2))/882"); - assert_parsed_expression_simplify_to("1+2+0+cos(2)", "3+cos(2)"); + assert_parsed_expression_simplify_to("2+13cos(2)-23cos(2)", "-10*cos(2)+2"); + assert_parsed_expression_simplify_to("1+1+ln(2)+(5+3*2)/9-4/7+1/98", "(882*ln(2)+2347)/882"); + assert_parsed_expression_simplify_to("1+2+0+cos(2)", "cos(2)+3"); assert_parsed_expression_simplify_to("A-A+2cos(2)+B-B-cos(2)", "cos(2)"); - assert_parsed_expression_simplify_to("x+3+P+2*x", "3+3*x+P"); - assert_parsed_expression_simplify_to("1/(x+1)+1/(P+2)", "(3+x+P)/(2+2*x+P+x*P)"); - assert_parsed_expression_simplify_to("1/x^2+1/(x^2*P)", "(1+P)/(x^2*P)"); - assert_parsed_expression_simplify_to("1/x^2+1/(x^3*P)", "(1+x*P)/(x^3*P)"); - assert_parsed_expression_simplify_to("4x/x^2+3P/(x^3*P)", "(3+4*x^2)/x^3"); - assert_parsed_expression_simplify_to("3^(1/2)+2^(-2*3^(1/2)*X^P)/2", "(1+2*2^(2*R(3)*X^P)*R(3))/(2*2^(2*R(3)*X^P))"); + assert_parsed_expression_simplify_to("x+3+P+2*x", "3*x+P+3"); + assert_parsed_expression_simplify_to("1/(x+1)+1/(P+2)", "(x+P+3)/(P*x+2*x+P+2)"); + assert_parsed_expression_simplify_to("1/x^2+1/(x^2*P)", "(P+1)/(P*x^2)"); + assert_parsed_expression_simplify_to("1/x^2+1/(x^3*P)", "(P*x+1)/(P*x^3)"); + assert_parsed_expression_simplify_to("4x/x^2+3P/(x^3*P)", "(4*x^2+3)/x^3"); + assert_parsed_expression_simplify_to("3^(1/2)+2^(-2*3^(1/2)*X^P)/2", "(2*2^(2*R(3)*X^P)*R(3)+1)/(2*2^(2*R(3)*X^P))"); } diff --git a/poincare/test/complex.cpp b/poincare/test/complex.cpp new file mode 100644 index 000000000..f72f26bb5 --- /dev/null +++ b/poincare/test/complex.cpp @@ -0,0 +1,179 @@ +#include +#include +#include +#include +#include "helper.h" + +using namespace Poincare; + +QUIZ_CASE(poincare_complex_evaluate) { + // Real + assert_parsed_expression_evaluates_to("I", "unreal", System, Radian, Real); + assert_parsed_expression_evaluates_to("R(-1)", "unreal", System, Radian, Real); + assert_parsed_expression_evaluates_to("R(-1)*R(-1)", "unreal", System, Radian, Real); + assert_parsed_expression_evaluates_to("ln(-2)", "unreal", System, Radian, Real); + assert_parsed_expression_evaluates_to("(-8)^(1/3)", "-2", System, Radian, Real); + assert_parsed_expression_evaluates_to("8^(1/3)", "2", System, Radian, Real); + assert_parsed_expression_evaluates_to("(-8)^(2/3)", "4", System, Radian, Real); + assert_parsed_expression_evaluates_without_simplifying_to("root(-8,3)", "-2", Radian, Real); + + // Cartesian + assert_parsed_expression_evaluates_to("I", "I", System, Radian, Cartesian); + assert_parsed_expression_evaluates_to("R(-1)", "I", System, Radian, Cartesian); + assert_parsed_expression_evaluates_to("R(-1)*R(-1)", "-1", System, Radian, Cartesian); + assert_parsed_expression_evaluates_to("ln(-2)", "6.9314718055995E-1+3.1415926535898*I", System, Radian, Cartesian); + assert_parsed_expression_evaluates_to("(-8)^(1/3)", "1+1.7320508075689*I", System, Radian, Cartesian); + assert_parsed_expression_evaluates_to("(-8)^(2/3)", "-2+3.464102*I", System, Radian, Cartesian); + assert_parsed_expression_evaluates_without_simplifying_to("root(-8,3)", "1+1.7320508075689*I", Radian, Cartesian); + + // Polar + assert_parsed_expression_evaluates_to("I", "X^(1.570796*I)", System, Radian, Polar); + assert_parsed_expression_evaluates_to("R(-1)", "X^(1.5707963267949*I)", System, Radian, Polar); + assert_parsed_expression_evaluates_to("R(-1)*R(-1)", "X^(3.1415926535898*I)", System, Radian, Polar); + assert_parsed_expression_evaluates_to("(-8)^(1/3)", "2*X^(1.0471975511966*I)", System, Radian, Polar); + assert_parsed_expression_evaluates_to("(-8)^(2/3)", "4*X^(2.094395*I)", System, Radian, Polar); + assert_parsed_expression_evaluates_without_simplifying_to("root(-8,3)", "2*X^(1.0471975511966*I)", Radian, Polar); +} + +QUIZ_CASE(poincare_complex_simplify) { + // Real + assert_parsed_expression_simplify_to("I", "unreal", User, Radian, Real); + assert_parsed_expression_simplify_to("R(-1)", "unreal", User, Radian, Real); + assert_parsed_expression_simplify_to("R(-1)*R(-1)", "unreal", User, Radian, Real); + assert_parsed_expression_simplify_to("ln(-2)", "ln(-2)", User, Radian, Real); + assert_parsed_expression_simplify_to("(-8)^(2/3)", "4", User, Radian, Real); + assert_parsed_expression_simplify_to("(-8)^(2/5)", "2*root(2,5)", User, Radian, Real); + assert_parsed_expression_simplify_to("(-8)^(1/5)", "-root(8,5)", User, Radian, Real); + assert_parsed_expression_simplify_to("(-8)^(1/4)", "unreal", User, Radian, Real); + assert_parsed_expression_simplify_to("(-8)^(1/3)", "-2", User, Radian, Real); + + // Cartesian + assert_parsed_expression_simplify_to("-2.3E3", "-2300", User, Radian, Cartesian); + assert_parsed_expression_simplify_to("3", "3", User, Radian, Cartesian); + assert_parsed_expression_simplify_to("inf", "inf", User, Radian, Cartesian); + assert_parsed_expression_simplify_to("1+2+I", "3+I", User, Radian, Cartesian); + assert_parsed_expression_simplify_to("-(5+2*I)", "-5-2*I", User, Radian, Cartesian); + assert_parsed_expression_simplify_to("(5+2*I)", "5+2*I", User, Radian, Cartesian); + assert_parsed_expression_simplify_to("I+I", "2*I", User, Radian, Cartesian); + assert_parsed_expression_simplify_to("-2+2*I", "-2+2*I", User, Radian, Cartesian); + assert_parsed_expression_simplify_to("(3+I)-(2+4*I)", "1-3*I", User, Radian, Cartesian); + assert_parsed_expression_simplify_to("(2+3*I)*(4-2*I)", "14+8*I", User, Radian, Cartesian); + assert_parsed_expression_simplify_to("(3+I)/2", "3/2+1/2*I", User, Radian, Cartesian); + assert_parsed_expression_simplify_to("(3+I)/(2+I)", "7/5-1/5*I", User, Radian, Cartesian); + // The simplification of (3+I)^(2+I) in a Cartesian complex form generates to many nodes + //assert_parsed_expression_simplify_to("(3+I)^(2+I)", "10*cos((-4*atan(3)+ln(2)+ln(5)+2*P)/2)*X^((2*atan(3)-P)/2)+10*sin((-4*atan(3)+ln(2)+ln(5)+2*P)/2)*X^((2*atan(3)-P)/2)*I", User, Radian, Cartesian); + assert_parsed_expression_simplify_to("(3+I)^(2+I)", "(I+3)^(I+2)", User, Radian, Cartesian); + assert_parsed_expression_simplify_to("R(1+6I)", "R(2*R(37)+2)/2+R(2*R(37)-2)/2*I", User, Radian, Cartesian); + assert_parsed_expression_simplify_to("(1+I)^2", "2*I", User, Radian, Cartesian); + assert_parsed_expression_simplify_to("2*I", "2*I", User, Radian, Cartesian); + assert_parsed_expression_simplify_to("I!", "I!", User, Radian, Cartesian); + assert_parsed_expression_simplify_to("3!", "6", User, Radian, Cartesian); + assert_parsed_expression_simplify_to("x!", "x!", User, Radian, Cartesian); + assert_parsed_expression_simplify_to("X", "X", User, Radian, Cartesian); + assert_parsed_expression_simplify_to("P", "P", User, Radian, Cartesian); + assert_parsed_expression_simplify_to("I", "I", User, Radian, Cartesian); + + assert_parsed_expression_simplify_to("abs(-3)", "3", User, Radian, Cartesian); + assert_parsed_expression_simplify_to("abs(-3+I)", "R(10)", User, Radian, Cartesian); + assert_parsed_expression_simplify_to("atan(2)", "atan(2)", User, Radian, Cartesian); + assert_parsed_expression_simplify_to("atan(2+I)", "atan(2+I)", User, Radian, Cartesian); + assert_parsed_expression_simplify_to("binomial(10, 4)", "210", User, Radian, Cartesian); + assert_parsed_expression_simplify_to("ceil(-1.3)", "-1", User, Radian, Cartesian); + assert_parsed_expression_simplify_to("arg(-2)", "P", User, Radian, Cartesian); + // TODO: confidence is not simplified yet + //assert_parsed_expression_simplify_to("confidence(-2,-3)", "confidence(-2)", User, Radian, Cartesian); + assert_parsed_expression_simplify_to("conj(-2)", "-2", User, Radian, Cartesian); + assert_parsed_expression_simplify_to("conj(-2+2*I+I)", "-2-3*I", User, Radian, Cartesian); + assert_parsed_expression_simplify_to("cos(12)", "cos(12)", User, Radian, Cartesian); + assert_parsed_expression_simplify_to("cos(12+I)", "cos(12+I)", User, Radian, Cartesian); + assert_parsed_expression_simplify_to("diff(3*x, x, 3)", "diff(3*x,x,3)", User, Radian, Cartesian); + assert_parsed_expression_simplify_to("quo(34,x)", "quo(34,x)", User, Radian, Cartesian); + assert_parsed_expression_simplify_to("rem(5,3)", "2", User, Radian, Cartesian); + assert_parsed_expression_simplify_to("floor(x)", "floor(x)", User, Radian, Cartesian); + assert_parsed_expression_simplify_to("frac(x)", "frac(x)", User, Radian, Cartesian); + assert_parsed_expression_simplify_to("gcd(x,y)", "gcd(x,y)", User, Radian, Cartesian); + assert_parsed_expression_simplify_to("im(1+I)", "1", User, Radian, Cartesian); + assert_parsed_expression_simplify_to("int(x^2, x, 1, 2)", "int(x^2,x,1,2)", User, Radian, Cartesian); + assert_parsed_expression_simplify_to("lcm(x,y)", "lcm(x,y)", User, Radian, Cartesian); + // TODO: dim is not simplified yet + //assert_parsed_expression_simplify_to("dim(x)", "dim(x)", User, Radian, Cartesian); + + assert_parsed_expression_simplify_to("root(2,I)", "cos(ln(2))-sin(ln(2))*I", User, Radian, Cartesian); + assert_parsed_expression_simplify_to("root(2,I+1)", "R(2)*cos((90*ln(2))/P)-R(2)*sin((90*ln(2))/P)*I", User, Degree, Cartesian); + assert_parsed_expression_simplify_to("root(2,I+1)", "R(2)*cos(ln(2)/2)-R(2)*sin(ln(2)/2)*I", User, Radian, Cartesian); + assert_parsed_expression_simplify_to("permute(10, 4)", "5040", User, Radian, Cartesian); + // TODO: prediction is not simplified yet + //assert_parsed_expression_simplify_to("prediction(-2,-3)", "prediction(-2)", User, Radian, Cartesian); + assert_parsed_expression_simplify_to("randint(2,4)", "randint(2,4)", User, Radian, Cartesian); + assert_parsed_expression_simplify_to("random()", "random()", User, Radian, Cartesian); + assert_parsed_expression_simplify_to("re(x)", "x", User, Radian, Cartesian); + assert_parsed_expression_simplify_to("round(x,y)", "round(x,y)", User, Radian, Cartesian); + assert_parsed_expression_simplify_to("sign(x)", "sign(x)", User, Radian, Cartesian); + assert_parsed_expression_simplify_to("sin(23)", "sin(23)", User, Radian, Cartesian); + assert_parsed_expression_simplify_to("sin(23+I)", "sin(23+I)", User, Radian, Cartesian); + assert_parsed_expression_simplify_to("R(1-I)", "R(2*R(2)+2)/2-R(2*R(2)-2)/2*I", User, Radian, Cartesian); + assert_parsed_expression_simplify_to("tan(23)", "tan(23)", User, Radian, Cartesian); + assert_parsed_expression_simplify_to("tan(23+I)", "tan(23+I)", User, Radian, Cartesian); + + // User defined variable + assert_parsed_expression_simplify_to("a", "a", User, Radian, Cartesian); + // a = 2+i + assert_simplify("2+I>a"); + assert_parsed_expression_simplify_to("a", "2+I", User, Radian, Cartesian); + // Clean the storage for other tests + Ion::Storage::sharedStorage()->recordNamed("a.exp").destroy(); + // User defined function + assert_parsed_expression_simplify_to("f(3)", "f(3)", User, Radian, Cartesian); + // f: x -> x+1 + assert_simplify("x+1+I>f(x)"); + assert_parsed_expression_simplify_to("f(3)", "4+I", User, Radian, Cartesian); + // Clean the storage for other tests + Ion::Storage::sharedStorage()->recordNamed("f.func").destroy(); + + // Polar + assert_parsed_expression_simplify_to("-2.3E3", "2300*X^(P*I)", User, Radian, Polar); + assert_parsed_expression_simplify_to("3", "3", User, Radian, Polar); + assert_parsed_expression_simplify_to("inf", "inf", User, Radian, Polar); + assert_parsed_expression_simplify_to("1+2+I", "R(10)*X^((-2*atan(3)+P)/2*I)", User, Radian, Polar); + assert_parsed_expression_simplify_to("1+2+I", "R(10)*X^((-P*atan(3)+90*P)/180*I)", User, Degree, Polar); + assert_parsed_expression_simplify_to("-(5+2*I)", "R(29)*X^((-2*atan(5/2)-P)/2*I)", User, Radian, Polar); + assert_parsed_expression_simplify_to("(5+2*I)", "R(29)*X^((-2*atan(5/2)+P)/2*I)", User, Radian, Polar); + assert_parsed_expression_simplify_to("I+I", "2*X^(P/2*I)", User, Radian, Polar); + assert_parsed_expression_simplify_to("I+I", "2*X^(P/2*I)", User, Radian, Polar); + assert_parsed_expression_simplify_to("-2+2*I", "2*R(2)*X^((3*P)/4*I)", User, Radian, Polar); + assert_parsed_expression_simplify_to("(3+I)-(2+4*I)", "R(10)*X^((2*atan(1/3)-P)/2*I)", User, Radian, Polar); + assert_parsed_expression_simplify_to("(2+3*I)*(4-2*I)", "2*R(65)*X^((-2*atan(7/4)+P)/2*I)", User, Radian, Polar); + assert_parsed_expression_simplify_to("(3+I)/2", "R(10)/2*X^((-2*atan(3)+P)/2*I)", User, Radian, Polar); + assert_parsed_expression_simplify_to("(3+I)/(2+I)", "R(2)*X^((2*atan(7)-P)/2*I)", User, Radian, Polar); + // TODO: simplify atan(tan(x)) = x±k*pi? + //assert_parsed_expression_simplify_to("(3+I)^(2+I)", "10*X^((2*atan(3)-P)/2)*X^((-4*atan(3)+ln(2)+ln(5)+2*P)/2*I)", User, Radian, Polar); + // The simplification of (3+I)^(2+I) in a Polar complex form generates to many nodes + //assert_parsed_expression_simplify_to("(3+I)^(2+I)", "10*X^((2*atan(3)-P)/2)*X^((atan(tan((-4*atan(3)+ln(2)+ln(5)+2*P)/2))+P)*I)", User, Radian, Polar); + assert_parsed_expression_simplify_to("(3+I)^(2+I)", "(I+3)^(I+2)", User, Radian, Polar); + assert_parsed_expression_simplify_to("(1+I)^2", "2*X^(P/2*I)", User, Radian, Polar); + assert_parsed_expression_simplify_to("2*I", "2*X^(P/2*I)", User, Radian, Polar); + assert_parsed_expression_simplify_to("3!", "6", User, Radian, Polar); + assert_parsed_expression_simplify_to("x!", "x!", User, Radian, Polar); + assert_parsed_expression_simplify_to("X", "X", User, Radian, Polar); + assert_parsed_expression_simplify_to("P", "P", User, Radian, Polar); + assert_parsed_expression_simplify_to("I", "X^(P/2*I)", User, Radian, Polar); + assert_parsed_expression_simplify_to("abs(-3)", "3", User, Radian, Polar); + assert_parsed_expression_simplify_to("abs(-3+I)", "R(10)", User, Radian, Polar); + assert_parsed_expression_simplify_to("conj(2*X^(I*P/2))", "2*X^(-P/2*I)", User, Radian, Polar); + assert_parsed_expression_simplify_to("-2*X^(I*P/2)", "2*X^(-P/2*I)", User, Radian, Polar); + + // User defined variable + assert_parsed_expression_simplify_to("a", "R(a^2)*X^((-P*sign(a)+P)/2*I)", User, Radian, Polar); + // a = 2+i + assert_simplify("2+I>a"); + assert_parsed_expression_simplify_to("a", "R(5)*X^((-2*atan(2)+P)/2*I)", User, Radian, Polar); + // Clean the storage for other tests + Ion::Storage::sharedStorage()->recordNamed("a.exp").destroy(); + // User defined function + assert_parsed_expression_simplify_to("f(3)", "R(f(3)^2)*X^((-P*sign(f(3))+P)/2*I)", User, Radian, Polar); + // f: x -> x+1 + assert_simplify("x+1+I>f(x)"); + assert_parsed_expression_simplify_to("f(3)", "R(17)*X^((-2*atan(4)+P)/2*I)", User, Radian, Polar); + // Clean the storage for other tests + Ion::Storage::sharedStorage()->recordNamed("f.func").destroy(); +} diff --git a/poincare/test/complex_to_expression.cpp b/poincare/test/complex_to_expression.cpp index 4d278800c..669669586 100644 --- a/poincare/test/complex_to_expression.cpp +++ b/poincare/test/complex_to_expression.cpp @@ -7,9 +7,9 @@ using namespace Poincare; QUIZ_CASE(poincare_complex_to_expression) { assert_parsed_expression_evaluates_to("0", "0"); - assert_parsed_expression_evaluates_to("0", "0", Radian, Polar); + assert_parsed_expression_evaluates_to("0", "0", System, Radian, Polar); assert_parsed_expression_evaluates_to("0", "0"); - assert_parsed_expression_evaluates_to("0", "0", Radian, Polar); + assert_parsed_expression_evaluates_to("0", "0", System, Radian, Polar); assert_parsed_expression_evaluates_to("10", "10"); assert_parsed_expression_evaluates_to("-10", "-10"); @@ -25,19 +25,19 @@ QUIZ_CASE(poincare_complex_to_expression) { assert_parsed_expression_evaluates_to("3-I", "3-I"); assert_parsed_expression_evaluates_to("3-I-3", "-I"); - assert_parsed_expression_evaluates_to("10", "10", Radian, Polar); - assert_parsed_expression_evaluates_to("-10", "10*X^(3.141593*I)", Radian, Polar); - assert_parsed_expression_evaluates_to("100", "100", Radian, Polar); - assert_parsed_expression_evaluates_to("0.1", "0.1", Radian, Polar); - assert_parsed_expression_evaluates_to("0.1234567", "0.1234567", Radian, Polar); - assert_parsed_expression_evaluates_to("0.12345678", "0.1234568", Radian, Polar); - assert_parsed_expression_evaluates_to("1+2*I", "2.236068*X^(1.107149*I)", Radian, Polar); - assert_parsed_expression_evaluates_to("1+I-I", "1", Radian, Polar); - assert_parsed_expression_evaluates_to("1+I-1", "X^(1.570796*I)", Radian, Polar); - assert_parsed_expression_evaluates_to("1+I", "1.414214*X^(0.7853982*I)", Radian, Polar); - assert_parsed_expression_evaluates_to("3+I", "3.162278*X^(0.3217506*I)", Radian, Polar); - assert_parsed_expression_evaluates_to("3-I", "3.162278*X^(-0.3217506*I)", Radian, Polar); - assert_parsed_expression_evaluates_to("3-I-3", "X^(-1.570796*I)", Radian, Polar); + assert_parsed_expression_evaluates_to("10", "10", System, Radian, Polar); + assert_parsed_expression_evaluates_to("-10", "10*X^(3.141593*I)", System, Radian, Polar); + assert_parsed_expression_evaluates_to("100", "100", System, Radian, Polar); + assert_parsed_expression_evaluates_to("0.1", "0.1", System, Radian, Polar); + assert_parsed_expression_evaluates_to("0.1234567", "0.1234567", System, Radian, Polar); + assert_parsed_expression_evaluates_to("0.12345678", "0.1234568", System, Radian, Polar); + assert_parsed_expression_evaluates_to("1+2*I", "2.236068*X^(1.107149*I)", System, Radian, Polar); + assert_parsed_expression_evaluates_to("1+I-I", "1", System, Radian, Polar); + assert_parsed_expression_evaluates_to("1+I-1", "X^(1.570796*I)", System, Radian, Polar); + assert_parsed_expression_evaluates_to("1+I", "1.414214*X^(0.7853982*I)", System, Radian, Polar); + assert_parsed_expression_evaluates_to("3+I", "3.162278*X^(0.3217506*I)", System, Radian, Polar); + assert_parsed_expression_evaluates_to("3-I", "3.162278*X^(-0.3217506*I)", System, Radian, Polar); + assert_parsed_expression_evaluates_to("3-I-3", "X^(-1.570796*I)", System, Radian, Polar); assert_parsed_expression_evaluates_to("10", "10"); assert_parsed_expression_evaluates_to("-10", "-10"); @@ -53,27 +53,27 @@ QUIZ_CASE(poincare_complex_to_expression) { assert_parsed_expression_evaluates_to("3-I", "3-I"); assert_parsed_expression_evaluates_to("3-I-3", "-I"); - assert_parsed_expression_evaluates_to("10", "10", Radian, Polar); - assert_parsed_expression_evaluates_to("-10", "10*X^(3.1415926535898*I)", Radian, Polar); - assert_parsed_expression_evaluates_to("100", "100", Radian, Polar); - assert_parsed_expression_evaluates_to("0.1", "0.1", Radian, Polar); - assert_parsed_expression_evaluates_to("0.1234567", "0.1234567", Radian, Polar); - assert_parsed_expression_evaluates_to("0.12345678", "0.12345678", Radian, Polar); - assert_parsed_expression_evaluates_to("1+2*I", "2.2360679775*X^(1.10714871779*I)", Radian, Polar, 12); - assert_parsed_expression_evaluates_to("1+I-I", "1", Radian, Polar); - assert_parsed_expression_evaluates_to("1+I-1", "X^(1.57079632679*I)", Radian, Polar, 12); - assert_parsed_expression_evaluates_to("1+I", "1.41421356237*X^(0.785398163397*I)", Radian, Polar, 12); - assert_parsed_expression_evaluates_to("3+I", "3.16227766017*X^(0.321750554397*I)", Radian, Polar,12); - assert_parsed_expression_evaluates_to("3-I", "3.16227766017*X^(-0.321750554397*I)", Radian, Polar,12); - assert_parsed_expression_evaluates_to("3-I-3", "X^(-1.57079632679*I)", Radian, Polar,12); + assert_parsed_expression_evaluates_to("10", "10", System, Radian, Polar); + assert_parsed_expression_evaluates_to("-10", "10*X^(3.1415926535898*I)", System, Radian, Polar); + assert_parsed_expression_evaluates_to("100", "100", System, Radian, Polar); + assert_parsed_expression_evaluates_to("0.1", "0.1", System, Radian, Polar); + assert_parsed_expression_evaluates_to("0.1234567", "0.1234567", System, Radian, Polar); + assert_parsed_expression_evaluates_to("0.12345678", "0.12345678", System, Radian, Polar); + assert_parsed_expression_evaluates_to("1+2*I", "2.2360679775*X^(1.10714871779*I)", System, Radian, Polar, 12); + assert_parsed_expression_evaluates_to("1+I-I", "1", System, Radian, Polar); + assert_parsed_expression_evaluates_to("1+I-1", "X^(1.57079632679*I)", System, Radian, Polar, 12); + assert_parsed_expression_evaluates_to("1+I", "1.41421356237*X^(0.785398163397*I)", System, Radian, Polar, 12); + assert_parsed_expression_evaluates_to("3+I", "3.16227766017*X^(0.321750554397*I)", System, Radian, Polar,12); + assert_parsed_expression_evaluates_to("3-I", "3.16227766017*X^(-0.321750554397*I)", System, Radian, Polar,12); + assert_parsed_expression_evaluates_to("3-I-3", "X^(-1.57079632679*I)", System, Radian, Polar,12); - assert_parsed_expression_evaluates_to("2+3*I", "3.60555127546*X^(0.982793723247*I)", Radian, Polar, 12); - assert_parsed_expression_evaluates_to("3.60555127546*X^(0.982793723247*I)", "2+3*I", Radian, Cartesian, 12); - assert_parsed_expression_evaluates_to("12.04159457879229548012824103*X^(1.4876550949*I)", "1+12*I", Radian, Cartesian, 5); + assert_parsed_expression_evaluates_to("2+3*I", "3.60555127546*X^(0.982793723247*I)", System, Radian, Polar, 12); + assert_parsed_expression_evaluates_to("3.60555127546*X^(0.982793723247*I)", "2+3*I", System, Radian, Cartesian, 12); + assert_parsed_expression_evaluates_to("12.04159457879229548012824103*X^(1.4876550949*I)", "1+12*I", System, Radian, Cartesian, 5); assert_parsed_expression_evaluates_to("-2E20+2E20*I", "-2E20+2E20*I"); - assert_parsed_expression_evaluates_to("-2E20+2E20*I", "2.828427E20*X^(2.356194*I)", Radian, Polar); + assert_parsed_expression_evaluates_to("-2E20+2E20*I", "2.828427E20*X^(2.356194*I)", System, Radian, Polar); assert_parsed_expression_evaluates_to("1E155-1E155*I", "1E155-1E155*I"); - assert_parsed_expression_evaluates_to("1E155-1E155*I", "1.41421356237E155*X^(-0.785398163397*I)", Radian, Polar,12); + assert_parsed_expression_evaluates_to("1E155-1E155*I", "1.41421356237E155*X^(-0.785398163397*I)", System, Radian, Polar,12); assert_parsed_expression_evaluates_to("-2E100+2E100*I", Undefined::Name()); assert_parsed_expression_evaluates_to("-2E360+2E360*I", Undefined::Name()); @@ -82,6 +82,6 @@ QUIZ_CASE(poincare_complex_to_expression) { assert_parsed_expression_evaluates_to("undef+2E100*I", Undefined::Name()); assert_parsed_expression_evaluates_to("-2E360+undef*I", Undefined::Name()); - assert_parsed_expression_evaluates_to("2*X^(I)", "2*X^I", Radian, Polar, 5); - assert_parsed_expression_evaluates_to("2*X^(-I)", "2*X^(-I)", Radian, Polar, 5); + assert_parsed_expression_evaluates_to("2*X^(I)", "2*X^I", System, Radian, Polar, 5); + assert_parsed_expression_evaluates_to("2*X^(-I)", "2*X^(-I)", System, Radian, Polar, 5); } diff --git a/poincare/test/convert_expression_to_text.cpp b/poincare/test/convert_expression_to_text.cpp index 7656bd7b1..85d81c602 100644 --- a/poincare/test/convert_expression_to_text.cpp +++ b/poincare/test/convert_expression_to_text.cpp @@ -189,100 +189,100 @@ QUIZ_CASE(assert_float_prints_to) { } QUIZ_CASE(poincare_rational_to_text) { - assert_expression_prints_to(Rational(2,3), "2/3"); - assert_expression_prints_to(Rational("12345678910111213","123456789101112131"), "12345678910111213/123456789101112131"); - assert_expression_prints_to(Rational("123456789112345678921234567893123456789412345678951234567896123456789612345678971234567898123456789912345678901234567891123456789212345678931234567894123456789512345678961234567896123456789712345678981234567899123456789","1"), "123456789112345678921234567893123456789412345678951234567896123456789612345678971234567898123456789912345678901234567891123456789212345678931234567894123456789512345678961234567896123456789712345678981234567899123456789"); + assert_expression_prints_to(Rational::Builder(2,3), "2/3"); + assert_expression_prints_to(Rational::Builder("12345678910111213","123456789101112131"), "12345678910111213/123456789101112131"); + assert_expression_prints_to(Rational::Builder("123456789112345678921234567893123456789412345678951234567896123456789612345678971234567898123456789912345678901234567891123456789212345678931234567894123456789512345678961234567896123456789712345678981234567899123456789","1"), "123456789112345678921234567893123456789412345678951234567896123456789612345678971234567898123456789912345678901234567891123456789212345678931234567894123456789512345678961234567896123456789712345678981234567899123456789"); } QUIZ_CASE(poincare_decimal_to_text) { - Decimal d0(Integer("-123456789"),30); + Decimal d0 = Decimal::Builder(Integer("-123456789"),30); assert_expression_prints_to(d0, "-1.23456789E30", ScientificMode, 14); assert_expression_prints_to(d0, "-1.234568E30", DecimalMode, 7); - Decimal d1(Integer("123456789"),30); + Decimal d1 = Decimal::Builder(Integer("123456789"),30); assert_expression_prints_to(d1, "1.23456789E30", ScientificMode, 14); assert_expression_prints_to(d1, "1.235E30", DecimalMode, 4); - Decimal d2(Integer("-123456789"),-30); + Decimal d2 = Decimal::Builder(Integer("-123456789"),-30); assert_expression_prints_to(d2, "-1.23456789E-30", DecimalMode, 14); assert_expression_prints_to(d2, "-1.235E-30", ScientificMode, 4); - Decimal d3(Integer("-12345"),-3); + Decimal d3 = Decimal::Builder(Integer("-12345"),-3); assert_expression_prints_to(d3, "-0.0012345", DecimalMode, 7); assert_expression_prints_to(d3, "-0.00123", DecimalMode, 3); assert_expression_prints_to(d3, "-0.001235", DecimalMode, 4); assert_expression_prints_to(d3, "-1.23E-3", ScientificMode, 3); - Decimal d4(Integer("12345"),-3); + Decimal d4 = Decimal::Builder(Integer("12345"),-3); assert_expression_prints_to(d4, "0.0012345", DecimalMode, 7); assert_expression_prints_to(d4, "1.2E-3", ScientificMode, 2); - Decimal d5(Integer("12345"),3); + Decimal d5 = Decimal::Builder(Integer("12345"),3); assert_expression_prints_to(d5, "1234.5", DecimalMode, 7); assert_expression_prints_to(d5, "1.23E3", DecimalMode, 3); assert_expression_prints_to(d5, "1235", DecimalMode, 4); assert_expression_prints_to(d5, "1.235E3", ScientificMode, 4); - Decimal d6(Integer("-12345"),3); + Decimal d6 = Decimal::Builder(Integer("-12345"),3); assert_expression_prints_to(d6, "-1234.5", DecimalMode, 7); assert_expression_prints_to(d6, "-1.2345E3", ScientificMode, 10); - Decimal d7(Integer("12345"),6); + Decimal d7 = Decimal::Builder(Integer("12345"),6); assert_expression_prints_to(d7, "1234500", DecimalMode, 7); assert_expression_prints_to(d7, "1.2345E6", DecimalMode, 6); assert_expression_prints_to(d7, "1.2345E6", ScientificMode); - Decimal d8(Integer("-12345"),6); + Decimal d8 = Decimal::Builder(Integer("-12345"),6); assert_expression_prints_to(d8, "-1234500", DecimalMode, 7); assert_expression_prints_to(d8, "-1.2345E6", DecimalMode, 5); assert_expression_prints_to(d7, "1.235E6", ScientificMode, 4); - Decimal d9(Integer("-12345"),-1); + Decimal d9 = Decimal::Builder(Integer("-12345"),-1); assert_expression_prints_to(d9, "-0.12345", DecimalMode, 7); assert_expression_prints_to(d9, "-0.1235", DecimalMode, 4); assert_expression_prints_to(d9, "-1.235E-1", ScientificMode, 4); - Decimal d10(Integer("12345"),-1); + Decimal d10 = Decimal::Builder(Integer("12345"),-1); assert_expression_prints_to(d10, "1.2345E-1"); assert_expression_prints_to(d10, "0.12345", DecimalMode, 7); assert_expression_prints_to(d10, "0.1235", DecimalMode, 4); assert_expression_prints_to(d10, "1.235E-1", ScientificMode, 4); - assert_expression_prints_to(Decimal(-1.23456789E30), "-1.23456789E30", ScientificMode, 14); - assert_expression_prints_to(Decimal(1.23456789E30), "1.23456789E30", ScientificMode, 14); - assert_expression_prints_to(Decimal(-1.23456789E-30), "-1.23456789E-30", ScientificMode, 14); - assert_expression_prints_to(Decimal(-1.2345E-3), "-0.0012345", DecimalMode); - assert_expression_prints_to(Decimal(1.2345E-3), "0.0012345", DecimalMode); - assert_expression_prints_to(Decimal(1.2345E3), "1234.5", DecimalMode); - assert_expression_prints_to(Decimal(-1.2345E3), "-1234.5", DecimalMode); - assert_expression_prints_to(Decimal(1.2345E6), "1234500", DecimalMode); - assert_expression_prints_to(Decimal(-1.2345E6), "-1234500", DecimalMode); - assert_expression_prints_to(Decimal(-1.2345E-1), "-0.12345", DecimalMode); - assert_expression_prints_to(Decimal(1.2345E-1), "0.12345", DecimalMode); - assert_expression_prints_to(Decimal(1.0), "1"); - assert_expression_prints_to(Decimal(0.9999999999999996), "1"); - assert_expression_prints_to(Decimal(0.99999999999995), "9.9999999999995E-1", ScientificMode, 14); - assert_expression_prints_to(Decimal(0.00000099999999999995), "9.9999999999995E-7", ScientificMode, 14); - assert_expression_prints_to(Decimal(0.000000999999999999995), "0.000001", DecimalMode); - assert_expression_prints_to(Decimal(0.000000999999999901200121020102010201201201021099995), "9.999999999012E-7", DecimalMode, 14); - assert_expression_prints_to(Decimal(9999999999999.54), "9999999999999.5", DecimalMode, 14); - assert_expression_prints_to(Decimal(99999999999999.54), "1E14", DecimalMode, 14); - assert_expression_prints_to(Decimal(999999999999999.54), "1E15", DecimalMode, 14); - assert_expression_prints_to(Decimal(9999999999999999.54), "1E16", DecimalMode, 14); - assert_expression_prints_to(Decimal(-9.702365051313E-297), "-9.702365051313E-297", DecimalMode, 14); + assert_expression_prints_to(Decimal::Builder(-1.23456789E30), "-1.23456789E30", ScientificMode, 14); + assert_expression_prints_to(Decimal::Builder(1.23456789E30), "1.23456789E30", ScientificMode, 14); + assert_expression_prints_to(Decimal::Builder(-1.23456789E-30), "-1.23456789E-30", ScientificMode, 14); + assert_expression_prints_to(Decimal::Builder(-1.2345E-3), "-0.0012345", DecimalMode); + assert_expression_prints_to(Decimal::Builder(1.2345E-3), "0.0012345", DecimalMode); + assert_expression_prints_to(Decimal::Builder(1.2345E3), "1234.5", DecimalMode); + assert_expression_prints_to(Decimal::Builder(-1.2345E3), "-1234.5", DecimalMode); + assert_expression_prints_to(Decimal::Builder(1.2345E6), "1234500", DecimalMode); + assert_expression_prints_to(Decimal::Builder(-1.2345E6), "-1234500", DecimalMode); + assert_expression_prints_to(Decimal::Builder(-1.2345E-1), "-0.12345", DecimalMode); + assert_expression_prints_to(Decimal::Builder(1.2345E-1), "0.12345", DecimalMode); + assert_expression_prints_to(Decimal::Builder(1.0), "1"); + assert_expression_prints_to(Decimal::Builder(0.9999999999999996), "1"); + assert_expression_prints_to(Decimal::Builder(0.99999999999995), "9.9999999999995E-1", ScientificMode, 14); + assert_expression_prints_to(Decimal::Builder(0.00000099999999999995), "9.9999999999995E-7", ScientificMode, 14); + assert_expression_prints_to(Decimal::Builder(0.000000999999999999995), "0.000001", DecimalMode); + assert_expression_prints_to(Decimal::Builder(0.000000999999999901200121020102010201201201021099995), "9.999999999012E-7", DecimalMode, 14); + assert_expression_prints_to(Decimal::Builder(9999999999999.54), "9999999999999.5", DecimalMode, 14); + assert_expression_prints_to(Decimal::Builder(99999999999999.54), "1E14", DecimalMode, 14); + assert_expression_prints_to(Decimal::Builder(999999999999999.54), "1E15", DecimalMode, 14); + assert_expression_prints_to(Decimal::Builder(9999999999999999.54), "1E16", DecimalMode, 14); + assert_expression_prints_to(Decimal::Builder(-9.702365051313E-297), "-9.702365051313E-297", DecimalMode, 14); } QUIZ_CASE(poincare_approximation_to_text) { - assert_expression_prints_to(Float(-1.23456789E30), "-1.23456789E30", DecimalMode, 14); - assert_expression_prints_to(Float(1.23456789E30), "1.23456789E30", DecimalMode, 14); - assert_expression_prints_to(Float(-1.23456789E-30), "-1.23456789E-30", DecimalMode, 14); - assert_expression_prints_to(Float(-1.2345E-3), "-0.0012345", DecimalMode); - assert_expression_prints_to(Float(1.2345E-3), "0.0012345", DecimalMode); - assert_expression_prints_to(Float(1.2345E3), "1234.5", DecimalMode); - assert_expression_prints_to(Float(-1.2345E3), "-1234.5", DecimalMode); - assert_expression_prints_to(Float(0.99999999999995), "9.9999999999995E-1", ScientificMode, 14); - assert_expression_prints_to(Float(0.00000099999999999995), "9.9999999999995E-7", DecimalMode, 14); - assert_expression_prints_to(Float(0.0000009999999999901200121020102010201201201021099995), "9.9999999999012E-7", DecimalMode, 14); - assert_expression_prints_to(Float(1.2345E-1), "0.12345", DecimalMode); - assert_expression_prints_to(Float(1), "1", DecimalMode); - assert_expression_prints_to(Float(0.9999999999999995), "1", DecimalMode); - assert_expression_prints_to(Float(1.2345E6), "1234500", DecimalMode); - assert_expression_prints_to(Float(-1.2345E6), "-1234500", DecimalMode); - assert_expression_prints_to(Float(0.0000009999999999999995), "0.000001", DecimalMode); - assert_expression_prints_to(Float(-1.2345E-1), "-0.12345", DecimalMode); + assert_expression_prints_to(Float::Builder(-1.23456789E30), "-1.23456789E30", DecimalMode, 14); + assert_expression_prints_to(Float::Builder(1.23456789E30), "1.23456789E30", DecimalMode, 14); + assert_expression_prints_to(Float::Builder(-1.23456789E-30), "-1.23456789E-30", DecimalMode, 14); + assert_expression_prints_to(Float::Builder(-1.2345E-3), "-0.0012345", DecimalMode); + assert_expression_prints_to(Float::Builder(1.2345E-3), "0.0012345", DecimalMode); + assert_expression_prints_to(Float::Builder(1.2345E3), "1234.5", DecimalMode); + assert_expression_prints_to(Float::Builder(-1.2345E3), "-1234.5", DecimalMode); + assert_expression_prints_to(Float::Builder(0.99999999999995), "9.9999999999995E-1", ScientificMode, 14); + assert_expression_prints_to(Float::Builder(0.00000099999999999995), "9.9999999999995E-7", DecimalMode, 14); + assert_expression_prints_to(Float::Builder(0.0000009999999999901200121020102010201201201021099995), "9.9999999999012E-7", DecimalMode, 14); + assert_expression_prints_to(Float::Builder(1.2345E-1), "0.12345", DecimalMode); + assert_expression_prints_to(Float::Builder(1), "1", DecimalMode); + assert_expression_prints_to(Float::Builder(0.9999999999999995), "1", DecimalMode); + assert_expression_prints_to(Float::Builder(1.2345E6), "1234500", DecimalMode); + assert_expression_prints_to(Float::Builder(-1.2345E6), "-1234500", DecimalMode); + assert_expression_prints_to(Float::Builder(0.0000009999999999999995), "0.000001", DecimalMode); + assert_expression_prints_to(Float::Builder(-1.2345E-1), "-0.12345", DecimalMode); - assert_expression_prints_to(Float(INFINITY), Infinity::Name(), DecimalMode); - assert_expression_prints_to(Float(0.0f), "0", DecimalMode); - assert_expression_prints_to(Float(NAN), Undefined::Name(), DecimalMode); + assert_expression_prints_to(Float::Builder(INFINITY), Infinity::Name(), DecimalMode); + assert_expression_prints_to(Float::Builder(0.0f), "0", DecimalMode); + assert_expression_prints_to(Float::Builder(NAN), Undefined::Name(), DecimalMode); } diff --git a/poincare/test/decimal.cpp b/poincare/test/decimal.cpp index 7945a0dd9..bb91c0114 100644 --- a/poincare/test/decimal.cpp +++ b/poincare/test/decimal.cpp @@ -8,10 +8,10 @@ using namespace Poincare; QUIZ_CASE(poincare_decimal_constructor) { int initialPoolSize = pool_size(); - Decimal a("123",2); - Decimal b("3456", -4); - Decimal c(2.34f); - Decimal d(2322.34); + Decimal a = Decimal::Builder("123",2); + Decimal b = Decimal::Builder("3456", -4); + Decimal c = Decimal::Builder(2.34f); + Decimal d = Decimal::Builder(2322.34); assert_pool_size(initialPoolSize+4); } @@ -24,22 +24,23 @@ static inline void assert_not_equal(const Decimal i, const Decimal j) { } QUIZ_CASE(poincare_decimal_compare) { - assert_equal(Decimal("25", 3), Decimal("25", 3)); - assert_equal(Decimal("1000", -3), Decimal("1", -3)); - assert_equal(Decimal("1000", 3), Decimal("1", 3)); - assert_not_equal(Decimal(123,234), Decimal(42, 108)); - assert_not_equal(Decimal(12,2), Decimal(123, 2)); - assert_not_equal(Decimal(1234,2), Decimal(1234,3)); - assert_not_equal(Decimal(12345,2), Decimal(1235,2)); - assert_not_equal(Decimal(123456, -2),Decimal(1234567, -3)); - assert_not_equal(Decimal(12345678, -2),Decimal(1234567, -2)); + assert_equal(Decimal::Builder("25", 3), Decimal::Builder("25", 3)); + assert_equal(Decimal::Builder("1000", -3), Decimal::Builder("1", -3)); + assert_equal(Decimal::Builder("1000", 3), Decimal::Builder("1", 3)); + assert_not_equal(Decimal::Builder(123,234), Decimal::Builder(42, 108)); + assert_not_equal(Decimal::Builder(12,2), Decimal::Builder(123, 2)); + assert_not_equal(Decimal::Builder(1234,2), Decimal::Builder(1234,3)); + assert_not_equal(Decimal::Builder(12345,2), Decimal::Builder(1235,2)); + assert_not_equal(Decimal::Builder(123456, -2),Decimal::Builder(1234567, -3)); + assert_not_equal(Decimal::Builder(12345678, -2),Decimal::Builder(1234567, -2)); } QUIZ_CASE(poincare_decimal_properties) { - quiz_assert(Decimal(-2, 3).sign() == ExpressionNode::Sign::Negative); - quiz_assert(Decimal(-2, -3).sign() == ExpressionNode::Sign::Negative); - quiz_assert(Decimal(2, -3).sign() == ExpressionNode::Sign::Positive); - quiz_assert(Decimal(2, 3).sign() == ExpressionNode::Sign::Positive); + quiz_assert(Decimal::Builder(-2, 3).sign() == ExpressionNode::Sign::Negative); + quiz_assert(Decimal::Builder(-2, -3).sign() == ExpressionNode::Sign::Negative); + quiz_assert(Decimal::Builder(2, -3).sign() == ExpressionNode::Sign::Positive); + quiz_assert(Decimal::Builder(2, 3).sign() == ExpressionNode::Sign::Positive); + quiz_assert(Decimal::Builder(0, 1).sign() == ExpressionNode::Sign::Positive); } // Simplify diff --git a/poincare/test/expression.cpp b/poincare/test/expression.cpp index 0f80f49fc..3881823bb 100644 --- a/poincare/test/expression.cpp +++ b/poincare/test/expression.cpp @@ -7,7 +7,7 @@ using namespace Poincare; QUIZ_CASE(expression_can_start_uninitialized) { Expression e; { - Rational i(1); + Rational i = Rational::Builder(1); e = i; } } diff --git a/poincare/test/expression_order.cpp b/poincare/test/expression_order.cpp new file mode 100644 index 000000000..3e90aa0c6 --- /dev/null +++ b/poincare/test/expression_order.cpp @@ -0,0 +1,109 @@ +#include +#include +#include +#include +#include "helper.h" + +using namespace Poincare; + +void assert_multiplication_or_addition_is_ordered_as(Expression e1, Expression e2) { + if (e1.type() == ExpressionNode::Type::Multiplication) { + static_cast(e1).sortChildrenInPlace( + [](const ExpressionNode * e1, const ExpressionNode * e2, bool canBeInterrupted) { return ExpressionNode::SimplificationOrder(e1, e2, true, canBeInterrupted); }, + true); + } else { + assert(e1.type() == ExpressionNode::Type::Addition); + static_cast(e1).sortChildrenInPlace( + [](const ExpressionNode * e1, const ExpressionNode * e2, bool canBeInterrupted) { return ExpressionNode::SimplificationOrder(e1, e2, false, canBeInterrupted); }, + true); + } + quiz_assert(e1.isIdenticalTo(e2)); +} + +QUIZ_CASE(poincare_expression_order) { + { + // 2 * 5 + Expression e1 = Multiplication::Builder(Rational::Builder(5), Rational::Builder(2)); + Expression e2 = Multiplication::Builder(Rational::Builder(2), Rational::Builder(5)); + assert_multiplication_or_addition_is_ordered_as(e1, e2); + } + { + // 2 + 1 + 0 + constexpr int numberOfChildren = 3; + Expression children[numberOfChildren] = {Rational::Builder(1), Rational::Builder(2), Rational::Builder(0)}; + Expression childrenSorted[numberOfChildren] = {Rational::Builder(2), Rational::Builder(1), Rational::Builder(0)}; + Expression e1 = Addition::Builder(children, numberOfChildren); + Expression e2 = Addition::Builder(childrenSorted, numberOfChildren); + assert_multiplication_or_addition_is_ordered_as(e1, e2); + } + { + // e + pi + i + Expression pi = Constant::Builder(Ion::Charset::SmallPi); + Expression i = Constant::Builder(Ion::Charset::IComplex); + Expression e = Constant::Builder(Ion::Charset::Exponential); + constexpr int numberOfChildren = 3; + Expression children[numberOfChildren] = {pi.clone(), i.clone(), e.clone()}; + Expression childrenSorted[numberOfChildren] = {e, pi, i}; + Expression e1 = Addition::Builder(children, numberOfChildren); + Expression e2 = Addition::Builder(childrenSorted, numberOfChildren); + assert_multiplication_or_addition_is_ordered_as(e1, e2); + } + { + // 2 * root(3) + Expression e1 = Multiplication::Builder(SquareRoot::Builder(Rational::Builder(3)), Rational::Builder(2)); + Expression e2 = Multiplication::Builder(Rational::Builder(2), SquareRoot::Builder(Rational::Builder(3))); + assert_multiplication_or_addition_is_ordered_as(e1, e2); + } + { + constexpr int numberOfChildren = 4; + Expression children[numberOfChildren] = { + Symbol::Builder('c'), + Power::Builder(Symbol::Builder('b'), Rational::Builder(2)), + Power::Builder(Symbol::Builder('a'), Rational::Builder(2)), + Symbol::Builder('a') + }; + Expression childrenSorted[numberOfChildren] = { + Power::Builder(Symbol::Builder('a'), Rational::Builder(2)), + Symbol::Builder('a'), + Power::Builder(Symbol::Builder('b'), Rational::Builder(2)), + Symbol::Builder('c') + }; + // a^2 + a + b^2 + c + Expression e1 = Addition::Builder(children, numberOfChildren); + Expression e2 = Addition::Builder(childrenSorted, numberOfChildren); + assert_multiplication_or_addition_is_ordered_as(e1, e2); + } + { + // 2*x^3 + 3*x^2 + Expression child1 = Multiplication::Builder(Rational::Builder(2), Power::Builder(Symbol::Builder('x'), Rational::Builder(3))); + Expression child2 = Multiplication::Builder(Rational::Builder(3), Power::Builder(Symbol::Builder('x'), Rational::Builder(2))); + Expression e1 = Addition::Builder(child2.clone(), child1.clone()); + Expression e2 = Addition::Builder(child1, child2); + assert_multiplication_or_addition_is_ordered_as(e1, e2); + } + { + // 3*x + 2*x + Expression child1 = Multiplication::Builder(Rational::Builder(3), Symbol::Builder('x')); + Expression child2 = Multiplication::Builder(Rational::Builder(2), Symbol::Builder('x')); + Expression e1 = Addition::Builder(child2.clone(), child1.clone()); + Expression e2 = Addition::Builder(child1, child2); + assert_multiplication_or_addition_is_ordered_as(e1, e2); + } + { + // pi^a * pi^b + Expression child1 = Power::Builder(Constant::Builder(Ion::Charset::SmallPi), Symbol::Builder('a')); + Expression child2 = Power::Builder(Constant::Builder(Ion::Charset::SmallPi), Symbol::Builder('b')); + Expression e1 = Multiplication::Builder(child2.clone(), child1.clone()); + Expression e2 = Multiplication::Builder(child1, child2); + assert_multiplication_or_addition_is_ordered_as(e1, e2); + } + { + // pi^2 * pi^3 + Expression child1 = Power::Builder(Constant::Builder(Ion::Charset::SmallPi), Rational::Builder(2)); + Expression child2 = Power::Builder(Constant::Builder(Ion::Charset::SmallPi), Rational::Builder(3)); + Expression e1 = Multiplication::Builder(child2.clone(), child1.clone()); + Expression e2 = Multiplication::Builder(child1, child2); + assert_multiplication_or_addition_is_ordered_as(e1, e2); + } + +} diff --git a/poincare/test/float.cpp b/poincare/test/float.cpp index fbd9d6b69..1a17527a3 100644 --- a/poincare/test/float.cpp +++ b/poincare/test/float.cpp @@ -16,32 +16,32 @@ void assert_float_evaluates_to(Float f, const char * result) { Shared::GlobalContext globalContext; int numberOfDigits = sizeof(T) == sizeof(double) ? PrintFloat::k_numberOfStoredSignificantDigits : PrintFloat::k_numberOfPrintedSignificantDigits; char buffer[500]; - f.template approximate(globalContext, Radian, Cartesian).serialize(buffer, sizeof(buffer), DecimalMode, numberOfDigits); + f.template approximate(globalContext, Cartesian, Radian).serialize(buffer, sizeof(buffer), DecimalMode, numberOfDigits); translate_in_ASCII_chars(buffer); quiz_assert(strcmp(buffer, result) == 0); } QUIZ_CASE(poincare_float_evaluate) { - assert_float_evaluates_to(Float(-1.23456789E30), "-1.23456789E30"); - assert_float_evaluates_to(Float(1.23456789E30), "1.23456789E30"); - assert_float_evaluates_to(Float(-1.23456789E-30), "-1.23456789E-30"); - assert_float_evaluates_to(Float(-1.2345E-3), "-0.0012345"); - assert_float_evaluates_to(Float(1.2345E-3), "0.0012345"); - assert_float_evaluates_to(Float(1.2345E3), "1234.5"); - assert_float_evaluates_to(Float(-1.2345E3), "-1234.5"); - assert_float_evaluates_to(Float(0.99999999999995), "9.9999999999995E-1"); - assert_float_evaluates_to(Float(0.00000099999999999995), "9.9999999999995E-7"); - assert_float_evaluates_to(Float(0.0000009999999999901200121020102010201201201021099995), "9.9999999999012E-7"); - assert_float_evaluates_to(Float(1.2345E-1), "0.12345"); - assert_float_evaluates_to(Float(1), "1"); - assert_float_evaluates_to(Float(0.9999999999999995), "1"); - assert_float_evaluates_to(Float(1.2345E6), "1234500"); - assert_float_evaluates_to(Float(-1.2345E6), "-1234500"); - assert_float_evaluates_to(Float(0.0000009999999999999995), "0.000001"); - assert_float_evaluates_to(Float(-1.2345E-1), "-0.12345"); + assert_float_evaluates_to(Float::Builder(-1.23456789E30), "-1.23456789E30"); + assert_float_evaluates_to(Float::Builder(1.23456789E30), "1.23456789E30"); + assert_float_evaluates_to(Float::Builder(-1.23456789E-30), "-1.23456789E-30"); + assert_float_evaluates_to(Float::Builder(-1.2345E-3), "-0.0012345"); + assert_float_evaluates_to(Float::Builder(1.2345E-3), "0.0012345"); + assert_float_evaluates_to(Float::Builder(1.2345E3), "1234.5"); + assert_float_evaluates_to(Float::Builder(-1.2345E3), "-1234.5"); + assert_float_evaluates_to(Float::Builder(0.99999999999995), "9.9999999999995E-1"); + assert_float_evaluates_to(Float::Builder(0.00000099999999999995), "9.9999999999995E-7"); + assert_float_evaluates_to(Float::Builder(0.0000009999999999901200121020102010201201201021099995), "9.9999999999012E-7"); + assert_float_evaluates_to(Float::Builder(1.2345E-1), "0.12345"); + assert_float_evaluates_to(Float::Builder(1), "1"); + assert_float_evaluates_to(Float::Builder(0.9999999999999995), "1"); + assert_float_evaluates_to(Float::Builder(1.2345E6), "1234500"); + assert_float_evaluates_to(Float::Builder(-1.2345E6), "-1234500"); + assert_float_evaluates_to(Float::Builder(0.0000009999999999999995), "0.000001"); + assert_float_evaluates_to(Float::Builder(-1.2345E-1), "-0.12345"); - assert_float_evaluates_to(Float(INFINITY), Infinity::Name()); - assert_float_evaluates_to(Float(0.0f), "0"); - assert_float_evaluates_to(Float(NAN), Undefined::Name()); + assert_float_evaluates_to(Float::Builder(INFINITY), Infinity::Name()); + assert_float_evaluates_to(Float::Builder(0.0f), "0"); + assert_float_evaluates_to(Float::Builder(NAN), Undefined::Name()); } diff --git a/poincare/test/fraction_layout.cpp b/poincare/test/fraction_layout.cpp index 095721bd5..51417ed3f 100644 --- a/poincare/test/fraction_layout.cpp +++ b/poincare/test/fraction_layout.cpp @@ -25,8 +25,8 @@ QUIZ_CASE(poincare_fraction_layout_delete) { * --- -> "BackSpace" -> 12|34 * |34 * */ - HorizontalLayout layout1 = HorizontalLayout( - FractionLayout( + HorizontalLayout layout1 = HorizontalLayout::Builder( + FractionLayout::Builder( LayoutHelper::String("12", 2), LayoutHelper::String("34", 2) ) @@ -40,12 +40,12 @@ QUIZ_CASE(poincare_fraction_layout_delete) { * 1 + --- -> "BackSpace" -> 1+|3 * |3 * */ - HorizontalLayout layout2 = HorizontalLayout( - CharLayout('1'), - CharLayout('+'), - FractionLayout( - EmptyLayout(), - CharLayout('3') + HorizontalLayout layout2 = HorizontalLayout::Builder( + CharLayout::Builder('1'), + CharLayout::Builder('+'), + FractionLayout::Builder( + EmptyLayout::Builder(), + CharLayout::Builder('3') ) ); LayoutCursor cursor2(layout2.childAtIndex(2).childAtIndex(1), LayoutCursor::Position::Left); @@ -55,8 +55,8 @@ QUIZ_CASE(poincare_fraction_layout_delete) { } QUIZ_CASE(poincare_fraction_layout_serialize) { - FractionLayout layout = FractionLayout( - CharLayout('1'), + FractionLayout layout = FractionLayout::Builder( + CharLayout::Builder('1'), LayoutHelper::String("2+3", 3) ); assert_expression_layout_serialize_to(layout, "(1)/(2+3)"); diff --git a/poincare/test/function.cpp b/poincare/test/function.cpp index c3b4cae9a..9b7d343c7 100644 --- a/poincare/test/function.cpp +++ b/poincare/test/function.cpp @@ -11,7 +11,7 @@ using namespace Poincare; template void assert_exp_is_bounded(Expression exp, T lowBound, T upBound, bool upBoundIncluded = false) { Shared::GlobalContext globalContext; - T result = exp.approximateToScalar(globalContext, Radian); + T result = exp.approximateToScalar(globalContext, Cartesian, Radian); quiz_assert(result >= lowBound); quiz_assert(result < upBound || (result == upBound && upBoundIncluded)); } @@ -54,6 +54,7 @@ QUIZ_CASE(poincare_parse_function) { assert_parsed_expression_type("root(2,3)", ExpressionNode::Type::NthRoot); assert_parsed_expression_type("R(2)", ExpressionNode::Type::SquareRoot); assert_parsed_expression_type("round(2,3)", ExpressionNode::Type::Round); + assert_parsed_expression_type("sign(3)", ExpressionNode::Type::SignFunction); assert_parsed_expression_type("sum(n,n, 4, 10)", ExpressionNode::Type::Sum); #if MATRICES_ARE_DEFINED assert_parsed_expression_type("trace([[1,2,3][4,5,6][7,8,9]])", ExpressionNode::Type::MatrixTrace); @@ -83,9 +84,9 @@ QUIZ_CASE(poincare_function_evaluate) { assert_parsed_expression_evaluates_to("ceil(0.2)", "1"); #if MATRICES_ARE_DEFINED - assert_parsed_expression_evaluates_to("det([[1,23,3][4,5,6][7,8,9]])", "126", Degree, Cartesian, 6); // FIXME: the determinant computation is not precised enough to be displayed with 7 significant digits + assert_parsed_expression_evaluates_to("det([[1,23,3][4,5,6][7,8,9]])", "126", System, Degree, Cartesian, 6); // FIXME: the determinant computation is not precised enough to be displayed with 7 significant digits assert_parsed_expression_evaluates_to("det([[1,23,3][4,5,6][7,8,9]])", "126"); - assert_parsed_expression_evaluates_to("det([[I,23-2I,3*I][4+I,5*I,6][7,8*I+2,9]])", "126-231*I", Degree, Cartesian, 6); // FIXME: the determinant computation is not precised enough to be displayed with 7 significant digits + assert_parsed_expression_evaluates_to("det([[I,23-2I,3*I][4+I,5*I,6][7,8*I+2,9]])", "126-231*I", System, Degree, Cartesian, 6); // FIXME: the determinant computation is not precised enough to be displayed with 7 significant digits assert_parsed_expression_evaluates_to("det([[I,23-2I,3*I][4+I,5*I,6][7,8*I+2,9]])", "126-231*I"); #endif assert_parsed_expression_evaluates_to("diff(2*x, x, 2)", "2"); @@ -164,10 +165,10 @@ QUIZ_CASE(poincare_function_evaluate) { assert_parsed_expression_evaluates_to("factor(-123/24)", "-5.125"); #if MATRICES_ARE_DEFINED - assert_parsed_expression_evaluates_to("inverse([[1,2,3][4,5,-6][7,8,9]])", "[[-1.2917,-0.083333,0.375][1.0833,0.16667,-0.25][0.041667,-0.083333,0.041667]]", Degree, Cartesian, 5); // inverse is not precise enough to display 7 significative digits + assert_parsed_expression_evaluates_to("inverse([[1,2,3][4,5,-6][7,8,9]])", "[[-1.2917,-0.083333,0.375][1.0833,0.16667,-0.25][0.041667,-0.083333,0.041667]]", System, Degree, Cartesian, 5); // inverse is not precise enough to display 7 significative digits assert_parsed_expression_evaluates_to("inverse([[1,2,3][4,5,-6][7,8,9]])", "[[-1.2916666666667,-8.3333333333333E-2,0.375][1.0833333333333,1.6666666666667E-1,-0.25][4.1666666666667E-2,-8.3333333333333E-2,4.1666666666667E-2]]"); - assert_parsed_expression_evaluates_to("inverse([[I,23-2I,3*I][4+I,5*I,6][7,8*I+2,9]])", "[[-0.0118-0.0455*I,-0.5-0.727*I,0.318+0.489*I][0.0409+0.00364*I,0.04-0.0218*I,-0.0255+0.00091*I][0.00334-0.00182*I,0.361+0.535*I,-0.13-0.358*I]]", Degree, Cartesian, 3); // inverse is not precise enough to display 7 significative digits - assert_parsed_expression_evaluates_to("inverse([[I,23-2I,3*I][4+I,5*I,6][7,8*I+2,9]])", "[[-0.0118289353958-0.0454959053685*I,-0.500454959054-0.727024567789*I,0.31847133758+0.488626023658*I][0.0409463148317+3.63967242948E-3*I,0.0400363967243-0.0218380345769*I,-0.0254777070064+9.0991810737E-4*I][3.33636639369E-3-1.81983621474E-3*I,0.36093418259+0.534728541098*I,-0.130118289354-0.357597816197*I]]", Degree, Cartesian, 12); // FIXME: inverse is not precise enough to display 14 significative digits + assert_parsed_expression_evaluates_to("inverse([[I,23-2I,3*I][4+I,5*I,6][7,8*I+2,9]])", "[[-0.0118-0.0455*I,-0.5-0.727*I,0.318+0.489*I][0.0409+0.00364*I,0.04-0.0218*I,-0.0255+0.00091*I][0.00334-0.00182*I,0.361+0.535*I,-0.13-0.358*I]]", System, Degree, Cartesian, 3); // inverse is not precise enough to display 7 significative digits + assert_parsed_expression_evaluates_to("inverse([[I,23-2I,3*I][4+I,5*I,6][7,8*I+2,9]])", "[[-0.0118289353958-0.0454959053685*I,-0.500454959054-0.727024567789*I,0.31847133758+0.488626023658*I][0.0409463148317+3.63967242948E-3*I,0.0400363967243-0.0218380345769*I,-0.0254777070064+9.0991810737E-4*I][3.33636639369E-3-1.81983621474E-3*I,0.36093418259+0.534728541098*I,-0.130118289354-0.357597816197*I]]", System, Degree, Cartesian, 12); // FIXME: inverse is not precise enough to display 14 significative digits #endif assert_parsed_expression_evaluates_to("prediction(0.1, 100)", "[[0,0.2]]"); @@ -185,12 +186,21 @@ QUIZ_CASE(poincare_function_evaluate) { assert_parsed_expression_evaluates_to("root(3, 3+I)", "1.382007-0.1524428*I"); assert_parsed_expression_evaluates_to("root(3, 3+I)", "1.3820069623326-0.1524427794159*I"); - assert_parsed_expression_evaluates_to("root(5^((-I)3^9),I)", "3.504", Degree, Cartesian, 4); - assert_parsed_expression_evaluates_to("root(5^((-I)3^9),I)", "3.5039410843", Degree, Cartesian, 11); + assert_parsed_expression_evaluates_to("root(5^((-I)3^9),I)", "3.504", System, Degree, Cartesian, 4); + assert_parsed_expression_evaluates_to("root(5^((-I)3^9),I)", "3.5039410843", System, Degree, Cartesian, 11); assert_parsed_expression_evaluates_to("R(3+I)", "1.755317+0.2848488*I"); assert_parsed_expression_evaluates_to("R(3+I)", "1.7553173018244+2.8484878459314E-1*I"); + assert_parsed_expression_evaluates_to("sign(-23+1)", "-1"); + assert_parsed_expression_evaluates_to("sign(inf)", "1"); + assert_parsed_expression_evaluates_to("sign(-inf)", "-1"); + assert_parsed_expression_evaluates_to("sign(0)", "0"); + assert_parsed_expression_evaluates_to("sign(-0)", "0"); + assert_parsed_expression_evaluates_to("sign(x)", "undef"); + assert_parsed_expression_evaluates_to("sign(2+I)", "undef"); + assert_parsed_expression_evaluates_to("sign(undef)", "undef"); + assert_parsed_expression_evaluates_to("sum(2+n*I,n,1,5)", "10+15*I"); assert_parsed_expression_evaluates_to("sum(2+n*I,n,1,5)", "10+15*I"); @@ -233,6 +243,9 @@ QUIZ_CASE(poincare_function_evaluate) { QUIZ_CASE(poincare_function_simplify) { assert_parsed_expression_simplify_to("abs(P)", "P"); assert_parsed_expression_simplify_to("abs(-P)", "P"); + assert_parsed_expression_simplify_to("abs(1+I)", "R(2)"); + assert_parsed_expression_simplify_to("abs(0)", "0"); + assert_parsed_expression_simplify_to("arg(1+I)", "P/4"); assert_parsed_expression_simplify_to("binomial(20,3)", "1140"); assert_parsed_expression_simplify_to("binomial(20,10)", "184756"); assert_parsed_expression_simplify_to("ceil(-1.3)", "-1"); @@ -252,9 +265,11 @@ QUIZ_CASE(poincare_function_simplify) { assert_parsed_expression_simplify_to("frac(-1.3)", "7/10"); assert_parsed_expression_simplify_to("gcd(123,278)", "1"); assert_parsed_expression_simplify_to("gcd(11,121)", "11"); + assert_parsed_expression_simplify_to("im(1+5*I)", "5"); assert_parsed_expression_simplify_to("lcm(123,278)", "34194"); assert_parsed_expression_simplify_to("lcm(11,121)", "121"); assert_parsed_expression_simplify_to("R(4)", "2"); + assert_parsed_expression_simplify_to("re(1+5*I)", "1"); assert_parsed_expression_simplify_to("root(4,3)", "root(4,3)"); assert_parsed_expression_simplify_to("root(4,P)", "4^(1/P)"); assert_parsed_expression_simplify_to("root(27,3)", "3"); @@ -263,6 +278,17 @@ QUIZ_CASE(poincare_function_simplify) { assert_parsed_expression_simplify_to("round(4.9,0)", "5"); assert_parsed_expression_simplify_to("round(12.9,-1)", "10"); assert_parsed_expression_simplify_to("round(12.9,-2)", "0"); + assert_parsed_expression_simplify_to("sign(-23)", "-1"); + assert_parsed_expression_simplify_to("sign(-I)", "sign(-I)"); + assert_parsed_expression_simplify_to("sign(0)", "0"); + assert_parsed_expression_simplify_to("sign(inf)", "1"); + assert_parsed_expression_simplify_to("sign(-inf)", "-1"); + assert_parsed_expression_simplify_to("sign(undef)", "undef"); + assert_parsed_expression_simplify_to("sign(23)", "1"); + assert_parsed_expression_simplify_to("sign(log(18))", "1"); + assert_parsed_expression_simplify_to("sign(-R(2))", "-1"); + assert_parsed_expression_simplify_to("sign(x)", "sign(x)"); + assert_parsed_expression_simplify_to("sign(2+I)", "sign(2+I)"); assert_parsed_expression_simplify_to("permute(99,4)", "90345024"); assert_parsed_expression_simplify_to("permute(20,-10)", Undefined::Name()); assert_parsed_expression_simplify_to("re(1/2)", "1/2"); diff --git a/poincare/test/helper.cpp b/poincare/test/helper.cpp index 02a62367d..e0e6d41be 100644 --- a/poincare/test/helper.cpp +++ b/poincare/test/helper.cpp @@ -28,6 +28,19 @@ const char * BigOverflowedIntegerString() { return s; } +bool expressions_are_equal(Poincare::Expression expected, Poincare::Expression got) { + bool identical = expected.isIdenticalTo(got); +#if POINCARE_TREE_LOG + if (!identical) { + std::cout << "Expecting" << std::endl; + expected.log(); + std::cout << "Got" << std::endl; + got.log(); + } +#endif + return identical; +} + void translate_in_special_chars(char * expression) { for (char *c = expression; *c; c++) { switch (*c) { @@ -87,39 +100,34 @@ void assert_parsed_expression_type(const char * expression, Poincare::Expression void assert_parsed_expression_is(const char * expression, Poincare::Expression r) { Expression e = parse_expression(expression); - bool identical = e.isIdenticalTo(r); -#if POINCARE_TREE_LOG - if (!identical) { - std::cout << "Expecting" << std::endl; - r.log(); - std::cout << "Got" << std::endl; - e.log(); - } -#endif - quiz_assert(identical); + quiz_assert(expressions_are_equal(r, e)); } -void assert_parsed_expression_polynomial_degree(const char * expression, int degree, const char * symbolName) { +void assert_parsed_expression_polynomial_degree(const char * expression, int degree, const char * symbolName, Preferences::ComplexFormat complexFormat) { Shared::GlobalContext globalContext; Expression e = parse_expression(expression); - Expression result = e.clone().simplify(globalContext, Radian); + Expression result = e.clone().reduce(globalContext, complexFormat, Radian); if (result.isUninitialized()) { result = e; } quiz_assert(result.polynomialDegree(globalContext, symbolName) == degree); } -void assert_simplify(const char * expression) { + +Expression parse_and_simplify(const char * expression) { Shared::GlobalContext globalContext; Expression e = parse_expression(expression); quiz_assert(!e.isUninitialized()); - e = e.simplify(globalContext, Radian); - quiz_assert(!e.isUninitialized()); + return e.simplify(globalContext, Cartesian, Radian); } -typedef Expression (*ProcessExpression)(Expression, Context & context, Preferences::AngleUnit angleUnit, Preferences::ComplexFormat complexFormat); +void assert_simplify(const char * expression) { + quiz_assert(!(parse_and_simplify(expression).isUninitialized())); +} -void assert_parsed_expression_process_to(const char * expression, const char * result, Preferences::AngleUnit angleUnit, Preferences::ComplexFormat complexFormat, ProcessExpression process, int numberOfSignifiantDigits = PrintFloat::k_numberOfStoredSignificantDigits) { +typedef Expression (*ProcessExpression)(Expression, Context & context, ExpressionNode::ReductionTarget target, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit); + +void assert_parsed_expression_process_to(const char * expression, const char * result, ExpressionNode::ReductionTarget target, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, ProcessExpression process, int numberOfSignifiantDigits = PrintFloat::k_numberOfStoredSignificantDigits) { Shared::GlobalContext globalContext; Expression e = parse_expression(expression); @@ -127,7 +135,7 @@ void assert_parsed_expression_process_to(const char * expression, const char * r cout << " Entry expression: " << expression << "----" << endl; print_expression(e, 0); #endif - Expression m = process(e, globalContext, angleUnit, complexFormat); + Expression m = process(e, globalContext, target, complexFormat, angleUnit); char buffer[500]; m.serialize(buffer, sizeof(buffer), DecimalMode, numberOfSignifiantDigits); translate_in_ASCII_chars(buffer); @@ -139,38 +147,65 @@ void assert_parsed_expression_process_to(const char * expression, const char * r } template -void assert_parsed_expression_evaluates_to(const char * expression, const char * approximation, Preferences::AngleUnit angleUnit, Preferences::ComplexFormat complexFormat, int numberOfSignificantDigits) { +void assert_parsed_expression_evaluates_to(const char * expression, const char * approximation, ExpressionNode::ReductionTarget target, Preferences::AngleUnit angleUnit, Preferences::ComplexFormat complexFormat, int numberOfSignificantDigits) { #if POINCARE_TESTS_PRINT_EXPRESSIONS cout << "--------- Approximation ---------" << endl; #endif int numberOfDigits = sizeof(T) == sizeof(double) ? PrintFloat::k_numberOfStoredSignificantDigits : PrintFloat::k_numberOfPrintedSignificantDigits; numberOfDigits = numberOfSignificantDigits > 0 ? numberOfSignificantDigits : numberOfDigits; - assert_parsed_expression_process_to(expression, approximation, angleUnit, complexFormat, [](Expression e, Context & context, Preferences::AngleUnit angleUnit, Preferences::ComplexFormat complexFormat) { - Expression result = e.clone().simplify(context, angleUnit); - if (result.isUninitialized()) { - result = e; - } - return result.approximate(context, angleUnit, complexFormat); - }, numberOfDigits); + + assert_parsed_expression_process_to(expression, approximation, target, complexFormat, angleUnit, [](Expression e, Context & context, ExpressionNode::ReductionTarget target, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) { + Expression simplified = e.clone(); + Expression result; + if (target == ExpressionNode::ReductionTarget::User) { + // When the reduction target is the User, we always approximate in double + assert(sizeof(T) == sizeof(double)); + simplified.simplifyAndApproximate(&simplified, &result, context, complexFormat, angleUnit); + } else { + simplified = simplified.simplify(context, complexFormat, angleUnit); + result = simplified.approximate(context, complexFormat, angleUnit); + } + // Simplification was interrupted + if (simplified.isUninitialized()) { + return e.approximate(context, complexFormat, angleUnit); + } + return result; + }, numberOfDigits); } template -void assert_parsed_expression_approximates_with_value_for_symbol(Expression expression, const char * symbol, T value, T approximation, Poincare::Preferences::AngleUnit angleUnit) { - Shared::GlobalContext globalContext; - T result = expression.approximateWithValueForSymbol(symbol, value, globalContext, angleUnit); - quiz_assert(std::fabs(result - approximation) < 10.0*Expression::epsilon()); +void assert_parsed_expression_evaluates_without_simplifying_to(const char * expression, const char * approximation, Preferences::AngleUnit angleUnit, Preferences::ComplexFormat complexFormat, int numberOfSignificantDigits) { + int numberOfDigits = sizeof(T) == sizeof(double) ? PrintFloat::k_numberOfStoredSignificantDigits : PrintFloat::k_numberOfPrintedSignificantDigits; + numberOfDigits = numberOfSignificantDigits > 0 ? numberOfSignificantDigits : numberOfDigits; + assert_parsed_expression_process_to(expression, approximation, ExpressionNode::ReductionTarget::System, complexFormat, angleUnit, [](Expression e, Context & context, ExpressionNode::ReductionTarget target, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) { + return e.approximate(context, complexFormat, angleUnit); + }, numberOfDigits); } -void assert_parsed_expression_simplify_to(const char * expression, const char * simplifiedExpression, Preferences::AngleUnit angleUnit) { + +template +void assert_parsed_expression_approximates_with_value_for_symbol(Expression expression, const char * symbol, T value, T approximation, Poincare::Preferences::ComplexFormat complexFormat, Poincare::Preferences::AngleUnit angleUnit) { + Shared::GlobalContext globalContext; + T result = expression.approximateWithValueForSymbol(symbol, value, globalContext, complexFormat, angleUnit); + quiz_assert((std::isnan(result) && std::isnan(approximation)) || std::fabs(result - approximation) < 10.0*Expression::Epsilon()); +} + +void assert_parsed_expression_simplify_to(const char * expression, const char * simplifiedExpression, ExpressionNode::ReductionTarget target, Preferences::AngleUnit angleUnit, Preferences::ComplexFormat complexFormat) { #if POINCARE_TESTS_PRINT_EXPRESSIONS cout << "--------- Simplification ---------" << endl; #endif - assert_parsed_expression_process_to(expression, simplifiedExpression, angleUnit, Preferences::ComplexFormat::Cartesian, [](Expression e, Context & context, Preferences::AngleUnit angleUnit, Preferences::ComplexFormat complexFormat) { - Expression result = e.clone().simplify(context, angleUnit); - if (result.isUninitialized()) { + assert_parsed_expression_process_to(expression, simplifiedExpression, target, complexFormat, angleUnit, [](Expression e, Context & context, ExpressionNode::ReductionTarget target, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit) { + Expression copy = e.clone(); + if (target == ExpressionNode::ReductionTarget::User) { + copy.simplifyAndApproximate(©, nullptr, context, complexFormat, angleUnit); + } else { + copy = copy.simplify(context, complexFormat, angleUnit); + } + if (copy.isUninitialized()) { return e; } - return result;}); + return copy; + }); } void assert_parsed_expression_serialize_to(Expression expression, const char * serializedExpression, Preferences::PrintFloatMode mode, int numberOfSignifiantDigits) { @@ -179,6 +214,7 @@ void assert_parsed_expression_serialize_to(Expression expression, const char * s #endif char buffer[500]; expression.serialize(buffer, sizeof(buffer), mode, numberOfSignifiantDigits); + translate_in_ASCII_chars(buffer); quiz_assert(strcmp(buffer, serializedExpression) == 0); } @@ -210,7 +246,9 @@ void assert_expression_layout_serialize_to(Poincare::Layout layout, const char * quiz_assert(strcmp(serialization, buffer) == 0); } -template void assert_parsed_expression_evaluates_to(char const*, char const *, Poincare::Preferences::AngleUnit, Poincare::Preferences::ComplexFormat, int); -template void assert_parsed_expression_evaluates_to(char const*, char const *, Poincare::Preferences::AngleUnit, Poincare::Preferences::ComplexFormat, int); -template void assert_parsed_expression_approximates_with_value_for_symbol(Poincare::Expression, const char *, float, float, Poincare::Preferences::AngleUnit); -template void assert_parsed_expression_approximates_with_value_for_symbol(Poincare::Expression, const char *, double, double, Poincare::Preferences::AngleUnit); +template void assert_parsed_expression_evaluates_to(char const*, char const *, Poincare::ExpressionNode::ReductionTarget, Poincare::Preferences::AngleUnit, Poincare::Preferences::ComplexFormat, int); +template void assert_parsed_expression_evaluates_to(char const*, char const *, Poincare::ExpressionNode::ReductionTarget, Poincare::Preferences::AngleUnit, Poincare::Preferences::ComplexFormat, int); +template void assert_parsed_expression_evaluates_without_simplifying_to(char const*, char const *, Poincare::Preferences::AngleUnit, Poincare::Preferences::ComplexFormat, int); +template void assert_parsed_expression_evaluates_without_simplifying_to(char const*, char const *, Poincare::Preferences::AngleUnit, Poincare::Preferences::ComplexFormat, int); +template void assert_parsed_expression_approximates_with_value_for_symbol(Poincare::Expression, const char *, float, float, Poincare::Preferences::ComplexFormat, Poincare::Preferences::AngleUnit); +template void assert_parsed_expression_approximates_with_value_for_symbol(Poincare::Expression, const char *, double, double, Poincare::Preferences::ComplexFormat, Poincare::Preferences::AngleUnit); diff --git a/poincare/test/helper.h b/poincare/test/helper.h index 1792f36bf..de0205017 100644 --- a/poincare/test/helper.h +++ b/poincare/test/helper.h @@ -8,30 +8,39 @@ const char * MaxIntegerString(); // (2^32)^k_maxNumberOfDigits-1 const char * OverflowedIntegerString(); // (2^32)^k_maxNumberOfDigits const char * BigOverflowedIntegerString(); // OverflowedIntegerString with a 2 on first digit +constexpr Poincare::ExpressionNode::ReductionTarget System = Poincare::ExpressionNode::ReductionTarget::System; +constexpr Poincare::ExpressionNode::ReductionTarget User = Poincare::ExpressionNode::ReductionTarget::User; constexpr Poincare::Preferences::AngleUnit Degree = Poincare::Preferences::AngleUnit::Degree; constexpr Poincare::Preferences::AngleUnit Radian = Poincare::Preferences::AngleUnit::Radian; constexpr Poincare::Preferences::ComplexFormat Cartesian = Poincare::Preferences::ComplexFormat::Cartesian; constexpr Poincare::Preferences::ComplexFormat Polar = Poincare::Preferences::ComplexFormat::Polar; +constexpr Poincare::Preferences::ComplexFormat Real = Poincare::Preferences::ComplexFormat::Real; constexpr Poincare::Preferences::PrintFloatMode DecimalMode = Poincare::Preferences::PrintFloatMode::Decimal; constexpr Poincare::Preferences::PrintFloatMode ScientificMode = Poincare::Preferences::PrintFloatMode::Scientific; +bool expressions_are_equal(Poincare::Expression expected, Poincare::Expression got); void translate_in_special_chars(char * expression); void translate_in_ASCII_chars(char * expression); Poincare::Expression parse_expression(const char * expression, bool canBeUnparsable = false); +Poincare::Expression parse_and_simplify(const char * expression); + void assert_expression_not_parsable(const char * expression); void assert_parsed_expression_type(const char * expression, Poincare::ExpressionNode::Type type); void assert_parsed_expression_is(const char * expression, Poincare::Expression r); -void assert_parsed_expression_polynomial_degree(const char * expression, int degree, const char * symbolName = "x"); +void assert_parsed_expression_polynomial_degree(const char * expression, int degree, const char * symbolName = "x", Poincare::Preferences::ComplexFormat complexFormat = Cartesian); void assert_simplify(const char * expression); template -void assert_parsed_expression_evaluates_to(const char * expression, const char * approximation, Poincare::Preferences::AngleUnit angleUnit = Degree, Poincare::Preferences::ComplexFormat complexFormat = Cartesian, int numberOfSignificantDigits = -1); +void assert_parsed_expression_evaluates_to(const char * expression, const char * approximation, Poincare::ExpressionNode::ReductionTarget target = System, Poincare::Preferences::AngleUnit angleUnit = Degree, Poincare::Preferences::ComplexFormat complexFormat = Cartesian, int numberOfSignificantDigits = -1); template -void assert_parsed_expression_approximates_with_value_for_symbol(Poincare::Expression expression, const char * symbol, T value, T approximation, Poincare::Preferences::AngleUnit angleUnit = Degree); +void assert_parsed_expression_evaluates_without_simplifying_to(const char * expression, const char * approximation, Poincare::Preferences::AngleUnit angleUnit = Degree, Poincare::Preferences::ComplexFormat complexFormat = Cartesian, int numberOfSignificantDigits = -1); -void assert_parsed_expression_simplify_to(const char * expression, const char * simplifiedExpression, Poincare::Preferences::AngleUnit angleUnit = Poincare::Preferences::AngleUnit::Radian); -void assert_parsed_expression_serialize_to(Poincare::Expression expression, const char * serializedExpression, Poincare::Preferences::PrintFloatMode mode = DecimalMode, int numberOfSignifiantDigits = -1); +template +void assert_parsed_expression_approximates_with_value_for_symbol(Poincare::Expression expression, const char * symbol, T value, T approximation, Poincare::Preferences::ComplexFormat complexFormat = Cartesian, Poincare::Preferences::AngleUnit angleUnit = Degree); + +void assert_parsed_expression_simplify_to(const char * expression, const char * simplifiedExpression, Poincare::ExpressionNode::ReductionTarget target = Poincare::ExpressionNode::ReductionTarget::User, Poincare::Preferences::AngleUnit angleUnit = Poincare::Preferences::AngleUnit::Radian, Poincare::Preferences::ComplexFormat complexFormat = Poincare::Preferences::ComplexFormat::Cartesian); +void assert_parsed_expression_serialize_to(Poincare::Expression expression, const char * serializedExpression, Poincare::Preferences::PrintFloatMode mode = DecimalMode, int numberOfSignifiantDigits = 7); // Layouts diff --git a/poincare/test/infinity.cpp b/poincare/test/infinity.cpp index aff3038ba..c84e8765f 100644 --- a/poincare/test/infinity.cpp +++ b/poincare/test/infinity.cpp @@ -24,6 +24,9 @@ QUIZ_CASE(poincare_infinity) { assert_parsed_expression_simplify_to("1E-1000", "0"); assert_parsed_expression_evaluates_to("1*10^1000", "inf"); + assert_parsed_expression_simplify_to("inf^0", "undef"); + assert_parsed_expression_simplify_to("1^inf", "1^inf"); + assert_parsed_expression_simplify_to("1^(X^inf)", "1^(X^inf)"); assert_parsed_expression_simplify_to("inf^(-1)", "0"); assert_parsed_expression_simplify_to("(-inf)^(-1)", "0"); assert_parsed_expression_simplify_to("inf^(-R(2))", "0"); @@ -34,5 +37,20 @@ QUIZ_CASE(poincare_infinity) { assert_parsed_expression_simplify_to("(-inf)^R(2)", "inf*(-1)^R(2)"); assert_parsed_expression_simplify_to("inf^x", "inf^x"); assert_parsed_expression_simplify_to("1/inf+24", "24"); + assert_parsed_expression_simplify_to("X^(inf)/inf", "0*X^inf"); + + // Logarithm + assert_parsed_expression_simplify_to("log(inf,0)", "undef"); + assert_parsed_expression_simplify_to("log(inf,1)", "undef"); + assert_parsed_expression_simplify_to("log(0,inf)", "undef"); + assert_parsed_expression_simplify_to("log(1,inf)", "0"); + assert_parsed_expression_simplify_to("log(inf,inf)", "undef"); + + assert_parsed_expression_simplify_to("ln(inf)", "inf"); + assert_parsed_expression_simplify_to("log(inf,-3)", "log(inf,-3)"); + assert_parsed_expression_simplify_to("log(inf,3)", "inf"); + assert_parsed_expression_simplify_to("log(inf,0.3)", "-inf"); + assert_parsed_expression_simplify_to("log(inf,x)", "log(inf,x)"); + assert_parsed_expression_simplify_to("ln(inf)*0", "undef"); } diff --git a/poincare/test/integer.cpp b/poincare/test/integer.cpp index c5a19f493..2d1cf098e 100644 --- a/poincare/test/integer.cpp +++ b/poincare/test/integer.cpp @@ -68,9 +68,9 @@ QUIZ_CASE(poincare_integer_properties) { quiz_assert(Integer(1).isOne()); quiz_assert(!Integer(-1).isOne()); quiz_assert(!Integer(0).isOne()); - quiz_assert(OverflowedInteger().isInfinity()); // 2^32^k_maxNumberOfDigits - quiz_assert(!MaxInteger().isInfinity()); // 2^32^k_maxNumberOfDigits-1 - quiz_assert(!Integer(0).isInfinity()); + quiz_assert(OverflowedInteger().isOverflow()); // 2^32^k_maxNumberOfDigits + quiz_assert(!MaxInteger().isOverflow()); // 2^32^k_maxNumberOfDigits-1 + quiz_assert(!Integer(0).isOverflow()); quiz_assert(Integer(8).isEven()); quiz_assert(!Integer(7).isEven()); quiz_assert(Integer(-8).isEven()); diff --git a/poincare/test/layouts.cpp b/poincare/test/layouts.cpp index e90da6bdf..c8ee32a97 100644 --- a/poincare/test/layouts.cpp +++ b/poincare/test/layouts.cpp @@ -14,13 +14,15 @@ void assert_parsed_layout_is(Layout l, Poincare::Expression r) { char buffer[bufferSize]; l.serializeForParsing(buffer, bufferSize); Expression e = parse_expression(buffer); - Expression eSimplified = e.clone().simplify(context, Preferences::AngleUnit::Degree); + Expression eSimplified; + e.clone().simplifyAndApproximate(&eSimplified, nullptr, context, Cartesian, Degree); if (eSimplified.isUninitialized()) { /* In case the simplification is impossible (if there are matrices for * instance), use the non simplified expression */ eSimplified = e; } - Expression rSimplified = r.clone().simplify(context, Preferences::AngleUnit::Degree); + Expression rSimplified; + r.clone().simplifyAndApproximate(&rSimplified, nullptr, context, Cartesian, Degree); if (rSimplified.isUninitialized()) { /* In case the simplification is impossible (if there are matrices for * instance), use the non simplified expression */ @@ -40,32 +42,38 @@ void assert_parsed_layout_is(Layout l, Poincare::Expression r) { } QUIZ_CASE(poincare_create_all_layouts) { - EmptyLayout e0; - AbsoluteValueLayout e1(e0); - CharLayout e2('a'); - BinomialCoefficientLayout e3(e1, e2); - CeilingLayout e4(e3); - RightParenthesisLayout e5; - RightSquareBracketLayout e6; - CondensedSumLayout e7(e4, e5, e6); - ConjugateLayout e8(e7); - LeftParenthesisLayout e10; - FloorLayout e11(e10); - FractionLayout e12(e8, e11); - HorizontalLayout e13; - LeftSquareBracketLayout e14; - IntegralLayout e15(e11, e12, e13, e14); - NthRootLayout e16(e15); - MatrixLayout e17; - EmptyLayout e18; - EmptyLayout e19; - EmptyLayout e20; - ProductLayout e21(e17, e18, e19, e20); - EmptyLayout e22; - EmptyLayout e23; - EmptyLayout e24; - SumLayout e25(e21, e22, e23, e24); - VerticalOffsetLayout e26(e25, VerticalOffsetLayoutNode::Type::Superscript); + EmptyLayout e0 = EmptyLayout::Builder(); + AbsoluteValueLayout e1 = AbsoluteValueLayout::Builder(e0); + CharLayout e2 = CharLayout::Builder('a'); + BinomialCoefficientLayout e3 = BinomialCoefficientLayout::Builder(e1, e2); + CeilingLayout e4 = CeilingLayout::Builder(e3); + RightParenthesisLayout e5 = RightParenthesisLayout::Builder(); + RightSquareBracketLayout e6 = RightSquareBracketLayout::Builder(); + CondensedSumLayout e7 = CondensedSumLayout::Builder(e4, e5, e6); + ConjugateLayout e8 = ConjugateLayout::Builder(e7); + LeftParenthesisLayout e10 = LeftParenthesisLayout::Builder(); + FloorLayout e11 = FloorLayout::Builder(e10); + FractionLayout e12 = FractionLayout::Builder(e8, e11); + HorizontalLayout e13 = HorizontalLayout::Builder(); + LeftSquareBracketLayout e14 = LeftSquareBracketLayout::Builder(); + IntegralLayout e15 = IntegralLayout::Builder(e11, e12, e13, e14); + NthRootLayout e16 = NthRootLayout::Builder(e15); + MatrixLayout e17 = MatrixLayout::Builder(); + EmptyLayout e18 = EmptyLayout::Builder(); + EmptyLayout e19 = EmptyLayout::Builder(); + EmptyLayout e20 = EmptyLayout::Builder(); + ProductLayout e21 = ProductLayout::Builder(e17, e18, e19, e20); + EmptyLayout e22 = EmptyLayout::Builder(); + EmptyLayout e23 = EmptyLayout::Builder(); + EmptyLayout e24 = EmptyLayout::Builder(); + SumLayout e25 = SumLayout::Builder(e21, e22, e23, e24); + VerticalOffsetLayout e26 = VerticalOffsetLayout::Builder(e25, VerticalOffsetLayoutNode::Type::Superscript); +} + +Matrix BuildOneChildMatrix(Expression entry) { + Matrix m = Matrix::Builder(); + m.addChildAtIndexInPlace(entry, 0, 0); + return m; } QUIZ_CASE(poincare_parse_layouts) { @@ -73,195 +81,195 @@ QUIZ_CASE(poincare_parse_layouts) { Expression e; // 1+2 - l = HorizontalLayout( - CharLayout('1'), - CharLayout('+'), - CharLayout('2')); - e = Addition(Rational(1), Rational(2)); + l = HorizontalLayout::Builder( + CharLayout::Builder('1'), + CharLayout::Builder('+'), + CharLayout::Builder('2')); + e = Addition::Builder(Rational::Builder(1), Rational::Builder(2)); assert_parsed_layout_is(l, e); // |3+3/6| - l = AbsoluteValueLayout( - HorizontalLayout( - CharLayout('3'), - CharLayout('+'), - FractionLayout( - CharLayout('3'), - CharLayout('6')))); + l = AbsoluteValueLayout:: Builder( + HorizontalLayout::Builder( + CharLayout::Builder('3'), + CharLayout::Builder('+'), + FractionLayout::Builder( + CharLayout::Builder('3'), + CharLayout::Builder('6')))); e = AbsoluteValue::Builder( - Addition( - Rational(3), - Division( - Rational(3), - Rational(6)))); + Addition::Builder( + Rational::Builder(3), + Division::Builder( + Rational::Builder(3), + Rational::Builder(6)))); assert_parsed_layout_is(l, e); // binCoef(4,5) - l = BinomialCoefficientLayout( - CharLayout('4'), - CharLayout('5')); + l = BinomialCoefficientLayout::Builder( + CharLayout::Builder('4'), + CharLayout::Builder('5')); e = BinomialCoefficient::Builder( - Rational(4), - Rational(5)); + Rational::Builder(4), + Rational::Builder(5)); assert_parsed_layout_is(l, e); // ceil(4.6) - l = CeilingLayout( - HorizontalLayout( - CharLayout('4'), - CharLayout('.'), - CharLayout('6'))); + l = CeilingLayout::Builder( + HorizontalLayout::Builder( + CharLayout::Builder('4'), + CharLayout::Builder('.'), + CharLayout::Builder('6'))); e = Ceiling::Builder( - Decimal(4.6)); + Decimal::Builder(4.6)); assert_parsed_layout_is(l, e); // floor(7.2) - l = FloorLayout( - HorizontalLayout( - CharLayout('7'), - CharLayout('.'), - CharLayout('2'))); + l = FloorLayout::Builder( + HorizontalLayout::Builder( + CharLayout::Builder('7'), + CharLayout::Builder('.'), + CharLayout::Builder('2'))); e = Floor::Builder( - Decimal(7.2)); + Decimal::Builder(7.2)); assert_parsed_layout_is(l, e); // 2^(3+4) - l = HorizontalLayout( - CharLayout('2'), - VerticalOffsetLayout( - HorizontalLayout( - CharLayout('3'), - CharLayout('+'), - CharLayout('4')), + l = HorizontalLayout::Builder( + CharLayout::Builder('2'), + VerticalOffsetLayout::Builder( + HorizontalLayout::Builder( + CharLayout::Builder('3'), + CharLayout::Builder('+'), + CharLayout::Builder('4')), VerticalOffsetLayoutNode::Type::Superscript)); - e = Power( - Rational(2), - Addition( - Rational(3), - Rational(4))); + e = Power::Builder( + Rational::Builder(2), + Addition::Builder( + Rational::Builder(3), + Rational::Builder(4))); assert_parsed_layout_is(l, e); // log_3(2) - HorizontalLayout l1 = HorizontalLayout(); - l1.addChildAtIndex(CharLayout('l'), l1.numberOfChildren(), l1.numberOfChildren(), nullptr); - l1.addChildAtIndex(CharLayout('o'), l1.numberOfChildren(), l1.numberOfChildren(), nullptr); - l1.addChildAtIndex(CharLayout('g'), l1.numberOfChildren(), l1.numberOfChildren(), nullptr); - l1.addChildAtIndex(VerticalOffsetLayout(CharLayout('3'), VerticalOffsetLayoutNode::Type::Subscript), l1.numberOfChildren(), l1.numberOfChildren(), nullptr); - l1.addChildAtIndex(LeftParenthesisLayout(), l1.numberOfChildren(), l1.numberOfChildren(), nullptr); - l1.addChildAtIndex(CharLayout('2'), l1.numberOfChildren(), l1.numberOfChildren(), nullptr); - l1.addChildAtIndex(RightParenthesisLayout(), l1.numberOfChildren(), l1.numberOfChildren(), nullptr); + HorizontalLayout l1 = HorizontalLayout::Builder(); + l1.addChildAtIndex(CharLayout::Builder('l'), l1.numberOfChildren(), l1.numberOfChildren(), nullptr); + l1.addChildAtIndex(CharLayout::Builder('o'), l1.numberOfChildren(), l1.numberOfChildren(), nullptr); + l1.addChildAtIndex(CharLayout::Builder('g'), l1.numberOfChildren(), l1.numberOfChildren(), nullptr); + l1.addChildAtIndex(VerticalOffsetLayout::Builder(CharLayout::Builder('3'), VerticalOffsetLayoutNode::Type::Subscript), l1.numberOfChildren(), l1.numberOfChildren(), nullptr); + l1.addChildAtIndex(LeftParenthesisLayout::Builder(), l1.numberOfChildren(), l1.numberOfChildren(), nullptr); + l1.addChildAtIndex(CharLayout::Builder('2'), l1.numberOfChildren(), l1.numberOfChildren(), nullptr); + l1.addChildAtIndex(RightParenthesisLayout::Builder(), l1.numberOfChildren(), l1.numberOfChildren(), nullptr); l = l1; e = Logarithm::Builder( - Rational(2), - Rational(3)); + Rational::Builder(2), + Rational::Builder(3)); assert_parsed_layout_is(l, e); // root(5,3) - l = NthRootLayout( - CharLayout('5'), - CharLayout('3')); - e = NthRoot::Builder(Rational(5), Rational(3)); + l = NthRootLayout::Builder( + CharLayout::Builder('5'), + CharLayout::Builder('3')); + e = NthRoot::Builder(Rational::Builder(5), Rational::Builder(3)); assert_parsed_layout_is(l, e); // int(7, x, 4, 5) - l = IntegralLayout( - CharLayout('7'), - CharLayout('x'), - CharLayout('4'), - CharLayout('5')); + l = IntegralLayout::Builder( + CharLayout::Builder('7'), + CharLayout::Builder('x'), + CharLayout::Builder('4'), + CharLayout::Builder('5')); e = Integral::Builder( - Rational(7), - Symbol('x'), - Rational(4), - Rational(5)); + Rational::Builder(7), + Symbol::Builder('x'), + Rational::Builder(4), + Rational::Builder(5)); assert_parsed_layout_is(l, e); // 2^2 ! - l = HorizontalLayout( - CharLayout('2'), - VerticalOffsetLayout( - CharLayout('2'), + l = HorizontalLayout::Builder( + CharLayout::Builder('2'), + VerticalOffsetLayout::Builder( + CharLayout::Builder('2'), VerticalOffsetLayoutNode::Type::Superscript), - CharLayout('!')); - e = Factorial( - Power( - Rational(2), - Rational(2))); + CharLayout::Builder('!')); + e = Factorial::Builder( + Power::Builder( + Rational::Builder(2), + Rational::Builder(2))); assert_parsed_layout_is(l, e); // 5* 6/(7+5) *3 - l = HorizontalLayout( - CharLayout('5'), - FractionLayout( - CharLayout('6'), - HorizontalLayout( - CharLayout('7'), - CharLayout('+'), - CharLayout('5'))), - CharLayout('3')); - e = Multiplication( - Rational(5), - Division( - Rational(6), - Addition( - Rational(7), - Rational(5))), - Rational(3)); + l = HorizontalLayout::Builder( + CharLayout::Builder('5'), + FractionLayout::Builder( + CharLayout::Builder('6'), + HorizontalLayout::Builder( + CharLayout::Builder('7'), + CharLayout::Builder('+'), + CharLayout::Builder('5'))), + CharLayout::Builder('3')); + e = Multiplication::Builder( + Rational::Builder(5), + Division::Builder( + Rational::Builder(6), + Addition::Builder( + Rational::Builder(7), + Rational::Builder(5))), + Rational::Builder(3)); assert_parsed_layout_is(l, e); // [[3^2!, 7][4,5] - l = MatrixLayout( - HorizontalLayout( - CharLayout('3'), - VerticalOffsetLayout( - CharLayout('2'), + l = MatrixLayout::Builder( + HorizontalLayout::Builder( + CharLayout::Builder('3'), + VerticalOffsetLayout::Builder( + CharLayout::Builder('2'), VerticalOffsetLayoutNode::Type::Superscript), - CharLayout('!')), - CharLayout('7'), - CharLayout('4'), - CharLayout('5')); - Matrix m = Matrix( - Factorial( - Power( - Rational(3), - Rational(2)))); - m.addChildAtIndexInPlace(Rational(7), 1, 1); - m.addChildAtIndexInPlace(Rational(4), 2, 2); - m.addChildAtIndexInPlace(Rational(5), 3, 3); + CharLayout::Builder('!')), + CharLayout::Builder('7'), + CharLayout::Builder('4'), + CharLayout::Builder('5')); + Matrix m = BuildOneChildMatrix( + Factorial::Builder( + Power::Builder( + Rational::Builder(3), + Rational::Builder(2)))); + m.addChildAtIndexInPlace(Rational::Builder(7), 1, 1); + m.addChildAtIndexInPlace(Rational::Builder(4), 2, 2); + m.addChildAtIndexInPlace(Rational::Builder(5), 3, 3); m.setDimensions(2,2); e = m; assert_parsed_layout_is(l, e); // 2^det([[3!, 7][4,5]) - l = HorizontalLayout( - CharLayout('2'), - VerticalOffsetLayout( - MatrixLayout( - HorizontalLayout( - CharLayout('3'), - CharLayout('!')), - CharLayout('7'), - CharLayout('4'), - CharLayout('5')), + l = HorizontalLayout::Builder( + CharLayout::Builder('2'), + VerticalOffsetLayout::Builder( + MatrixLayout::Builder( + HorizontalLayout::Builder( + CharLayout::Builder('3'), + CharLayout::Builder('!')), + CharLayout::Builder('7'), + CharLayout::Builder('4'), + CharLayout::Builder('5')), VerticalOffsetLayoutNode::Type::Superscript)); - m = Matrix( - Factorial( - Rational(3))); - m.addChildAtIndexInPlace(Rational(7), 1, 1); - m.addChildAtIndexInPlace(Rational(4), 2, 2); - m.addChildAtIndexInPlace(Rational(5), 3, 3); + m = BuildOneChildMatrix( + Factorial::Builder( + Rational::Builder(3))); + m.addChildAtIndexInPlace(Rational::Builder(7), 1, 1); + m.addChildAtIndexInPlace(Rational::Builder(4), 2, 2); + m.addChildAtIndexInPlace(Rational::Builder(5), 3, 3); m.setDimensions(2,2); - e = Power(Rational(2), m); + e = Power::Builder(Rational::Builder(2), m); assert_parsed_layout_is(l, e); // 2e^3 - l = HorizontalLayout( - CharLayout('2'), - CharLayout(Ion::Charset::Exponential), - VerticalOffsetLayout( - CharLayout('3'), + l = HorizontalLayout::Builder( + CharLayout::Builder('2'), + CharLayout::Builder(Ion::Charset::Exponential), + VerticalOffsetLayout::Builder( + CharLayout::Builder('3'), VerticalOffsetLayoutNode::Type::Superscript)); - e = Multiplication(Rational(2),Power(Constant(Ion::Charset::Exponential),Parenthesis(Rational(3)))); - assert_parsed_expression_is("2X^(3)", Multiplication(Rational(2),Power(Constant(Ion::Charset::Exponential),Parenthesis(Rational(3))))); + e = Multiplication::Builder(Rational::Builder(2),Power::Builder(Constant::Builder(Ion::Charset::Exponential),Parenthesis::Builder(Rational::Builder(3)))); + assert_parsed_expression_is("2X^(3)", Multiplication::Builder(Rational::Builder(2),Power::Builder(Constant::Builder(Ion::Charset::Exponential),Parenthesis::Builder(Rational::Builder(3))))); assert_parsed_layout_is(l, e); } diff --git a/poincare/test/logarithm.cpp b/poincare/test/logarithm.cpp index d2628b7cf..8354a4bec 100644 --- a/poincare/test/logarithm.cpp +++ b/poincare/test/logarithm.cpp @@ -32,10 +32,10 @@ QUIZ_CASE(poincare_logarithm_simplify) { assert_parsed_expression_simplify_to("log(0,0.14+I)", Undefined::Name()); assert_parsed_expression_simplify_to("log(2,1)", Undefined::Name()); assert_parsed_expression_simplify_to("log(x,1)", Undefined::Name()); - assert_parsed_expression_simplify_to("log(12925)", "2*log(5)+log(11)+log(47)"); - assert_parsed_expression_simplify_to("ln(12925)", "2*ln(5)+ln(11)+ln(47)"); - assert_parsed_expression_simplify_to("log(1742279/12925, 6)", "-2*log(5,6)+log(7,6)+3*log(11,6)+log(17,6)-log(47,6)"); - assert_parsed_expression_simplify_to("ln(2/3)", "ln(2)-ln(3)"); + assert_parsed_expression_simplify_to("log(12925)", "log(47)+log(11)+2*log(5)"); + assert_parsed_expression_simplify_to("ln(12925)", "ln(47)+ln(11)+2*ln(5)"); + assert_parsed_expression_simplify_to("log(1742279/12925, 6)", "-log(47,6)+log(17,6)+3*log(11,6)+log(7,6)-2*log(5,6)"); + assert_parsed_expression_simplify_to("ln(2/3)", "-ln(3)+ln(2)"); assert_parsed_expression_simplify_to("log(1742279/12925, -6)", "log(158389/1175,-6)"); assert_parsed_expression_simplify_to("ln(R(2))", "ln(2)/2"); assert_parsed_expression_simplify_to("ln(X^3)", "3"); @@ -43,21 +43,21 @@ QUIZ_CASE(poincare_logarithm_simplify) { assert_parsed_expression_simplify_to("log(R(3),R(3))", "1"); assert_parsed_expression_simplify_to("log(1/R(2))", "-log(2)/2"); assert_parsed_expression_simplify_to("log(-I)", "log(-I)"); - assert_parsed_expression_simplify_to("ln(X^(IP/7))", "(P*I)/7"); + assert_parsed_expression_simplify_to("ln(X^(IP/7))", "P/7*I"); assert_parsed_expression_simplify_to("log(10^24)", "24"); assert_parsed_expression_simplify_to("log((23P)^4,23P)", "4"); - assert_parsed_expression_simplify_to("log(10^(2+P))", "2+P"); + assert_parsed_expression_simplify_to("log(10^(2+P))", "P+2"); assert_parsed_expression_simplify_to("ln(1881676377434183981909562699940347954480361860897069)", "ln(1881676377434183981909562699940347954480361860897069)"); /* log(1002101470343) does no reduce to 3*log(10007) because it involves * prime factors above k_biggestPrimeFactor */ assert_parsed_expression_simplify_to("log(1002101470343)", "log(1002101470343)"); assert_parsed_expression_simplify_to("log(64,2)", "6"); assert_parsed_expression_simplify_to("log(2,64)", "log(2,64)"); - assert_parsed_expression_simplify_to("log(1476225,5)", "2+10*log(3,5)"); + assert_parsed_expression_simplify_to("log(1476225,5)", "10*log(3,5)+2"); assert_parsed_expression_simplify_to("log(100)", "2"); assert_parsed_expression_simplify_to("log(1000000)", "6"); - assert_parsed_expression_simplify_to("log(70992768,14)", "5+2*log(2,14)+log(3,14)+log(11,14)"); - assert_parsed_expression_simplify_to("log(1/6991712,14)", "-5-log(13,14)"); + assert_parsed_expression_simplify_to("log(70992768,14)", "log(11,14)+log(3,14)+2*log(2,14)+5"); + assert_parsed_expression_simplify_to("log(1/6991712,14)", "-log(13,14)-5"); assert_parsed_expression_simplify_to("log(4,10)", "2*log(2)"); } diff --git a/poincare/test/multiplication.cpp b/poincare/test/multiplication.cpp index 69c34e72b..41d4fd816 100644 --- a/poincare/test/multiplication.cpp +++ b/poincare/test/multiplication.cpp @@ -20,9 +20,10 @@ QUIZ_CASE(poincare_multiplication_evaluate) { } QUIZ_CASE(poincare_multiplication_simplify) { + assert_parsed_expression_simplify_to("undef*x", "undef"); assert_parsed_expression_simplify_to("0*x+B", "B"); assert_parsed_expression_simplify_to("0*x*0*32*cos(3)", "0"); - assert_parsed_expression_simplify_to("3*A^4*B^x*B^2*(A^2+2)*2*1.2", "(72*A^4*B^(2+x)+36*A^6*B^(2+x))/5"); + assert_parsed_expression_simplify_to("3*A^4*B^x*B^2*(A^2+2)*2*1.2", "(36*A^6*B^(x+2)+72*A^4*B^(x+2))/5"); assert_parsed_expression_simplify_to("A*(B+C)*(D+3)", "3*A*B+3*A*C+A*B*D+A*C*D"); assert_parsed_expression_simplify_to("A/B", "A/B"); assert_parsed_expression_simplify_to("(A*B)^2", "A^2*B^2"); @@ -52,8 +53,8 @@ QUIZ_CASE(poincare_multiplication_simplify) { assert_parsed_expression_simplify_to("A^3*A^(-3)", "1"); assert_parsed_expression_simplify_to("2^P*(1/2)^P", "1"); assert_parsed_expression_simplify_to("A^3*A^(-3)", "1"); - assert_parsed_expression_simplify_to("(x+1)*(x+2)", "2+3*x+x^2"); - assert_parsed_expression_simplify_to("(x+1)*(x-1)", "-1+x^2"); + assert_parsed_expression_simplify_to("(x+1)*(x+2)", "x^2+3*x+2"); + assert_parsed_expression_simplify_to("(x+1)*(x-1)", "x^2-1"); assert_parsed_expression_simplify_to("11P/(22P+11P)", "1/3"); assert_parsed_expression_simplify_to("11/(22P+11P)", "1/(3*P)"); assert_parsed_expression_simplify_to("-11/(22P+11P)", "-1/(3*P)"); diff --git a/poincare/test/number.cpp b/poincare/test/number.cpp index 5bdcb2316..078ced52d 100644 --- a/poincare/test/number.cpp +++ b/poincare/test/number.cpp @@ -12,27 +12,27 @@ using namespace Poincare; QUIZ_CASE(poincare_number_parser) { // Integer - assert_parsed_expression_is("123456789012345678765434567", Rational("123456789012345678765434567")); - assert_parsed_expression_is(MaxIntegerString(), Rational(MaxIntegerString())); + assert_parsed_expression_is("123456789012345678765434567", Rational::Builder("123456789012345678765434567")); + assert_parsed_expression_is(MaxIntegerString(), Rational::Builder(MaxIntegerString())); // Integer parsed in Decimal because they overflow Integer - assert_parsed_expression_is(OverflowedIntegerString(), Decimal(Integer("17976931348623"), 308)); - assert_parsed_expression_is("179769313486235590772930519078902473361797697894230657273430081157732675805500963132708477322407536021120113879871393357658789768814416622492847430639474124377767893424865485276302219601246094119453082952085005768838150682342462881473913110540827237163350510684586298239947245938479716304835356329624224137216", Decimal(Integer("17976931348624"), 308)); + assert_parsed_expression_is(OverflowedIntegerString(), Decimal::Builder(Integer("17976931348623"), 308)); + assert_parsed_expression_is("179769313486235590772930519078902473361797697894230657273430081157732675805500963132708477322407536021120113879871393357658789768814416622492847430639474124377767893424865485276302219601246094119453082952085005768838150682342462881473913110540827237163350510684586298239947245938479716304835356329624224137216", Decimal::Builder(Integer("17976931348624"), 308)); // Decimal with rounding when digits are above 14 - assert_parsed_expression_is("0.0000012345678901234", Decimal(Integer("12345678901234"), -6)); - assert_parsed_expression_is("0.00000123456789012345", Decimal(Integer("12345678901235"), -6)); - assert_parsed_expression_is("0.00000123456789012341", Decimal(Integer("12345678901234"), -6)); - assert_parsed_expression_is("1234567890123.4", Decimal(Integer("12345678901234"), 12)); - assert_parsed_expression_is("123456789012345.2", Decimal(Integer("12345678901235"), 14)); - assert_parsed_expression_is("123456789012341.2", Decimal(Integer("12345678901234"), 14)); - assert_parsed_expression_is("12.34567", Decimal(Integer("1234567"), 1)); + assert_parsed_expression_is("0.0000012345678901234", Decimal::Builder(Integer("12345678901234"), -6)); + assert_parsed_expression_is("0.00000123456789012345", Decimal::Builder(Integer("12345678901235"), -6)); + assert_parsed_expression_is("0.00000123456789012341", Decimal::Builder(Integer("12345678901234"), -6)); + assert_parsed_expression_is("1234567890123.4", Decimal::Builder(Integer("12345678901234"), 12)); + assert_parsed_expression_is("123456789012345.2", Decimal::Builder(Integer("12345678901235"), 14)); + assert_parsed_expression_is("123456789012341.2", Decimal::Builder(Integer("12345678901234"), 14)); + assert_parsed_expression_is("12.34567", Decimal::Builder(Integer("1234567"), 1)); // Infinity - assert_parsed_expression_is("23E1000", Infinity(false)); - assert_parsed_expression_is("2.3E1000", Decimal(Integer(23), 1000)); + assert_parsed_expression_is("23E1000", Infinity::Builder(false)); + assert_parsed_expression_is("2.3E1000", Decimal::Builder(Integer(23), 1000)); // Zero - assert_parsed_expression_is("0.23E-1000", Decimal(Integer(0), 0)); - assert_parsed_expression_is("0.23E-999", Decimal(Integer(23), -1000)); + assert_parsed_expression_is("0.23E-1000", Decimal::Builder(Integer(0), 0)); + assert_parsed_expression_is("0.23E-999", Decimal::Builder(Integer(23), -1000)); } diff --git a/poincare/test/parentheses_layout.cpp b/poincare/test/parentheses_layout.cpp index 2518d312a..1cb82c0d3 100644 --- a/poincare/test/parentheses_layout.cpp +++ b/poincare/test/parentheses_layout.cpp @@ -12,20 +12,20 @@ QUIZ_CASE(poincare_parenthesis_layout_size) { * 4 * Assert that the first and last parentheses have the same size. */ - HorizontalLayout layout = HorizontalLayout(); - LeftParenthesisLayout leftPar = LeftParenthesisLayout(); - RightParenthesisLayout rightPar = RightParenthesisLayout(); + HorizontalLayout layout = HorizontalLayout::Builder(); + LeftParenthesisLayout leftPar = LeftParenthesisLayout::Builder(); + RightParenthesisLayout rightPar = RightParenthesisLayout::Builder(); layout.addChildAtIndex(leftPar, 0, 0, nullptr); - layout.addChildAtIndex(CharLayout('2'), 1, 1, nullptr); - layout.addChildAtIndex(CharLayout('+'), 2, 2, nullptr); - layout.addChildAtIndex(LeftParenthesisLayout(), 3, 3, nullptr); - layout.addChildAtIndex(FractionLayout( - CharLayout('3'), - CharLayout('4')), + layout.addChildAtIndex(CharLayout::Builder('2'), 1, 1, nullptr); + layout.addChildAtIndex(CharLayout::Builder('+'), 2, 2, nullptr); + layout.addChildAtIndex(LeftParenthesisLayout::Builder(), 3, 3, nullptr); + layout.addChildAtIndex(FractionLayout::Builder( + CharLayout::Builder('3'), + CharLayout::Builder('4')), 4, 4, nullptr); - layout.addChildAtIndex(RightParenthesisLayout(), 4, 4, nullptr); - layout.addChildAtIndex(CharLayout('6'), 5, 5, nullptr); + layout.addChildAtIndex(RightParenthesisLayout::Builder(), 4, 4, nullptr); + layout.addChildAtIndex(CharLayout::Builder('6'), 5, 5, nullptr); layout.addChildAtIndex(rightPar, 7, 7, nullptr); - layout.addChildAtIndex(CharLayout('1'), 8, 8, nullptr); + layout.addChildAtIndex(CharLayout::Builder('1'), 8, 8, nullptr); quiz_assert(leftPar.layoutSize().height() == rightPar.layoutSize().height()); } diff --git a/poincare/test/parser.cpp b/poincare/test/parser.cpp index 56488fdfe..2c50972f9 100644 --- a/poincare/test/parser.cpp +++ b/poincare/test/parser.cpp @@ -84,18 +84,18 @@ QUIZ_CASE(poincare_parser_parse_numbers) { assert_pool_size(initialPoolSize); // Parse digits - assert_parsed_expression_is("0", Rational(0)); - assert_parsed_expression_is("0.1", Decimal(0.1)); - assert_parsed_expression_is("1.", Rational(1)); - assert_parsed_expression_is(".1", Decimal(0.1)); - assert_parsed_expression_is("0E2", Decimal(0.0)); - assert_parsed_expression_is("0.1E2", Decimal(10.0)); - assert_parsed_expression_is("1.E2", Decimal(100.0)); - assert_parsed_expression_is(".1E2", Decimal(10.0)); - assert_parsed_expression_is("0E-2", Decimal(0.0)); - assert_parsed_expression_is("0.1E-2", Decimal(0.001)); - assert_parsed_expression_is("1.E-2", Decimal(0.01)); - assert_parsed_expression_is(".1E-2", Decimal(0.001)); + assert_parsed_expression_is("0", Rational::Builder(0)); + assert_parsed_expression_is("0.1", Decimal::Builder(0.1)); + assert_parsed_expression_is("1.", Rational::Builder(1)); + assert_parsed_expression_is(".1", Decimal::Builder(0.1)); + assert_parsed_expression_is("0E2", Decimal::Builder(0.0)); + assert_parsed_expression_is("0.1E2", Decimal::Builder(10.0)); + assert_parsed_expression_is("1.E2", Decimal::Builder(100.0)); + assert_parsed_expression_is(".1E2", Decimal::Builder(10.0)); + assert_parsed_expression_is("0E-2", Decimal::Builder(0.0)); + assert_parsed_expression_is("0.1E-2", Decimal::Builder(0.001)); + assert_parsed_expression_is("1.E-2", Decimal::Builder(0.01)); + assert_parsed_expression_is(".1E-2", Decimal::Builder(0.001)); } QUIZ_CASE(poincare_parser_memory_exhaustion) { @@ -104,7 +104,7 @@ QUIZ_CASE(poincare_parser_memory_exhaustion) { { Poincare::ExceptionCheckpoint ecp; if (ExceptionRun(ecp)) { - Addition a = Addition(); + Addition a = Addition::Builder(); while (true) { Expression e = Expression::Parse("1+2+3+4+5+6+7+8+9+10"); a.addChildAtIndexInPlace(e, 0, a.numberOfChildren()); @@ -123,66 +123,66 @@ QUIZ_CASE(poincare_parser_memory_exhaustion) { } QUIZ_CASE(poincare_parser_parse) { - assert_parsed_expression_is("1", Rational(1)); - assert_parsed_expression_is("(1)", Parenthesis(Rational(1))); - assert_parsed_expression_is("((1))", Parenthesis((Expression)Parenthesis(Rational(1)))); - assert_parsed_expression_is("1+2", Addition(Rational(1),Rational(2))); - assert_parsed_expression_is("(1)+2", Addition(Parenthesis(Rational(1)),Rational(2))); - assert_parsed_expression_is("(1+2)", Parenthesis(Addition(Rational(1),Rational(2)))); - assert_parsed_expression_is("1+2+3", Addition(Addition(Rational(1),Rational(2)),Rational(3))); - assert_parsed_expression_is("1+2+(3+4)", Addition(Addition(Rational(1),Rational(2)),Parenthesis(Addition(Rational(3),Rational(4))))); - assert_parsed_expression_is("1*2", Multiplication(Rational(1),Rational(2))); - assert_parsed_expression_is("1*2*3", Multiplication(Multiplication(Rational(1),Rational(2)),Rational(3))); - assert_parsed_expression_is("1+2*3", Addition(Rational(1), Multiplication(Rational(2), Rational(3)))); - assert_parsed_expression_is("1/2", Division(Rational(1),Rational(2))); - assert_parsed_expression_is("(1/2)", Parenthesis(Division(Rational(1),Rational(2)))); - assert_parsed_expression_is("1/2/3", Division(Division(Rational(1),Rational(2)),Rational(3))); - assert_parsed_expression_is("1/2*3", Multiplication(Division(Rational(1),Rational(2)),Rational(3))); - assert_parsed_expression_is("(1/2*3)", Parenthesis(Multiplication(Division(Rational(1),Rational(2)),Rational(3)))); - assert_parsed_expression_is("1*2/3", Multiplication(Rational(1),Division(Rational(2),Rational(3)))); - assert_parsed_expression_is("(1*2/3)", Parenthesis(Multiplication(Rational(1),Division(Rational(2),Rational(3))))); - assert_parsed_expression_is("(1/2/3)", Parenthesis(Division(Division(Rational(1),Rational(2)),Rational(3)))); - assert_parsed_expression_is("1^2", Power(Rational(1),Rational(2))); - assert_parsed_expression_is("1^2^3", Power(Rational(1),Power(Rational(2),Rational(3)))); - assert_parsed_expression_is("1=2", Equal(Rational(1),Rational(2))); + assert_parsed_expression_is("1", Rational::Builder(1)); + assert_parsed_expression_is("(1)", Parenthesis::Builder(Rational::Builder(1))); + assert_parsed_expression_is("((1))", Parenthesis::Builder((Expression)Parenthesis::Builder(Rational::Builder(1)))); + assert_parsed_expression_is("1+2", Addition::Builder(Rational::Builder(1),Rational::Builder(2))); + assert_parsed_expression_is("(1)+2", Addition::Builder(Parenthesis::Builder(Rational::Builder(1)),Rational::Builder(2))); + assert_parsed_expression_is("(1+2)", Parenthesis::Builder(Addition::Builder(Rational::Builder(1),Rational::Builder(2)))); + assert_parsed_expression_is("1+2+3", Addition::Builder(Addition::Builder(Rational::Builder(1),Rational::Builder(2)),Rational::Builder(3))); + assert_parsed_expression_is("1+2+(3+4)", Addition::Builder(Addition::Builder(Rational::Builder(1),Rational::Builder(2)),Parenthesis::Builder(Addition::Builder(Rational::Builder(3),Rational::Builder(4))))); + assert_parsed_expression_is("1*2", Multiplication::Builder(Rational::Builder(1),Rational::Builder(2))); + assert_parsed_expression_is("1*2*3", Multiplication::Builder(Multiplication::Builder(Rational::Builder(1),Rational::Builder(2)),Rational::Builder(3))); + assert_parsed_expression_is("1+2*3", Addition::Builder(Rational::Builder(1), Multiplication::Builder(Rational::Builder(2), Rational::Builder(3)))); + assert_parsed_expression_is("1/2", Division::Builder(Rational::Builder(1),Rational::Builder(2))); + assert_parsed_expression_is("(1/2)", Parenthesis::Builder(Division::Builder(Rational::Builder(1),Rational::Builder(2)))); + assert_parsed_expression_is("1/2/3", Division::Builder(Division::Builder(Rational::Builder(1),Rational::Builder(2)),Rational::Builder(3))); + assert_parsed_expression_is("1/2*3", Multiplication::Builder(Division::Builder(Rational::Builder(1),Rational::Builder(2)),Rational::Builder(3))); + assert_parsed_expression_is("(1/2*3)", Parenthesis::Builder(Multiplication::Builder(Division::Builder(Rational::Builder(1),Rational::Builder(2)),Rational::Builder(3)))); + assert_parsed_expression_is("1*2/3", Multiplication::Builder(Rational::Builder(1),Division::Builder(Rational::Builder(2),Rational::Builder(3)))); + assert_parsed_expression_is("(1*2/3)", Parenthesis::Builder(Multiplication::Builder(Rational::Builder(1),Division::Builder(Rational::Builder(2),Rational::Builder(3))))); + assert_parsed_expression_is("(1/2/3)", Parenthesis::Builder(Division::Builder(Division::Builder(Rational::Builder(1),Rational::Builder(2)),Rational::Builder(3)))); + assert_parsed_expression_is("1^2", Power::Builder(Rational::Builder(1),Rational::Builder(2))); + assert_parsed_expression_is("1^2^3", Power::Builder(Rational::Builder(1),Power::Builder(Rational::Builder(2),Rational::Builder(3)))); + assert_parsed_expression_is("1=2", Equal::Builder(Rational::Builder(1),Rational::Builder(2))); assert_raises_parsing_error("=5"); assert_raises_parsing_error("1=2=3"); - assert_parsed_expression_is("-1", Opposite(Rational(1))); - assert_parsed_expression_is("(-1)", Parenthesis(Opposite(Rational(1)))); - assert_parsed_expression_is("1-2", Subtraction(Rational(1),Rational(2))); - assert_parsed_expression_is("-1-2", Subtraction(Opposite(Rational(1)),Rational(2))); - assert_parsed_expression_is("1-2-3", Subtraction(Subtraction(Rational(1),Rational(2)),Rational(3))); - assert_parsed_expression_is("(1-2)", Parenthesis(Subtraction(Rational(1),Rational(2)))); - assert_parsed_expression_is("1+-2", Addition(Rational(1),Opposite(Rational(2)))); - assert_parsed_expression_is("--1", Opposite((Expression)Opposite(Rational(1)))); - assert_parsed_expression_is("(1+2)-3", Subtraction(Parenthesis(Addition(Rational(1),Rational(2))),Rational(3))); - assert_parsed_expression_is("(2*-3)", Parenthesis(Multiplication(Rational(2),Opposite(Rational(3))))); - assert_parsed_expression_is("1^(2)-3", Subtraction(Power(Rational(1),Parenthesis(Rational(2))),Rational(3))); - assert_parsed_expression_is("1^2-3", Subtraction(Power(Rational(1),Rational(2)),Rational(3))); - assert_parsed_expression_is("2^-3", Power(Rational(2),Opposite(Rational(3)))); - assert_parsed_expression_is("2--2+-1", Addition(Subtraction(Rational(2),Opposite(Rational(2))),Opposite(Rational(1)))); - assert_parsed_expression_is("2--2*-1", Subtraction(Rational(2),Multiplication(Opposite(Rational(2)),Opposite(Rational(1))))); - assert_parsed_expression_is("-1^2", Opposite(Power(Rational(1),Rational(2)))); - assert_parsed_expression_is("2/-3/-4", Division(Division(Rational(2),Opposite(Rational(3))),Opposite(Rational(4)))); - assert_parsed_expression_is("1*2-3*4", Subtraction(Multiplication(Rational(1),Rational(2)),Multiplication(Rational(3),Rational(4)))); - assert_parsed_expression_is("-1*2", Multiplication(Opposite(Rational(1)), Rational(2))); // Unary minus shall have precedence - assert_parsed_expression_is("1!", Factorial(Rational(1))); - assert_parsed_expression_is("1+2!", Addition(Rational(1),Factorial(Rational(2)))); - assert_parsed_expression_is("1!+2", Addition(Factorial(Rational(1)),Rational(2))); - assert_parsed_expression_is("1!+2!", Addition(Factorial(Rational(1)),Factorial(Rational(2)))); - assert_parsed_expression_is("1*2!", Multiplication(Rational(1),Factorial(Rational(2)))); - assert_parsed_expression_is("1!*2", Multiplication(Factorial(Rational(1)),Rational(2))); - assert_parsed_expression_is("1!*2!", Multiplication(Factorial(Rational(1)),Factorial(Rational(2)))); - assert_parsed_expression_is("1-2!", Subtraction(Rational(1),Factorial(Rational(2)))); - assert_parsed_expression_is("1!-2", Subtraction(Factorial(Rational(1)),Rational(2))); - assert_parsed_expression_is("1!-2!", Subtraction(Factorial(Rational(1)),Factorial(Rational(2)))); - assert_parsed_expression_is("1/2!", Division(Rational(1),Factorial(Rational(2)))); - assert_parsed_expression_is("1!/2", Division(Factorial(Rational(1)),Rational(2))); - assert_parsed_expression_is("1!/2!", Division(Factorial(Rational(1)),Factorial(Rational(2)))); - assert_parsed_expression_is("1^2!", Power(Rational(1),Factorial(Rational(2)))); - assert_parsed_expression_is("1!^2", Power(Factorial(Rational(1)),Rational(2))); - assert_parsed_expression_is("1!^2!", Power(Factorial(Rational(1)),Factorial(Rational(2)))); - assert_parsed_expression_is("(1)!", Factorial(Parenthesis(Rational(1)))); + assert_parsed_expression_is("-1", Opposite::Builder(Rational::Builder(1))); + assert_parsed_expression_is("(-1)", Parenthesis::Builder(Opposite::Builder(Rational::Builder(1)))); + assert_parsed_expression_is("1-2", Subtraction::Builder(Rational::Builder(1),Rational::Builder(2))); + assert_parsed_expression_is("-1-2", Subtraction::Builder(Opposite::Builder(Rational::Builder(1)),Rational::Builder(2))); + assert_parsed_expression_is("1-2-3", Subtraction::Builder(Subtraction::Builder(Rational::Builder(1),Rational::Builder(2)),Rational::Builder(3))); + assert_parsed_expression_is("(1-2)", Parenthesis::Builder(Subtraction::Builder(Rational::Builder(1),Rational::Builder(2)))); + assert_parsed_expression_is("1+-2", Addition::Builder(Rational::Builder(1),Opposite::Builder(Rational::Builder(2)))); + assert_parsed_expression_is("--1", Opposite::Builder((Expression)Opposite::Builder(Rational::Builder(1)))); + assert_parsed_expression_is("(1+2)-3", Subtraction::Builder(Parenthesis::Builder(Addition::Builder(Rational::Builder(1),Rational::Builder(2))),Rational::Builder(3))); + assert_parsed_expression_is("(2*-3)", Parenthesis::Builder(Multiplication::Builder(Rational::Builder(2),Opposite::Builder(Rational::Builder(3))))); + assert_parsed_expression_is("1^(2)-3", Subtraction::Builder(Power::Builder(Rational::Builder(1),Parenthesis::Builder(Rational::Builder(2))),Rational::Builder(3))); + assert_parsed_expression_is("1^2-3", Subtraction::Builder(Power::Builder(Rational::Builder(1),Rational::Builder(2)),Rational::Builder(3))); + assert_parsed_expression_is("2^-3", Power::Builder(Rational::Builder(2),Opposite::Builder(Rational::Builder(3)))); + assert_parsed_expression_is("2--2+-1", Addition::Builder(Subtraction::Builder(Rational::Builder(2),Opposite::Builder(Rational::Builder(2))),Opposite::Builder(Rational::Builder(1)))); + assert_parsed_expression_is("2--2*-1", Subtraction::Builder(Rational::Builder(2),Opposite::Builder(Multiplication::Builder(Rational::Builder(2),Opposite::Builder(Rational::Builder(1)))))); + assert_parsed_expression_is("-1^2", Opposite::Builder(Power::Builder(Rational::Builder(1),Rational::Builder(2)))); + assert_parsed_expression_is("2/-3/-4", Division::Builder(Division::Builder(Rational::Builder(2),Opposite::Builder(Rational::Builder(3))),Opposite::Builder(Rational::Builder(4)))); + assert_parsed_expression_is("1*2-3*4", Subtraction::Builder(Multiplication::Builder(Rational::Builder(1),Rational::Builder(2)),Multiplication::Builder(Rational::Builder(3),Rational::Builder(4)))); + assert_parsed_expression_is("-1*2", Opposite::Builder(Multiplication::Builder(Rational::Builder(1), Rational::Builder(2)))); + assert_parsed_expression_is("1!", Factorial::Builder(Rational::Builder(1))); + assert_parsed_expression_is("1+2!", Addition::Builder(Rational::Builder(1),Factorial::Builder(Rational::Builder(2)))); + assert_parsed_expression_is("1!+2", Addition::Builder(Factorial::Builder(Rational::Builder(1)),Rational::Builder(2))); + assert_parsed_expression_is("1!+2!", Addition::Builder(Factorial::Builder(Rational::Builder(1)),Factorial::Builder(Rational::Builder(2)))); + assert_parsed_expression_is("1*2!", Multiplication::Builder(Rational::Builder(1),Factorial::Builder(Rational::Builder(2)))); + assert_parsed_expression_is("1!*2", Multiplication::Builder(Factorial::Builder(Rational::Builder(1)),Rational::Builder(2))); + assert_parsed_expression_is("1!*2!", Multiplication::Builder(Factorial::Builder(Rational::Builder(1)),Factorial::Builder(Rational::Builder(2)))); + assert_parsed_expression_is("1-2!", Subtraction::Builder(Rational::Builder(1),Factorial::Builder(Rational::Builder(2)))); + assert_parsed_expression_is("1!-2", Subtraction::Builder(Factorial::Builder(Rational::Builder(1)),Rational::Builder(2))); + assert_parsed_expression_is("1!-2!", Subtraction::Builder(Factorial::Builder(Rational::Builder(1)),Factorial::Builder(Rational::Builder(2)))); + assert_parsed_expression_is("1/2!", Division::Builder(Rational::Builder(1),Factorial::Builder(Rational::Builder(2)))); + assert_parsed_expression_is("1!/2", Division::Builder(Factorial::Builder(Rational::Builder(1)),Rational::Builder(2))); + assert_parsed_expression_is("1!/2!", Division::Builder(Factorial::Builder(Rational::Builder(1)),Factorial::Builder(Rational::Builder(2)))); + assert_parsed_expression_is("1^2!", Power::Builder(Rational::Builder(1),Factorial::Builder(Rational::Builder(2)))); + assert_parsed_expression_is("1!^2", Power::Builder(Factorial::Builder(Rational::Builder(1)),Rational::Builder(2))); + assert_parsed_expression_is("1!^2!", Power::Builder(Factorial::Builder(Rational::Builder(1)),Factorial::Builder(Rational::Builder(2)))); + assert_parsed_expression_is("(1)!", Factorial::Builder(Parenthesis::Builder(Rational::Builder(1)))); assert_raises_parsing_error("1+"); assert_raises_parsing_error(")"); assert_raises_parsing_error(")("); @@ -202,7 +202,7 @@ QUIZ_CASE(poincare_parser_parse) { } Matrix BuildMatrix(int rows, int columns, Expression entries[]) { - Matrix m; + Matrix m = Matrix::Builder(); int position = 0; for (int i = 0; i < rows; i++) { for (int j = 0; j < columns; j++) { @@ -215,13 +215,13 @@ Matrix BuildMatrix(int rows, int columns, Expression entries[]) { } QUIZ_CASE(poincare_parser_matrices) { - Expression m1[] = {Rational(1)}; + Expression m1[] = {Rational::Builder(1)}; assert_parsed_expression_is("[[1]]", BuildMatrix(1,1,m1)); - Expression m2[] = {Rational(1),Rational(2),Rational(3)}; + Expression m2[] = {Rational::Builder(1),Rational::Builder(2),Rational::Builder(3)}; assert_parsed_expression_is("[[1,2,3]]", BuildMatrix(1,3,m2)); - Expression m3[] = {Rational(1),Rational(2),Rational(3),Rational(4),Rational(5),Rational(6)}; + Expression m3[] = {Rational::Builder(1),Rational::Builder(2),Rational::Builder(3),Rational::Builder(4),Rational::Builder(5),Rational::Builder(6)}; assert_parsed_expression_is("[[1,2,3][4,5,6]]", BuildMatrix(2,3,m3)); - Expression m4[] = {Rational(1), BuildMatrix(1,1,m1)}; + Expression m4[] = {Rational::Builder(1), BuildMatrix(1,1,m1)}; assert_parsed_expression_is("[[1,[[1]]]]", BuildMatrix(1,2,m4)); assert_raises_parsing_error("["); assert_raises_parsing_error("]"); @@ -241,103 +241,105 @@ QUIZ_CASE(poincare_parser_matrices) { QUIZ_CASE(poincare_parser_symbols_and_functions) { // User-defined symbols - assert_parsed_expression_is("a", Symbol("a", 1)); - assert_parsed_expression_is("x", Symbol("x", 1)); - assert_parsed_expression_is("toot", Symbol("toot", 4)); - assert_parsed_expression_is("toto_", Symbol("toto_", 5)); - assert_parsed_expression_is("t_toto", Symbol("t_toto", 6)); - assert_parsed_expression_is("tot12", Symbol("tot12", 5)); - assert_parsed_expression_is("TOto", Symbol("TOto", 4)); - assert_parsed_expression_is("TO12_Or", Symbol("TO12_Or", 7)); + assert_parsed_expression_is("a", Symbol::Builder("a", 1)); + assert_parsed_expression_is("x", Symbol::Builder("x", 1)); + assert_parsed_expression_is("toot", Symbol::Builder("toot", 4)); + assert_parsed_expression_is("toto_", Symbol::Builder("toto_", 5)); + assert_parsed_expression_is("t_toto", Symbol::Builder("t_toto", 6)); + assert_parsed_expression_is("tot12", Symbol::Builder("tot12", 5)); + assert_parsed_expression_is("TOto", Symbol::Builder("TOto", 4)); + assert_parsed_expression_is("TO12_Or", Symbol::Builder("TO12_Or", 7)); assert_raises_parsing_error("_a"); assert_raises_parsing_error("abcdefgh"); // User-defined functions - assert_parsed_expression_is("f(x)", Function("f", 1, Symbol("x",1))); - assert_parsed_expression_is("f(1)", Function("f", 1, Rational(1))); - assert_parsed_expression_is("ab12AB_(x)", Function("ab12AB_", 7, Symbol("x",1))); - assert_parsed_expression_is("ab12AB_(1)", Function("ab12AB_", 7, Rational(1))); - assert_parsed_expression_is("f(g(x))", Function("f", 1, Function("g", 1, Symbol("x",1)))); - assert_parsed_expression_is("f(g(1))", Function("f", 1, Function("g", 1, Rational(1)))); - assert_parsed_expression_is("f((1))", Function("f", 1, Parenthesis(Rational(1)))); + assert_parsed_expression_is("f(x)", Function::Builder("f", 1, Symbol::Builder("x",1))); + assert_parsed_expression_is("f(1)", Function::Builder("f", 1, Rational::Builder(1))); + assert_parsed_expression_is("ab12AB_(x)", Function::Builder("ab12AB_", 7, Symbol::Builder("x",1))); + assert_parsed_expression_is("ab12AB_(1)", Function::Builder("ab12AB_", 7, Rational::Builder(1))); + assert_parsed_expression_is("f(g(x))", Function::Builder("f", 1, Function::Builder("g", 1, Symbol::Builder("x",1)))); + assert_parsed_expression_is("f(g(1))", Function::Builder("f", 1, Function::Builder("g", 1, Rational::Builder(1)))); + assert_parsed_expression_is("f((1))", Function::Builder("f", 1, Parenthesis::Builder(Rational::Builder(1)))); assert_raises_parsing_error("f(1,2)"); assert_raises_parsing_error("f(f)"); assert_raises_parsing_error("abcdefgh(1)"); // Reserved symbols - assert_parsed_expression_is("ans", Symbol("ans", 3)); - assert_parsed_expression_is("I", Constant(Ion::Charset::IComplex)); - assert_parsed_expression_is("P", Constant(Ion::Charset::SmallPi)); - assert_parsed_expression_is("X", Constant(Ion::Charset::Exponential)); - assert_parsed_expression_is(Infinity::Name(), Infinity(false)); - assert_parsed_expression_is(Undefined::Name(), Undefined()); + assert_parsed_expression_is("ans", Symbol::Builder("ans", 3)); + assert_parsed_expression_is("I", Constant::Builder(Ion::Charset::IComplex)); + assert_parsed_expression_is("P", Constant::Builder(Ion::Charset::SmallPi)); + assert_parsed_expression_is("X", Constant::Builder(Ion::Charset::Exponential)); + assert_parsed_expression_is(Infinity::Name(), Infinity::Builder(false)); + assert_parsed_expression_is(Undefined::Name(), Undefined::Builder()); assert_raises_parsing_error("u"); assert_raises_parsing_error("v"); // Reserved functions - assert_parsed_expression_is("acos(1)", ArcCosine::Builder(Rational(1))); - assert_parsed_expression_is("acosh(1)", HyperbolicArcCosine::Builder(Rational(1))); - assert_parsed_expression_is("abs(1)", AbsoluteValue::Builder(Rational(1))); - assert_parsed_expression_is("arg(1)", ComplexArgument::Builder(Rational(1))); - assert_parsed_expression_is("asin(1)", ArcSine::Builder(Rational(1))); - assert_parsed_expression_is("asinh(1)", HyperbolicArcSine::Builder(Rational(1))); - assert_parsed_expression_is("atan(1)", ArcTangent::Builder(Rational(1))); - assert_parsed_expression_is("atanh(1)", HyperbolicArcTangent::Builder(Rational(1))); - assert_parsed_expression_is("binomial(2,1)", BinomialCoefficient::Builder(Rational(2),Rational(1))); - assert_parsed_expression_is("ceil(1)", Ceiling::Builder(Rational(1))); - assert_parsed_expression_is("confidence(1,2)", ConfidenceInterval::Builder(Rational(1),Rational(2))); + assert_parsed_expression_is("acos(1)", ArcCosine::Builder(Rational::Builder(1))); + assert_parsed_expression_is("acosh(1)", HyperbolicArcCosine::Builder(Rational::Builder(1))); + assert_parsed_expression_is("abs(1)", AbsoluteValue::Builder(Rational::Builder(1))); + assert_parsed_expression_is("arg(1)", ComplexArgument::Builder(Rational::Builder(1))); + assert_parsed_expression_is("asin(1)", ArcSine::Builder(Rational::Builder(1))); + assert_parsed_expression_is("asinh(1)", HyperbolicArcSine::Builder(Rational::Builder(1))); + assert_parsed_expression_is("atan(1)", ArcTangent::Builder(Rational::Builder(1))); + assert_parsed_expression_is("atanh(1)", HyperbolicArcTangent::Builder(Rational::Builder(1))); + assert_parsed_expression_is("binomial(2,1)", BinomialCoefficient::Builder(Rational::Builder(2),Rational::Builder(1))); + assert_parsed_expression_is("ceil(1)", Ceiling::Builder(Rational::Builder(1))); + assert_parsed_expression_is("confidence(1,2)", ConfidenceInterval::Builder(Rational::Builder(1),Rational::Builder(2))); assert_raises_parsing_error("diff(1,2,3)"); - assert_parsed_expression_is("diff(1,x,3)", Derivative::Builder(Rational(1),Symbol("x",1),Rational(3))); - assert_parsed_expression_is("dim(1)", MatrixDimension::Builder(Rational(1))); - assert_parsed_expression_is("conj(1)", Conjugate::Builder(Rational(1))); - assert_parsed_expression_is("det(1)", Determinant::Builder(Rational(1))); - assert_parsed_expression_is("cos(1)", Cosine::Builder(Rational(1))); - assert_parsed_expression_is("cosh(1)", HyperbolicCosine::Builder(Rational(1))); - assert_parsed_expression_is("factor(1)", Factor::Builder(Rational(1))); - assert_parsed_expression_is("floor(1)", Floor::Builder(Rational(1))); - assert_parsed_expression_is("frac(1)", FracPart::Builder(Rational(1))); - assert_parsed_expression_is("gcd(1,2)", GreatCommonDivisor::Builder(Rational(1),Rational(2))); - assert_parsed_expression_is("im(1)", ImaginaryPart::Builder(Rational(1))); - assert_parsed_expression_is("int(1,x,2,3)", Integral::Builder(Rational(1),Symbol("x",1),Rational(2),Rational(3))); + assert_parsed_expression_is("diff(1,x,3)", Derivative::Builder(Rational::Builder(1),Symbol::Builder("x",1),Rational::Builder(3))); + assert_parsed_expression_is("dim(1)", MatrixDimension::Builder(Rational::Builder(1))); + assert_parsed_expression_is("conj(1)", Conjugate::Builder(Rational::Builder(1))); + assert_parsed_expression_is("det(1)", Determinant::Builder(Rational::Builder(1))); + assert_parsed_expression_is("cos(1)", Cosine::Builder(Rational::Builder(1))); + assert_parsed_expression_is("cosh(1)", HyperbolicCosine::Builder(Rational::Builder(1))); + assert_parsed_expression_is("factor(1)", Factor::Builder(Rational::Builder(1))); + assert_parsed_expression_is("floor(1)", Floor::Builder(Rational::Builder(1))); + assert_parsed_expression_is("frac(1)", FracPart::Builder(Rational::Builder(1))); + assert_parsed_expression_is("gcd(1,2)", GreatCommonDivisor::Builder(Rational::Builder(1),Rational::Builder(2))); + assert_parsed_expression_is("im(1)", ImaginaryPart::Builder(Rational::Builder(1))); + assert_parsed_expression_is("int(1,x,2,3)", Integral::Builder(Rational::Builder(1),Symbol::Builder("x",1),Rational::Builder(2),Rational::Builder(3))); assert_raises_parsing_error("int(1,2,3,4)"); - assert_parsed_expression_is("inverse(1)", MatrixInverse::Builder(Rational(1))); - assert_parsed_expression_is("lcm(1,2)", LeastCommonMultiple::Builder(Rational(1),Rational(2))); - assert_parsed_expression_is("ln(1)", NaperianLogarithm::Builder(Rational(1))); - assert_parsed_expression_is("log(1)", CommonLogarithm::Builder(Rational(1))); - assert_parsed_expression_is("log(1,2)", Logarithm::Builder(Rational(1),Rational(2))); - assert_parsed_expression_is("log_{2}(1)", Logarithm::Builder(Rational(1),Rational(2))); - assert_parsed_expression_is("permute(2,1)", PermuteCoefficient::Builder(Rational(2),Rational(1))); - assert_parsed_expression_is("prediction95(1,2)", PredictionInterval::Builder(Rational(1),Rational(2))); - assert_parsed_expression_is("prediction(1,2)", SimplePredictionInterval::Builder(Rational(1),Rational(2))); - assert_parsed_expression_is("product(1,n,2,3)", Product::Builder(Rational(1),Symbol("n",1),Rational(2),Rational(3))); + assert_parsed_expression_is("inverse(1)", MatrixInverse::Builder(Rational::Builder(1))); + assert_parsed_expression_is("lcm(1,2)", LeastCommonMultiple::Builder(Rational::Builder(1),Rational::Builder(2))); + assert_parsed_expression_is("ln(1)", NaperianLogarithm::Builder(Rational::Builder(1))); + assert_parsed_expression_is("log(1)", CommonLogarithm::Builder(Rational::Builder(1))); + assert_parsed_expression_is("log(1,2)", Logarithm::Builder(Rational::Builder(1),Rational::Builder(2))); + assert_parsed_expression_is("log_{2}(1)", Logarithm::Builder(Rational::Builder(1),Rational::Builder(2))); + assert_parsed_expression_is("permute(2,1)", PermuteCoefficient::Builder(Rational::Builder(2),Rational::Builder(1))); + assert_parsed_expression_is("prediction95(1,2)", PredictionInterval::Builder(Rational::Builder(1),Rational::Builder(2))); + assert_parsed_expression_is("prediction(1,2)", SimplePredictionInterval::Builder(Rational::Builder(1),Rational::Builder(2))); + assert_parsed_expression_is("product(1,n,2,3)", Product::Builder(Rational::Builder(1),Symbol::Builder("n",1),Rational::Builder(2),Rational::Builder(3))); assert_raises_parsing_error("product(1,2,3,4)"); - assert_parsed_expression_is("quo(1,2)", DivisionQuotient::Builder(Rational(1),Rational(2))); + assert_parsed_expression_is("quo(1,2)", DivisionQuotient::Builder(Rational::Builder(1),Rational::Builder(2))); assert_parsed_expression_is("random()", Random::Builder()); - assert_parsed_expression_is("randint(1,2)", Randint::Builder(Rational(1),Rational(2))); - assert_parsed_expression_is("re(1)", RealPart::Builder(Rational(1))); - assert_parsed_expression_is("rem(1,2)", DivisionRemainder::Builder(Rational(1),Rational(2))); - assert_parsed_expression_is("root(1,2)", NthRoot::Builder(Rational(1),Rational(2))); - assert_parsed_expression_is("round(1,2)", Round::Builder(Rational(1),Rational(2))); - assert_parsed_expression_is("sin(1)", Sine::Builder(Rational(1))); - assert_parsed_expression_is("sinh(1)", HyperbolicSine::Builder(Rational(1))); - assert_parsed_expression_is("sum(1,n,2,3)", Sum::Builder(Rational(1),Symbol("n",1),Rational(2),Rational(3))); + assert_parsed_expression_is("randint(1,2)", Randint::Builder(Rational::Builder(1),Rational::Builder(2))); + assert_parsed_expression_is("re(1)", RealPart::Builder(Rational::Builder(1))); + assert_parsed_expression_is("rem(1,2)", DivisionRemainder::Builder(Rational::Builder(1),Rational::Builder(2))); + assert_parsed_expression_is("root(1,2)", NthRoot::Builder(Rational::Builder(1),Rational::Builder(2))); + assert_parsed_expression_is("round(1,2)", Round::Builder(Rational::Builder(1),Rational::Builder(2))); + assert_parsed_expression_is("sin(1)", Sine::Builder(Rational::Builder(1))); + assert_parsed_expression_is("sinh(1)", HyperbolicSine::Builder(Rational::Builder(1))); + assert_parsed_expression_is("sum(1,n,2,3)", Sum::Builder(Rational::Builder(1),Symbol::Builder("n",1),Rational::Builder(2),Rational::Builder(3))); assert_raises_parsing_error("sum(1,2,3,4)"); - assert_parsed_expression_is("tan(1)", Tangent::Builder(Rational(1))); - assert_parsed_expression_is("tanh(1)", HyperbolicTangent::Builder(Rational(1))); - assert_parsed_expression_is("trace(1)", MatrixTrace::Builder(Rational(1))); - assert_parsed_expression_is("transpose(1)", MatrixTranspose::Builder(Rational(1))); - assert_parsed_expression_is("\x91(1)", SquareRoot::Builder(Rational(1))); + assert_parsed_expression_is("tan(1)", Tangent::Builder(Rational::Builder(1))); + assert_parsed_expression_is("tanh(1)", HyperbolicTangent::Builder(Rational::Builder(1))); + assert_parsed_expression_is("trace(1)", MatrixTrace::Builder(Rational::Builder(1))); + assert_parsed_expression_is("transpose(1)", MatrixTranspose::Builder(Rational::Builder(1))); + assert_parsed_expression_is("\x91(1)", SquareRoot::Builder(Rational::Builder(1))); assert_raises_parsing_error("cos(1,2)"); assert_raises_parsing_error("log(1,2,3)"); } QUIZ_CASE(poincare_parser_parse_store) { - assert_parsed_expression_is("1>a", Store(Rational(1),Symbol("a",1))); - assert_parsed_expression_is("1>e", Store(Rational(1),Symbol("e",1))); - assert_parsed_expression_is("1>f(x)", Store(Rational(1),Function("f",1,Symbol("x",1)))); - assert_parsed_expression_is("x>f(x)", Store(Symbol("x",1),Function("f",1,Symbol("x",1)))); - assert_parsed_expression_is("n>f(x)", Store(Symbol("n",1),Function("f",1,Symbol("x",1)))); + assert_parsed_expression_is("1>a", Store::Builder(Rational::Builder(1),Symbol::Builder("a",1))); + assert_parsed_expression_is("1>e", Store::Builder(Rational::Builder(1),Symbol::Builder("e",1))); + assert_parsed_expression_is("1>f(x)", Store::Builder(Rational::Builder(1),Function::Builder("f",1,Symbol::Builder("x",1)))); + assert_parsed_expression_is("x>f(x)", Store::Builder(Symbol::Builder("x",1),Function::Builder("f",1,Symbol::Builder("x",1)))); + assert_parsed_expression_is("n>f(x)", Store::Builder(Symbol::Builder("n",1),Function::Builder("f",1,Symbol::Builder("x",1)))); + Expression m0[] = {Symbol::Builder('x')}; + assert_parsed_expression_is("[[x]]>f(x)", Store::Builder(BuildMatrix(1,1,m0), Function::Builder("f", 1, Symbol::Builder('x')))); assert_raises_parsing_error("a>b>c"); assert_raises_parsing_error("1>2"); assert_raises_parsing_error("1>"); @@ -367,27 +369,27 @@ QUIZ_CASE(poincare_parser_parse_store) { QUIZ_CASE(poincare_parser_implicit_multiplication) { assert_raises_parsing_error(".1.2"); assert_raises_parsing_error("1 2"); - assert_parsed_expression_is("1x", Multiplication(Rational(1),Symbol("x", 1))); - assert_parsed_expression_is("1ans", Multiplication(Rational(1),Symbol("ans", 3))); - assert_parsed_expression_is("x1", Symbol("x1", 2)); - assert_parsed_expression_is("1x+2", Addition(Multiplication(Rational(1),Symbol("x", 1)),Rational(2))); - assert_parsed_expression_is("1P", Multiplication(Rational(1),Constant(Ion::Charset::SmallPi))); - assert_parsed_expression_is("1x-2", Subtraction(Multiplication(Rational(1),Symbol("x", 1)),Rational(2))); - assert_parsed_expression_is("-1x", Opposite(Multiplication(Rational(1),Symbol("x", 1)))); - assert_parsed_expression_is("2*1x", Multiplication(Rational(2),Multiplication(Rational(1),Symbol("x", 1)))); - assert_parsed_expression_is("2^1x", Multiplication(Power(Rational(2),Rational(1)),Symbol("x", 1))); - assert_parsed_expression_is("1x^2", Multiplication(Rational(1),Power(Symbol("x", 1),Rational(2)))); - assert_parsed_expression_is("2/1x", Division(Rational(2),Multiplication(Rational(1),Symbol("x", 1)))); - assert_parsed_expression_is("1x/2", Division(Multiplication(Rational(1),Symbol("x", 1)),Rational(2))); - assert_parsed_expression_is("(1)2", Multiplication(Parenthesis(Rational(1)),Rational(2))); - assert_parsed_expression_is("1(2)", Multiplication(Rational(1),Parenthesis(Rational(2)))); - assert_parsed_expression_is("sin(1)2", Multiplication(Sine::Builder(Rational(1)),Rational(2))); - assert_parsed_expression_is("1cos(2)", Multiplication(Rational(1),Cosine::Builder(Rational(2)))); - assert_parsed_expression_is("1!2", Multiplication(Factorial(Rational(1)),Rational(2))); - assert_parsed_expression_is("2X^(3)", Multiplication(Rational(2),Power(Constant(Ion::Charset::Exponential),Parenthesis(Rational(3))))); - Expression m1[] = {Rational(1)}; Matrix M1 = BuildMatrix(1,1,m1); - Expression m2[] = {Rational(2)}; Matrix M2 = BuildMatrix(1,1,m2); - assert_parsed_expression_is("[[1]][[2]]", Multiplication(M1,M2)); + assert_parsed_expression_is("1x", Multiplication::Builder(Rational::Builder(1),Symbol::Builder("x", 1))); + assert_parsed_expression_is("1ans", Multiplication::Builder(Rational::Builder(1),Symbol::Builder("ans", 3))); + assert_parsed_expression_is("x1", Symbol::Builder("x1", 2)); + assert_parsed_expression_is("1x+2", Addition::Builder(Multiplication::Builder(Rational::Builder(1),Symbol::Builder("x", 1)),Rational::Builder(2))); + assert_parsed_expression_is("1P", Multiplication::Builder(Rational::Builder(1),Constant::Builder(Ion::Charset::SmallPi))); + assert_parsed_expression_is("1x-2", Subtraction::Builder(Multiplication::Builder(Rational::Builder(1),Symbol::Builder("x", 1)),Rational::Builder(2))); + assert_parsed_expression_is("-1x", Opposite::Builder(Multiplication::Builder(Rational::Builder(1),Symbol::Builder("x", 1)))); + assert_parsed_expression_is("2*1x", Multiplication::Builder(Rational::Builder(2),Multiplication::Builder(Rational::Builder(1),Symbol::Builder("x", 1)))); + assert_parsed_expression_is("2^1x", Multiplication::Builder(Power::Builder(Rational::Builder(2),Rational::Builder(1)),Symbol::Builder("x", 1))); + assert_parsed_expression_is("1x^2", Multiplication::Builder(Rational::Builder(1),Power::Builder(Symbol::Builder("x", 1),Rational::Builder(2)))); + assert_parsed_expression_is("2/1x", Division::Builder(Rational::Builder(2),Multiplication::Builder(Rational::Builder(1),Symbol::Builder("x", 1)))); + assert_parsed_expression_is("1x/2", Division::Builder(Multiplication::Builder(Rational::Builder(1),Symbol::Builder("x", 1)),Rational::Builder(2))); + assert_parsed_expression_is("(1)2", Multiplication::Builder(Parenthesis::Builder(Rational::Builder(1)),Rational::Builder(2))); + assert_parsed_expression_is("1(2)", Multiplication::Builder(Rational::Builder(1),Parenthesis::Builder(Rational::Builder(2)))); + assert_parsed_expression_is("sin(1)2", Multiplication::Builder(Sine::Builder(Rational::Builder(1)),Rational::Builder(2))); + assert_parsed_expression_is("1cos(2)", Multiplication::Builder(Rational::Builder(1),Cosine::Builder(Rational::Builder(2)))); + assert_parsed_expression_is("1!2", Multiplication::Builder(Factorial::Builder(Rational::Builder(1)),Rational::Builder(2))); + assert_parsed_expression_is("2X^(3)", Multiplication::Builder(Rational::Builder(2),Power::Builder(Constant::Builder(Ion::Charset::Exponential),Parenthesis::Builder(Rational::Builder(3))))); + Expression m1[] = {Rational::Builder(1)}; Matrix M1 = BuildMatrix(1,1,m1); + Expression m2[] = {Rational::Builder(2)}; Matrix M2 = BuildMatrix(1,1,m2); + assert_parsed_expression_is("[[1]][[2]]", Multiplication::Builder(M1,M2)); } QUIZ_CASE(poincare_parser_expression_evaluation) { @@ -406,18 +408,18 @@ QUIZ_CASE(poincare_parser_expression_evaluation) { assert_parsed_expression_evaluates_to("-2-3", "-5"); assert_parsed_expression_evaluates_to("1.2*X^(1)", "3.261938"); - assert_parsed_expression_evaluates_to("2X^(3)", "40.1711", Radian, Cartesian, 6); // WARNING: the 7th significant digit is wrong on blackbos simulator - assert_parsed_expression_evaluates_to("X^2*X^(1)", "20.0855", Radian, Cartesian, 6); // WARNING: the 7th significant digit is wrong on simulator + assert_parsed_expression_evaluates_to("2X^(3)", "40.1711", System, Radian, Cartesian, 6); // WARNING: the 7th significant digit is wrong on blackbos simulator + assert_parsed_expression_evaluates_to("X^2*X^(1)", "20.0855", System, Radian, Cartesian, 6); // WARNING: the 7th significant digit is wrong on simulator assert_parsed_expression_evaluates_to("X^2*X^(1)", "20.085536923188"); assert_parsed_expression_evaluates_to("2*3^4+2", "164"); assert_parsed_expression_evaluates_to("-2*3^4+2", "-160"); - assert_parsed_expression_evaluates_to("-sin(3)*2-3", "-3.2822400161197", Radian); + assert_parsed_expression_evaluates_to("-sin(3)*2-3", "-3.2822400161197", System, Radian); assert_parsed_expression_evaluates_to("-.003", "-0.003"); assert_parsed_expression_evaluates_to(".02E2", "2"); assert_parsed_expression_evaluates_to("5-2/3", "4.333333"); assert_parsed_expression_evaluates_to("2/3-5", "-4.3333333333333"); assert_parsed_expression_evaluates_to("-2/3-5", "-5.666667"); - assert_parsed_expression_evaluates_to("sin(3)2(4+2)", "1.6934400967184", Radian); + assert_parsed_expression_evaluates_to("sin(3)2(4+2)", "1.6934400967184", System, Radian); assert_parsed_expression_evaluates_to("4/2*(2+3)", "10"); assert_parsed_expression_evaluates_to("4/2*(2+3)", "10"); } diff --git a/poincare/test/power.cpp b/poincare/test/power.cpp index 50fc3871b..99f86e06c 100644 --- a/poincare/test/power.cpp +++ b/poincare/test/power.cpp @@ -15,20 +15,26 @@ QUIZ_CASE(poincare_power_evaluate) { assert_parsed_expression_evaluates_to("0^0", Undefined::Name()); assert_parsed_expression_evaluates_to("0^2", "0"); assert_parsed_expression_evaluates_to("0^(-2)", Undefined::Name()); - assert_parsed_expression_evaluates_to("(-2)^4.2", "14.8690638497+10.8030072384*I", Radian, Cartesian, 12); - assert_parsed_expression_evaluates_to("(-0.1)^4", "0.0001", Radian, Cartesian, 12); + assert_parsed_expression_evaluates_to("(-2)^4.2", "14.8690638497+10.8030072384*I", System, Radian, Cartesian, 12); + assert_parsed_expression_evaluates_to("(-0.1)^4", "0.0001", System, Radian, Cartesian, 12); #if MATRICES_ARE_DEFINED - assert_parsed_expression_evaluates_to("[[1,2][3,4]]^(-3)", "[[-14.75,6.75][10.125,-4.625]]", Degree, Cartesian, 6); + assert_parsed_expression_evaluates_to("[[1,2][3,4]]^(-3)", "[[-14.75,6.75][10.125,-4.625]]", System, Degree, Cartesian, 6); assert_parsed_expression_evaluates_to("[[1,2][3,4]]^3", "[[37,54][81,118]]"); #endif assert_parsed_expression_evaluates_to("0^2", "0"); assert_parsed_expression_evaluates_to("I^I", "2.0787957635076E-1"); - assert_parsed_expression_evaluates_to("1.0066666666667^60", "1.48985", Radian, Cartesian, 6); + assert_parsed_expression_evaluates_to("1.0066666666667^60", "1.48985", System, Radian, Cartesian, 6); assert_parsed_expression_evaluates_to("1.0066666666667^60", "1.4898457083046"); assert_parsed_expression_evaluates_to("X^(I*P)", "-1"); assert_parsed_expression_evaluates_to("X^(I*P)", "-1"); - assert_parsed_expression_evaluates_to("X^(I*P+2)", "-7.38906", Radian, Cartesian, 6); + assert_parsed_expression_evaluates_to("X^(I*P+2)", "-7.38906", System, Radian, Cartesian, 6); assert_parsed_expression_evaluates_to("X^(I*P+2)", "-7.3890560989307"); + assert_parsed_expression_evaluates_to("(-1)^(1/3)", "0.5+0.8660254*I"); + assert_parsed_expression_evaluates_to("(-1)^(1/3)", "0.5+8.6602540378444E-1*I"); + assert_parsed_expression_evaluates_to("X^(I*P/3)", "0.5+0.8660254*I"); + assert_parsed_expression_evaluates_to("X^(I*P/3)", "0.5+8.6602540378444E-1*I"); + assert_parsed_expression_evaluates_to("I^(2/3)", "0.5+0.8660254*I"); + assert_parsed_expression_evaluates_to("I^(2/3)", "0.5+8.6602540378444E-1*I"); } QUIZ_CASE(poincare_power_simplify) { @@ -52,6 +58,7 @@ QUIZ_CASE(poincare_power_simplify) { assert_parsed_expression_simplify_to("(12^4*x)^(0.5)", "144*R(x)"); assert_parsed_expression_simplify_to("R(32)", "4*R(2)"); assert_parsed_expression_simplify_to("R(-1)", "I"); + assert_parsed_expression_simplify_to("R(-1*R(-1))", "R(2)/2-R(2)/2*I"); assert_parsed_expression_simplify_to("R(3^2)", "3"); assert_parsed_expression_simplify_to("2^(2+P)", "4*2^P"); assert_parsed_expression_simplify_to("R(5513219850886344455940081)", "2348024669991"); @@ -60,10 +67,9 @@ QUIZ_CASE(poincare_power_simplify) { assert_parsed_expression_simplify_to("R(P^2)", "P"); assert_parsed_expression_simplify_to("R((-P)^2)", "P"); assert_parsed_expression_simplify_to("R(x*144)", "12*R(x)"); - assert_parsed_expression_simplify_to("R(x*144*P^2)", "12*R(x)*P"); - assert_parsed_expression_simplify_to("R(x*144*P)", "12*R(x)*R(P)"); - assert_parsed_expression_simplify_to("(-1)*(2+(-4*R(2)))", "-2+4*R(2)"); - assert_parsed_expression_simplify_to("R(2-4*R(2))", "R(-2+4*R(2))*I"); + assert_parsed_expression_simplify_to("R(x*144*P^2)", "12*P*R(x)"); + assert_parsed_expression_simplify_to("R(x*144*P)", "12*R(P)*R(x)"); + assert_parsed_expression_simplify_to("(-1)*(2+(-4*R(2)))", "4*R(2)-2"); assert_parsed_expression_simplify_to("x^(1/2)", "R(x)"); assert_parsed_expression_simplify_to("x^(-1/2)", "1/R(x)"); assert_parsed_expression_simplify_to("x^(1/7)", "root(x,7)"); @@ -75,7 +81,7 @@ QUIZ_CASE(poincare_power_simplify) { assert_parsed_expression_simplify_to("10^log(P)", "P"); assert_parsed_expression_simplify_to("X^ln(65)", "65"); assert_parsed_expression_simplify_to("X^ln(PX)", "P*X"); - assert_parsed_expression_simplify_to("X^log(PX)", "X^(log(P)+log(X))"); + assert_parsed_expression_simplify_to("X^log(PX)", "X^(log(X)+log(P))"); assert_parsed_expression_simplify_to("R(X^2)", "X"); assert_parsed_expression_simplify_to("999^(10000/3)", "999^(10000/3)"); /* This does not reduce but should not as the integer is above @@ -86,11 +92,17 @@ QUIZ_CASE(poincare_power_simplify) { * factors above k_maxNumberOfPrimeFactors. */ assert_parsed_expression_simplify_to("1002101470343^(1/3)", "root(1002101470343,3)"); assert_parsed_expression_simplify_to("P*P*P", "P^3"); - assert_parsed_expression_simplify_to("(x+P)^(3)", "x^3+3*x^2*P+3*x*P^2+P^3"); - assert_parsed_expression_simplify_to("(5+R(2))^(-8)", "(1446241-1003320*R(2))/78310985281"); - assert_parsed_expression_simplify_to("(5*P+R(2))^(-5)", "1/(4*R(2)+100*P+500*R(2)*P^2+2500*P^3+3125*R(2)*P^4+3125*P^5)"); - assert_parsed_expression_simplify_to("(1+R(2)+R(3))^5", "296+224*R(2)+184*R(3)+120*R(6)"); - assert_parsed_expression_simplify_to("(P+R(2)+R(3)+x)^(-3)", "1/(11*R(2)+9*R(3)+15*x+6*R(6)*x+3*R(2)*x^2+3*R(3)*x^2+x^3+15*P+6*R(6)*P+6*R(2)*x*P+6*R(3)*x*P+3*x^2*P+3*R(2)*P^2+3*R(3)*P^2+3*x*P^2+P^3)"); + assert_parsed_expression_simplify_to("(x+P)^(3)", "x^3+3*P*x^2+3*P^2*x+P^3"); + assert_parsed_expression_simplify_to("(5+R(2))^(-8)", "(-1003320*R(2)+1446241)/78310985281"); + assert_parsed_expression_simplify_to("(5*P+R(2))^(-5)", "1/(3125*P^5+3125*R(2)*P^4+2500*P^3+500*R(2)*P^2+100*P+4*R(2))"); + assert_parsed_expression_simplify_to("(1+R(2)+R(3))^5", "120*R(6)+184*R(3)+224*R(2)+296"); + assert_parsed_expression_simplify_to("(P+R(2)+R(3)+x)^(-3)", "1/(x^3+3*P*x^2+3*R(3)*x^2+3*R(2)*x^2+3*P^2*x+6*R(3)*P*x+6*R(2)*P*x+6*R(6)*x+15*x+P^3+3*R(3)*P^2+3*R(2)*P^2+6*R(6)*P+15*P+9*R(3)+11*R(2))"); assert_parsed_expression_simplify_to("1.0066666666667^60", "(10066666666667/10000000000000)^60"); assert_parsed_expression_simplify_to("2^(6+P+x)", "64*2^(x+P)"); + assert_parsed_expression_simplify_to("I^(2/3)", "1/2+R(3)/2*I"); + assert_parsed_expression_simplify_to("X^(I*P/3)", "1/2+R(3)/2*I"); + assert_parsed_expression_simplify_to("(-1)^(1/3)", "1/2+R(3)/2*I"); + assert_parsed_expression_simplify_to("R(-x)", "R(-x)"); + assert_parsed_expression_simplify_to("R(x)^2", "x", User, Radian, Cartesian); + assert_parsed_expression_simplify_to("R(x)^2", "R(x)^2", User, Radian, Real); } diff --git a/poincare/test/properties.cpp b/poincare/test/properties.cpp index 126477ef6..e36b93fb2 100644 --- a/poincare/test/properties.cpp +++ b/poincare/test/properties.cpp @@ -11,20 +11,21 @@ constexpr Poincare::ExpressionNode::Sign Positive = Poincare::ExpressionNode::Si constexpr Poincare::ExpressionNode::Sign Negative = Poincare::ExpressionNode::Sign::Negative; constexpr Poincare::ExpressionNode::Sign Unknown = Poincare::ExpressionNode::Sign::Unknown; -void assert_parsed_expression_sign(const char * expression, Poincare::ExpressionNode::Sign sign) { +void assert_parsed_expression_sign(const char * expression, Poincare::ExpressionNode::Sign sign, Poincare::Preferences::ComplexFormat complexFormat = Cartesian) { Shared::GlobalContext globalContext; Expression e = parse_expression(expression); quiz_assert(!e.isUninitialized()); - e = e.simplify(globalContext, Degree); - quiz_assert(e.sign() == sign); + e = e.reduce(globalContext, complexFormat, Degree); + quiz_assert(e.sign(&globalContext) == sign); } QUIZ_CASE(poincare_sign) { - assert_parsed_expression_sign("abs(-cos(2))", Positive); + assert_parsed_expression_sign("abs(-cos(2)+I)", Positive); assert_parsed_expression_sign("2.345E-23", Positive); assert_parsed_expression_sign("-2.345E-23", Negative); assert_parsed_expression_sign("2*(-3)*abs(-32)", Negative); assert_parsed_expression_sign("2*(-3)*abs(-32)*cos(3)", Unknown); + assert_parsed_expression_sign("x", Unknown); assert_parsed_expression_sign("2^(-abs(3))", Positive); assert_parsed_expression_sign("(-2)^4", Positive); assert_parsed_expression_sign("(-2)^3", Negative); @@ -33,6 +34,10 @@ QUIZ_CASE(poincare_sign) { assert_parsed_expression_sign("-23/32", Negative); assert_parsed_expression_sign("P", Positive); assert_parsed_expression_sign("X", Positive); + assert_parsed_expression_sign("0", Positive); + assert_parsed_expression_sign("cos(90)", Positive); + assert_parsed_expression_sign("R(-1)", Unknown); + assert_parsed_expression_sign("R(-1)", Unknown, Real); } QUIZ_CASE(poincare_polynomial_degree) { @@ -53,6 +58,7 @@ QUIZ_CASE(poincare_polynomial_degree) { assert_parsed_expression_polynomial_degree("prediction(0.2,10)+1", -1); assert_parsed_expression_polynomial_degree("2-x-x^3", 3); assert_parsed_expression_polynomial_degree("P*x", 1); + assert_parsed_expression_polynomial_degree("R(-1)*x", -1, "x", Real); // f: x->x^2+Px+1 assert_simplify("1+P*x+x^2>f(x)"); assert_parsed_expression_polynomial_degree("f(x)", 2); @@ -61,7 +67,7 @@ QUIZ_CASE(poincare_polynomial_degree) { void assert_expression_has_characteristic_range(Expression e, float range, Preferences::AngleUnit angleUnit = Preferences::AngleUnit::Degree) { Shared::GlobalContext globalContext; quiz_assert(!e.isUninitialized()); - e = e.simplify(globalContext, angleUnit); + e = e.reduce(globalContext, Preferences::ComplexFormat::Cartesian, angleUnit); if (std::isnan(range)) { quiz_assert(std::isnan(e.characteristicXRange(globalContext, angleUnit))); } else { @@ -70,19 +76,19 @@ void assert_expression_has_characteristic_range(Expression e, float range, Prefe } QUIZ_CASE(poincare_characteristic_range) { - assert_expression_has_characteristic_range(Cosine::Builder(Symbol(Poincare::Symbol::SpecialSymbols::UnknownX)), 360.0f); - assert_expression_has_characteristic_range(Cosine::Builder(Opposite(Symbol(Poincare::Symbol::SpecialSymbols::UnknownX))), 360.0f); - assert_expression_has_characteristic_range(Cosine::Builder(Symbol(Poincare::Symbol::SpecialSymbols::UnknownX)), 2.0f*M_PI, Preferences::AngleUnit::Radian); - assert_expression_has_characteristic_range(Cosine::Builder(Opposite(Symbol(Poincare::Symbol::SpecialSymbols::UnknownX))), 2.0f*M_PI, Preferences::AngleUnit::Radian); - assert_expression_has_characteristic_range(Sine::Builder(Addition(Multiplication(Rational(9),Symbol(Poincare::Symbol::SpecialSymbols::UnknownX)),Rational(10))), 40.0f); - assert_expression_has_characteristic_range(Addition(Sine::Builder(Addition(Multiplication(Rational(9),Symbol(Poincare::Symbol::SpecialSymbols::UnknownX)),Rational(10))),Cosine::Builder(Division(Symbol(Poincare::Symbol::SpecialSymbols::UnknownX),Rational(2)))), 720.0f); - assert_expression_has_characteristic_range(Addition(Sine::Builder(Addition(Multiplication(Rational(9),Symbol(Poincare::Symbol::SpecialSymbols::UnknownX)),Rational(10))),Cosine::Builder(Division(Symbol(Poincare::Symbol::SpecialSymbols::UnknownX),Rational(2)))), 4.0f*M_PI, Preferences::AngleUnit::Radian); - assert_expression_has_characteristic_range(Symbol(Poincare::Symbol::SpecialSymbols::UnknownX), NAN); - assert_expression_has_characteristic_range(Addition(Cosine::Builder(Rational(3)),Rational(2)), 0.0f); - assert_expression_has_characteristic_range(CommonLogarithm::Builder(Cosine::Builder(Multiplication(Rational(40),Symbol(Poincare::Symbol::SpecialSymbols::UnknownX)))), 9.0f); - assert_expression_has_characteristic_range(Cosine::Builder((Expression)Cosine::Builder(Symbol(Poincare::Symbol::SpecialSymbols::UnknownX))), 360.0f); + assert_expression_has_characteristic_range(Cosine::Builder(Symbol::Builder(Poincare::Symbol::SpecialSymbols::UnknownX)), 360.0f); + assert_expression_has_characteristic_range(Cosine::Builder(Opposite::Builder(Symbol::Builder(Poincare::Symbol::SpecialSymbols::UnknownX))), 360.0f); + assert_expression_has_characteristic_range(Cosine::Builder(Symbol::Builder(Poincare::Symbol::SpecialSymbols::UnknownX)), 2.0f*M_PI, Preferences::AngleUnit::Radian); + assert_expression_has_characteristic_range(Cosine::Builder(Opposite::Builder(Symbol::Builder(Poincare::Symbol::SpecialSymbols::UnknownX))), 2.0f*M_PI, Preferences::AngleUnit::Radian); + assert_expression_has_characteristic_range(Sine::Builder(Addition::Builder(Multiplication::Builder(Rational::Builder(9),Symbol::Builder(Poincare::Symbol::SpecialSymbols::UnknownX)),Rational::Builder(10))), 40.0f); + assert_expression_has_characteristic_range(Addition::Builder(Sine::Builder(Addition::Builder(Multiplication::Builder(Rational::Builder(9),Symbol::Builder(Poincare::Symbol::SpecialSymbols::UnknownX)),Rational::Builder(10))),Cosine::Builder(Division::Builder(Symbol::Builder(Poincare::Symbol::SpecialSymbols::UnknownX),Rational::Builder(2)))), 720.0f); + assert_expression_has_characteristic_range(Addition::Builder(Sine::Builder(Addition::Builder(Multiplication::Builder(Rational::Builder(9),Symbol::Builder(Poincare::Symbol::SpecialSymbols::UnknownX)),Rational::Builder(10))),Cosine::Builder(Division::Builder(Symbol::Builder(Poincare::Symbol::SpecialSymbols::UnknownX),Rational::Builder(2)))), 4.0f*M_PI, Preferences::AngleUnit::Radian); + assert_expression_has_characteristic_range(Symbol::Builder(Poincare::Symbol::SpecialSymbols::UnknownX), NAN); + assert_expression_has_characteristic_range(Addition::Builder(Cosine::Builder(Rational::Builder(3)),Rational::Builder(2)), 0.0f); + assert_expression_has_characteristic_range(CommonLogarithm::Builder(Cosine::Builder(Multiplication::Builder(Rational::Builder(40),Symbol::Builder(Poincare::Symbol::SpecialSymbols::UnknownX)))), 9.0f); + assert_expression_has_characteristic_range(Cosine::Builder((Expression)Cosine::Builder(Symbol::Builder(Poincare::Symbol::SpecialSymbols::UnknownX))), 360.0f); assert_simplify("cos(x)>f(x)"); - assert_expression_has_characteristic_range(Function("f",1,Symbol(Poincare::Symbol::SpecialSymbols::UnknownX)), 360.0f); + assert_expression_has_characteristic_range(Function::Builder("f",1,Symbol::Builder(Poincare::Symbol::SpecialSymbols::UnknownX)), 360.0f); } void assert_parsed_expression_has_variables(const char * expression, const char * variables[], int trueNumberOfVariables) { @@ -123,18 +129,18 @@ QUIZ_CASE(poincare_get_variables) { assert_parsed_expression_has_variables("f(tata)", variableBuffer7, 2); } -void assert_parsed_expression_has_polynomial_coefficient(const char * expression, const char * symbolName, const char ** coefficients, Preferences::AngleUnit angleUnit = Preferences::AngleUnit::Degree) { +void assert_parsed_expression_has_polynomial_coefficient(const char * expression, const char * symbolName, const char ** coefficients, Preferences::ComplexFormat complexFormat = Preferences::ComplexFormat::Cartesian, Preferences::AngleUnit angleUnit = Preferences::AngleUnit::Degree) { Shared::GlobalContext globalContext; Expression e = parse_expression(expression); quiz_assert(!e.isUninitialized()); - e = e.reduce(globalContext, angleUnit); + e = e.reduce(globalContext, complexFormat, angleUnit); Expression coefficientBuffer[Poincare::Expression::k_maxNumberOfPolynomialCoefficients]; - int d = e.getPolynomialReducedCoefficients(symbolName, coefficientBuffer, globalContext, Radian); + int d = e.getPolynomialReducedCoefficients(symbolName, coefficientBuffer, globalContext, complexFormat, Radian); for (int i = 0; i <= d; i++) { Expression f = parse_expression(coefficients[i]); quiz_assert(!f.isUninitialized()); - coefficientBuffer[i] = coefficientBuffer[i].reduce(globalContext, angleUnit); - f = f.reduce(globalContext, angleUnit); + coefficientBuffer[i] = coefficientBuffer[i].reduce(globalContext, complexFormat, angleUnit); + f = f.reduce(globalContext, complexFormat, angleUnit); quiz_assert(coefficientBuffer[i].isIdenticalTo(f)); } quiz_assert(coefficients[d+1] == 0); @@ -154,4 +160,8 @@ QUIZ_CASE(poincare_get_polynomial_coefficients) { const char * coefficient4[] = {"1", "P", "1", 0}; //x^2+Pi*x+1 assert_simplify("1+P*x+x^2>f(x)"); assert_parsed_expression_has_polynomial_coefficient("f(x)", "x", coefficient4); + const char * coefficient5[] = {"0", "I", 0}; //R(-1)x + assert_parsed_expression_has_polynomial_coefficient("R(-1)x", "x", coefficient5); + const char * coefficient6[] = {0}; //R(-1)x + assert_parsed_expression_has_polynomial_coefficient("R(-1)x", "x", coefficient6, Real); } diff --git a/poincare/test/rational.cpp b/poincare/test/rational.cpp index b8d740a36..66d42daaf 100644 --- a/poincare/test/rational.cpp +++ b/poincare/test/rational.cpp @@ -9,13 +9,13 @@ using namespace Poincare; QUIZ_CASE(poincare_rational_constructor) { int initialPoolSize = pool_size(); - Rational a("123","324"); - Rational b("3456"); - Rational c(123,324); - Rational d(3456789); + Rational a = Rational::Builder("123","324"); + Rational b = Rational::Builder("3456"); + Rational c = Rational::Builder(123,324); + Rational d = Rational::Builder(3456789); Integer overflow = Integer::Overflow(false); - Rational e(overflow); - Rational f(overflow, overflow); + Rational e = Rational::Builder(overflow); + Rational f = Rational::Builder(overflow, overflow); assert_pool_size(initialPoolSize+6); } @@ -35,30 +35,31 @@ static inline void assert_greater(const Rational i, const Rational j) { } QUIZ_CASE(poincare_rational_compare) { - assert_equal(Rational(123,324), Rational(41,108)); - assert_not_equal(Rational(123,234), Rational(42, 108)); - assert_lower(Rational(123,234), Rational(456,567)); - assert_lower(Rational(-123, 234),Rational(456, 567)); - assert_greater(Rational(123, 234),Rational(-456, 567)); - assert_greater(Rational(123, 234),Rational("123456789123456789", "12345678912345678910")); + assert_equal(Rational::Builder(123,324), Rational::Builder(41,108)); + assert_not_equal(Rational::Builder(123,234), Rational::Builder(42, 108)); + assert_lower(Rational::Builder(123,234), Rational::Builder(456,567)); + assert_lower(Rational::Builder(-123, 234),Rational::Builder(456, 567)); + assert_greater(Rational::Builder(123, 234),Rational::Builder(-456, 567)); + assert_greater(Rational::Builder(123, 234),Rational::Builder("123456789123456789", "12345678912345678910")); } QUIZ_CASE(poincare_rational_properties) { - quiz_assert(Rational(-2).sign() == ExpressionNode::Sign::Negative); - quiz_assert(Rational(-2, 3).sign() == ExpressionNode::Sign::Negative); - quiz_assert(Rational(2, 3).sign() == ExpressionNode::Sign::Positive); - quiz_assert(Rational(0).isZero()); - quiz_assert(!Rational(231).isZero()); - quiz_assert(Rational(1).isOne()); - quiz_assert(!Rational(-1).isOne()); - quiz_assert(!Rational(1).isMinusOne()); - quiz_assert(Rational(-1).isMinusOne()); - quiz_assert(Rational(1,2).isHalf()); - quiz_assert(!Rational(-1).isHalf()); - quiz_assert(Rational(-1,2).isMinusHalf()); - quiz_assert(!Rational(3,2).isMinusHalf()); - quiz_assert(Rational(10).isTen()); - quiz_assert(!Rational(-1).isTen()); + quiz_assert(Rational::Builder(-2).sign() == ExpressionNode::Sign::Negative); + quiz_assert(Rational::Builder(-2, 3).sign() == ExpressionNode::Sign::Negative); + quiz_assert(Rational::Builder(2, 3).sign() == ExpressionNode::Sign::Positive); + quiz_assert(Rational::Builder(0, 3).sign() == ExpressionNode::Sign::Positive); + quiz_assert(Rational::Builder(0).isZero()); + quiz_assert(!Rational::Builder(231).isZero()); + quiz_assert(Rational::Builder(1).isOne()); + quiz_assert(!Rational::Builder(-1).isOne()); + quiz_assert(!Rational::Builder(1).isMinusOne()); + quiz_assert(Rational::Builder(-1).isMinusOne()); + quiz_assert(Rational::Builder(1,2).isHalf()); + quiz_assert(!Rational::Builder(-1).isHalf()); + quiz_assert(Rational::Builder(-1,2).isMinusHalf()); + quiz_assert(!Rational::Builder(3,2).isMinusHalf()); + quiz_assert(Rational::Builder(10).isTen()); + quiz_assert(!Rational::Builder(-1).isTen()); } static inline void assert_add_to(const Rational i, const Rational j, const Rational k) { @@ -66,9 +67,9 @@ static inline void assert_add_to(const Rational i, const Rational j, const Ratio } QUIZ_CASE(poincare_rational_addition) { - assert_add_to(Rational(1,2), Rational(1), Rational(3,2)); - assert_add_to(Rational("18446744073709551616","4294967296"), Rational(8,9), Rational("38654705672","9")); - assert_add_to(Rational("18446744073709551616","4294967296"), Rational(-8,9), Rational("38654705656","9")); + assert_add_to(Rational::Builder(1,2), Rational::Builder(1), Rational::Builder(3,2)); + assert_add_to(Rational::Builder("18446744073709551616","4294967296"), Rational::Builder(8,9), Rational::Builder("38654705672","9")); + assert_add_to(Rational::Builder("18446744073709551616","4294967296"), Rational::Builder(-8,9), Rational::Builder("38654705656","9")); } static inline void assert_pow_to(const Rational i,const Integer j, const Rational k) { @@ -76,8 +77,8 @@ static inline void assert_pow_to(const Rational i,const Integer j, const Rationa } QUIZ_CASE(poincare_rational_power) { - assert_pow_to(Rational(4,5), Rational(3).signedIntegerNumerator(), Rational(64,125)); - assert_pow_to(Rational(4,5), Rational(-3).signedIntegerNumerator(), Rational(125,64)); + assert_pow_to(Rational::Builder(4,5), Rational::Builder(3).signedIntegerNumerator(), Rational::Builder(64,125)); + assert_pow_to(Rational::Builder(4,5), Rational::Builder(-3).signedIntegerNumerator(), Rational::Builder(125,64)); } // Simplify @@ -136,12 +137,12 @@ QUIZ_CASE(poincare_rational_approximate) { //Serialize QUIZ_CASE(poincare_rational_serialize) { - assert_parsed_expression_serialize_to(Rational(-2, 3), "-2/3"); - assert_parsed_expression_serialize_to(Rational("2345678909876"), "2345678909876"); - assert_parsed_expression_serialize_to(Rational("-2345678909876", "5"), "-2345678909876/5"); - assert_parsed_expression_serialize_to(Rational(MaxIntegerString()), MaxIntegerString()); + assert_parsed_expression_serialize_to(Rational::Builder(-2, 3), "-2/3"); + assert_parsed_expression_serialize_to(Rational::Builder("2345678909876"), "2345678909876"); + assert_parsed_expression_serialize_to(Rational::Builder("-2345678909876", "5"), "-2345678909876/5"); + assert_parsed_expression_serialize_to(Rational::Builder(MaxIntegerString()), MaxIntegerString()); Integer one(1); Integer overflow = Integer::Overflow(false); - assert_parsed_expression_serialize_to(Rational(one, overflow), "1/inf"); - assert_parsed_expression_serialize_to(Rational(overflow), Infinity::Name()); + assert_parsed_expression_serialize_to(Rational::Builder(one, overflow), "1/inf"); + assert_parsed_expression_serialize_to(Rational::Builder(overflow), Infinity::Name()); } diff --git a/poincare/test/simplify.cpp b/poincare/test/simplify.cpp new file mode 100644 index 000000000..e6a5e6e2f --- /dev/null +++ b/poincare/test/simplify.cpp @@ -0,0 +1,90 @@ +#include +#include +#include +#include "helper.h" +#if POINCARE_TESTS_PRINT_EXPRESSIONS +#include "../src/expression_debug.h" +#include +using namespace std; +#endif + +using namespace Poincare; +QUIZ_CASE(poincare_reduction_target) { + assert_parsed_expression_simplify_to("1/P+1/x", "1/x+1/P", System); + assert_parsed_expression_simplify_to("1/P+1/x", "(x+P)/(P*x)", User); + + assert_parsed_expression_simplify_to("1/(1+I)", "1/(I+1)", System); + assert_parsed_expression_simplify_to("1/(1+I)", "1/2-1/2*I", User); + + assert_parsed_expression_simplify_to("sin(x)/(cos(x)*cos(x))", "sin(x)/cos(x)^2", System); + assert_parsed_expression_simplify_to("sin(x)/(cos(x)*cos(x))", "tan(x)/cos(x)", User); + + assert_parsed_expression_simplify_to("x^0", "x^0", System); + assert_parsed_expression_simplify_to("x^0", "1", User); + + assert_parsed_expression_simplify_to("x^(2/3)", "root(x,3)^2", System); + assert_parsed_expression_simplify_to("x^(2/3)", "x^(2/3)", User); + assert_parsed_expression_simplify_to("x^(1/3)", "root(x,3)", System); + assert_parsed_expression_simplify_to("x^(1/3)", "root(x,3)", System); + assert_parsed_expression_simplify_to("x^2", "x^2", System); + assert_parsed_expression_simplify_to("x^2", "x^2", User); + + assert_parsed_expression_simplify_to("1/(R(2)+R(3))", "1/(R(3)+R(2))", System); + assert_parsed_expression_simplify_to("1/(R(2)+R(3))", "R(3)-R(2)", User); + + assert_parsed_expression_simplify_to("sign(abs(x))", "sign(abs(x))", System); + assert_parsed_expression_simplify_to("sign(abs(x))", "1", User); + + assert_parsed_expression_simplify_to("atan(1/x)", "atan(1/x)", System); + assert_parsed_expression_simplify_to("atan(1/x)", "(P*sign(x)-2*atan(x))/2", User); + + assert_parsed_expression_simplify_to("(1+x)/(1+x)", "(x+1)^0", System); + assert_parsed_expression_simplify_to("(1+x)/(1+x)", "1", User); +} + +QUIZ_CASE(poincare_simplify_mix) { + // Root at denominator + assert_parsed_expression_simplify_to("1/(R(2)+R(3))", "R(3)-R(2)"); + assert_parsed_expression_simplify_to("1/(5+R(3))", "(-R(3)+5)/22"); + assert_parsed_expression_simplify_to("1/(R(2)+4)", "(-R(2)+4)/14"); + assert_parsed_expression_simplify_to("1/(2R(2)-4)", "(-R(2)-2)/4"); + assert_parsed_expression_simplify_to("1/(-2R(2)+4)", "(R(2)+2)/4"); + assert_parsed_expression_simplify_to("45^2", "2025"); + assert_parsed_expression_simplify_to("1/(R(2)ln(3))", "R(2)/(2*ln(3))"); + assert_parsed_expression_simplify_to("R(3/2)", "R(6)/2"); + + // Common operation mix + assert_parsed_expression_simplify_to("(R(2)*P + R(2)*X)/R(2)", "X+P"); + assert_parsed_expression_simplify_to("P+(3R(2)-2R(3))/25", "(25*P-2*R(3)+3*R(2))/25"); + assert_parsed_expression_simplify_to("ln(2+3)", "ln(5)"); + assert_parsed_expression_simplify_to("3*A*B*C+4*cos(2)-2*A*B*C+A*B*C+ln(3)+4*A*B-5*A*B*C+cos(3)*ln(5)+cos(2)-45*cos(2)", "cos(3)*ln(5)+ln(3)-40*cos(2)+4*A*B-3*A*B*C"); + assert_parsed_expression_simplify_to("2*A+3*cos(2)+3+4*ln(5)+5*A+2*ln(5)+1+0", "6*ln(5)+3*cos(2)+7*A+4"); + assert_parsed_expression_simplify_to("2.3*A+3*cos(2)+3+4*ln(5)+5*A+2*ln(5)+1.2+0.235", "(1200*ln(5)+600*cos(2)+1460*A+887)/200"); + assert_parsed_expression_simplify_to("2*A*B*C+2.3*A*B+3*A^2+5.2*A*B*C+4*A^2", "(70*A^2+23*A*B+72*A*B*C)/10"); + assert_parsed_expression_simplify_to("(A*B)^2*A+4*A^3", "4*A^3+A^3*B^2"); + assert_parsed_expression_simplify_to("(A*3)^2*A+4*A^3", "13*A^3"); + assert_parsed_expression_simplify_to("A^2^2*A+4*A^3", "A^5+4*A^3"); + assert_parsed_expression_simplify_to("0.5+4*A*B-2.3+2*A*B-2*B*A^C-cos(4)+2*A^C*B+A*B*C*D", "(-5*cos(4)+30*A*B+5*A*B*C*D-9)/5"); + assert_parsed_expression_simplify_to("(1+R(2))/5", "(R(2)+1)/5"); + assert_parsed_expression_simplify_to("(2+R(6))^2", "4*R(6)+10"); + assert_parsed_expression_simplify_to("tan(3)ln(2)+P", "tan(3)*ln(2)+P"); + + // Complex + assert_parsed_expression_simplify_to("I", "I"); + assert_parsed_expression_simplify_to("R(-33)", "R(33)*I"); + assert_parsed_expression_simplify_to("I^(3/5)", "(R(2)*R(-R(5)+5))/4+(R(5)+1)/4*I"); + assert_parsed_expression_simplify_to("IIII", "1"); + assert_parsed_expression_simplify_to("R(-I)", "R(2)/2-R(2)/2*I"); + assert_parsed_expression_simplify_to("A*cos(9)IIln(2)", "-A*cos(9)*ln(2)"); + assert_parsed_expression_simplify_to("(R(2)+R(2)*I)/2(R(2)+R(2)*I)/2(R(2)+R(2)*I)/2", "R(2)/32-R(2)/32*I"); + assert_parsed_expression_simplify_to("root(5^((-I)3^9),I)", "1/X^atan(tan(19683*ln(5)))"); + assert_parsed_expression_simplify_to("I^I", "1/X^(P/2)"); + assert_parsed_expression_simplify_to("I/(1+I*R(x))", "I/(R(x)*I+1)"); + assert_parsed_expression_simplify_to("x+I/(1+I*R(x))", "(x^(3/2)*I+I+x)/(R(x)*I+1)"); + + //assert_parsed_expression_simplify_to("log(cos(9)^ln(6), cos(9))", "ln(2)+ln(3)"); // TODO: for this to work, we must know the sign of cos(9) + //assert_parsed_expression_simplify_to("log(cos(9)^ln(6), 9)", "ln(6)*log(cos(9), 9)"); // TODO: for this to work, we must know the sign of cos(9) + assert_parsed_expression_simplify_to("(((R(6)-R(2))/4)/((R(6)+R(2))/4))+1", "-R(3)+3"); + //assert_parsed_expression_simplify_to("1/R(I) * (R(2)-I*R(2))", "-2I"); // TODO: get rid of complex at denominator? + +} diff --git a/poincare/test/simplify_mix.cpp b/poincare/test/simplify_mix.cpp deleted file mode 100644 index 4db707824..000000000 --- a/poincare/test/simplify_mix.cpp +++ /dev/null @@ -1,56 +0,0 @@ -#include -#include -#include -#include "helper.h" -#if POINCARE_TESTS_PRINT_EXPRESSIONS -#include "../src/expression_debug.h" -#include -using namespace std; -#endif - -using namespace Poincare; - -QUIZ_CASE(poincare_simplify_mix) { - // Root at denominator - assert_parsed_expression_simplify_to("1/(R(2)+R(3))", "-R(2)+R(3)"); - assert_parsed_expression_simplify_to("1/(5+R(3))", "(5-R(3))/22"); - assert_parsed_expression_simplify_to("1/(R(2)+4)", "(4-R(2))/14"); - assert_parsed_expression_simplify_to("1/(2R(2)-4)", "(-2-R(2))/4"); - assert_parsed_expression_simplify_to("1/(-2R(2)+4)", "(2+R(2))/4"); - assert_parsed_expression_simplify_to("45^2", "2025"); - assert_parsed_expression_simplify_to("1/(R(2)ln(3))", "R(2)/(2*ln(3))"); - assert_parsed_expression_simplify_to("R(3/2)", "R(6)/2"); - - // Common operation mix - assert_parsed_expression_simplify_to("(R(2)*P + R(2)*X)/R(2)", "P+X"); - assert_parsed_expression_simplify_to("P+(3R(2)-2R(3))/25", "(3*R(2)-2*R(3)+25*P)/25"); - assert_parsed_expression_simplify_to("ln(2+3)", "ln(5)"); - assert_parsed_expression_simplify_to("3*A*B*C+4*cos(2)-2*A*B*C+A*B*C+ln(3)+4*A*B-5*A*B*C+cos(3)*ln(5)+cos(2)-45*cos(2)", "-40*cos(2)+ln(3)+cos(3)*ln(5)+4*A*B-3*A*B*C"); - assert_parsed_expression_simplify_to("2*A+3*cos(2)+3+4*ln(5)+5*A+2*ln(5)+1+0", "4+3*cos(2)+6*ln(5)+7*A"); - assert_parsed_expression_simplify_to("2.3*A+3*cos(2)+3+4*ln(5)+5*A+2*ln(5)+1.2+0.235", "(887+600*cos(2)+1200*ln(5)+1460*A)/200"); - assert_parsed_expression_simplify_to("2*A*B*C+2.3*A*B+3*A^2+5.2*A*B*C+4*A^2", "(70*A^2+23*A*B+72*A*B*C)/10"); - assert_parsed_expression_simplify_to("(A*B)^2*A+4*A^3", "4*A^3+A^3*B^2"); - assert_parsed_expression_simplify_to("(A*3)^2*A+4*A^3", "13*A^3"); - assert_parsed_expression_simplify_to("A^2^2*A+4*A^3", "4*A^3+A^5"); - assert_parsed_expression_simplify_to("0.5+4*A*B-2.3+2*A*B-2*B*A^C-cos(4)+2*A^C*B+A*B*C*D", "(-9-5*cos(4)+30*A*B+5*A*B*C*D)/5"); - assert_parsed_expression_simplify_to("(1+R(2))/5", "(1+R(2))/5"); - assert_parsed_expression_simplify_to("(2+R(6))^2", "10+4*R(6)"); - assert_parsed_expression_simplify_to("tan(3)ln(2)+P", "tan(3)*ln(2)+P"); - - // Complex - assert_parsed_expression_simplify_to("I", "I"); - assert_parsed_expression_simplify_to("R(-33)", "R(33)*I"); - assert_parsed_expression_simplify_to("I^(3/5)", "(R(2)*R(5-R(5))+I+R(5)*I)/4"); - assert_parsed_expression_simplify_to("IIII", "1"); - assert_parsed_expression_simplify_to("R(-I)", "R(-I)"); - assert_parsed_expression_simplify_to("A*cos(9)IIln(2)", "-cos(9)*ln(2)*A"); - assert_parsed_expression_simplify_to("(R(2)+R(2)*I)/2(R(2)+R(2)*I)/2(R(2)+R(2)*I)/2", "(R(2)-R(2)*I)/32"); - assert_parsed_expression_simplify_to("root(5^((-I)3^9),I)", "(5^(-19683*I))^(-I)"); - assert_parsed_expression_simplify_to("I^I", "I^I"); - - //assert_parsed_expression_simplify_to("log(cos(9)^ln(6), cos(9))", "ln(2)+ln(3)"); // TODO: for this to work, we must know the sign of cos(9) - //assert_parsed_expression_simplify_to("log(cos(9)^ln(6), 9)", "ln(6)*log(cos(9), 9)"); // TODO: for this to work, we must know the sign of cos(9) - assert_parsed_expression_simplify_to("(((R(6)-R(2))/4)/((R(6)+R(2))/4))+1", "3-R(3)"); - //assert_parsed_expression_simplify_to("1/R(I) * (R(2)-I*R(2))", "-2I"); // TODO: get rid of complex at denominator? - -} diff --git a/poincare/test/store.cpp b/poincare/test/store.cpp index bcd170f76..deb1dc30d 100644 --- a/poincare/test/store.cpp +++ b/poincare/test/store.cpp @@ -15,17 +15,17 @@ QUIZ_CASE(poincare_store_evaluate) { } QUIZ_CASE(poincare_store_simplify) { - assert_parsed_expression_simplify_to("1+2>A", "1+2"); - assert_parsed_expression_simplify_to("1+2>x", "1+2"); + assert_parsed_expression_simplify_to("1+2>x", "3"); + assert_parsed_expression_simplify_to("0.1+0.2>x", "3/10"); + assert_parsed_expression_simplify_to("a+a>x", "2*a"); // Clean the storage for other tests - Ion::Storage::sharedStorage()->recordNamed("A.exp").destroy(); Ion::Storage::sharedStorage()->recordNamed("x.exp").destroy(); } QUIZ_CASE(poincare_store_matrix) { assert_parsed_expression_evaluates_to("[[7]]>a", "[[7]]"); - assert_parsed_expression_simplify_to("1+1>a", "1+1"); + assert_parsed_expression_simplify_to("1+1>a", "2"); // Clean the storage for other tests Ion::Storage::sharedStorage()->recordNamed("a.exp").destroy(); @@ -54,7 +54,7 @@ QUIZ_CASE(poincare_store_overwrite) { QUIZ_CASE(poincare_store_do_not_overwrite) { assert_parsed_expression_simplify_to("-1>g(x)", "-1"); - assert_parsed_expression_simplify_to("1+g(x)>f(x)", "1+g(x)"); + assert_parsed_expression_simplify_to("1+g(x)>f(x)", "0"); assert_parsed_expression_simplify_to("2>g", Undefined::Name()); assert_parsed_expression_evaluates_to("g(4)", "-1"); assert_parsed_expression_evaluates_to("f(4)", "0"); diff --git a/poincare/test/tree/blob_node.h b/poincare/test/tree/blob_node.h index 598fbbdb7..6bbd86395 100644 --- a/poincare/test/tree/blob_node.h +++ b/poincare/test/tree/blob_node.h @@ -8,9 +8,9 @@ namespace Poincare { class BlobNode : public TreeNode { public: + BlobNode(int data) : m_data(data) {} virtual size_t size() const override { return sizeof(BlobNode); } int data() { return m_data; } - void setData(int data) { m_data = data; } virtual int numberOfChildren() const override { return 0; } #if POINCARE_TREE_LOG virtual void logNodeName(std::ostream & stream) const override { @@ -23,9 +23,13 @@ private: class BlobByReference : public TreeHandle { public: - BlobByReference(int data = 0) : TreeHandle(TreePool::sharedPool()->createTreeNode()) { - node()->setData(data); + static BlobByReference Builder(int data = 0) { + void * bufferNode = TreePool::sharedPool()->alloc(sizeof(BlobNode)); + BlobNode * node = new (bufferNode) BlobNode(data); + TreeHandle h = TreeHandle::BuildWithGhostChildren(node); + return static_cast(h); } + BlobByReference() = delete; int data() { return node()->data(); } private: BlobNode * node() const { return static_cast(TreeHandle::node()); } diff --git a/poincare/test/tree/pair_node.h b/poincare/test/tree/pair_node.h index 2618aa0b0..a6eb9dd06 100644 --- a/poincare/test/tree/pair_node.h +++ b/poincare/test/tree/pair_node.h @@ -4,6 +4,7 @@ #include #include + namespace Poincare { class PairNode : public TreeNode { @@ -19,10 +20,16 @@ public: class PairByReference : public TreeHandle { public: - PairByReference(TreeHandle t1, TreeHandle t2) : TreeHandle(TreePool::sharedPool()->createTreeNode()) { - replaceChildAtIndexInPlace(0, t1); - replaceChildAtIndexInPlace(1, t2); + static PairByReference Builder(TreeHandle t1, TreeHandle t2) { + void * bufferNode = TreePool::sharedPool()->alloc(sizeof(PairNode)); + PairNode * node = new (bufferNode) PairNode(); + TreeHandle children[2] = {t1, t2}; + TreeHandle h = TreeHandle::BuildWithGhostChildren(node); + h.replaceChildAtIndexInPlace(0, t1); + h.replaceChildAtIndexInPlace(1, t2); + return static_cast(h); } + PairByReference() = delete; }; } diff --git a/poincare/test/tree/tree_handle.cpp b/poincare/test/tree/tree_handle.cpp index 44c380ca0..ff66ba727 100644 --- a/poincare/test/tree/tree_handle.cpp +++ b/poincare/test/tree/tree_handle.cpp @@ -13,14 +13,14 @@ using namespace Poincare; QUIZ_CASE(tree_handle_are_discared_after_block) { int initialPoolSize = pool_size(); { - BlobByReference b(0); + BlobByReference b = BlobByReference::Builder(0); assert_pool_size(initialPoolSize+1); } assert_pool_size(initialPoolSize); } static void make_temp_blob() { - BlobByReference b(5); + BlobByReference b = BlobByReference::Builder(5); } QUIZ_CASE(tree_handle_are_discared_after_function_call) { int initialPoolSize = pool_size(); @@ -31,7 +31,7 @@ QUIZ_CASE(tree_handle_are_discared_after_function_call) { QUIZ_CASE(tree_handle_can_be_copied) { int initialPoolSize = pool_size(); { - BlobByReference b(123); + BlobByReference b = BlobByReference::Builder(123); assert_pool_size(initialPoolSize+1); TreeHandle t = b; assert_pool_size(initialPoolSize+1); @@ -42,19 +42,19 @@ QUIZ_CASE(tree_handle_can_be_copied) { QUIZ_CASE(tree_handle_can_be_moved) { int initialPoolSize = pool_size(); { - TreeHandle t = BlobByReference(123); + TreeHandle t = BlobByReference::Builder(123); assert_pool_size(initialPoolSize+1); } { - TreeHandle t = BlobByReference(123); - t = BlobByReference(456); + TreeHandle t = BlobByReference::Builder(123); + t = BlobByReference::Builder(456); assert_pool_size(initialPoolSize+1); } assert_pool_size(initialPoolSize); } static TreeHandle blob_with_data_3() { - return BlobByReference(3); + return BlobByReference::Builder(3); } QUIZ_CASE(tree_handle_can_be_returned) { @@ -68,9 +68,9 @@ QUIZ_CASE(tree_handle_memory_failure) { int memoryFailureHasBeenHandled = false; Poincare::ExceptionCheckpoint ecp; if (ExceptionRun(ecp)) { - TreeHandle tree = BlobByReference(1); + TreeHandle tree = BlobByReference::Builder(1); while (true) { - tree = PairByReference(tree, BlobByReference(1)); + tree = PairByReference::Builder(tree, BlobByReference::Builder(1)); } } else { Poincare::Tidy(); @@ -82,10 +82,10 @@ QUIZ_CASE(tree_handle_memory_failure) { QUIZ_CASE(tree_handle_does_not_copy) { int initialPoolSize = pool_size(); - BlobByReference b1(1); - BlobByReference b2(2); + BlobByReference b1 = BlobByReference::Builder(1); + BlobByReference b2 = BlobByReference::Builder(2); assert_pool_size(initialPoolSize+2); - PairByReference p(b1, b2); + PairByReference p = PairByReference::Builder(b1, b2); assert_pool_size(initialPoolSize+3); PairByReference p2 = p; assert_pool_size(initialPoolSize+3); diff --git a/poincare/test/trigo.cpp b/poincare/test/trigo.cpp index e37543900..bef58efdc 100644 --- a/poincare/test/trigo.cpp +++ b/poincare/test/trigo.cpp @@ -26,65 +26,65 @@ QUIZ_CASE(poincare_trigo_evaluate) { * Ri -> R (even) */ // On R - assert_parsed_expression_evaluates_to("cos(2)", "-4.1614683654714E-1", Radian); - assert_parsed_expression_evaluates_to("cos(2)", "0.9993908270191", Degree); + assert_parsed_expression_evaluates_to("cos(2)", "-4.1614683654714E-1", System, Radian); + assert_parsed_expression_evaluates_to("cos(2)", "0.9993908270191", System, Degree); // Oscillator - assert_parsed_expression_evaluates_to("cos(P/2)", "0", Radian); - assert_parsed_expression_evaluates_to("cos(3*P/2)", "0", Radian); - assert_parsed_expression_evaluates_to("cos(3*P)", "-1", Radian); - assert_parsed_expression_evaluates_to("cos(-540)", "-1", Degree); + assert_parsed_expression_evaluates_to("cos(P/2)", "0", System, Radian); + assert_parsed_expression_evaluates_to("cos(3*P/2)", "0", System, Radian); + assert_parsed_expression_evaluates_to("cos(3*P)", "-1", System, Radian); + assert_parsed_expression_evaluates_to("cos(-540)", "-1", System, Degree); // On R*i - assert_parsed_expression_evaluates_to("cos(-2*I)", "3.7621956910836", Radian); - assert_parsed_expression_evaluates_to("cos(-2*I)", "1.0006092967033", Degree); + assert_parsed_expression_evaluates_to("cos(-2*I)", "3.7621956910836", System, Radian); + assert_parsed_expression_evaluates_to("cos(-2*I)", "1.0006092967033", System, Degree); // Symmetry: even - assert_parsed_expression_evaluates_to("cos(2*I)", "3.7621956910836", Radian); - assert_parsed_expression_evaluates_to("cos(2*I)", "1.0006092967033", Degree); + assert_parsed_expression_evaluates_to("cos(2*I)", "3.7621956910836", System, Radian); + assert_parsed_expression_evaluates_to("cos(2*I)", "1.0006092967033", System, Degree); // On C - assert_parsed_expression_evaluates_to("cos(I-4)", "-1.008625-0.8893952*I", Radian); - assert_parsed_expression_evaluates_to("cos(I-4)", "0.997716+0.00121754*I", Degree, Cartesian, 6); + assert_parsed_expression_evaluates_to("cos(I-4)", "-1.008625-0.8893952*I", System, Radian); + assert_parsed_expression_evaluates_to("cos(I-4)", "0.997716+0.00121754*I", System, Degree, Cartesian, 6); /* sin: R -> R (oscillator) * Ri -> Ri (odd) */ // On R - assert_parsed_expression_evaluates_to("sin(2)", "9.0929742682568E-1", Radian); - assert_parsed_expression_evaluates_to("sin(2)", "3.4899496702501E-2", Degree); + assert_parsed_expression_evaluates_to("sin(2)", "9.0929742682568E-1", System, Radian); + assert_parsed_expression_evaluates_to("sin(2)", "3.4899496702501E-2", System, Degree); // Oscillator - assert_parsed_expression_evaluates_to("sin(P/2)", "1", Radian); - assert_parsed_expression_evaluates_to("sin(3*P/2)", "-1", Radian); - assert_parsed_expression_evaluates_to("sin(3*P)", "0", Radian); - assert_parsed_expression_evaluates_to("sin(-540)", "0", Degree); + assert_parsed_expression_evaluates_to("sin(P/2)", "1", System, Radian); + assert_parsed_expression_evaluates_to("sin(3*P/2)", "-1", System, Radian); + assert_parsed_expression_evaluates_to("sin(3*P)", "0", System, Radian); + assert_parsed_expression_evaluates_to("sin(-540)", "0", System, Degree); // On R*i - assert_parsed_expression_evaluates_to("sin(3*I)", "10.01787492741*I", Radian); - assert_parsed_expression_evaluates_to("sin(3*I)", "0.05238381*I", Degree); + assert_parsed_expression_evaluates_to("sin(3*I)", "10.01787492741*I", System, Radian); + assert_parsed_expression_evaluates_to("sin(3*I)", "0.05238381*I", System, Degree); // Symmetry: odd - assert_parsed_expression_evaluates_to("sin(-3*I)", "-10.01787492741*I", Radian); - assert_parsed_expression_evaluates_to("sin(-3*I)", "-0.05238381*I", Degree); + assert_parsed_expression_evaluates_to("sin(-3*I)", "-10.01787492741*I", System, Radian); + assert_parsed_expression_evaluates_to("sin(-3*I)", "-0.05238381*I", System, Degree); // On: C - assert_parsed_expression_evaluates_to("sin(I-4)", "1.16781-0.768163*I", Radian, Cartesian, 6); - assert_parsed_expression_evaluates_to("sin(I-4)", "-0.0697671+0.0174117*I", Degree, Cartesian, 6); - assert_parsed_expression_evaluates_to("sin(1.234567890123456E-15)", "1.23457E-15", Radian, Cartesian, 6); + assert_parsed_expression_evaluates_to("sin(I-4)", "1.16781-0.768163*I", System, Radian, Cartesian, 6); + assert_parsed_expression_evaluates_to("sin(I-4)", "-0.0697671+0.0174117*I", System, Degree, Cartesian, 6); + assert_parsed_expression_evaluates_to("sin(1.234567890123456E-15)", "1.23457E-15", System, Radian, Cartesian, 6); /* tan: R -> R (tangent-style) * Ri -> Ri (odd) */ // On R - assert_parsed_expression_evaluates_to("tan(2)", "-2.1850398632615", Radian); - assert_parsed_expression_evaluates_to("tan(2)", "3.4920769491748E-2", Degree); + assert_parsed_expression_evaluates_to("tan(2)", "-2.1850398632615", System, Radian); + assert_parsed_expression_evaluates_to("tan(2)", "3.4920769491748E-2", System, Degree); // Tangent-style - assert_parsed_expression_evaluates_to("tan(P/2)", Undefined::Name(), Radian); - assert_parsed_expression_evaluates_to("tan(3*P/2)", Undefined::Name(), Radian); - assert_parsed_expression_evaluates_to("tan(3*P)", "0", Radian); - assert_parsed_expression_evaluates_to("tan(-540)", "0", Degree); + assert_parsed_expression_evaluates_to("tan(P/2)", Undefined::Name(), System, Radian); + assert_parsed_expression_evaluates_to("tan(3*P/2)", Undefined::Name(), System, Radian); + assert_parsed_expression_evaluates_to("tan(3*P)", "0", System, Radian); + assert_parsed_expression_evaluates_to("tan(-540)", "0", System, Degree); // On R*i - assert_parsed_expression_evaluates_to("tan(-2*I)", "-9.6402758007582E-1*I", Radian); - assert_parsed_expression_evaluates_to("tan(-2*I)", "-0.03489241*I", Degree); + assert_parsed_expression_evaluates_to("tan(-2*I)", "-9.6402758007582E-1*I", System, Radian); + assert_parsed_expression_evaluates_to("tan(-2*I)", "-0.03489241*I", System, Degree); // Symmetry: odd - assert_parsed_expression_evaluates_to("tan(2*I)", "9.6402758007582E-1*I", Radian); - assert_parsed_expression_evaluates_to("tan(2*I)", "0.03489241*I", Degree); + assert_parsed_expression_evaluates_to("tan(2*I)", "9.6402758007582E-1*I", System, Radian); + assert_parsed_expression_evaluates_to("tan(2*I)", "0.03489241*I", System, Degree); // On C - assert_parsed_expression_evaluates_to("tan(I-4)", "-0.273553+1.00281*I", Radian, Cartesian, 6); - assert_parsed_expression_evaluates_to("tan(I-4)", "-0.0699054+0.0175368*I", Degree, Cartesian, 6); + assert_parsed_expression_evaluates_to("tan(I-4)", "-0.273553+1.00281*I", System, Radian, Cartesian, 6); + assert_parsed_expression_evaluates_to("tan(I-4)", "-0.0699054+0.0175368*I", System, Degree, Cartesian, 6); /* acos: [-1,1] -> R * ]-inf,-1[ -> Pi+R*i (odd imaginary) @@ -92,31 +92,31 @@ QUIZ_CASE(poincare_trigo_evaluate) { * R*i -> Pi/2+R*i (odd imaginary) */ // On [-1, 1] - assert_parsed_expression_evaluates_to("acos(0.5)", "1.0471975511966", Radian); - assert_parsed_expression_evaluates_to("acos(0.03)", "1.5407918249714", Radian); - assert_parsed_expression_evaluates_to("acos(0.5)", "60", Degree); + assert_parsed_expression_evaluates_to("acos(0.5)", "1.0471975511966", System, Radian); + assert_parsed_expression_evaluates_to("acos(0.03)", "1.5407918249714", System, Radian); + assert_parsed_expression_evaluates_to("acos(0.5)", "60", System, Degree); // On [1, inf[ - assert_parsed_expression_evaluates_to("acos(2)", "1.3169578969248*I", Radian); - assert_parsed_expression_evaluates_to("acos(2)", "75.456129290217*I", Degree); + assert_parsed_expression_evaluates_to("acos(2)", "1.3169578969248*I", System, Radian); + assert_parsed_expression_evaluates_to("acos(2)", "75.456129290217*I", System, Degree); // Symmetry: odd on imaginary - assert_parsed_expression_evaluates_to("acos(-2)", "3.1415926535898-1.3169578969248*I", Radian); - assert_parsed_expression_evaluates_to("acos(-2)", "180-75.456129290217*I", Degree); + assert_parsed_expression_evaluates_to("acos(-2)", "3.1415926535898-1.3169578969248*I", System, Radian); + assert_parsed_expression_evaluates_to("acos(-2)", "180-75.456129290217*I", System, Degree); // On ]-inf, -1[ - assert_parsed_expression_evaluates_to("acos(-32)", "3.1415926535898-4.1586388532792*I", Radian); - assert_parsed_expression_evaluates_to("acos(-32)", "180-238.2725*I", Degree); + assert_parsed_expression_evaluates_to("acos(-32)", "3.1415926535898-4.1586388532792*I", System, Radian); + assert_parsed_expression_evaluates_to("acos(-32)", "180-238.2725*I", System, Degree); // On R*i - assert_parsed_expression_evaluates_to("acos(3*I)", "1.5708-1.8184*I", Radian, Cartesian, 5); - assert_parsed_expression_evaluates_to("acos(3*I)", "90-104.19*I", Degree, Cartesian, 5); + assert_parsed_expression_evaluates_to("acos(3*I)", "1.5708-1.8184*I", System, Radian, Cartesian, 5); + assert_parsed_expression_evaluates_to("acos(3*I)", "90-104.19*I", System, Degree, Cartesian, 5); // Symmetry: odd on imaginary - assert_parsed_expression_evaluates_to("acos(-3*I)", "1.5708+1.8184*I", Radian, Cartesian, 5); - assert_parsed_expression_evaluates_to("acos(-3*I)", "90+104.19*I", Degree, Cartesian, 5); + assert_parsed_expression_evaluates_to("acos(-3*I)", "1.5708+1.8184*I", System, Radian, Cartesian, 5); + assert_parsed_expression_evaluates_to("acos(-3*I)", "90+104.19*I", System, Degree, Cartesian, 5); // On C - assert_parsed_expression_evaluates_to("acos(I-4)", "2.8894-2.0966*I", Radian, Cartesian, 5); - assert_parsed_expression_evaluates_to("acos(I-4)", "165.551-120.126*I", Degree, Cartesian, 6); + assert_parsed_expression_evaluates_to("acos(I-4)", "2.8894-2.0966*I", System, Radian, Cartesian, 5); + assert_parsed_expression_evaluates_to("acos(I-4)", "165.551-120.126*I", System, Degree, Cartesian, 6); // Key values - assert_parsed_expression_evaluates_to("acos(0)", "90", Degree); - assert_parsed_expression_evaluates_to("acos(-1)", "180", Degree); - assert_parsed_expression_evaluates_to("acos(1)", "0", Degree); + assert_parsed_expression_evaluates_to("acos(0)", "90", System, Degree); + assert_parsed_expression_evaluates_to("acos(-1)", "180", System, Degree); + assert_parsed_expression_evaluates_to("acos(1)", "0", System, Degree); /* asin: [-1,1] -> R * ]-inf,-1[ -> -Pi/2+R*i (odd) @@ -124,29 +124,29 @@ QUIZ_CASE(poincare_trigo_evaluate) { * R*i -> R*i (odd) */ // On [-1, 1] - assert_parsed_expression_evaluates_to("asin(0.5)", "0.5235987755983", Radian); - assert_parsed_expression_evaluates_to("asin(0.03)", "3.0004501823477E-2", Radian); - assert_parsed_expression_evaluates_to("asin(0.5)", "30", Degree); + assert_parsed_expression_evaluates_to("asin(0.5)", "0.5235987755983", System, Radian); + assert_parsed_expression_evaluates_to("asin(0.03)", "3.0004501823477E-2", System, Radian); + assert_parsed_expression_evaluates_to("asin(0.5)", "30", System, Degree); // On [1, inf[ - assert_parsed_expression_evaluates_to("asin(2)", "1.5707963267949-1.3169578969248*I", Radian); - assert_parsed_expression_evaluates_to("asin(2)", "90-75.456129290217*I", Degree); + assert_parsed_expression_evaluates_to("asin(2)", "1.5707963267949-1.3169578969248*I", System, Radian); + assert_parsed_expression_evaluates_to("asin(2)", "90-75.456129290217*I", System, Degree); // Symmetry: odd - assert_parsed_expression_evaluates_to("asin(-2)", "-1.5707963267949+1.3169578969248*I", Radian); - assert_parsed_expression_evaluates_to("asin(-2)", "-90+75.456129290217*I", Degree); + assert_parsed_expression_evaluates_to("asin(-2)", "-1.5707963267949+1.3169578969248*I", System, Radian); + assert_parsed_expression_evaluates_to("asin(-2)", "-90+75.456129290217*I", System, Degree); // On ]-inf, -1[ - assert_parsed_expression_evaluates_to("asin(-32)", "-1.571+4.159*I", Radian, Cartesian, 4); - assert_parsed_expression_evaluates_to("asin(-32)", "-90+238*I", Degree, Cartesian, 3); + assert_parsed_expression_evaluates_to("asin(-32)", "-1.571+4.159*I", System, Radian, Cartesian, 4); + assert_parsed_expression_evaluates_to("asin(-32)", "-90+238*I", System, Degree, Cartesian, 3); // On R*i - assert_parsed_expression_evaluates_to("asin(3*I)", "1.8184464592321*I", Radian); + assert_parsed_expression_evaluates_to("asin(3*I)", "1.8184464592321*I", System, Radian); // Symmetry: odd - assert_parsed_expression_evaluates_to("asin(-3*I)", "-1.8184464592321*I", Radian); + assert_parsed_expression_evaluates_to("asin(-3*I)", "-1.8184464592321*I", System, Radian); // On C - assert_parsed_expression_evaluates_to("asin(I-4)", "-1.3186+2.0966*I", Radian, Cartesian, 5); - assert_parsed_expression_evaluates_to("asin(I-4)", "-75.551+120.13*I", Degree, Cartesian, 5); + assert_parsed_expression_evaluates_to("asin(I-4)", "-1.3186+2.0966*I", System, Radian, Cartesian, 5); + assert_parsed_expression_evaluates_to("asin(I-4)", "-75.551+120.13*I", System, Degree, Cartesian, 5); // Key values - assert_parsed_expression_evaluates_to("asin(0)", "0", Degree); - assert_parsed_expression_evaluates_to("asin(-1)", "-90", Degree); - assert_parsed_expression_evaluates_to("asin(1)", "90", Degree); + assert_parsed_expression_evaluates_to("asin(0)", "0", System, Degree); + assert_parsed_expression_evaluates_to("asin(-1)", "-90", System, Degree); + assert_parsed_expression_evaluates_to("asin(1)", "90", System, Degree); /* atan: R -> R (odd) * [-i,i] -> R*i (odd) @@ -154,91 +154,91 @@ QUIZ_CASE(poincare_trigo_evaluate) { * ]i, inf*i[ -> Pi/2+R*i (odd) */ // On R - assert_parsed_expression_evaluates_to("atan(2)", "1.1071487177941", Radian); - assert_parsed_expression_evaluates_to("atan(0.01)", "9.9996666866652E-3", Radian); - assert_parsed_expression_evaluates_to("atan(2)", "63.434948822922", Degree); - assert_parsed_expression_evaluates_to("atan(0.5)", "0.4636476", Radian); + assert_parsed_expression_evaluates_to("atan(2)", "1.1071487177941", System, Radian); + assert_parsed_expression_evaluates_to("atan(0.01)", "9.9996666866652E-3", System, Radian); + assert_parsed_expression_evaluates_to("atan(2)", "63.434948822922", System, Degree); + assert_parsed_expression_evaluates_to("atan(0.5)", "0.4636476", System, Radian); // Symmetry: odd - assert_parsed_expression_evaluates_to("atan(-2)", "-1.1071487177941", Radian); + assert_parsed_expression_evaluates_to("atan(-2)", "-1.1071487177941", System, Radian); // On [-i, i] - assert_parsed_expression_evaluates_to("atan(0.2*I)", "0.202733*I", Radian, Cartesian, 6); + assert_parsed_expression_evaluates_to("atan(0.2*I)", "0.202733*I", System, Radian, Cartesian, 6); // Symmetry: odd - assert_parsed_expression_evaluates_to("atan(-0.2*I)", "-0.202733*I", Radian, Cartesian, 6); + assert_parsed_expression_evaluates_to("atan(-0.2*I)", "-0.202733*I", System, Radian, Cartesian, 6); // On [i, inf*i[ - assert_parsed_expression_evaluates_to("atan(26*I)", "1.5707963267949+3.8480520568064E-2*I", Radian); - assert_parsed_expression_evaluates_to("atan(26*I)", "90+2.2047714220164*I", Degree); + assert_parsed_expression_evaluates_to("atan(26*I)", "1.5707963267949+3.8480520568064E-2*I", System, Radian); + assert_parsed_expression_evaluates_to("atan(26*I)", "90+2.2047714220164*I", System, Degree); // Symmetry: odd - assert_parsed_expression_evaluates_to("atan(-26*I)", "-1.5707963267949-3.8480520568064E-2*I", Radian); + assert_parsed_expression_evaluates_to("atan(-26*I)", "-1.5707963267949-3.8480520568064E-2*I", System, Radian); // On ]-inf*i, -i[ - assert_parsed_expression_evaluates_to("atan(-3.4*I)", "-1.570796-0.3030679*I", Radian); - assert_parsed_expression_evaluates_to("atan(-3.4*I)", "-90-17.3645*I", Degree, Cartesian, 6); + assert_parsed_expression_evaluates_to("atan(-3.4*I)", "-1.570796-0.3030679*I", System, Radian); + assert_parsed_expression_evaluates_to("atan(-3.4*I)", "-90-17.3645*I", System, Degree, Cartesian, 6); // On C - assert_parsed_expression_evaluates_to("atan(I-4)", "-1.338973+0.05578589*I", Radian); - assert_parsed_expression_evaluates_to("atan(I-4)", "-76.7175+3.1963*I", Degree, Cartesian, 6); + assert_parsed_expression_evaluates_to("atan(I-4)", "-1.338973+0.05578589*I", System, Radian); + assert_parsed_expression_evaluates_to("atan(I-4)", "-76.7175+3.1963*I", System, Degree, Cartesian, 6); // Key values - assert_parsed_expression_evaluates_to("atan(0)", "0", Degree); - assert_parsed_expression_evaluates_to("atan(-I)", "-inf*I", Radian); - assert_parsed_expression_evaluates_to("atan(I)", "inf*I", Radian); + assert_parsed_expression_evaluates_to("atan(0)", "0", System, Degree); + assert_parsed_expression_evaluates_to("atan(-I)", "-inf*I", System, Radian); + assert_parsed_expression_evaluates_to("atan(I)", "inf*I", System, Radian); /* cosh: R -> R (even) * R*i -> R (oscillator) */ // On R - assert_parsed_expression_evaluates_to("cosh(2)", "3.7621956910836", Radian); - assert_parsed_expression_evaluates_to("cosh(2)", "3.7621956910836", Degree); + assert_parsed_expression_evaluates_to("cosh(2)", "3.7621956910836", System, Radian); + assert_parsed_expression_evaluates_to("cosh(2)", "3.7621956910836", System, Degree); // Symmetry: even - assert_parsed_expression_evaluates_to("cosh(-2)", "3.7621956910836", Radian); - assert_parsed_expression_evaluates_to("cosh(-2)", "3.7621956910836", Degree); + assert_parsed_expression_evaluates_to("cosh(-2)", "3.7621956910836", System, Radian); + assert_parsed_expression_evaluates_to("cosh(-2)", "3.7621956910836", System, Degree); // On R*i - assert_parsed_expression_evaluates_to("cosh(43*I)", "5.5511330152063E-1", Radian); + assert_parsed_expression_evaluates_to("cosh(43*I)", "5.5511330152063E-1", System, Radian); // Oscillator - assert_parsed_expression_evaluates_to("cosh(P*I/2)", "0", Radian); - assert_parsed_expression_evaluates_to("cosh(5*P*I/2)", "0", Radian); - assert_parsed_expression_evaluates_to("cosh(8*P*I/2)", "1", Radian); - assert_parsed_expression_evaluates_to("cosh(9*P*I/2)", "0", Radian); + assert_parsed_expression_evaluates_to("cosh(P*I/2)", "0", System, Radian); + assert_parsed_expression_evaluates_to("cosh(5*P*I/2)", "0", System, Radian); + assert_parsed_expression_evaluates_to("cosh(8*P*I/2)", "1", System, Radian); + assert_parsed_expression_evaluates_to("cosh(9*P*I/2)", "0", System, Radian); // On C - assert_parsed_expression_evaluates_to("cosh(I-4)", "14.7547-22.9637*I", Radian, Cartesian, 6); - assert_parsed_expression_evaluates_to("cosh(I-4)", "14.7547-22.9637*I", Degree, Cartesian, 6); + assert_parsed_expression_evaluates_to("cosh(I-4)", "14.7547-22.9637*I", System, Radian, Cartesian, 6); + assert_parsed_expression_evaluates_to("cosh(I-4)", "14.7547-22.9637*I", System, Degree, Cartesian, 6); /* sinh: R -> R (odd) * R*i -> R*i (oscillator) */ // On R - assert_parsed_expression_evaluates_to("sinh(2)", "3.626860407847", Radian); - assert_parsed_expression_evaluates_to("sinh(2)", "3.626860407847", Degree); + assert_parsed_expression_evaluates_to("sinh(2)", "3.626860407847", System, Radian); + assert_parsed_expression_evaluates_to("sinh(2)", "3.626860407847", System, Degree); // Symmetry: odd - assert_parsed_expression_evaluates_to("sinh(-2)", "-3.626860407847", Radian); + assert_parsed_expression_evaluates_to("sinh(-2)", "-3.626860407847", System, Radian); // On R*i - assert_parsed_expression_evaluates_to("sinh(43*I)", "-0.8317747426286*I", Radian); + assert_parsed_expression_evaluates_to("sinh(43*I)", "-0.8317747426286*I", System, Radian); // Oscillator - assert_parsed_expression_evaluates_to("sinh(P*I/2)", "I", Radian); - assert_parsed_expression_evaluates_to("sinh(5*P*I/2)", "I", Radian); - assert_parsed_expression_evaluates_to("sinh(7*P*I/2)", "-I", Radian); - assert_parsed_expression_evaluates_to("sinh(8*P*I/2)", "0", Radian); - assert_parsed_expression_evaluates_to("sinh(9*P*I/2)", "I", Radian); + assert_parsed_expression_evaluates_to("sinh(P*I/2)", "I", System, Radian); + assert_parsed_expression_evaluates_to("sinh(5*P*I/2)", "I", System, Radian); + assert_parsed_expression_evaluates_to("sinh(7*P*I/2)", "-I", System, Radian); + assert_parsed_expression_evaluates_to("sinh(8*P*I/2)", "0", System, Radian); + assert_parsed_expression_evaluates_to("sinh(9*P*I/2)", "I", System, Radian); // On C - assert_parsed_expression_evaluates_to("sinh(I-4)", "-14.7448+22.9791*I", Radian, Cartesian, 6); - assert_parsed_expression_evaluates_to("sinh(I-4)", "-14.7448+22.9791*I", Degree, Cartesian, 6); + assert_parsed_expression_evaluates_to("sinh(I-4)", "-14.7448+22.9791*I", System, Radian, Cartesian, 6); + assert_parsed_expression_evaluates_to("sinh(I-4)", "-14.7448+22.9791*I", System, Degree, Cartesian, 6); /* tanh: R -> R (odd) * R*i -> R*i (tangent-style) */ // On R - assert_parsed_expression_evaluates_to("tanh(2)", "9.6402758007582E-1", Radian); + assert_parsed_expression_evaluates_to("tanh(2)", "9.6402758007582E-1", System, Radian); // Symmetry: odd - assert_parsed_expression_evaluates_to("tanh(-2)", "-9.6402758007582E-1", Degree); + assert_parsed_expression_evaluates_to("tanh(-2)", "-9.6402758007582E-1", System, Degree); // On R*i - assert_parsed_expression_evaluates_to("tanh(43*I)", "-1.4983873388552*I", Radian); + assert_parsed_expression_evaluates_to("tanh(43*I)", "-1.4983873388552*I", System, Radian); // Tangent-style // FIXME: this depends on the libm implementation and does not work on travis/appveyor servers - /*assert_parsed_expression_evaluates_to("tanh(P*I/2)", Undefined::Name(), Radian); - assert_parsed_expression_evaluates_to("tanh(5*P*I/2)", Undefined::Name(), Radian); - assert_parsed_expression_evaluates_to("tanh(7*P*I/2)", Undefined::Name(), Radian); - assert_parsed_expression_evaluates_to("tanh(8*P*I/2)", "0", Radian); - assert_parsed_expression_evaluates_to("tanh(9*P*I/2)", Undefined::Name(), Radian);*/ + /*assert_parsed_expression_evaluates_to("tanh(P*I/2)", Undefined::Name(), System, Radian); + assert_parsed_expression_evaluates_to("tanh(5*P*I/2)", Undefined::Name(), System, Radian); + assert_parsed_expression_evaluates_to("tanh(7*P*I/2)", Undefined::Name(), System, Radian); + assert_parsed_expression_evaluates_to("tanh(8*P*I/2)", "0", System, Radian); + assert_parsed_expression_evaluates_to("tanh(9*P*I/2)", Undefined::Name(), System, Radian);*/ // On C - assert_parsed_expression_evaluates_to("tanh(I-4)", "-1.00028+0.000610241*I", Radian, Cartesian, 6); - assert_parsed_expression_evaluates_to("tanh(I-4)", "-1.00028+0.000610241*I", Degree, Cartesian, 6); + assert_parsed_expression_evaluates_to("tanh(I-4)", "-1.00028+0.000610241*I", System, Radian, Cartesian, 6); + assert_parsed_expression_evaluates_to("tanh(I-4)", "-1.00028+0.000610241*I", System, Degree, Cartesian, 6); /* acosh: [-1,1] -> R*i * ]-inf,-1[ -> Pi*i+R (even on real) @@ -247,27 +247,27 @@ QUIZ_CASE(poincare_trigo_evaluate) { * ]0, inf*i[ -> Pi/2*i+R (even on real) */ // On [-1,1] - assert_parsed_expression_evaluates_to("acosh(2)", "1.3169578969248", Radian); - assert_parsed_expression_evaluates_to("acosh(2)", "1.3169578969248", Degree); + assert_parsed_expression_evaluates_to("acosh(2)", "1.3169578969248", System, Radian); + assert_parsed_expression_evaluates_to("acosh(2)", "1.3169578969248", System, Degree); // On ]-inf, -1[ - assert_parsed_expression_evaluates_to("acosh(-4)", "2.0634370688956+3.1415926535898*I", Radian); - assert_parsed_expression_evaluates_to("acosh(-4)", "2.06344+3.14159*I", Radian, Cartesian, 6); + assert_parsed_expression_evaluates_to("acosh(-4)", "2.0634370688956+3.1415926535898*I", System, Radian); + assert_parsed_expression_evaluates_to("acosh(-4)", "2.06344+3.14159*I", System, Radian, Cartesian, 6); // On ]1,inf[: Symmetry: even on real - assert_parsed_expression_evaluates_to("acosh(4)", "2.0634370688956", Radian); - assert_parsed_expression_evaluates_to("acosh(4)", "2.063437", Radian); + assert_parsed_expression_evaluates_to("acosh(4)", "2.0634370688956", System, Radian); + assert_parsed_expression_evaluates_to("acosh(4)", "2.063437", System, Radian); // On ]-inf*i, 0[ - assert_parsed_expression_evaluates_to("acosh(-42*I)", "4.4309584920805-1.5707963267949*I", Radian); - assert_parsed_expression_evaluates_to("acosh(-42*I)", "4.431-1.571*I", Radian, Cartesian, 4); + assert_parsed_expression_evaluates_to("acosh(-42*I)", "4.4309584920805-1.5707963267949*I", System, Radian); + assert_parsed_expression_evaluates_to("acosh(-42*I)", "4.431-1.571*I", System, Radian, Cartesian, 4); // On ]0, i*inf[: Symmetry: even on real - assert_parsed_expression_evaluates_to("acosh(42*I)", "4.4309584920805+1.5707963267949*I", Radian); - assert_parsed_expression_evaluates_to("acosh(42*I)", "4.431+1.571*I", Radian, Cartesian, 4); + assert_parsed_expression_evaluates_to("acosh(42*I)", "4.4309584920805+1.5707963267949*I", System, Radian); + assert_parsed_expression_evaluates_to("acosh(42*I)", "4.431+1.571*I", System, Radian, Cartesian, 4); // On C - assert_parsed_expression_evaluates_to("acosh(I-4)", "2.0966+2.8894*I", Radian, Cartesian, 5); - assert_parsed_expression_evaluates_to("acosh(I-4)", "2.0966+2.8894*I", Degree, Cartesian, 5); + assert_parsed_expression_evaluates_to("acosh(I-4)", "2.0966+2.8894*I", System, Radian, Cartesian, 5); + assert_parsed_expression_evaluates_to("acosh(I-4)", "2.0966+2.8894*I", System, Degree, Cartesian, 5); // Key values - //assert_parsed_expression_evaluates_to("acosh(-1)", "3.1415926535898*I", Radian); - assert_parsed_expression_evaluates_to("acosh(1)", "0", Radian); - assert_parsed_expression_evaluates_to("acosh(0)", "1.570796*I", Radian); + //assert_parsed_expression_evaluates_to("acosh(-1)", "3.1415926535898*I", System, Radian); + assert_parsed_expression_evaluates_to("acosh(1)", "0", System, Radian); + assert_parsed_expression_evaluates_to("acosh(0)", "1.570796*I", System, Radian); /* asinh: R -> R (odd) * [-i,i] -> R*i (odd) @@ -275,26 +275,26 @@ QUIZ_CASE(poincare_trigo_evaluate) { * ]i, inf*I[ -> Pi/2*I+R (odd) */ // On R - assert_parsed_expression_evaluates_to("asinh(2)", "1.4436354751788", Radian); - assert_parsed_expression_evaluates_to("asinh(2)", "1.4436354751788", Degree); + assert_parsed_expression_evaluates_to("asinh(2)", "1.4436354751788", System, Radian); + assert_parsed_expression_evaluates_to("asinh(2)", "1.4436354751788", System, Degree); // Symmetry: odd - assert_parsed_expression_evaluates_to("asinh(-2)", "-1.4436354751788", Radian); - assert_parsed_expression_evaluates_to("asinh(-2)", "-1.4436354751788", Degree); + assert_parsed_expression_evaluates_to("asinh(-2)", "-1.4436354751788", System, Radian); + assert_parsed_expression_evaluates_to("asinh(-2)", "-1.4436354751788", System, Degree); // On [-i,i] - assert_parsed_expression_evaluates_to("asinh(0.2*I)", "2.0135792079033E-1*I", Radian); - assert_parsed_expression_evaluates_to("asinh(0.2*I)", "0.2013579*I", Degree); + assert_parsed_expression_evaluates_to("asinh(0.2*I)", "2.0135792079033E-1*I", System, Radian); + assert_parsed_expression_evaluates_to("asinh(0.2*I)", "0.2013579*I", System, Degree); // Symmetry: odd - assert_parsed_expression_evaluates_to("asinh(-0.2*I)", "-2.0135792079033E-1*I", Radian); - assert_parsed_expression_evaluates_to("asinh(-0.2*I)", "-0.2013579*I", Degree); + assert_parsed_expression_evaluates_to("asinh(-0.2*I)", "-2.0135792079033E-1*I", System, Radian); + assert_parsed_expression_evaluates_to("asinh(-0.2*I)", "-0.2013579*I", System, Degree); // On ]-inf*i, -i[ - assert_parsed_expression_evaluates_to("asinh(-22*I)", "-3.7836727043295-1.5707963267949*I", Radian); - assert_parsed_expression_evaluates_to("asinh(-22*I)", "-3.784-1.571*I", Degree, Cartesian, 4); + assert_parsed_expression_evaluates_to("asinh(-22*I)", "-3.7836727043295-1.5707963267949*I", System, Radian); + assert_parsed_expression_evaluates_to("asinh(-22*I)", "-3.784-1.571*I", System, Degree, Cartesian, 4); // On ]i, inf*i[, Symmetry: odd - assert_parsed_expression_evaluates_to("asinh(22*I)", "3.7836727043295+1.5707963267949*I", Radian); - assert_parsed_expression_evaluates_to("asinh(22*I)", "3.784+1.571*I", Degree, Cartesian, 4); + assert_parsed_expression_evaluates_to("asinh(22*I)", "3.7836727043295+1.5707963267949*I", System, Radian); + assert_parsed_expression_evaluates_to("asinh(22*I)", "3.784+1.571*I", System, Degree, Cartesian, 4); // On C - assert_parsed_expression_evaluates_to("asinh(I-4)", "-2.123+0.2383*I", Radian, Cartesian, 4); - assert_parsed_expression_evaluates_to("asinh(I-4)", "-2.123+0.2383*I", Degree, Cartesian, 4); + assert_parsed_expression_evaluates_to("asinh(I-4)", "-2.123+0.2383*I", System, Radian, Cartesian, 4); + assert_parsed_expression_evaluates_to("asinh(I-4)", "-2.123+0.2383*I", System, Degree, Cartesian, 4); /* atanh: [-1,1] -> R (odd) * ]-inf,-1[ -> Pi/2*i+R (odd) @@ -302,54 +302,54 @@ QUIZ_CASE(poincare_trigo_evaluate) { * R*i -> R*i (odd) */ // On [-1,1] - assert_parsed_expression_evaluates_to("atanh(0.4)", "0.4236489301936", Radian); - assert_parsed_expression_evaluates_to("atanh(0.4)", "0.4236489301936", Degree); + assert_parsed_expression_evaluates_to("atanh(0.4)", "0.4236489301936", System, Radian); + assert_parsed_expression_evaluates_to("atanh(0.4)", "0.4236489301936", System, Degree); // Symmetry: odd - assert_parsed_expression_evaluates_to("atanh(-0.4)", "-0.4236489301936", Radian); - assert_parsed_expression_evaluates_to("atanh(-0.4)", "-0.4236489301936", Degree); + assert_parsed_expression_evaluates_to("atanh(-0.4)", "-0.4236489301936", System, Radian); + assert_parsed_expression_evaluates_to("atanh(-0.4)", "-0.4236489301936", System, Degree); // On ]1, inf[ - assert_parsed_expression_evaluates_to("atanh(4)", "0.255412811883-1.5707963267949*I", Radian); - assert_parsed_expression_evaluates_to("atanh(4)", "0.2554128-1.570796*I", Degree); + assert_parsed_expression_evaluates_to("atanh(4)", "0.255412811883-1.5707963267949*I", System, Radian); + assert_parsed_expression_evaluates_to("atanh(4)", "0.2554128-1.570796*I", System, Degree); // On ]-inf,-1[, Symmetry: odd - assert_parsed_expression_evaluates_to("atanh(-4)", "-0.255412811883+1.5707963267949*I", Radian); - assert_parsed_expression_evaluates_to("atanh(-4)", "-0.2554128+1.570796*I", Degree); + assert_parsed_expression_evaluates_to("atanh(-4)", "-0.255412811883+1.5707963267949*I", System, Radian); + assert_parsed_expression_evaluates_to("atanh(-4)", "-0.2554128+1.570796*I", System, Degree); // On R*i - assert_parsed_expression_evaluates_to("atanh(4*I)", "1.325817663668*I", Radian); - assert_parsed_expression_evaluates_to("atanh(4*I)", "1.325818*I", Radian); + assert_parsed_expression_evaluates_to("atanh(4*I)", "1.325817663668*I", System, Radian); + assert_parsed_expression_evaluates_to("atanh(4*I)", "1.325818*I", System, Radian); // Symmetry: odd - assert_parsed_expression_evaluates_to("atanh(-4*I)", "-1.325817663668*I", Radian); - assert_parsed_expression_evaluates_to("atanh(-4*I)", "-1.325818*I", Radian); + assert_parsed_expression_evaluates_to("atanh(-4*I)", "-1.325817663668*I", System, Radian); + assert_parsed_expression_evaluates_to("atanh(-4*I)", "-1.325818*I", System, Radian); // On C - assert_parsed_expression_evaluates_to("atanh(I-4)", "-0.238878+1.50862*I", Radian, Cartesian, 6); - assert_parsed_expression_evaluates_to("atanh(I-4)", "-0.238878+1.50862*I", Degree, Cartesian, 6); + assert_parsed_expression_evaluates_to("atanh(I-4)", "-0.238878+1.50862*I", System, Radian, Cartesian, 6); + assert_parsed_expression_evaluates_to("atanh(I-4)", "-0.238878+1.50862*I", System, Degree, Cartesian, 6); // WARNING: evaluate on branch cut can be multivalued - assert_parsed_expression_evaluates_to("acos(2)", "1.3169578969248*I", Radian); - assert_parsed_expression_evaluates_to("acos(2)", "75.456129290217*I", Degree); - assert_parsed_expression_evaluates_to("asin(2)", "1.5707963267949-1.3169578969248*I", Radian); - assert_parsed_expression_evaluates_to("asin(2)", "90-75.456129290217*I", Degree); - assert_parsed_expression_evaluates_to("atanh(2)", "5.4930614433405E-1-1.5707963267949*I", Radian); - assert_parsed_expression_evaluates_to("atan(2I)", "1.5707963267949+5.4930614433405E-1*I", Radian); - assert_parsed_expression_evaluates_to("atan(2I)", "90+31.472923730945*I", Degree); - assert_parsed_expression_evaluates_to("asinh(2I)", "1.3169578969248+1.5707963267949*I", Radian); - assert_parsed_expression_evaluates_to("acosh(-2)", "1.3169578969248+3.1415926535898*I", Radian); + assert_parsed_expression_evaluates_to("acos(2)", "1.3169578969248*I", System, Radian); + assert_parsed_expression_evaluates_to("acos(2)", "75.456129290217*I", System, Degree); + assert_parsed_expression_evaluates_to("asin(2)", "1.5707963267949-1.3169578969248*I", System, Radian); + assert_parsed_expression_evaluates_to("asin(2)", "90-75.456129290217*I", System, Degree); + assert_parsed_expression_evaluates_to("atanh(2)", "5.4930614433405E-1-1.5707963267949*I", System, Radian); + assert_parsed_expression_evaluates_to("atan(2I)", "1.5707963267949+5.4930614433405E-1*I", System, Radian); + assert_parsed_expression_evaluates_to("atan(2I)", "90+31.472923730945*I", System, Degree); + assert_parsed_expression_evaluates_to("asinh(2I)", "1.3169578969248+1.5707963267949*I", System, Radian); + assert_parsed_expression_evaluates_to("acosh(-2)", "1.3169578969248+3.1415926535898*I", System, Radian); } QUIZ_CASE(poincare_trigo_simplify) { // -- sin/cos -> tan assert_parsed_expression_simplify_to("sin(x)/cos(x)", "tan(x)"); assert_parsed_expression_simplify_to("cos(x)/sin(x)", "1/tan(x)"); - assert_parsed_expression_simplify_to("sin(x)*P/cos(x)", "tan(x)*P"); + assert_parsed_expression_simplify_to("sin(x)*P/cos(x)", "P*tan(x)"); assert_parsed_expression_simplify_to("sin(x)/(P*cos(x))", "tan(x)/P"); assert_parsed_expression_simplify_to("1*tan(2)*tan(5)", "tan(2)*tan(5)"); assert_parsed_expression_simplify_to("tan(62P/21)", "-tan(P/21)"); assert_parsed_expression_simplify_to("cos(26P/21)/sin(25P/17)", "cos((5*P)/21)/sin((8*P)/17)"); assert_parsed_expression_simplify_to("cos(62P/21)*P*3/sin(62P/21)", "-(3*P)/tan(P/21)"); - assert_parsed_expression_simplify_to("cos(62P/21)/(P*3*sin(62P/21))", "-1/(3*tan(P/21)*P)"); - assert_parsed_expression_simplify_to("sin(62P/21)*P*3/cos(62P/21)", "-3*tan(P/21)*P"); + assert_parsed_expression_simplify_to("cos(62P/21)/(P*3*sin(62P/21))", "-1/(3*P*tan(P/21))"); + assert_parsed_expression_simplify_to("sin(62P/21)*P*3/cos(62P/21)", "-3*P*tan(P/21)"); assert_parsed_expression_simplify_to("sin(62P/21)/(P*3cos(62P/21))", "-tan(P/21)/(3*P)"); - assert_parsed_expression_simplify_to("-cos(P/62)ln(3)/(sin(P/62)P)", "-ln(3)/(tan(P/62)*P)"); - assert_parsed_expression_simplify_to("-2cos(P/62)ln(3)/(sin(P/62)P)", "-(2*ln(3))/(tan(P/62)*P)"); + assert_parsed_expression_simplify_to("-cos(P/62)ln(3)/(sin(P/62)P)", "-ln(3)/(P*tan(P/62))"); + assert_parsed_expression_simplify_to("-2cos(P/62)ln(3)/(sin(P/62)P)", "-(2*ln(3))/(P*tan(P/62))"); // -- cos assert_parsed_expression_simplify_to("cos(0)", "1"); assert_parsed_expression_simplify_to("cos(P)", "-1"); @@ -360,31 +360,31 @@ QUIZ_CASE(poincare_trigo_simplify) { assert_parsed_expression_simplify_to("cos(-P*340001)", "-1"); assert_parsed_expression_simplify_to("cos(-P*R(2))", "cos(R(2)*P)"); assert_parsed_expression_simplify_to("cos(1311P/6)", "0"); - assert_parsed_expression_simplify_to("cos(P/12)", "(R(2)+R(6))/4"); - assert_parsed_expression_simplify_to("cos(-P/12)", "(R(2)+R(6))/4"); - assert_parsed_expression_simplify_to("cos(-P17/8)", "R(2+R(2))/2"); + assert_parsed_expression_simplify_to("cos(P/12)", "(R(6)+R(2))/4"); + assert_parsed_expression_simplify_to("cos(-P/12)", "(R(6)+R(2))/4"); + assert_parsed_expression_simplify_to("cos(-P17/8)", "R(R(2)+2)/2"); assert_parsed_expression_simplify_to("cos(41P/6)", "-R(3)/2"); assert_parsed_expression_simplify_to("cos(P/4+1000P)", "R(2)/2"); assert_parsed_expression_simplify_to("cos(-P/3)", "1/2"); - assert_parsed_expression_simplify_to("cos(41P/5)", "(1+R(5))/4"); - assert_parsed_expression_simplify_to("cos(7P/10)", "-(R(2)*R(5-R(5)))/4"); - assert_parsed_expression_simplify_to("cos(0)", "1", Preferences::AngleUnit::Degree); - assert_parsed_expression_simplify_to("cos(180)", "-1", Preferences::AngleUnit::Degree); - assert_parsed_expression_simplify_to("cos(720/7)", "-cos(540/7)", Preferences::AngleUnit::Degree); - assert_parsed_expression_simplify_to("cos(6300/29)", "-cos(1080/29)", Preferences::AngleUnit::Degree); - assert_parsed_expression_simplify_to("cos(-6300/29)", "-cos(1080/29)", Preferences::AngleUnit::Degree); - assert_parsed_expression_simplify_to("cos(61200000)", "1", Preferences::AngleUnit::Degree); - assert_parsed_expression_simplify_to("cos(-61200180)", "-1", Preferences::AngleUnit::Degree); - assert_parsed_expression_simplify_to("cos(-180*R(2))", "cos(180*R(2))", Preferences::AngleUnit::Degree); - assert_parsed_expression_simplify_to("cos(39330)", "0", Preferences::AngleUnit::Degree); - assert_parsed_expression_simplify_to("cos(15)", "(R(2)+R(6))/4", Preferences::AngleUnit::Degree); - assert_parsed_expression_simplify_to("cos(-15)", "(R(2)+R(6))/4", Preferences::AngleUnit::Degree); - assert_parsed_expression_simplify_to("cos(-765/2)", "R(2+R(2))/2", Preferences::AngleUnit::Degree); - assert_parsed_expression_simplify_to("cos(7380/6)", "-R(3)/2", Preferences::AngleUnit::Degree); - assert_parsed_expression_simplify_to("cos(180045)", "R(2)/2", Preferences::AngleUnit::Degree); - assert_parsed_expression_simplify_to("cos(-60)", "1/2", Preferences::AngleUnit::Degree); - assert_parsed_expression_simplify_to("cos(7380/5)", "(1+R(5))/4", Preferences::AngleUnit::Degree); - assert_parsed_expression_simplify_to("cos(112.5)", "-R(2-R(2))/2", Preferences::AngleUnit::Degree); + assert_parsed_expression_simplify_to("cos(41P/5)", "(R(5)+1)/4"); + assert_parsed_expression_simplify_to("cos(7P/10)", "-(R(2)*R(-R(5)+5))/4"); + assert_parsed_expression_simplify_to("cos(0)", "1", User, Degree); + assert_parsed_expression_simplify_to("cos(180)", "-1", User, Degree); + assert_parsed_expression_simplify_to("cos(720/7)", "-cos(540/7)", User, Degree); + assert_parsed_expression_simplify_to("cos(6300/29)", "-cos(1080/29)", User, Degree); + assert_parsed_expression_simplify_to("cos(-6300/29)", "-cos(1080/29)", User, Degree); + assert_parsed_expression_simplify_to("cos(61200000)", "1", User, Degree); + assert_parsed_expression_simplify_to("cos(-61200180)", "-1", User, Degree); + assert_parsed_expression_simplify_to("cos(-180*R(2))", "cos(180*R(2))", User, Degree); + assert_parsed_expression_simplify_to("cos(39330)", "0", User, Degree); + assert_parsed_expression_simplify_to("cos(15)", "(R(6)+R(2))/4", User, Degree); + assert_parsed_expression_simplify_to("cos(-15)", "(R(6)+R(2))/4", User, Degree); + assert_parsed_expression_simplify_to("cos(-765/2)", "R(R(2)+2)/2", User, Degree); + assert_parsed_expression_simplify_to("cos(7380/6)", "-R(3)/2", User, Degree); + assert_parsed_expression_simplify_to("cos(180045)", "R(2)/2", User, Degree); + assert_parsed_expression_simplify_to("cos(-60)", "1/2", User, Degree); + assert_parsed_expression_simplify_to("cos(7380/5)", "(R(5)+1)/4", User, Degree); + assert_parsed_expression_simplify_to("cos(112.5)", "-R(-R(2)+2)/2", User, Degree); // -- sin assert_parsed_expression_simplify_to("sin(0)", "0"); assert_parsed_expression_simplify_to("sin(P)", "0"); @@ -393,34 +393,34 @@ QUIZ_CASE(poincare_trigo_simplify) { assert_parsed_expression_simplify_to("sin(P*340000)", "0"); assert_parsed_expression_simplify_to("sin(P*340001)", "0"); assert_parsed_expression_simplify_to("sin(-P*340001)", "0"); - assert_parsed_expression_simplify_to("sin(P/12)", "(-R(2)+R(6))/4"); - assert_parsed_expression_simplify_to("sin(-P/12)", "(R(2)-R(6))/4"); + assert_parsed_expression_simplify_to("sin(P/12)", "(R(6)-R(2))/4"); + assert_parsed_expression_simplify_to("sin(-P/12)", "(-R(6)+R(2))/4"); assert_parsed_expression_simplify_to("sin(-P*R(2))", "-sin(R(2)*P)"); assert_parsed_expression_simplify_to("sin(1311P/6)", "1"); - assert_parsed_expression_simplify_to("sin(-P17/8)", "-R(2-R(2))/2"); + assert_parsed_expression_simplify_to("sin(-P17/8)", "-R(-R(2)+2)/2"); assert_parsed_expression_simplify_to("sin(41P/6)", "1/2"); - assert_parsed_expression_simplify_to("sin(-3P/10)", "(-1-R(5))/4"); + assert_parsed_expression_simplify_to("sin(-3P/10)", "(-R(5)-1)/4"); assert_parsed_expression_simplify_to("sin(P/4+1000P)", "R(2)/2"); assert_parsed_expression_simplify_to("sin(-P/3)", "-R(3)/2"); - assert_parsed_expression_simplify_to("sin(17P/5)", "-(R(2)*R(5+R(5)))/4"); - assert_parsed_expression_simplify_to("sin(P/5)", "(R(2)*R(5-R(5)))/4"); - assert_parsed_expression_simplify_to("sin(0)", "0", Preferences::AngleUnit::Degree); - assert_parsed_expression_simplify_to("sin(180)", "0", Preferences::AngleUnit::Degree); - assert_parsed_expression_simplify_to("sin(6300/29)", "-sin(1080/29)", Preferences::AngleUnit::Degree); - assert_parsed_expression_simplify_to("sin(-6300/29)", "sin(1080/29)", Preferences::AngleUnit::Degree); - assert_parsed_expression_simplify_to("sin(61200000)", "0", Preferences::AngleUnit::Degree); - assert_parsed_expression_simplify_to("sin(61200180)", "0", Preferences::AngleUnit::Degree); - assert_parsed_expression_simplify_to("sin(-61200180)", "0", Preferences::AngleUnit::Degree); - assert_parsed_expression_simplify_to("sin(15)", "(-R(2)+R(6))/4", Preferences::AngleUnit::Degree); - assert_parsed_expression_simplify_to("sin(-15)", "(R(2)-R(6))/4", Preferences::AngleUnit::Degree); - assert_parsed_expression_simplify_to("sin(-180*R(2))", "-sin(180*R(2))", Preferences::AngleUnit::Degree); - assert_parsed_expression_simplify_to("sin(39330)", "1", Preferences::AngleUnit::Degree); - assert_parsed_expression_simplify_to("sin(-765/2)", "-R(2-R(2))/2", Preferences::AngleUnit::Degree); - assert_parsed_expression_simplify_to("sin(1230)", "1/2", Preferences::AngleUnit::Degree); - assert_parsed_expression_simplify_to("sin(180045)", "R(2)/2", Preferences::AngleUnit::Degree); - assert_parsed_expression_simplify_to("sin(-60)", "-R(3)/2", Preferences::AngleUnit::Degree); - assert_parsed_expression_simplify_to("sin(612)", "-(R(2)*R(5+R(5)))/4", Preferences::AngleUnit::Degree); - assert_parsed_expression_simplify_to("sin(36)", "(R(2)*R(5-R(5)))/4", Preferences::AngleUnit::Degree); + assert_parsed_expression_simplify_to("sin(17P/5)", "-(R(2)*R(R(5)+5))/4"); + assert_parsed_expression_simplify_to("sin(P/5)", "(R(2)*R(-R(5)+5))/4"); + assert_parsed_expression_simplify_to("sin(0)", "0", User, Degree); + assert_parsed_expression_simplify_to("sin(180)", "0", User, Degree); + assert_parsed_expression_simplify_to("sin(6300/29)", "-sin(1080/29)", User, Degree); + assert_parsed_expression_simplify_to("sin(-6300/29)", "sin(1080/29)", User, Degree); + assert_parsed_expression_simplify_to("sin(61200000)", "0", User, Degree); + assert_parsed_expression_simplify_to("sin(61200180)", "0", User, Degree); + assert_parsed_expression_simplify_to("sin(-61200180)", "0", User, Degree); + assert_parsed_expression_simplify_to("sin(15)", "(R(6)-R(2))/4", User, Degree); + assert_parsed_expression_simplify_to("sin(-15)", "(-R(6)+R(2))/4", User, Degree); + assert_parsed_expression_simplify_to("sin(-180*R(2))", "-sin(180*R(2))", User, Degree); + assert_parsed_expression_simplify_to("sin(39330)", "1", User, Degree); + assert_parsed_expression_simplify_to("sin(-765/2)", "-R(-R(2)+2)/2", User, Degree); + assert_parsed_expression_simplify_to("sin(1230)", "1/2", User, Degree); + assert_parsed_expression_simplify_to("sin(180045)", "R(2)/2", User, Degree); + assert_parsed_expression_simplify_to("sin(-60)", "-R(3)/2", User, Degree); + assert_parsed_expression_simplify_to("sin(612)", "-(R(2)*R(R(5)+5))/4", User, Degree); + assert_parsed_expression_simplify_to("sin(36)", "(R(2)*R(-R(5)+5))/4", User, Degree); // -- tan assert_parsed_expression_simplify_to("tan(0)", "0"); assert_parsed_expression_simplify_to("tan(P)", "0"); @@ -429,30 +429,30 @@ QUIZ_CASE(poincare_trigo_simplify) { assert_parsed_expression_simplify_to("tan(P*340000)", "0"); assert_parsed_expression_simplify_to("tan(P*340001)", "0"); assert_parsed_expression_simplify_to("tan(-P*340001)", "0"); - assert_parsed_expression_simplify_to("tan(P/12)", "2-R(3)"); - assert_parsed_expression_simplify_to("tan(-P/12)", "-2+R(3)"); + assert_parsed_expression_simplify_to("tan(P/12)", "-R(3)+2"); + assert_parsed_expression_simplify_to("tan(-P/12)", "R(3)-2"); assert_parsed_expression_simplify_to("tan(-P*R(2))", "-tan(R(2)*P)"); assert_parsed_expression_simplify_to("tan(1311P/6)", Undefined::Name()); - assert_parsed_expression_simplify_to("tan(-P17/8)", "1-R(2)"); + assert_parsed_expression_simplify_to("tan(-P17/8)", "-R(2)+1"); assert_parsed_expression_simplify_to("tan(41P/6)", "-R(3)/3"); assert_parsed_expression_simplify_to("tan(P/4+1000P)", "1"); assert_parsed_expression_simplify_to("tan(-P/3)", "-R(3)"); - assert_parsed_expression_simplify_to("tan(-P/10)", "-(R(5)*R(5-2*R(5)))/5"); - assert_parsed_expression_simplify_to("tan(0)", "0", Preferences::AngleUnit::Degree); - assert_parsed_expression_simplify_to("tan(180)", "0", Preferences::AngleUnit::Degree); - assert_parsed_expression_simplify_to("tan(6300/29)", "tan(1080/29)", Preferences::AngleUnit::Degree); - assert_parsed_expression_simplify_to("tan(-6300/29)", "-tan(1080/29)", Preferences::AngleUnit::Degree); - assert_parsed_expression_simplify_to("tan(61200000)", "0", Preferences::AngleUnit::Degree); - assert_parsed_expression_simplify_to("tan(61200180)", "0", Preferences::AngleUnit::Degree); - assert_parsed_expression_simplify_to("tan(-61200180)", "0", Preferences::AngleUnit::Degree); - assert_parsed_expression_simplify_to("tan(15)", "2-R(3)", Preferences::AngleUnit::Degree); - assert_parsed_expression_simplify_to("tan(-15)", "-2+R(3)", Preferences::AngleUnit::Degree); - assert_parsed_expression_simplify_to("tan(-180*R(2))", "-tan(180*R(2))", Preferences::AngleUnit::Degree); - assert_parsed_expression_simplify_to("tan(39330)", Undefined::Name(), Preferences::AngleUnit::Degree); - assert_parsed_expression_simplify_to("tan(-382.5)", "1-R(2)", Preferences::AngleUnit::Degree); - assert_parsed_expression_simplify_to("tan(1230)", "-R(3)/3", Preferences::AngleUnit::Degree); - assert_parsed_expression_simplify_to("tan(180045)", "1", Preferences::AngleUnit::Degree); - assert_parsed_expression_simplify_to("tan(-60)", "-R(3)", Preferences::AngleUnit::Degree); + assert_parsed_expression_simplify_to("tan(-P/10)", "-(R(5)*R(-2*R(5)+5))/5"); + assert_parsed_expression_simplify_to("tan(0)", "0", User, Degree); + assert_parsed_expression_simplify_to("tan(180)", "0", User, Degree); + assert_parsed_expression_simplify_to("tan(6300/29)", "tan(1080/29)", User, Degree); + assert_parsed_expression_simplify_to("tan(-6300/29)", "-tan(1080/29)", User, Degree); + assert_parsed_expression_simplify_to("tan(61200000)", "0", User, Degree); + assert_parsed_expression_simplify_to("tan(61200180)", "0", User, Degree); + assert_parsed_expression_simplify_to("tan(-61200180)", "0", User, Degree); + assert_parsed_expression_simplify_to("tan(15)", "-R(3)+2", User, Degree); + assert_parsed_expression_simplify_to("tan(-15)", "R(3)-2", User, Degree); + assert_parsed_expression_simplify_to("tan(-180*R(2))", "-tan(180*R(2))", User, Degree); + assert_parsed_expression_simplify_to("tan(39330)", Undefined::Name(), User, Degree); + assert_parsed_expression_simplify_to("tan(-382.5)", "-R(2)+1", User, Degree); + assert_parsed_expression_simplify_to("tan(1230)", "-R(3)/3", User, Degree); + assert_parsed_expression_simplify_to("tan(180045)", "1", User, Degree); + assert_parsed_expression_simplify_to("tan(-60)", "-R(3)", User, Degree); assert_parsed_expression_simplify_to("tan(tan(tan(tan(9))))", "tan(tan(tan(tan(9))))"); // -- acos assert_parsed_expression_simplify_to("acos(-1/2)", "(2*P)/3"); @@ -463,16 +463,16 @@ QUIZ_CASE(poincare_trigo_simplify) { assert_parsed_expression_simplify_to("cos(acos(2/3))", "2/3"); assert_parsed_expression_simplify_to("acos(cos(12))", "acos(cos(12))"); assert_parsed_expression_simplify_to("acos(cos(4P/7))", "(4*P)/7"); - assert_parsed_expression_simplify_to("acos(-cos(2))", "-2+P"); - assert_parsed_expression_simplify_to("acos(-1/2)", "120", Preferences::AngleUnit::Degree); - assert_parsed_expression_simplify_to("acos(-1.2)", "180-acos(6/5)", Preferences::AngleUnit::Degree); - assert_parsed_expression_simplify_to("acos(cos(2/3))", "2/3", Preferences::AngleUnit::Degree); - assert_parsed_expression_simplify_to("acos(cos(190))", "170", Preferences::AngleUnit::Degree); - assert_parsed_expression_simplify_to("acos(cos(75))", "75", Preferences::AngleUnit::Degree); - assert_parsed_expression_simplify_to("cos(acos(190))", "190", Preferences::AngleUnit::Degree); - assert_parsed_expression_simplify_to("cos(acos(75))", "75", Preferences::AngleUnit::Degree); - assert_parsed_expression_simplify_to("acos(cos(12))", "12", Preferences::AngleUnit::Degree); - assert_parsed_expression_simplify_to("acos(cos(720/7))", "720/7", Preferences::AngleUnit::Degree); + assert_parsed_expression_simplify_to("acos(-cos(2))", "P-2"); + assert_parsed_expression_simplify_to("acos(-1/2)", "120", User, Degree); + assert_parsed_expression_simplify_to("acos(-1.2)", "-acos(6/5)+180", User, Degree); + assert_parsed_expression_simplify_to("acos(cos(2/3))", "2/3", User, Degree); + assert_parsed_expression_simplify_to("acos(cos(190))", "170", User, Degree); + assert_parsed_expression_simplify_to("acos(cos(75))", "75", User, Degree); + assert_parsed_expression_simplify_to("cos(acos(190))", "190", User, Degree); + assert_parsed_expression_simplify_to("cos(acos(75))", "75", User, Degree); + assert_parsed_expression_simplify_to("acos(cos(12))", "12", User, Degree); + assert_parsed_expression_simplify_to("acos(cos(720/7))", "720/7", User, Degree); // -- asin assert_parsed_expression_simplify_to("asin(-1/2)", "-P/6"); assert_parsed_expression_simplify_to("asin(-1.2)", "-asin(6/5)"); @@ -483,14 +483,14 @@ QUIZ_CASE(poincare_trigo_simplify) { assert_parsed_expression_simplify_to("asin(sin(12))", "asin(sin(12))"); assert_parsed_expression_simplify_to("asin(sin(-P/7))", "-P/7"); assert_parsed_expression_simplify_to("asin(sin(-R(2)))", "-R(2)"); - assert_parsed_expression_simplify_to("asin(-1/2)", "-30", Preferences::AngleUnit::Degree); - assert_parsed_expression_simplify_to("asin(-1.2)", "-asin(6/5)", Preferences::AngleUnit::Degree); - assert_parsed_expression_simplify_to("asin(sin(75))", "75", Preferences::AngleUnit::Degree); - assert_parsed_expression_simplify_to("sin(asin(75))", "75", Preferences::AngleUnit::Degree); - assert_parsed_expression_simplify_to("sin(asin(190))", "190", Preferences::AngleUnit::Degree); - assert_parsed_expression_simplify_to("asin(sin(32))", "32", Preferences::AngleUnit::Degree); - assert_parsed_expression_simplify_to("asin(sin(400))", "40", Preferences::AngleUnit::Degree); - assert_parsed_expression_simplify_to("asin(sin(-180/7))", "-180/7", Preferences::AngleUnit::Degree); + assert_parsed_expression_simplify_to("asin(-1/2)", "-30", User, Degree); + assert_parsed_expression_simplify_to("asin(-1.2)", "-asin(6/5)", User, Degree); + assert_parsed_expression_simplify_to("asin(sin(75))", "75", User, Degree); + assert_parsed_expression_simplify_to("sin(asin(75))", "75", User, Degree); + assert_parsed_expression_simplify_to("sin(asin(190))", "190", User, Degree); + assert_parsed_expression_simplify_to("asin(sin(32))", "32", User, Degree); + assert_parsed_expression_simplify_to("asin(sin(400))", "40", User, Degree); + assert_parsed_expression_simplify_to("asin(sin(-180/7))", "-180/7", User, Degree); // -- atan assert_parsed_expression_simplify_to("atan(-1)", "-P/4"); assert_parsed_expression_simplify_to("atan(-1.2)", "-atan(6/5)"); @@ -502,13 +502,33 @@ QUIZ_CASE(poincare_trigo_simplify) { assert_parsed_expression_simplify_to("atan(tan(-P/7))", "-P/7"); assert_parsed_expression_simplify_to("atan(R(3))", "P/3"); assert_parsed_expression_simplify_to("atan(tan(-R(2)))", "-R(2)"); - assert_parsed_expression_simplify_to("atan(-1)", "-45", Preferences::AngleUnit::Degree); - assert_parsed_expression_simplify_to("atan(-1.2)", "-atan(6/5)", Preferences::AngleUnit::Degree); - assert_parsed_expression_simplify_to("atan(tan(-45))", "-45", Preferences::AngleUnit::Degree); - assert_parsed_expression_simplify_to("tan(atan(120))", "120", Preferences::AngleUnit::Degree); - assert_parsed_expression_simplify_to("tan(atan(2293))", "2293", Preferences::AngleUnit::Degree); - assert_parsed_expression_simplify_to("atan(tan(2293))", "-47", Preferences::AngleUnit::Degree); - assert_parsed_expression_simplify_to("atan(tan(1808))", "8", Preferences::AngleUnit::Degree); - assert_parsed_expression_simplify_to("atan(tan(-180/7))", "-180/7", Preferences::AngleUnit::Degree); - assert_parsed_expression_simplify_to("atan(R(3))", "60", Preferences::AngleUnit::Degree); + assert_parsed_expression_simplify_to("atan(-1)", "-45", User, Degree); + assert_parsed_expression_simplify_to("atan(-1.2)", "-atan(6/5)", User, Degree); + assert_parsed_expression_simplify_to("atan(tan(-45))", "-45", User, Degree); + assert_parsed_expression_simplify_to("tan(atan(120))", "120", User, Degree); + assert_parsed_expression_simplify_to("tan(atan(2293))", "2293", User, Degree); + assert_parsed_expression_simplify_to("atan(tan(2293))", "-47", User, Degree); + assert_parsed_expression_simplify_to("atan(tan(1808))", "8", User, Degree); + assert_parsed_expression_simplify_to("atan(tan(-180/7))", "-180/7", User, Degree); + assert_parsed_expression_simplify_to("atan(R(3))", "60", User, Degree); + assert_parsed_expression_simplify_to("atan(1/x)", "(P*sign(x)-2*atan(x))/2", User, Degree); + + // cos(arcsin) + assert_parsed_expression_simplify_to("cos(asin(x))", "R(-x^2+1)", User, Degree); + assert_parsed_expression_simplify_to("cos(asin(-x))", "R(-x^2+1)", User, Degree); + // cos(arctan) + assert_parsed_expression_simplify_to("cos(atan(x))", "1/R(x^2+1)", User, Degree); + assert_parsed_expression_simplify_to("cos(atan(-x))", "1/R(x^2+1)", User, Degree); + // sin(arccos) + assert_parsed_expression_simplify_to("sin(acos(x))", "R(-x^2+1)", User, Degree); + assert_parsed_expression_simplify_to("sin(acos(-x))", "R(-x^2+1)", User, Degree); + // sin(arctan) + assert_parsed_expression_simplify_to("sin(atan(x))", "x/R(x^2+1)", User, Degree); + assert_parsed_expression_simplify_to("sin(atan(-x))", "-x/R(x^2+1)", User, Degree); + // tan(arccos) + assert_parsed_expression_simplify_to("tan(acos(x))", "R(-x^2+1)/x", User, Degree); + assert_parsed_expression_simplify_to("tan(acos(-x))", "-R(-x^2+1)/x", User, Degree); + // tan(arcsin) + assert_parsed_expression_simplify_to("tan(asin(x))", "x/R(-x^2+1)", User, Degree); + assert_parsed_expression_simplify_to("tan(asin(-x))", "-x/R(-x^2+1)", User, Degree); } diff --git a/poincare/test/user_variable.cpp b/poincare/test/user_variable.cpp index 5ac0ce40c..8e336d245 100644 --- a/poincare/test/user_variable.cpp +++ b/poincare/test/user_variable.cpp @@ -8,11 +8,11 @@ using namespace Poincare; QUIZ_CASE(poincare_user_variable_simple) { // Fill variable - assert_parsed_expression_simplify_to("1+2>Adadas", "1+2"); + assert_parsed_expression_simplify_to("1+2>Adadas", "3"); assert_parsed_expression_simplify_to("Adadas", "3"); // Fill f1 - assert_parsed_expression_simplify_to("1+x>f1(x)", "1+x"); + assert_parsed_expression_simplify_to("1+x>f1(x)", "x+1"); assert_parsed_expression_simplify_to("f1(4)", "5"); assert_parsed_expression_simplify_to("f1(Adadas)", "4"); @@ -22,7 +22,7 @@ QUIZ_CASE(poincare_user_variable_simple) { assert_parsed_expression_simplify_to("f2(Adadas)", "2"); // Define fBoth with f1 and f2 - assert_parsed_expression_simplify_to("f1(x)+f2(x)>fBoth(x)", "f1(x)+f2(x)"); + assert_parsed_expression_simplify_to("f1(x)+f2(x)>fBoth(x)", "2*x"); assert_parsed_expression_simplify_to("fBoth(4)", "8"); assert_parsed_expression_simplify_to("fBoth(Adadas)", "6"); @@ -157,9 +157,20 @@ QUIZ_CASE(poincare_user_variable_functions_with_context) { assert_simplify("x^2>f(x)"); // Approximate f(?-2) with ? = 5 const char x[] = {Symbol::SpecialSymbols::UnknownX, 0}; - assert_parsed_expression_approximates_with_value_for_symbol(Function("f", 1, Subtraction(Symbol(Symbol::SpecialSymbols::UnknownX), Rational(2))), x, 5.0, 9.0); + assert_parsed_expression_approximates_with_value_for_symbol(Function::Builder("f", 1, Subtraction::Builder(Symbol::Builder(Symbol::SpecialSymbols::UnknownX), Rational::Builder(2))), x, 5.0, 9.0); // Approximate f(?-1)+f(?+1) with ? = 3 - assert_parsed_expression_approximates_with_value_for_symbol(Addition(Function("f", 1, Subtraction(Symbol(Symbol::SpecialSymbols::UnknownX), Rational(1))), Function("f", 1, Addition(Symbol(Symbol::SpecialSymbols::UnknownX), Rational(1)))), x, 3.0, 20.0); + assert_parsed_expression_approximates_with_value_for_symbol(Addition::Builder(Function::Builder("f", 1, Subtraction::Builder(Symbol::Builder(Symbol::SpecialSymbols::UnknownX), Rational::Builder(1))), Function::Builder("f", 1, Addition::Builder(Symbol::Builder(Symbol::SpecialSymbols::UnknownX), Rational::Builder(1)))), x, 3.0, 20.0); + + // Clean the storage for other tests + Ion::Storage::sharedStorage()->recordNamed("f.func").destroy(); + + // f: x->R(-1) + assert_simplify("R(-1)*R(-1)>f(x)"); + // Approximate f(?) with ? = 5 + // Cartesian + assert_parsed_expression_approximates_with_value_for_symbol(Function::Builder("f", 1, Symbol::Builder(Symbol::SpecialSymbols::UnknownX)), x, 1.0, -1.0); + // Real + assert_parsed_expression_approximates_with_value_for_symbol(Function::Builder("f", 1, Symbol::Builder(Symbol::SpecialSymbols::UnknownX)), x, 1.0, (double)NAN, Real); // Clean the storage for other tests Ion::Storage::sharedStorage()->recordNamed("f.func").destroy(); @@ -168,13 +179,16 @@ QUIZ_CASE(poincare_user_variable_functions_with_context) { QUIZ_CASE(poincare_user_variable_properties) { Shared::GlobalContext context; - assert_simplify("[[1]]>a"); - assert(Symbol('a').isApproximate(context)); - assert(Poincare::Expression::IsMatrix(Symbol('a'), context, true)); + assert_parsed_expression_evaluates_to("[[1]]>a", "[[1]]"); + quiz_assert(Symbol::Builder('a').isApproximate(context)); + quiz_assert(Poincare::Expression::IsMatrix(Symbol::Builder('a'), context, true)); - assert_simplify("[[x]]>f(x)"); - assert(Function("f", 1, Rational(2)).isApproximate(context)); - assert(Poincare::Expression::IsMatrix(Function("f", 1, Symbol('x')), context, true)); + /* [[x]]->f(x) expression contains a matrix, so its simplification is going + * to be interrupted. We thus rather approximate it instead of simplifying it. + * TODO: use parse_and_simplify when matrix are simplified. */ + assert_parsed_expression_evaluates_to("[[x]]>f(x)", "[[undef]]"); + quiz_assert(Function::Builder("f", 1, Rational::Builder(2)).isApproximate(context)); + quiz_assert(Poincare::Expression::IsMatrix(Function::Builder("f", 1, Symbol::Builder('x')), context, true)); // Clean the storage for other tests Ion::Storage::sharedStorage()->recordNamed("a.exp").destroy(); diff --git a/poincare/test/vertical_offset_layout.cpp b/poincare/test/vertical_offset_layout.cpp index 77c7385f3..4c4e71750 100644 --- a/poincare/test/vertical_offset_layout.cpp +++ b/poincare/test/vertical_offset_layout.cpp @@ -7,9 +7,9 @@ using namespace Poincare; QUIZ_CASE(poincare_vertical_offset_layout_serialize) { - HorizontalLayout layout = HorizontalLayout( - CharLayout('2'), - VerticalOffsetLayout( + HorizontalLayout layout = HorizontalLayout::Builder( + CharLayout::Builder('2'), + VerticalOffsetLayout::Builder( LayoutHelper::String("x+5", 3), VerticalOffsetLayoutNode::Type::Superscript ) diff --git a/python/Makefile b/python/Makefile index a053976a1..1072474dd 100644 --- a/python/Makefile +++ b/python/Makefile @@ -1,142 +1,143 @@ SFLAGS += -Ipython/src SFLAGS += -Ipython/port +SFLAGS += -I$(BUILD_DIR)/python/port # How to maintain this Makefile # - Copy PY_CORE_O_BASENAME from py.mk into py_objs # - Copy select PY_EXTMOD_O_BASENAME from py.mk into extmod_objs # - Edit special-case workarounds below as needed -py_objs = $(addprefix python/src/py/,\ - mpstate.o \ - nlr.o \ - nlrx86.o \ - nlrx64.o \ - nlrthumb.o \ - nlrxtensa.o \ - nlrsetjmp.o \ - malloc.o \ - gc.o \ - pystack.o \ - qstr.o \ - vstr.o \ - mpprint.o \ - unicode.o \ - mpz.o \ - reader.o \ - lexer.o \ - parse.o \ - scope.o \ - compile.o \ - emitcommon.o \ - emitbc.o \ - asmbase.o \ - asmx64.o \ - emitnx64.o \ - asmx86.o \ - emitnx86.o \ - asmthumb.o \ - emitnthumb.o \ - emitinlinethumb.o \ - asmarm.o \ - emitnarm.o \ - asmxtensa.o \ - emitnxtensa.o \ - emitinlinextensa.o \ - formatfloat.o \ - parsenumbase.o \ - parsenum.o \ - emitglue.o \ - persistentcode.o \ - runtime.o \ - runtime_utils.o \ - scheduler.o \ - nativeglue.o \ - stackctrl.o \ - argcheck.o \ - warning.o \ - map.o \ - obj.o \ - objarray.o \ - objattrtuple.o \ - objbool.o \ - objboundmeth.o \ - objcell.o \ - objclosure.o \ - objcomplex.o \ - objdeque.o \ - objdict.o \ - objenumerate.o \ - objexcept.o \ - objfilter.o \ - objfloat.o \ - objfun.o \ - objgenerator.o \ - objgetitemiter.o \ - objint.o \ - objint_longlong.o \ - objint_mpz.o \ - objlist.o \ - objmap.o \ - objmodule.o \ - objobject.o \ - objpolyiter.o \ - objproperty.o \ - objnone.o \ - objnamedtuple.o \ - objrange.o \ - objreversed.o \ - objset.o \ - objsingleton.o \ - objslice.o \ - objstr.o \ - objstrunicode.o \ - objstringio.o \ - objtuple.o \ - objtype.o \ - objzip.o \ - opmethods.o \ - sequence.o \ - stream.o \ - binary.o \ - builtinimport.o \ - builtinevex.o \ - builtinhelp.o \ - modarray.o \ - modbuiltins.o \ - modcollections.o \ - modgc.o \ - modio.o \ - modmath.o \ - modcmath.o \ - modmicropython.o \ - modstruct.o \ - modsys.o \ - moduerrno.o \ - modthread.o \ - vm.o \ - bc.o \ - showbc.o \ - repl.o \ - smallint.o \ - frozenmod.o \ +py_src = $(addprefix python/src/py/,\ + mpstate.c \ + nlr.c \ + nlrx86.c \ + nlrx64.c \ + nlrthumb.c \ + nlrxtensa.c \ + nlrsetjmp.c \ + malloc.c \ + gc.c \ + pystack.c \ + qstr.c \ + vstr.c \ + mpprint.c \ + unicode.c \ + mpz.c \ + reader.c \ + lexer.c \ + parse.c \ + scope.c \ + compile.c \ + emitcommon.c \ + emitbc.c \ + asmbase.c \ + asmx64.c \ + emitnx64.c \ + asmx86.c \ + emitnx86.c \ + asmthumb.c \ + emitnthumb.c \ + emitinlinethumb.c \ + asmarm.c \ + emitnarm.c \ + asmxtensa.c \ + emitnxtensa.c \ + emitinlinextensa.c \ + formatfloat.c \ + parsenumbase.c \ + parsenum.c \ + emitglue.c \ + persistentcode.c \ + runtime.c \ + runtime_utils.c \ + scheduler.c \ + nativeglue.c \ + stackctrl.c \ + argcheck.c \ + warning.c \ + map.c \ + obj.c \ + objarray.c \ + objattrtuple.c \ + objbool.c \ + objboundmeth.c \ + objcell.c \ + objclosure.c \ + objcomplex.c \ + objdeque.c \ + objdict.c \ + objenumerate.c \ + objexcept.c \ + objfilter.c \ + objfloat.c \ + objfun.c \ + objgenerator.c \ + objgetitemiter.c \ + objint.c \ + objint_longlong.c \ + objint_mpz.c \ + objlist.c \ + objmap.c \ + objmodule.c \ + objobject.c \ + objpolyiter.c \ + objproperty.c \ + objnone.c \ + objnamedtuple.c \ + objrange.c \ + objreversed.c \ + objset.c \ + objsingleton.c \ + objslice.c \ + objstr.c \ + objstrunicode.c \ + objstringio.c \ + objtuple.c \ + objtype.c \ + objzip.c \ + opmethods.c \ + sequence.c \ + stream.c \ + binary.c \ + builtinimport.c \ + builtinevex.c \ + builtinhelp.c \ + modarray.c \ + modbuiltins.c \ + modcollections.c \ + modgc.c \ + modio.c \ + modmath.c \ + modcmath.c \ + modmicropython.c \ + modstruct.c \ + modsys.c \ + moduerrno.c \ + modthread.c \ + vm.c \ + bc.c \ + showbc.c \ + repl.c \ + smallint.c \ + frozenmod.c \ ) -extmod_objs += $(addprefix python/src/extmod/,\ - modurandom.o \ +extmod_src += $(addprefix python/src/extmod/,\ + modurandom.c \ ) -port_objs += $(addprefix python/port/,\ - port.o \ - builtins.o\ - helpers.o \ - mod/kandinsky/modkandinsky.o \ - mod/kandinsky/modkandinsky_table.o \ - mod/time/modtime.o \ - mod/time/modtime_table.o \ - mod/turtle/modturtle.o \ - mod/turtle/modturtle_table.o \ - mod/turtle/turtle.o \ - mphalport.o \ +port_src += $(addprefix python/port/,\ + port.c \ + builtins.c \ + helpers.c \ + mod/kandinsky/modkandinsky.cpp \ + mod/kandinsky/modkandinsky_table.cpp \ + mod/time/modtime.c \ + mod/time/modtime_table.c \ + mod/turtle/modturtle.cpp \ + mod/turtle/modturtle_table.cpp \ + mod/turtle/turtle.cpp \ + mphalport.c \ ) # Workarounds @@ -145,14 +146,14 @@ port_objs += $(addprefix python/port/,\ # In order to change the name of the micropython module 'urandom' to 'random' # (without altering micropython files), we redefined the macro MP_QSTR_urandom # by DMP_QSTR_random. -python/src/py/objmodule.o: SFLAGS += -DMP_QSTR_urandom="MP_QSTR_random" -python/src/extmod/modurandom.o: SFLAGS += -DMP_QSTR_urandom="MP_QSTR_random" +$(call object_for,python/src/py/objmodule.c): SFLAGS += -DMP_QSTR_urandom="MP_QSTR_random" +$(call object_for,python/src/extmod/modurandom.c): SFLAGS += -DMP_QSTR_urandom="MP_QSTR_random" # Handle upward-growing stack # Some platforms such as emscripten have a stack that grows up. We've rewritten # the stack control file to handle this case. -py_objs := $(filter-out python/src/py/stackctrl.o, $(py_objs)) -port_objs += python/port/stackctrl.o +py_src := $(filter-out python/src/py/stackctrl.c, $(py_src)) +port_src += python/port/stackctrl.c # Fix the GC on emscripten # With optimizations, register and stack variables might be held in a JavaScript @@ -162,21 +163,20 @@ port_objs += python/port/stackctrl.o # computing resumes, if necessary heap objects have been destroyed, the Python # program crashes. ifeq ($(PLATFORM),emscripten) -$(py_objs): SFLAGS := $(subst -Os,-O0,$(SFLAGS)) +$(call object_for,$(py_src)): SFLAGS := $(subst -Os,-O0,$(SFLAGS)) endif +python_src = $(py_src) $(extmod_src) $(port_src) + # QSTR generation -generated_headers += $(addprefix python/port/genhdr/, qstrdefs.generated.h) +$(eval $(call rule_for, \ + QSTRDAT, \ + python/port/genhdr/qstrdefs.generated.h, \ + python/port/genhdr/qstrdefs.in.h, \ + $$(PYTHON) python/src/py/makeqstrdata.py $$< > $$@ \ +)) -python/port/genhdr/qstrdefs.generated.h: python/port/genhdr/qstrdefs.in.h - @echo "QSTRDAT $@" - $(Q) $(PYTHON) python/src/py/makeqstrdata.py $< > $@ +$(call object_for,$(python_src)): $(BUILD_DIR)/python/port/genhdr/qstrdefs.generated.h -products += python/port/genhdr/qstrdefs.generated.h - -$(py_objs) $(extmod_objs) $(port_objs): python/port/genhdr/qstrdefs.generated.h - -# List all objects needed - -objs += $(extmod_objs) $(py_objs) $(port_objs) \ No newline at end of file +src += $(python_src) diff --git a/python/port/builtins.c b/python/port/builtins.c index a04ad75c6..fa0e1f3be 100644 --- a/python/port/builtins.c +++ b/python/port/builtins.c @@ -15,17 +15,11 @@ mp_obj_t mp_builtin_input(size_t n_args, const mp_obj_t *args) { prompt = mp_obj_str_get_str(args[0]); } - // 2 - Perform the HAL input command + // 2 - Perform the HAL input command. This logs the prompt and the result const char * result = mp_hal_input(prompt); - // 3 - Log the prompt, result and flush a new line + // 3 - Return the input mp_obj_t resultStr = mp_obj_new_str(result, strlen(result)); - if (n_args == 1) { - mp_obj_print(args[0], PRINT_STR); - } - mp_obj_print(resultStr, PRINT_STR); - mp_print_str(MP_PYTHON_PRINTER, "\n"); - return resultStr; } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_input_obj, 0, 1, mp_builtin_input); diff --git a/python/port/mod/turtle/turtle.cpp b/python/port/mod/turtle/turtle.cpp index 2bbc45487..0b7dcad89 100644 --- a/python/port/mod/turtle/turtle.cpp +++ b/python/port/mod/turtle/turtle.cpp @@ -1,12 +1,12 @@ #include "turtle.h" #include +#include extern "C" { #include } #include "../../helpers.h" #include "../../port.h" -static inline mp_float_t maxF(mp_float_t x, mp_float_t y) { return x >= y ? x : y;} static inline mp_float_t absF(mp_float_t x) { return x >= 0 ? x : -x;} static constexpr KDCoordinate k_iconSize = 15; @@ -31,7 +31,7 @@ void Turtle::reset() { // Reset turtle values m_x = 0; m_y = 0; - m_heading = k_headingOffset; + m_heading = 0; m_color = k_defaultColor; m_penDown = true; m_visible = true; @@ -44,21 +44,36 @@ void Turtle::reset() { } bool Turtle::forward(mp_float_t length) { + /* cos and sin use radians, we thus need to multiply m_heading by PI/180 to + * compute the new turtle position. This induces rounding errors that are + * really visible when one expects a horizontal/vertical line and it is not. + * We thus make special cases for angles in degrees creating vertical / + * horizontal lines. */ + if (m_heading == 0) { + return goTo(m_x + length, m_y); + } + if (m_heading == 180 || m_heading == -180) { + return goTo(m_x - length, m_y); + } + if (m_heading == 90 || m_heading == -270) { + return goTo(m_x, m_y + length); + } + if (m_heading == 270 || m_heading == -90) { + return goTo(m_x, m_y - length); + } return goTo( - m_x + length * sin(m_heading), - m_y + length * cos(m_heading) + m_x + length * std::cos(m_heading * k_headingScale), + m_y + length * std::sin(m_heading * k_headingScale) ); } void Turtle::left(mp_float_t angle) { - setHeading( - k_invertedYAxisCoefficient * ((m_heading - k_headingOffset) + k_invertedYAxisCoefficient * (angle * k_headingScale)) / k_headingScale - ); + setHeading(m_heading + angle); } void Turtle::circle(mp_int_t radius, mp_float_t angle) { mp_float_t oldHeading = heading(); - mp_float_t length = ((angle > 0 ? 1 : -1) * angle * k_headingScale) * radius; + mp_float_t length = (angle > 0 ? 1 : -1) * angle * k_headingScale * radius; if (length > 1) { for (int i = 1; i < length; i++) { mp_float_t progress = i / length; @@ -77,16 +92,35 @@ void Turtle::circle(mp_int_t radius, mp_float_t angle) { bool Turtle::goTo(mp_float_t x, mp_float_t y) { mp_float_t oldx = m_x; mp_float_t oldy = m_y; - mp_float_t length = maxF(absF(floor(x) - floor(oldx)), absF(floor(y) - floor(oldy))); + mp_float_t xLength = absF(std::floor(x) - std::floor(oldx)); + mp_float_t yLength = absF(std::floor(y) - std::floor(oldy)); + + enum PrincipalDirection { + None = 0, + X = 1, + Y = 2 + }; + + PrincipalDirection principalDirection = xLength > yLength ? + PrincipalDirection::X : + (xLength == yLength ? + PrincipalDirection::None : + PrincipalDirection::Y); + + mp_float_t length = principalDirection == PrincipalDirection::X ? xLength : yLength; if (length > 1) { // Tweening function for (int i = 1; i < length; i++) { mp_float_t progress = i / length; erase(); - if (dot(x * progress + oldx * (1 - progress), y * progress + oldy * (1 - progress)) - || draw(false)) - { + /* We make sure that each pixel along the principal direction is drawn. If + * the computation of the position on the principal coordinate is done + * using a barycenter, roundings might skip some pixels, which results in + * a dotted line. */ + mp_float_t currentX = principalDirection == PrincipalDirection::Y ? x * progress + oldx * (1 - progress) : oldx + (x > oldx ? i : -i); + mp_float_t currentY = principalDirection == PrincipalDirection::X ? y * progress + oldy * (1 - progress) : oldy + (y > oldy ? i : -i); + if (dot(currentX, currentY) || draw(false)) { // Keyboard interruption. Return now to let MicroPython process it. return true; } @@ -99,10 +133,6 @@ bool Turtle::goTo(mp_float_t x, mp_float_t y) { return false; } -mp_float_t Turtle::heading() const { - return k_invertedYAxisCoefficient * (m_heading - k_headingOffset) / k_headingScale; -} - void Turtle::setHeading(mp_float_t angle) { micropython_port_vm_hook_loop(); setHeadingPrivate(angle); @@ -174,11 +204,19 @@ void Turtle::viewDidDisappear() { // Private functions void Turtle::setHeadingPrivate(mp_float_t angle) { - m_heading = k_invertedYAxisCoefficient * angle * k_headingScale + k_headingOffset; + // Put the angle in [0; 360[ + mp_float_t angleLimit = 360; + mp_float_t angleBetween0And360 = angle - ((angle >= 0 && angle < angleLimit) ? 0 : std::floor(angle/angleLimit) * angleLimit); + if (angleBetween0And360 >= 0 && angleBetween0And360 < angleLimit) { + m_heading = angleBetween0And360; + } else { + // When angle is too big, our formula does not put it properly in [0; 360[ + m_heading = 0; + } } KDPoint Turtle::position(mp_float_t x, mp_float_t y) const { - return KDPoint(floor(x + k_xOffset), floor(k_invertedYAxisCoefficient * y + k_yOffset)); + return KDPoint(std::floor(x + k_xOffset), std::floor(k_invertedYAxisCoefficient * y + k_yOffset)); } bool Turtle::hasUnderneathPixelBuffer() { @@ -245,8 +283,8 @@ bool Turtle::draw(bool force) { // Draw the head KDCoordinate headOffsetLength = 6; - KDCoordinate headOffsetX = headOffsetLength * sin(m_heading); - KDCoordinate headOffsetY = -headOffsetLength * cos(m_heading); + KDCoordinate headOffsetX = headOffsetLength * std::cos(m_heading * k_headingScale); + KDCoordinate headOffsetY = k_invertedYAxisCoefficient * headOffsetLength * std::sin(m_heading * k_headingScale); KDPoint headOffset(headOffsetX, headOffsetY); drawingRect = KDRect( position().translatedBy(headOffset).translatedBy(KDPoint(-k_iconHeadSize/2, -k_iconHeadSize/2)), @@ -297,11 +335,11 @@ bool Turtle::draw(bool force) { m_drawn = true; } - if (m_mileage > 1000) { + if (m_mileage > k_mileageLimit) { if (micropython_port_interruptible_msleep(1 + (m_speed == 0 ? 0 : 3 * (k_maxSpeed - m_speed)))) { return true; } - m_mileage -= 1000; + m_mileage -= k_mileageLimit; } return false; } @@ -309,6 +347,7 @@ bool Turtle::draw(bool force) { bool Turtle::dot(mp_float_t x, mp_float_t y) { MicroPython::ExecutionEnvironment::currentExecutionEnvironment()->displaySandbox(); + // Draw the dot if the pen is down if (m_penDown && hasDotBuffers()) { KDContext * ctx = KDIonContext::sharedContext(); KDRect rect( @@ -318,7 +357,12 @@ bool Turtle::dot(mp_float_t x, mp_float_t y) { ctx->blendRectWithMask(rect, m_color, m_dotMask, m_dotWorkingPixelBuffer); } - m_mileage += sqrt((x - m_x) * (x - m_x) + (y - m_y) * (y - m_y)) * 1000; + /* Increase the turtle's mileage. We need to make sure the mileage is not + * overflowed, otherwise we might skip some msleeps in draw. */ + uint16_t additionalMileage = sqrt((x - m_x) * (x - m_x) + (y - m_y) * (y - m_y)) * 1000; + m_mileage = ((m_mileage > k_mileageLimit) + && ((m_mileage + additionalMileage) < k_mileageLimit)) ? + k_mileageLimit + 1 : m_mileage + additionalMileage; m_x = x; m_y = y; @@ -336,8 +380,8 @@ void Turtle::drawPaw(PawType type, PawPosition pos) { // Compute the paw offset from the turtle center float currentAngle = angles[(int) type]; float crawlDelta = ((float)((int)pos)) * crawlOffset; - float pawX = pawOffset * sin(m_heading+currentAngle) + crawlDelta * sin(m_heading); - float pawY = - pawOffset * cos(m_heading+currentAngle) - crawlDelta * cos(m_heading); + float pawX = pawOffset * std::cos(m_heading * k_headingScale + currentAngle) + crawlDelta * std::cos(m_heading * k_headingScale); + float pawY = k_invertedYAxisCoefficient * (pawOffset * std::sin(m_heading * k_headingScale + currentAngle) + crawlDelta * std::sin(m_heading * k_headingScale)); KDCoordinate pawOffsetX = ((int)pawX) - (pawX < 0 ? 1 : 0); KDCoordinate pawOffsetY = ((int)pawY) - (pawY < 0 ? 1 : 0); KDPoint offset(pawOffsetX, pawOffsetY); diff --git a/python/port/mod/turtle/turtle.h b/python/port/mod/turtle/turtle.h index 532669939..87aa58e8f 100644 --- a/python/port/mod/turtle/turtle.h +++ b/python/port/mod/turtle/turtle.h @@ -22,19 +22,19 @@ extern "C" { class Turtle { public: constexpr Turtle() : + m_underneathPixelBuffer(nullptr), + m_dotMask(nullptr), + m_dotWorkingPixelBuffer(nullptr), m_x(0), m_y(0), - m_heading(k_headingOffset), + m_heading(0), m_color(k_defaultColor), m_penDown(true), m_visible(true), m_speed(k_defaultSpeed), m_penSize(k_defaultPenSize), m_mileage(0), - m_drawn(false), - m_underneathPixelBuffer(nullptr), - m_dotMask(nullptr), - m_dotWorkingPixelBuffer(nullptr) + m_drawn(false) { } @@ -47,7 +47,7 @@ public: void circle(mp_int_t radius, mp_float_t angle = 360); bool goTo(mp_float_t x, mp_float_t y); - mp_float_t heading() const; + mp_float_t heading() const { return m_heading; } void setHeading(mp_float_t angle); uint8_t speed() const { return m_speed; } @@ -76,7 +76,6 @@ public: void viewDidDisappear(); private: - static constexpr mp_float_t k_headingOffset = M_PI_2; static constexpr mp_float_t k_headingScale = M_PI / 180; /* The Y axis is oriented upwards in Turtle and downwards in Kandinsky, so we * need to invert some values, hence k_invertedYAxisCoefficient. */ @@ -133,11 +132,19 @@ private: void drawPaw(PawType type, PawPosition position); void erase(); + /* When GC is performed, sTurtle is marked as root for GC collection and its + * data is scanned for pointers that point to the Python heap. We put the 3 + * pointers that should be marked at the beginning of the object to maximize + * the chances they will be correctly aligned and interpreted as pointers. */ + KDColor * m_underneathPixelBuffer; + uint8_t * m_dotMask; + KDColor * m_dotWorkingPixelBuffer; + /* The frame's center is the center of the screen, the x axis goes to the * right and the y axis goes upwards. */ mp_float_t m_x; mp_float_t m_y; - /* The heading is the angle in radians between the direction of the turtle and + /* The heading is the angle in degrees between the direction of the turtle and * the X axis, in the counterclockwise direction. */ mp_float_t m_heading; @@ -147,12 +154,23 @@ private: uint8_t m_speed; // Speed is between 0 and 10 KDCoordinate m_penSize; - KDCoordinate m_mileage; + + /* We sleep every time the turtle walks a mileageLimit amount, to allow user + * interruptions. The length of each sleep is determined by the speed of the + * turtle. + * With emscripten, sleep gives control to the web browser, which decides when + * to return from sleep: this makes the turtle significantly slower on the web + * emulator than on the calculator. We thus decided to sleep less often on the + * emscripten platform. */ +#if __EMSCRIPTEN__ + static constexpr uint16_t k_mileageLimit = 10000; +#else + static constexpr uint16_t k_mileageLimit = 1000; +#endif + + uint16_t m_mileage; bool m_drawn; - KDColor * m_underneathPixelBuffer; - uint8_t * m_dotMask; - KDColor * m_dotWorkingPixelBuffer; }; #endif diff --git a/python/port/port.cpp b/python/port/port.cpp index 302427e45..6f01f26ca 100644 --- a/python/port/port.cpp +++ b/python/port/port.cpp @@ -2,6 +2,7 @@ #include +#include #include #include #include @@ -138,13 +139,34 @@ void gc_collect(void) { * the case on a computer. We thus have to take the absolute value of the * addresses difference. */ size_t stackLength; + void ** scanStart; if ((uintptr_t)python_stack_top > (uintptr_t)®s) { - stackLength = ((uintptr_t)python_stack_top - (uintptr_t)®s) / sizeof(uintptr_t); - gc_collect_root(regs_ptr, stackLength); + + /* To compute the stack length: + * regs + * <-----------> + * STACK -> ...| | | | | |--|--|--|--| | | | | | | + * ^®s ^python_stack_top + * */ + + stackLength = ceil((float)((uintptr_t)python_stack_top - (uintptr_t)®s) / (float)sizeof(uintptr_t)); + scanStart = regs_ptr; + } else { - stackLength = ((uintptr_t)(®s) - (uintptr_t)python_stack_top) / sizeof(uintptr_t); - gc_collect_root((void **)python_stack_top, stackLength); + + /* When computing the stack length, take into account regs' size. + * regs + * <-----------> + * STACK -> | | | | | | | | | | | |--|--|--|--| | | |... + * ^python_stack_top ^®s + * */ + + size_t sizeOfRegs = ceil(((float)sizeof(regs))/(float)sizeof(uintptr_t)); + stackLength = (size_t)ceil(((float)((uintptr_t)(®s) - (uintptr_t)python_stack_top)) / (float)sizeof(uintptr_t)) + sizeOfRegs; + scanStart = (void **)python_stack_top; + } + gc_collect_root(scanStart, stackLength); gc_collect_end(); } diff --git a/python/port/port.h b/python/port/port.h index 29219d073..f0b3a0407 100644 --- a/python/port/port.h +++ b/python/port/port.h @@ -19,6 +19,7 @@ public: void runCode(const char * ); virtual const char * inputText(const char * prompt) { return nullptr; } virtual void displaySandbox() {} + virtual void hideSandbox() {} virtual void resetSandbox() {} virtual void printText(const char * text, size_t length) {} void interrupt(); diff --git a/python/src/py/gc.c b/python/src/py/gc.c index 84c9918fd..5b5088920 100644 --- a/python/src/py/gc.c +++ b/python/src/py/gc.c @@ -444,7 +444,7 @@ void *gc_alloc(size_t n_bytes, bool has_finaliser) { size_t i; size_t end_block; size_t start_block; - size_t n_free = 0; + size_t n_free; int collected = !MP_STATE_MEM(gc_auto_collect_enabled); #if MICROPY_GC_ALLOC_THRESHOLD @@ -456,7 +456,7 @@ void *gc_alloc(size_t n_bytes, bool has_finaliser) { #endif for (;;) { - + n_free = 0; // Fixes bug, should be written in the next MicroPython version // look for a run of n_blocks available blocks for (i = MP_STATE_MEM(gc_last_free_atb_index); i < MP_STATE_MEM(gc_alloc_table_byte_len); i++) { byte a = MP_STATE_MEM(gc_alloc_table_start)[i]; diff --git a/quiz/Makefile b/quiz/Makefile index a4974c936..45129c49c 100644 --- a/quiz/Makefile +++ b/quiz/Makefile @@ -1,18 +1,23 @@ SFLAGS += -Iquiz/include QUIZ_USE_CONSOLE ?= 0 -quiz/src/runner.o: SFLAGS += -DQUIZ_USE_CONSOLE=$(QUIZ_USE_CONSOLE) +$(call object_for,quiz/src/runner.cpp): SFLAGS += -DQUIZ_USE_CONSOLE=$(QUIZ_USE_CONSOLE) -symbols_file = $(addprefix quiz/src/, symbols.c) -products += $(symbols_file) +symbols_file = $(BUILD_DIR)/quiz/src/symbols.c $(symbols_file): $(tests) @echo "AWK $@" $(Q) awk -f quiz/src/symbols.awk $(tests) > $@ -runner_objs += $(addprefix quiz/src/, runner.o assertions.o symbols.o i18n.o) -test_objs += $(subst .c,.o, $(subst .cpp,.o,$(tests))) +runner_src += $(addprefix quiz/src/, \ + assertions.cpp \ + i18n.cpp \ + runner.cpp \ +) -test.$(EXE): $(runner_objs) $(test_objs) +runner_src += $(symbols_file) -products += test.$(EXE) $(runner_objs) $(test_objs) +runner_objs = $(call object_for,$(runner_src)) +$(runner_objs): SFLAGS += -Iquiz/src + +$(BUILD_DIR)/test.$(EXE): $(runner_objs) $(call object_for,$(tests)) diff --git a/build/config.mak b/scripts/config.mak similarity index 89% rename from build/config.mak rename to scripts/config.mak index 4181b8944..db7ca1277 100644 --- a/build/config.mak +++ b/scripts/config.mak @@ -3,7 +3,7 @@ PLATFORM ?= device DEBUG ?= 0 -EPSILON_VERSION ?= 1.9.0 +EPSILON_VERSION ?= 10.0.0 EPSILON_ONBOARDING_APP ?= 1 # Valid values are "none", "update", "beta" EPSILON_BOOT_PROMPT ?= none @@ -13,15 +13,15 @@ EPSILON_GETOPT ?= 0 MATRICES_ARE_DEFINED ?=1 ESCHER_LOG_EVENTS_BINARY ?= 0 -include build/defaults.mak -include build/platform.$(PLATFORM).mak +include scripts/defaults.mak +include scripts/platform.$(PLATFORM).mak ifndef USE_LIBA $(error platform.mak should define USE_LIBA) endif ifndef EXE $(error platform.mak should define EXE, the extension for executables) endif -include build/toolchain.$(TOOLCHAIN).mak +include scripts/toolchain.$(TOOLCHAIN).mak SFLAGS += -DDEBUG=$(DEBUG) SFLAGS += -DEPSILON_ONBOARDING_APP=$(EPSILON_ONBOARDING_APP) diff --git a/build/defaults.mak b/scripts/defaults.mak similarity index 95% rename from build/defaults.mak rename to scripts/defaults.mak index e90a2b158..460db324c 100644 --- a/build/defaults.mak +++ b/scripts/defaults.mak @@ -33,3 +33,5 @@ ifeq ("$(origin V)", "command line") Q= endif endif + +BUILD_DIR = build/$(PLATFORM) diff --git a/build/device/elf2dfu.py b/scripts/device/elf2dfu.py similarity index 100% rename from build/device/elf2dfu.py rename to scripts/device/elf2dfu.py diff --git a/build/device/gdb_script.gdb b/scripts/device/gdb_script.gdb similarity index 100% rename from build/device/gdb_script.gdb rename to scripts/device/gdb_script.gdb diff --git a/build/device/memory_map.awk b/scripts/device/memory_map.awk similarity index 100% rename from build/device/memory_map.awk rename to scripts/device/memory_map.awk diff --git a/build/device/openocd.n0100.cfg b/scripts/device/openocd.n0100.cfg similarity index 100% rename from build/device/openocd.n0100.cfg rename to scripts/device/openocd.n0100.cfg diff --git a/build/device/openocd.n0101.cfg b/scripts/device/openocd.n0101.cfg similarity index 100% rename from build/device/openocd.n0101.cfg rename to scripts/device/openocd.n0101.cfg diff --git a/build/platform.blackbox.mak b/scripts/platform.blackbox.mak similarity index 100% rename from build/platform.blackbox.mak rename to scripts/platform.blackbox.mak diff --git a/build/platform.device.mak b/scripts/platform.device.mak similarity index 56% rename from build/platform.device.mak rename to scripts/platform.device.mak index 528a97c9e..405fbaa36 100644 --- a/build/platform.device.mak +++ b/scripts/platform.device.mak @@ -6,6 +6,6 @@ EPSILON_BOOT_PROMPT = update EPSILON_DEVICE_BENCH ?= 1 SFLAGS += -DEPSILON_DEVICE_BENCH=$(EPSILON_DEVICE_BENCH) -python/port/port.o: CXXFLAGS += -DMP_PORT_USE_STACK_SYMBOLS=1 +$(BUILD_DIR)/python/port/port.o: CXXFLAGS += -DMP_PORT_USE_STACK_SYMBOLS=1 -include build/platform.device.$(MODEL).mak +include scripts/platform.device.$(MODEL).mak diff --git a/build/platform.device.n0100.mak b/scripts/platform.device.n0100.mak similarity index 100% rename from build/platform.device.n0100.mak rename to scripts/platform.device.n0100.mak diff --git a/build/platform.device.n0101.mak b/scripts/platform.device.n0101.mak similarity index 100% rename from build/platform.device.n0101.mak rename to scripts/platform.device.n0101.mak diff --git a/build/platform.emscripten.mak b/scripts/platform.emscripten.mak similarity index 100% rename from build/platform.emscripten.mak rename to scripts/platform.emscripten.mak diff --git a/build/platform.simulator.mak b/scripts/platform.simulator.mak similarity index 100% rename from build/platform.simulator.mak rename to scripts/platform.simulator.mak diff --git a/build/scenario/Makefile b/scripts/scenario/Makefile similarity index 100% rename from build/scenario/Makefile rename to scripts/scenario/Makefile diff --git a/build/struct_layout/Makefile b/scripts/struct_layout/Makefile similarity index 69% rename from build/struct_layout/Makefile rename to scripts/struct_layout/Makefile index 1d15f379a..df5ec0241 100644 --- a/build/struct_layout/Makefile +++ b/scripts/struct_layout/Makefile @@ -1,4 +1,4 @@ -products += apps/main.ast build/struct_layout/data.json +products += apps/main.ast scripts/struct_layout/data.json .PHONY: apps_container_struct_layout OPEN = open @@ -11,10 +11,10 @@ ifeq ($(CXX),clang++) %.ast.json: %.ast @echo "JSON $@" - @cat $< | ruby build/struct_layout/ast_to_json.rb AppsContainer > $@ + @cat $< | ruby scripts/struct_layout/ast_to_json.rb AppsContainer > $@ apps_container_struct_layout: apps/main.ast.json - $(OPEN) build/struct_layout/visualization.html + $(OPEN) scripts/struct_layout/visualization.html else diff --git a/build/struct_layout/ast_to_json.rb b/scripts/struct_layout/ast_to_json.rb similarity index 100% rename from build/struct_layout/ast_to_json.rb rename to scripts/struct_layout/ast_to_json.rb diff --git a/build/struct_layout/visualization.html b/scripts/struct_layout/visualization.html similarity index 100% rename from build/struct_layout/visualization.html rename to scripts/struct_layout/visualization.html diff --git a/build/targets.blackbox.mak b/scripts/targets.blackbox.mak similarity index 57% rename from build/targets.blackbox.mak rename to scripts/targets.blackbox.mak index 858a22887..9d4241003 100644 --- a/build/targets.blackbox.mak +++ b/scripts/targets.blackbox.mak @@ -1,20 +1,18 @@ # Compare -products += $(wildcard ion/src/blackbox/library_*.o) - -ion/src/blackbox/library_%.o: SFLAGS += -D EPSILON_LIB_PREFIX=$(*F) -ion/src/blackbox/library_%.o: ion/src/blackbox/library.cpp +$(BUILD_DIR)/ion/src/blackbox/library_%.o: SFLAGS += -D EPSILON_LIB_PREFIX=$(*F) +$(BUILD_DIR)/ion/src/blackbox/library_%.o: ion/src/blackbox/library.cpp @echo "CXX $@" $(Q) $(CXX) $(SFLAGS) $(CXXFLAGS) -c $< -o $@ -libepsilon_objs = $(filter-out $(addprefix ion/src/blackbox/,boot.o events.o),$(objs)) +libepsilon_src = $(filter-out $(addprefix ion/src/blackbox/,boot.cpp events.cpp),$(src)) -libepsilon_%.o: LDFLAGS += -exported_symbols_list ion/src/blackbox/lib_export_list.txt -libepsilon_%.o: $(libepsilon_objs) $(app_objs) $(app_image_objs) ion/src/blackbox/library_%.o +$(BUILD_DIR)/libepsilon_%.o: LDFLAGS += -exported_symbols_list ion/src/blackbox/lib_export_list.txt +$(BUILD_DIR)/libepsilon_%.o: $(call object_for,$(libepsilon_src)) $(call object_for,$(app_src)) $(BUILD_DIR)/ion/src/blackbox/library_%.o @echo "LD $@" $(Q) $(LD) $^ $(LDFLAGS) -r -s -o $@ -compare: ion/src/blackbox/compare.o +$(BUILD_DIR)/compare: $(call object_for,ion/src/blackbox/compare.cpp) @echo "LD $@" $(Q) $(LD) $^ libepsilon_first.o libepsilon_second.o $(LDFLAGS) -L. -o $@ @@ -41,9 +39,9 @@ integration_tests: $(scenarios:.esc=.run) # Fuzzing .PHONY: epsilon_fuzz ifeq ($(TOOLCHAIN),afl) -epsilon_fuzz: epsilon.$(EXE) +epsilon_fuzz: $(BUILD_DIR)/epsilon.$(EXE) @echo "FUZZ $<" - @afl-fuzz -i tests -o afl ./epsilon.$(EXE) + @afl-fuzz -i tests -o afl $(BUILD_DIR)/epsilon.$(EXE) else epsilon_fuzz: @echo "Fuzzing requires TOOLCHAIN=afl" @@ -51,9 +49,9 @@ endif .PHONY: compare_fuzz ifeq ($(TOOLCHAIN),afl) -compare_fuzz: compare +compare_fuzz: $(BUILD_DIR)/compare @echo "FUZZ $<" - @afl-fuzz -t 3000 -i tests -o afl ./compare + @afl-fuzz -t 3000 -i tests -o afl $(BUILD_DIR)/compare else compare_fuzz: @echo "Fuzzing requires TOOLCHAIN=afl" diff --git a/build/targets.device.mak b/scripts/targets.device.mak similarity index 73% rename from build/targets.device.mak rename to scripts/targets.device.mak index 9f49be554..da6403872 100644 --- a/build/targets.device.mak +++ b/scripts/targets.device.mak @@ -1,7 +1,3 @@ -products += $(patsubst %.$(EXE),%.hex,$(filter %.$(EXE),$(products))) -products += $(patsubst %.$(EXE),%.bin,$(filter %.$(EXE),$(products))) -products += $(patsubst %.$(EXE),%.map,$(filter %.$(EXE),$(products))) - %.dfu: %.$(EXE) @echo "DFUSE $@" $(Q) $(PYTHON) build/device/elf2dfu.py $< $@ @@ -29,7 +25,7 @@ products += $(patsubst %.$(EXE),%.map,$(filter %.$(EXE),$(products))) .PHONY: %_run %_run: %.$(EXE) - $(GDB) -x build/$(PLATFORM)/gdb_script.gdb $< + $(GDB) -x scripts/$(PLATFORM)/gdb_script.gdb $< %.map: %.elf @echo "LDMAP $@" @@ -38,7 +34,7 @@ products += $(patsubst %.$(EXE),%.map,$(filter %.$(EXE),$(products))) .PHONY: %_memory_map %_memory_map: %.map @echo "========== MEMORY MAP =========" - $(Q) awk -f build/device/memory_map.awk < $< + $(Q) awk -f scripts/device/memory_map.awk < $< @echo "===============================" .PHONY: %_flash @@ -52,17 +48,15 @@ products += $(patsubst %.$(EXE),%.map,$(filter %.$(EXE),$(products))) .PHONY: openocd openocd: - openocd -f build/$(PLATFORM)/openocd.$(MODEL).cfg + openocd -f scripts/$(PLATFORM)/openocd.$(MODEL).cfg # The flasher target is defined here because otherwise $(objs) has not been # fully filled ifeq ($(EPSILON_USB_DFU_XIP)$(EPSILON_DEVICE_BENCH),10) -ion/src/$(PLATFORM)/shared/usb/flasher.o: SFLAGS += $(ION_DEVICE_SFLAGS) -flasher.$(EXE): LDSCRIPT = ion/src/$(PLATFORM)/shared/ram.ld -flasher.$(EXE): $(objs) ion/src/$(PLATFORM)/shared/usb/flasher.o +$(BUILD_DIR)/ion/src/$(PLATFORM)/shared/usb/flasher.o: SFLAGS += $(ION_DEVICE_SFLAGS) +$(BUILD_DIR)/flasher.$(EXE): LDSCRIPT = ion/src/$(PLATFORM)/shared/ram.ld +$(BUILD_DIR)/flasher.$(EXE): $(objs) $(BUILD_DIR)/ion/src/$(PLATFORM)/shared/usb/flasher.o else -flasher.$(EXE): +$(BUILD_DIR)/flasher.$(EXE): @echo "Error: flasher.elf requires EPSILON_DEVICE_BENCH=0 EPSILON_USB_DFU_XIP=1" endif - -products += flasher.$(EXE) flasher.bin diff --git a/scripts/targets.emscripten.mak b/scripts/targets.emscripten.mak new file mode 100644 index 000000000..436890c53 --- /dev/null +++ b/scripts/targets.emscripten.mak @@ -0,0 +1,12 @@ +$(BUILD_DIR)/epsilon.packed.js: LDFLAGS += --memory-init-file 0 +$(BUILD_DIR)/epsilon.packed.js: $(objs) $(call object_for,$(epsilon_src)) + +$(BUILD_DIR)/simulator.zip: $(BUILD_DIR)/epsilon.packed.js + @rm -rf $(basename $@) + @mkdir -p $(basename $@) + @cp $^ $(basename $@)/epsilon.js + @cp ion/src/emscripten/background.jpg $(basename $@)/ + @cp ion/src/emscripten/simulator.html $(basename $@)/ + @echo "ZIP $@" + @zip -r -9 -j $@ $(basename $@) > /dev/null + @rm -rf $(basename $@) diff --git a/build/toolchain.afl.mak b/scripts/toolchain.afl.mak similarity index 100% rename from build/toolchain.afl.mak rename to scripts/toolchain.afl.mak diff --git a/build/toolchain.arm-gcc-m4f.mak b/scripts/toolchain.arm-gcc-m4f.mak similarity index 71% rename from build/toolchain.arm-gcc-m4f.mak rename to scripts/toolchain.arm-gcc-m4f.mak index 5f5ae4056..591fd7fbb 100644 --- a/build/toolchain.arm-gcc-m4f.mak +++ b/scripts/toolchain.arm-gcc-m4f.mak @@ -1,3 +1,3 @@ -include build/toolchain.arm-gcc.mak +include scripts/toolchain.arm-gcc.mak SFLAGS += -mthumb -march=armv7e-m -mfloat-abi=hard SFLAGS += -mcpu=cortex-m4 -mfpu=fpv4-sp-d16 diff --git a/build/toolchain.arm-gcc-m7f.mak b/scripts/toolchain.arm-gcc-m7f.mak similarity index 71% rename from build/toolchain.arm-gcc-m7f.mak rename to scripts/toolchain.arm-gcc-m7f.mak index b23b86b9d..d22f0c1eb 100644 --- a/build/toolchain.arm-gcc-m7f.mak +++ b/scripts/toolchain.arm-gcc-m7f.mak @@ -1,3 +1,3 @@ -include build/toolchain.arm-gcc.mak +include scripts/toolchain.arm-gcc.mak SFLAGS += -mthumb -march=armv7e-m -mfloat-abi=hard SFLAGS += -mcpu=cortex-m7 -mfpu=fpv5-sp-d16 diff --git a/build/toolchain.arm-gcc.mak b/scripts/toolchain.arm-gcc.mak similarity index 100% rename from build/toolchain.arm-gcc.mak rename to scripts/toolchain.arm-gcc.mak diff --git a/build/toolchain.arm-llvm.mak b/scripts/toolchain.arm-llvm.mak similarity index 100% rename from build/toolchain.arm-llvm.mak rename to scripts/toolchain.arm-llvm.mak diff --git a/build/toolchain.emscripten.mak b/scripts/toolchain.emscripten.mak similarity index 100% rename from build/toolchain.emscripten.mak rename to scripts/toolchain.emscripten.mak diff --git a/build/toolchain.host-clang.mak b/scripts/toolchain.host-clang.mak similarity index 100% rename from build/toolchain.host-clang.mak rename to scripts/toolchain.host-clang.mak diff --git a/build/toolchain.host-gcc.mak b/scripts/toolchain.host-gcc.mak similarity index 100% rename from build/toolchain.host-gcc.mak rename to scripts/toolchain.host-gcc.mak diff --git a/scripts/toolchain.mingw.mak b/scripts/toolchain.mingw.mak new file mode 100644 index 000000000..fb2d1b3c8 --- /dev/null +++ b/scripts/toolchain.mingw.mak @@ -0,0 +1,18 @@ +CC = gcc +CXX = g++ +LD = g++ +EXE = exe + +SFLAGS += -D_USE_MATH_DEFINES +LDFLAGS += -static -mwindows + +# Work around command-line length limit +# On Msys2 the max command line is 32 000 characters. Our standard LD command +# can be longer than that because we have quite a lot of object files. To work +# around this issue, we write the object list in a "target.objs" file, and tell +# the linker to read its arguments from this file. +$(BUILD_DIR)/%.$(EXE): + $(Q) echo $^ > $@.objs + @echo "LD $(@:$(BUILD_DIR)/%=%)" + $(Q) $(LD) @$@.objs $(LDFLAGS) -o $@ + $(Q) rm $@.objs