From 4af76cc3ae3798cfb3e85c4106ca1006262f32e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Quentin=20Guid=C3=A9e?= Date: Tue, 21 Jul 2020 22:32:19 +0200 Subject: [PATCH 01/48] [omega] 1.20.1 --- apps/settings/sub_menu/about_controller.cpp | 2 +- build/config.mak | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/settings/sub_menu/about_controller.cpp b/apps/settings/sub_menu/about_controller.cpp index fe64d7453..0028328cf 100644 --- a/apps/settings/sub_menu/about_controller.cpp +++ b/apps/settings/sub_menu/about_controller.cpp @@ -53,7 +53,7 @@ bool AboutController::handleEvent(Ion::Events::Event event) { if (childLabel == I18n::Message::OmegaVersion) { MessageTableCellWithBuffer * myCell = (MessageTableCellWithBuffer *)m_selectableTableView.selectedCell(); if (strcmp(myCell->accessoryText(), Ion::omegaVersion()) == 0) { - myCell->setAccessoryText("Dev"); //Change for public/dev + myCell->setAccessoryText("Public"); //Change for public/dev return true; } myCell->setAccessoryText(Ion::omegaVersion()); diff --git a/build/config.mak b/build/config.mak index a3850528c..3b938123a 100644 --- a/build/config.mak +++ b/build/config.mak @@ -5,7 +5,7 @@ DEBUG ?= 0 HOME_DISPLAY_EXTERNALS ?= 1 EPSILON_VERSION ?= 14.4.1 -OMEGA_VERSION ?= 1.20.0 +OMEGA_VERSION ?= 1.20.1 # USERNAME ?= N/A EPSILON_APPS ?= calculation rpn graph code statistics probability solver atom sequence regression settings external EPSILON_I18N ?= en fr nl pt it de es hu From 0235de80101785c6dedf0b7f4651584cfd56cbad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Quentin=20Guid=C3=A9e?= Date: Thu, 23 Jul 2020 18:23:30 +0200 Subject: [PATCH 02/48] [omega] 1.20.2 --- build/config.mak | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/config.mak b/build/config.mak index 3b938123a..4eec56a94 100644 --- a/build/config.mak +++ b/build/config.mak @@ -5,7 +5,7 @@ DEBUG ?= 0 HOME_DISPLAY_EXTERNALS ?= 1 EPSILON_VERSION ?= 14.4.1 -OMEGA_VERSION ?= 1.20.1 +OMEGA_VERSION ?= 1.20.2 # USERNAME ?= N/A EPSILON_APPS ?= calculation rpn graph code statistics probability solver atom sequence regression settings external EPSILON_I18N ?= en fr nl pt it de es hu From a0acdc171b44bbe5473bb84e9cfdc996469bd832 Mon Sep 17 00:00:00 2001 From: Gabriel Date: Fri, 13 Nov 2020 00:48:46 +0100 Subject: [PATCH 03/48] Squelette --- apps/reader/Makefile | 14 ++++++++++++++ apps/reader/app.cpp | 37 ++++++++++++++++++++++++++++++++++++ apps/reader/app.h | 28 +++++++++++++++++++++++++++ apps/reader/base.de.i18n | 2 ++ apps/reader/base.en.i18n | 2 ++ apps/reader/base.es.i18n | 2 ++ apps/reader/base.fr.i18n | 2 ++ apps/reader/base.hu.i18n | 2 ++ apps/reader/base.it.i18n | 2 ++ apps/reader/base.nl.i18n | 2 ++ apps/reader/base.pt.i18n | 2 ++ apps/reader/reader_icon.png | Bin 0 -> 1541 bytes build/config.mak | 2 +- 13 files changed, 96 insertions(+), 1 deletion(-) create mode 100644 apps/reader/Makefile create mode 100644 apps/reader/app.cpp create mode 100644 apps/reader/app.h create mode 100644 apps/reader/base.de.i18n create mode 100644 apps/reader/base.en.i18n create mode 100644 apps/reader/base.es.i18n create mode 100644 apps/reader/base.fr.i18n create mode 100644 apps/reader/base.hu.i18n create mode 100644 apps/reader/base.it.i18n create mode 100644 apps/reader/base.nl.i18n create mode 100644 apps/reader/base.pt.i18n create mode 100644 apps/reader/reader_icon.png diff --git a/apps/reader/Makefile b/apps/reader/Makefile new file mode 100644 index 000000000..cf57b4311 --- /dev/null +++ b/apps/reader/Makefile @@ -0,0 +1,14 @@ +apps += reader::App +app_headers += apps/reader/app.h + +app_sreader_src = $(addprefix apps/reader/,\ + app.cpp \ +) + +apps_src += $(app_sreader_src) + +app_images += apps/reader/reader_icon.png + +i18n_files += $(call i18n_without_universal_for,reader/base) + +$(eval $(call depends_on_image,apps/reader/app.cpp,apps/reader/reader_icon.png)) \ No newline at end of file diff --git a/apps/reader/app.cpp b/apps/reader/app.cpp new file mode 100644 index 000000000..8a848bbe8 --- /dev/null +++ b/apps/reader/app.cpp @@ -0,0 +1,37 @@ +#include "app.h" +#include "reader_icon.h" +#include "apps/apps_container.h" +#include "apps/i18n.h" + + +namespace reader { + +I18n::Message App::Descriptor::name() { + return I18n::Message::ReaderApp; +} + +I18n::Message App::Descriptor::upperName() { + return I18n::Message::ReaderAppCapital; +} + +const Image * App::Descriptor::icon() { + return ImageStore::ReaderIcon; +} + + +App * App::Snapshot::unpack(Container * container) { + return new (container->currentAppBuffer()) App(this); +} + +App::Descriptor * App::Snapshot::descriptor() { + static Descriptor descriptor; + return &descriptor; +} + + +App::App(Snapshot * snapshot) : + ::App(snapshot, nullptr) +{ +} + +} diff --git a/apps/reader/app.h b/apps/reader/app.h new file mode 100644 index 000000000..a46ec88b8 --- /dev/null +++ b/apps/reader/app.h @@ -0,0 +1,28 @@ +#ifndef READER_H +#define READER_H + +#include + +namespace reader { + +class App : public ::App { +public: + class Descriptor : public ::App::Descriptor { + public: + I18n::Message name() override; + I18n::Message upperName() override; + const Image * icon() override; + }; + class Snapshot : public ::App::Snapshot { + public: + App * unpack(Container * container) override; + Descriptor * descriptor() override; + }; +private: + App(Snapshot * snapshot); + +}; + +} + +#endif \ No newline at end of file diff --git a/apps/reader/base.de.i18n b/apps/reader/base.de.i18n new file mode 100644 index 000000000..ecc03a749 --- /dev/null +++ b/apps/reader/base.de.i18n @@ -0,0 +1,2 @@ +ReaderApp = "Reader" +ReaderAppCapital = "READER" \ No newline at end of file diff --git a/apps/reader/base.en.i18n b/apps/reader/base.en.i18n new file mode 100644 index 000000000..ecc03a749 --- /dev/null +++ b/apps/reader/base.en.i18n @@ -0,0 +1,2 @@ +ReaderApp = "Reader" +ReaderAppCapital = "READER" \ No newline at end of file diff --git a/apps/reader/base.es.i18n b/apps/reader/base.es.i18n new file mode 100644 index 000000000..ecc03a749 --- /dev/null +++ b/apps/reader/base.es.i18n @@ -0,0 +1,2 @@ +ReaderApp = "Reader" +ReaderAppCapital = "READER" \ No newline at end of file diff --git a/apps/reader/base.fr.i18n b/apps/reader/base.fr.i18n new file mode 100644 index 000000000..ecc03a749 --- /dev/null +++ b/apps/reader/base.fr.i18n @@ -0,0 +1,2 @@ +ReaderApp = "Reader" +ReaderAppCapital = "READER" \ No newline at end of file diff --git a/apps/reader/base.hu.i18n b/apps/reader/base.hu.i18n new file mode 100644 index 000000000..ecc03a749 --- /dev/null +++ b/apps/reader/base.hu.i18n @@ -0,0 +1,2 @@ +ReaderApp = "Reader" +ReaderAppCapital = "READER" \ No newline at end of file diff --git a/apps/reader/base.it.i18n b/apps/reader/base.it.i18n new file mode 100644 index 000000000..ecc03a749 --- /dev/null +++ b/apps/reader/base.it.i18n @@ -0,0 +1,2 @@ +ReaderApp = "Reader" +ReaderAppCapital = "READER" \ No newline at end of file diff --git a/apps/reader/base.nl.i18n b/apps/reader/base.nl.i18n new file mode 100644 index 000000000..ecc03a749 --- /dev/null +++ b/apps/reader/base.nl.i18n @@ -0,0 +1,2 @@ +ReaderApp = "Reader" +ReaderAppCapital = "READER" \ No newline at end of file diff --git a/apps/reader/base.pt.i18n b/apps/reader/base.pt.i18n new file mode 100644 index 000000000..ecc03a749 --- /dev/null +++ b/apps/reader/base.pt.i18n @@ -0,0 +1,2 @@ +ReaderApp = "Reader" +ReaderAppCapital = "READER" \ No newline at end of file diff --git a/apps/reader/reader_icon.png b/apps/reader/reader_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..78e23e1395151985a715f9ae71b8113af7fbd712 GIT binary patch literal 1541 zcmV+g2KxDlP)Px)zez+vRA>d=T1!kMWe~3J8JGvmvJ%2G5d(W5yus{%=mFGl@Bn0WqlSZr-4I{F z7%#Yq9+dUs#ej(rSeFEii4wyBE(8K-U@^dguqy%9fV>%y83P#@9)ndKhRlxBFT0!R z*-CnP{=e$4s;~e0-#-HZw7k4*VOh40VVIYYm4(mc01-VUUFggM^tCTBG4Tt0ANo7T z_$SuZ)~Ydm$7C{j5g>sCEZ{GqtN*8`r{MK^;pF6G|LEw*i2vV8NlE#^=j6HP8yg!R zU}l}wYEASp!|lDlzrTS1KTAqV`i6XG>F2D*J_=`Ggu`O7Bx1DP!isvG(hRjRgTdg8 zj*d1_Mj$mbG{EriaA0;w($qw*zjht2y?Ra1{@&i+G1i_HfwFbj#Cd+SQnm8 zZS2vXk3{W8Mn*cY_C7WuxaaXn7AldRo(?x}+!PYgl(+cTA~ZKQgUx1x>T2rqgb5=t z>G1IIIqG2w5!`vl608`8Eac|p24h`F-pFqwoSmwwD!|@Q)aDr;JBxArl%BhLLPAZ$ zXf#4;X{jhM5;(&X%k`F$N`y!V!wa^zw;>@R0kAih<%Lm}bbv4poY2+P1#Y)HfTLcL zY>@us0+|tq#w6?ZNHl{=n2WZ*F6@g1jON09QdN?>ZkZo8_fu^P=aJgJ? ze0S-L;^e{t zoZW5*9GGEnaF7d4sR$*7<<`~~bar-1)v3B1O}k=aV_|i56{z-#i;E@M5Cuo=3Rex# z($XSHq1Hk@Jw57ytEi}ezP>(gQK+@!V)}7H3}t0yVq)if25aS%=8jJq;j#xr!_ptL z7ss>Rmb>6}{o|haeK1Gb#6m)!xh=|~d&>BtF~bW`R#+NRp9VHT-o_}e zWfd5V)d0Bvz^h62dhP_5`loLBdx*RqC}1#F3mACL4rH(A$PYec3VhV8sNnQ#cXt;W z8ykT(i1a{ladA*vTgx3~N%2Pz+|<+*(4!YxUtfp$`FSWTER^JuAUNODth~G&Qd3hU zDbzxU_ABS+<{%>@1G2M)mpT6S1;P0QX`isDsK}SR(6`6qfsT$2z@u%bsi^_l0h3Cy zh61Td)s$OYTm+}n2{}1Avd#&}2i=2PSy|z}6@imWlERWtDSc6`JDkbMNzQysObnkJ zy!nd2wY9ZDZ*MQJay1!rd@Pp~fvc>n4J<7pN{%Bf{o$^P5{1?c_>%}D4*!|3Q}09P=%L)W7w za3=C1om}$K!omVr%oaYj=J|9@gQ&Z^8%jz_xKmaMM(Mha)tE2uF>sWL!$JhzGR61Hn55b2 rWy;qnIkDrQtGvBj8)KNq&IbMk=AgRv>wm2i00000NkvXXu0mjf!Ls{N literal 0 HcmV?d00001 diff --git a/build/config.mak b/build/config.mak index 3f104d92a..f5f93b0b3 100644 --- a/build/config.mak +++ b/build/config.mak @@ -7,7 +7,7 @@ HOME_DISPLAY_EXTERNALS ?= 1 EPSILON_VERSION ?= 14.4.1 OMEGA_VERSION ?= 1.20.3 # OMEGA_USERNAME ?= N/A -EPSILON_APPS ?= calculation rpn graph code statistics probability solver atom sequence regression settings external +EPSILON_APPS ?= reader calculation rpn graph code statistics probability solver atom sequence regression settings external EPSILON_I18N ?= en fr nl pt it de es hu EPSILON_GETOPT ?= 0 EPSILON_TELEMETRY ?= 0 From c37b4bd1f4f2aac3f22e02c360c432dbce22e271 Mon Sep 17 00:00:00 2001 From: Fournier Gabriel <> Date: Sat, 14 Nov 2020 13:39:11 +0100 Subject: [PATCH 04/48] ListBookController - partie 1 --- apps/reader/Makefile | 1 + apps/reader/app.cpp | 3 ++- apps/reader/app.h | 3 ++- apps/reader/list_book_controller.cpp | 37 ++++++++++++++++++++++++++++ apps/reader/list_book_controller.h | 25 +++++++++++++++++++ themes | 2 +- 6 files changed, 68 insertions(+), 3 deletions(-) create mode 100644 apps/reader/list_book_controller.cpp create mode 100644 apps/reader/list_book_controller.h diff --git a/apps/reader/Makefile b/apps/reader/Makefile index cf57b4311..a3ddd6cd8 100644 --- a/apps/reader/Makefile +++ b/apps/reader/Makefile @@ -3,6 +3,7 @@ app_headers += apps/reader/app.h app_sreader_src = $(addprefix apps/reader/,\ app.cpp \ + list_book_controller.cpp \ ) apps_src += $(app_sreader_src) diff --git a/apps/reader/app.cpp b/apps/reader/app.cpp index 8a848bbe8..68ca41115 100644 --- a/apps/reader/app.cpp +++ b/apps/reader/app.cpp @@ -30,7 +30,8 @@ App::Descriptor * App::Snapshot::descriptor() { App::App(Snapshot * snapshot) : - ::App(snapshot, nullptr) + ::App(snapshot, &m_listBookController), + m_listBookController(nullptr) { } diff --git a/apps/reader/app.h b/apps/reader/app.h index a46ec88b8..d2f4fbc1d 100644 --- a/apps/reader/app.h +++ b/apps/reader/app.h @@ -2,6 +2,7 @@ #define READER_H #include +#include "list_book_controller.h" namespace reader { @@ -20,7 +21,7 @@ public: }; private: App(Snapshot * snapshot); - + ListBookController m_listBookController; }; } diff --git a/apps/reader/list_book_controller.cpp b/apps/reader/list_book_controller.cpp new file mode 100644 index 000000000..67e5246bf --- /dev/null +++ b/apps/reader/list_book_controller.cpp @@ -0,0 +1,37 @@ +#include "list_book_controller.h" + +namespace reader +{ + +View* ListBookController::view() +{ + return &m_tableView; +} + +ListBookController::ListBookController(Responder * parentResponder): + ViewController(parentResponder), + m_tableView(this, this) +{ +} + +int ListBookController::numberOfRows() const +{ + return 0; +} + +KDCoordinate ListBookController::cellHeight() +{ + return 50; +} + +HighlightCell * ListBookController::reusableCell(int index) +{ + return nullptr; +} + +int ListBookController::reusableCellCount() const +{ + return 0; +} + +} \ No newline at end of file diff --git a/apps/reader/list_book_controller.h b/apps/reader/list_book_controller.h new file mode 100644 index 000000000..7e868fe76 --- /dev/null +++ b/apps/reader/list_book_controller.h @@ -0,0 +1,25 @@ +#ifndef __LIST_BOOK_CONTROLLER_H__ +#define __LIST_BOOK_CONTROLLER_H__ + +#include + +namespace reader +{ + +class ListBookController : public ViewController, public SimpleListViewDataSource, public ScrollViewDataSource +{ +public: + ListBookController(Responder * parentResponder); + View* view() override; + + int numberOfRows() const override; + KDCoordinate cellHeight() override; + HighlightCell * reusableCell(int index) override; + int reusableCellCount() const override; +private: + TableView m_tableView; +}; + +} + +#endif \ No newline at end of file diff --git a/themes b/themes index 48fc1cc72..20073ead0 160000 --- a/themes +++ b/themes @@ -1 +1 @@ -Subproject commit 48fc1cc72739ad766abbf161d78c2f98cf2f797d +Subproject commit 20073ead0928aa88bbc370de3d412c11cf8b30f5 From b4e9ceed18528443aa9e5f650cda48732df9a5ef Mon Sep 17 00:00:00 2001 From: Fournier Gabriel <> Date: Sun, 15 Nov 2020 18:55:34 +0100 Subject: [PATCH 05/48] ListBookController - partie 2 --- apps/reader/list_book_controller.cpp | 16 +++++++++++++--- apps/reader/list_book_controller.h | 9 +++++++++ 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/apps/reader/list_book_controller.cpp b/apps/reader/list_book_controller.cpp index 67e5246bf..d785f6e5b 100644 --- a/apps/reader/list_book_controller.cpp +++ b/apps/reader/list_book_controller.cpp @@ -12,11 +12,13 @@ ListBookController::ListBookController(Responder * parentResponder): ViewController(parentResponder), m_tableView(this, this) { + m_files[0].name= "Harry Potter 1.txt"; + m_nbFiles = 1; } int ListBookController::numberOfRows() const { - return 0; + return m_nbFiles; } KDCoordinate ListBookController::cellHeight() @@ -26,12 +28,20 @@ KDCoordinate ListBookController::cellHeight() HighlightCell * ListBookController::reusableCell(int index) { - return nullptr; + return &m_cells[index]; } int ListBookController::reusableCellCount() const { - return 0; + return NB_CELLS; +} + +void ListBookController::willDisplayCellForIndex(HighlightCell * cell, int index) +{ + MessageTableCell* myTextCell = static_cast(cell); + MessageTextView* textView = static_cast(myTextCell->labelView()); + textView->setText(m_files[index].name); + myTextCell->setMessageFont(KDFont::LargeFont); } } \ No newline at end of file diff --git a/apps/reader/list_book_controller.h b/apps/reader/list_book_controller.h index 7e868fe76..4c5050c98 100644 --- a/apps/reader/list_book_controller.h +++ b/apps/reader/list_book_controller.h @@ -2,6 +2,7 @@ #define __LIST_BOOK_CONTROLLER_H__ #include +#include namespace reader { @@ -16,8 +17,16 @@ public: KDCoordinate cellHeight() override; HighlightCell * reusableCell(int index) override; int reusableCellCount() const override; + void willDisplayCellForIndex(HighlightCell * cell, int index) override; private: TableView m_tableView; + + static const int NB_FILES = 20; + External::Archive::File m_files[NB_FILES]; + int m_nbFiles = 0; + + static const int NB_CELLS = 6; + MessageTableCell m_cells[NB_CELLS]; }; } From dd258a79753bac031d5223eaf4e8776a4cc05159 Mon Sep 17 00:00:00 2001 From: Fournier Gabriel <> Date: Mon, 16 Nov 2020 23:37:45 +0100 Subject: [PATCH 06/48] Lister des fichiers --- apps/reader/Makefile | 1 + apps/reader/list_book_controller.cpp | 16 +++++++++++++--- apps/reader/utility.cpp | 27 +++++++++++++++++++++++++++ apps/reader/utility.h | 10 ++++++++++ 4 files changed, 51 insertions(+), 3 deletions(-) create mode 100644 apps/reader/utility.cpp create mode 100644 apps/reader/utility.h diff --git a/apps/reader/Makefile b/apps/reader/Makefile index a3ddd6cd8..0d5267e5f 100644 --- a/apps/reader/Makefile +++ b/apps/reader/Makefile @@ -4,6 +4,7 @@ app_headers += apps/reader/app.h app_sreader_src = $(addprefix apps/reader/,\ app.cpp \ list_book_controller.cpp \ + utility.cpp \ ) apps_src += $(app_sreader_src) diff --git a/apps/reader/list_book_controller.cpp b/apps/reader/list_book_controller.cpp index d785f6e5b..150d46bff 100644 --- a/apps/reader/list_book_controller.cpp +++ b/apps/reader/list_book_controller.cpp @@ -1,5 +1,5 @@ #include "list_book_controller.h" - +#include "utility.h" namespace reader { @@ -12,8 +12,18 @@ ListBookController::ListBookController(Responder * parentResponder): ViewController(parentResponder), m_tableView(this, this) { - m_files[0].name= "Harry Potter 1.txt"; - m_nbFiles = 1; + size_t nbTotalFiles = External::Archive::numberOfFiles(); + + for(size_t i=0; i < nbTotalFiles; ++i) + { + External::Archive::File file; + External::Archive::fileAtIndex(i, file); + if(stringEndsWith(file.name, ".txt")) + { + m_files[m_nbFiles] = file; + m_nbFiles++; + } + } } int ListBookController::numberOfRows() const diff --git a/apps/reader/utility.cpp b/apps/reader/utility.cpp new file mode 100644 index 000000000..40eceaea7 --- /dev/null +++ b/apps/reader/utility.cpp @@ -0,0 +1,27 @@ +#include "utility.h" +#include + +namespace reader +{ + +bool stringEndsWith(const char* str, const char* pattern) +{ + int strLength = strlen(str); + int patternLength = strlen(pattern); + if (patternLength > strLength) + return false; + + const char* strIter = str + strlen(str); + const char* patternIter = pattern + strlen(pattern); + + while(*strIter == *patternIter) + { + if(patternIter == pattern) + return true; + strIter--; + patternIter--; + } + return false; +} + +} \ No newline at end of file diff --git a/apps/reader/utility.h b/apps/reader/utility.h new file mode 100644 index 000000000..b4d417618 --- /dev/null +++ b/apps/reader/utility.h @@ -0,0 +1,10 @@ +#ifndef __UTILITY_H__ +#define __UTILITY_H__ + +namespace reader +{ + +bool stringEndsWith(const char* str, const char* end); + +} +#endif \ No newline at end of file From aacd1a6f32c673e9b5cb97608f6c26ba02708e6d Mon Sep 17 00:00:00 2001 From: Fournier Gabriel <> Date: Mon, 16 Nov 2020 23:37:45 +0100 Subject: [PATCH 07/48] Lister des fichiers --- apps/reader/Makefile | 1 + apps/reader/list_book_controller.cpp | 18 +++++++++++++++--- apps/reader/utility.cpp | 27 +++++++++++++++++++++++++++ apps/reader/utility.h | 10 ++++++++++ 4 files changed, 53 insertions(+), 3 deletions(-) create mode 100644 apps/reader/utility.cpp create mode 100644 apps/reader/utility.h diff --git a/apps/reader/Makefile b/apps/reader/Makefile index a3ddd6cd8..0d5267e5f 100644 --- a/apps/reader/Makefile +++ b/apps/reader/Makefile @@ -4,6 +4,7 @@ app_headers += apps/reader/app.h app_sreader_src = $(addprefix apps/reader/,\ app.cpp \ list_book_controller.cpp \ + utility.cpp \ ) apps_src += $(app_sreader_src) diff --git a/apps/reader/list_book_controller.cpp b/apps/reader/list_book_controller.cpp index d785f6e5b..4818df4f3 100644 --- a/apps/reader/list_book_controller.cpp +++ b/apps/reader/list_book_controller.cpp @@ -1,5 +1,5 @@ #include "list_book_controller.h" - +#include "utility.h" namespace reader { @@ -12,8 +12,20 @@ ListBookController::ListBookController(Responder * parentResponder): ViewController(parentResponder), m_tableView(this, this) { - m_files[0].name= "Harry Potter 1.txt"; - m_nbFiles = 1; + size_t nbTotalFiles = External::Archive::numberOfFiles(); + + for(size_t i=0; i < nbTotalFiles; ++i) + { + External::Archive::File file; + External::Archive::fileAtIndex(i, file); + if(stringEndsWith(file.name, ".txt")) + { + m_files[m_nbFiles] = file; + m_nbFiles++; + if(m_nbFiles == NB_FILES) + break; + } + } } int ListBookController::numberOfRows() const diff --git a/apps/reader/utility.cpp b/apps/reader/utility.cpp new file mode 100644 index 000000000..40eceaea7 --- /dev/null +++ b/apps/reader/utility.cpp @@ -0,0 +1,27 @@ +#include "utility.h" +#include + +namespace reader +{ + +bool stringEndsWith(const char* str, const char* pattern) +{ + int strLength = strlen(str); + int patternLength = strlen(pattern); + if (patternLength > strLength) + return false; + + const char* strIter = str + strlen(str); + const char* patternIter = pattern + strlen(pattern); + + while(*strIter == *patternIter) + { + if(patternIter == pattern) + return true; + strIter--; + patternIter--; + } + return false; +} + +} \ No newline at end of file diff --git a/apps/reader/utility.h b/apps/reader/utility.h new file mode 100644 index 000000000..b4d417618 --- /dev/null +++ b/apps/reader/utility.h @@ -0,0 +1,10 @@ +#ifndef __UTILITY_H__ +#define __UTILITY_H__ + +namespace reader +{ + +bool stringEndsWith(const char* str, const char* end); + +} +#endif \ No newline at end of file From 76beed20da3e12ae26fe7503d045513691369df9 Mon Sep 17 00:00:00 2001 From: Fournier Gabriel <> Date: Tue, 17 Nov 2020 00:04:15 +0100 Subject: [PATCH 08/48] fix nb_Files limit --- apps/reader/list_book_controller.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/reader/list_book_controller.cpp b/apps/reader/list_book_controller.cpp index 150d46bff..4818df4f3 100644 --- a/apps/reader/list_book_controller.cpp +++ b/apps/reader/list_book_controller.cpp @@ -22,6 +22,8 @@ ListBookController::ListBookController(Responder * parentResponder): { m_files[m_nbFiles] = file; m_nbFiles++; + if(m_nbFiles == NB_FILES) + break; } } } From 5b21c57e4c412eb7d73353898746752206e3f606 Mon Sep 17 00:00:00 2001 From: Fournier Gabriel <> Date: Tue, 17 Nov 2020 22:44:35 +0100 Subject: [PATCH 09/48] list files on simulator --- apps/reader/list_book_controller.cpp | 15 +------- apps/reader/utility.cpp | 56 +++++++++++++++++++++++++++- apps/reader/utility.h | 3 ++ 3 files changed, 59 insertions(+), 15 deletions(-) diff --git a/apps/reader/list_book_controller.cpp b/apps/reader/list_book_controller.cpp index 4818df4f3..bbc327f16 100644 --- a/apps/reader/list_book_controller.cpp +++ b/apps/reader/list_book_controller.cpp @@ -12,20 +12,7 @@ ListBookController::ListBookController(Responder * parentResponder): ViewController(parentResponder), m_tableView(this, this) { - size_t nbTotalFiles = External::Archive::numberOfFiles(); - - for(size_t i=0; i < nbTotalFiles; ++i) - { - External::Archive::File file; - External::Archive::fileAtIndex(i, file); - if(stringEndsWith(file.name, ".txt")) - { - m_files[m_nbFiles] = file; - m_nbFiles++; - if(m_nbFiles == NB_FILES) - break; - } - } + m_nbFiles = filesWithExtension(".txt", m_files, NB_FILES); } int ListBookController::numberOfRows() const diff --git a/apps/reader/utility.cpp b/apps/reader/utility.cpp index 40eceaea7..7a9779909 100644 --- a/apps/reader/utility.cpp +++ b/apps/reader/utility.cpp @@ -1,6 +1,13 @@ #include "utility.h" + #include +#ifndef DEVICE +#include +#include +#include +#endif + namespace reader { @@ -10,7 +17,7 @@ bool stringEndsWith(const char* str, const char* pattern) int patternLength = strlen(pattern); if (patternLength > strLength) return false; - + const char* strIter = str + strlen(str); const char* patternIter = pattern + strlen(pattern); @@ -24,4 +31,51 @@ bool stringEndsWith(const char* str, const char* pattern) return false; } + +#ifdef DEVICE + +int filesWithExtension(const char* extension, External::Archive::File* files, int filesSize) +{ + size_t nbTotalFiles = External::Archive::numberOfFiles(); + int nbFiles = 0; + for(size_t i=0; i < nbTotalFiles; ++i) + { + External::Archive::File file; + External::Archive::fileAtIndex(i, file); + if(stringEndsWith(file.name, ".txt")) + { + files[nbFiles] = file; + nbFiles++; + if(nbFiles == filesSize) + break; + } + } + return nbFiles; +} +#else + +int filesWithExtension(const char* extension, External::Archive::File* files, int filesSize) +{ + dirent *file; + DIR *d = opendir("."); + int nb = 0; + if (d) + { + while ((file = readdir(d)) != NULL) + { + if(stringEndsWith(dir->d_name, extension)) + { + files[nb].name = strdup(file->d_name);//will probably leak + nb++; + if(nb == filesSize) + break; + } + } + closedir(d); + } + return nb; +} + +#endif + } \ No newline at end of file diff --git a/apps/reader/utility.h b/apps/reader/utility.h index b4d417618..9cd455008 100644 --- a/apps/reader/utility.h +++ b/apps/reader/utility.h @@ -1,10 +1,13 @@ #ifndef __UTILITY_H__ #define __UTILITY_H__ +#include + namespace reader { bool stringEndsWith(const char* str, const char* end); +int filesWithExtension(const char* extension, External::Archive::File* files, int filesSize) ; } #endif \ No newline at end of file From 3e2b5178edf62861b1bf0e84ea21e87d52400198 Mon Sep 17 00:00:00 2001 From: Fournier Gabriel <> Date: Sat, 21 Nov 2020 19:29:46 +0100 Subject: [PATCH 10/48] word wrap - part 1 and 2 --- apps/reader/Makefile | 2 + apps/reader/app.cpp | 5 +- apps/reader/app.h | 1 + apps/reader/base.de.i18n | 3 +- apps/reader/base.en.i18n | 3 +- apps/reader/base.es.i18n | 3 +- apps/reader/base.fr.i18n | 3 +- apps/reader/base.hu.i18n | 3 +- apps/reader/base.it.i18n | 3 +- apps/reader/base.nl.i18n | 3 +- apps/reader/base.pt.i18n | 3 +- apps/reader/list_book_controller.cpp | 31 ++++++++++++- apps/reader/list_book_controller.h | 10 +++- apps/reader/normalize.py | 18 ++++++++ apps/reader/read_book_controller.cpp | 21 +++++++++ apps/reader/read_book_controller.h | 23 ++++++++++ apps/reader/utility.cpp | 45 +++++++++++++++++- apps/reader/utility.h | 1 + apps/reader/word_wrap_view.cpp | 68 ++++++++++++++++++++++++++++ apps/reader/word_wrap_view.h | 20 ++++++++ 20 files changed, 254 insertions(+), 15 deletions(-) create mode 100644 apps/reader/normalize.py create mode 100644 apps/reader/read_book_controller.cpp create mode 100644 apps/reader/read_book_controller.h create mode 100644 apps/reader/word_wrap_view.cpp create mode 100644 apps/reader/word_wrap_view.h diff --git a/apps/reader/Makefile b/apps/reader/Makefile index 0d5267e5f..71adec9e0 100644 --- a/apps/reader/Makefile +++ b/apps/reader/Makefile @@ -5,6 +5,8 @@ app_sreader_src = $(addprefix apps/reader/,\ app.cpp \ list_book_controller.cpp \ utility.cpp \ + read_book_controller \ + word_wrap_view.cpp \ ) apps_src += $(app_sreader_src) diff --git a/apps/reader/app.cpp b/apps/reader/app.cpp index 68ca41115..18ae33da0 100644 --- a/apps/reader/app.cpp +++ b/apps/reader/app.cpp @@ -30,8 +30,9 @@ App::Descriptor * App::Snapshot::descriptor() { App::App(Snapshot * snapshot) : - ::App(snapshot, &m_listBookController), - m_listBookController(nullptr) + ::App(snapshot, &m_stackViewController), + m_listBookController(&m_stackViewController), + m_stackViewController(nullptr, &m_listBookController) { } diff --git a/apps/reader/app.h b/apps/reader/app.h index d2f4fbc1d..1204862c4 100644 --- a/apps/reader/app.h +++ b/apps/reader/app.h @@ -22,6 +22,7 @@ public: private: App(Snapshot * snapshot); ListBookController m_listBookController; + StackViewController m_stackViewController; }; } diff --git a/apps/reader/base.de.i18n b/apps/reader/base.de.i18n index ecc03a749..68a64986d 100644 --- a/apps/reader/base.de.i18n +++ b/apps/reader/base.de.i18n @@ -1,2 +1,3 @@ ReaderApp = "Reader" -ReaderAppCapital = "READER" \ No newline at end of file +ReaderAppCapital = "READER" +NoFileToDisplay = "Keine Dateien zum Anzeigen!" \ No newline at end of file diff --git a/apps/reader/base.en.i18n b/apps/reader/base.en.i18n index ecc03a749..0101ebe2a 100644 --- a/apps/reader/base.en.i18n +++ b/apps/reader/base.en.i18n @@ -1,2 +1,3 @@ ReaderApp = "Reader" -ReaderAppCapital = "READER" \ No newline at end of file +ReaderAppCapital = "READER" +NoFileToDisplay = "No file to display!" \ No newline at end of file diff --git a/apps/reader/base.es.i18n b/apps/reader/base.es.i18n index ecc03a749..51be1eaf4 100644 --- a/apps/reader/base.es.i18n +++ b/apps/reader/base.es.i18n @@ -1,2 +1,3 @@ ReaderApp = "Reader" -ReaderAppCapital = "READER" \ No newline at end of file +ReaderAppCapital = "READER" +NoFileToDisplay ="No hay archivos para mostrar!" \ No newline at end of file diff --git a/apps/reader/base.fr.i18n b/apps/reader/base.fr.i18n index ecc03a749..220c36fa1 100644 --- a/apps/reader/base.fr.i18n +++ b/apps/reader/base.fr.i18n @@ -1,2 +1,3 @@ ReaderApp = "Reader" -ReaderAppCapital = "READER" \ No newline at end of file +ReaderAppCapital = "READER" +NoFileToDisplay = "Aucun fichier à afficher!" \ No newline at end of file diff --git a/apps/reader/base.hu.i18n b/apps/reader/base.hu.i18n index ecc03a749..264d6e878 100644 --- a/apps/reader/base.hu.i18n +++ b/apps/reader/base.hu.i18n @@ -1,2 +1,3 @@ ReaderApp = "Reader" -ReaderAppCapital = "READER" \ No newline at end of file +ReaderAppCapital = "READER" +NoFileToDisplay ="Nincs megjeleníthető fájl" \ No newline at end of file diff --git a/apps/reader/base.it.i18n b/apps/reader/base.it.i18n index ecc03a749..4464ff077 100644 --- a/apps/reader/base.it.i18n +++ b/apps/reader/base.it.i18n @@ -1,2 +1,3 @@ ReaderApp = "Reader" -ReaderAppCapital = "READER" \ No newline at end of file +ReaderAppCapital = "READER" +NoFileToDisplay ="essun file da visualizzare" \ No newline at end of file diff --git a/apps/reader/base.nl.i18n b/apps/reader/base.nl.i18n index ecc03a749..55693de3e 100644 --- a/apps/reader/base.nl.i18n +++ b/apps/reader/base.nl.i18n @@ -1,2 +1,3 @@ ReaderApp = "Reader" -ReaderAppCapital = "READER" \ No newline at end of file +ReaderAppCapital = "READER" +NoFileToDisplay ="Geen bestanden om weer te geven" \ No newline at end of file diff --git a/apps/reader/base.pt.i18n b/apps/reader/base.pt.i18n index ecc03a749..886d4930c 100644 --- a/apps/reader/base.pt.i18n +++ b/apps/reader/base.pt.i18n @@ -1,2 +1,3 @@ ReaderApp = "Reader" -ReaderAppCapital = "READER" \ No newline at end of file +ReaderAppCapital = "READER" +NoFileToDisplay ="Nenhum arquivo para exibir" \ No newline at end of file diff --git a/apps/reader/list_book_controller.cpp b/apps/reader/list_book_controller.cpp index bbc327f16..a963ad0ba 100644 --- a/apps/reader/list_book_controller.cpp +++ b/apps/reader/list_book_controller.cpp @@ -1,5 +1,7 @@ #include "list_book_controller.h" #include "utility.h" +#include "apps/i18n.h" + namespace reader { @@ -10,7 +12,8 @@ View* ListBookController::view() ListBookController::ListBookController(Responder * parentResponder): ViewController(parentResponder), - m_tableView(this, this) + m_tableView(this, this, this), + m_readBookController(this) { m_nbFiles = filesWithExtension(".txt", m_files, NB_FILES); } @@ -43,4 +46,30 @@ void ListBookController::willDisplayCellForIndex(HighlightCell * cell, int index myTextCell->setMessageFont(KDFont::LargeFont); } +void ListBookController::didBecomeFirstResponder() +{ + if (selectedRow() < 0) { + selectCellAtLocation(0, 0); + } + Container::activeApp()->setFirstResponder(&m_tableView); + if(m_nbFiles == 0) + { + Container::activeApp()->displayWarning(I18n::Message::NoFileToDisplay); + } +} + +bool ListBookController::handleEvent(Ion::Events::Event event) +{ + if (event == Ion::Events::OK || event == Ion::Events::EXE || event == Ion::Events::Right) + { + + m_readBookController.setBook(m_files[selectedRow()]); + static_cast(parentResponder())->push(&m_readBookController); + Container::activeApp()->setFirstResponder(&m_readBookController); + return true; + } + + return false; +} + } \ No newline at end of file diff --git a/apps/reader/list_book_controller.h b/apps/reader/list_book_controller.h index 4c5050c98..9fdd11262 100644 --- a/apps/reader/list_book_controller.h +++ b/apps/reader/list_book_controller.h @@ -4,10 +4,12 @@ #include #include +#include "read_book_controller.h" + namespace reader { -class ListBookController : public ViewController, public SimpleListViewDataSource, public ScrollViewDataSource +class ListBookController : public ViewController, public SimpleListViewDataSource, public SelectableTableViewDataSource { public: ListBookController(Responder * parentResponder); @@ -18,8 +20,10 @@ public: HighlightCell * reusableCell(int index) override; int reusableCellCount() const override; void willDisplayCellForIndex(HighlightCell * cell, int index) override; + void didBecomeFirstResponder() override; + bool handleEvent(Ion::Events::Event event) override; private: - TableView m_tableView; + SelectableTableView m_tableView; static const int NB_FILES = 20; External::Archive::File m_files[NB_FILES]; @@ -27,6 +31,8 @@ private: static const int NB_CELLS = 6; MessageTableCell m_cells[NB_CELLS]; + + ReadBookController m_readBookController; }; } diff --git a/apps/reader/normalize.py b/apps/reader/normalize.py new file mode 100644 index 000000000..fff638b33 --- /dev/null +++ b/apps/reader/normalize.py @@ -0,0 +1,18 @@ +import sys +import unicodedata +import argparse +import io +import shutil + +filename = sys.argv[1] + +print("Normalization of "+filename) + +output = open(filename+".tmp", "wb") + +with io.open(filename, "r", encoding='utf-8') as file: + for line in file: + unicodeLine = unicodedata.normalize("NFKD", line) + output.write(unicodeLine.encode("UTF-8")) +output.close() +shutil.move(filename+".tmp",filename) diff --git a/apps/reader/read_book_controller.cpp b/apps/reader/read_book_controller.cpp new file mode 100644 index 000000000..75288029f --- /dev/null +++ b/apps/reader/read_book_controller.cpp @@ -0,0 +1,21 @@ +#include "read_book_controller.h" + +namespace reader +{ + +ReadBookController::ReadBookController(Responder * parentResponder) : + ViewController(parentResponder) +{ +} + +View * ReadBookController::view() +{ + return &m_readerView; +} + +void ReadBookController::setBook(const External::Archive::File& file) +{ + m_readerView.setText(reinterpret_cast(file.data)); +} + +} \ No newline at end of file diff --git a/apps/reader/read_book_controller.h b/apps/reader/read_book_controller.h new file mode 100644 index 000000000..3c734f1a4 --- /dev/null +++ b/apps/reader/read_book_controller.h @@ -0,0 +1,23 @@ +#ifndef _READ_BOOK_CONTROLLER_H_ +#define _READ_BOOK_CONTROLLER_H_ + +#include +#include "apps/external/archive.h" +#include "word_wrap_view.h" + +namespace reader { + +class ReadBookController : public ViewController { +public: + ReadBookController(Responder * parentResponder); + View * view() override; + + void setBook(const External::Archive::File& file); + +private: + WordWrapTextView m_readerView; +}; + +} + +#endif \ No newline at end of file diff --git a/apps/reader/utility.cpp b/apps/reader/utility.cpp index 7a9779909..2f82b61f3 100644 --- a/apps/reader/utility.cpp +++ b/apps/reader/utility.cpp @@ -31,6 +31,19 @@ bool stringEndsWith(const char* str, const char* pattern) return false; } +void stringNCopy(char* dest, int max, const char* src, int len) +{ + while(len>0 && max >1 && *src) + { + *dest = *src; + dest++; + src++; + len--; + max--; + } + *dest=0; +} + #ifdef DEVICE @@ -54,6 +67,34 @@ int filesWithExtension(const char* extension, External::Archive::File* files, in } #else +static void fillFileData(External::Archive::File& file) +{ + file.data = nullptr; + file.dataLength = 0; + + struct stat info; + if (stat(file.name, &info) != 0) + { + return; + } + + unsigned char* content = new unsigned char[info.st_size]; + if (content == NULL) + { + return ; + } + FILE *fp = fopen(file.name, "rb"); + if (fp == NULL) + { + return ; + } + + fread(content, info.st_size, 1, fp); + fclose(fp); + file.data = content; + file.dataLength = info.st_size; +} + int filesWithExtension(const char* extension, External::Archive::File* files, int filesSize) { dirent *file; @@ -63,9 +104,10 @@ int filesWithExtension(const char* extension, External::Archive::File* files, in { while ((file = readdir(d)) != NULL) { - if(stringEndsWith(dir->d_name, extension)) + if(stringEndsWith(file->d_name, extension)) { files[nb].name = strdup(file->d_name);//will probably leak + fillFileData(files[nb]); nb++; if(nb == filesSize) break; @@ -75,7 +117,6 @@ int filesWithExtension(const char* extension, External::Archive::File* files, in } return nb; } - #endif } \ No newline at end of file diff --git a/apps/reader/utility.h b/apps/reader/utility.h index 9cd455008..6893ab95c 100644 --- a/apps/reader/utility.h +++ b/apps/reader/utility.h @@ -8,6 +8,7 @@ namespace reader bool stringEndsWith(const char* str, const char* end); int filesWithExtension(const char* extension, External::Archive::File* files, int filesSize) ; +void stringNCopy(char* dest, int max, const char* src, int len); } #endif \ No newline at end of file diff --git a/apps/reader/word_wrap_view.cpp b/apps/reader/word_wrap_view.cpp new file mode 100644 index 000000000..f0f673a1a --- /dev/null +++ b/apps/reader/word_wrap_view.cpp @@ -0,0 +1,68 @@ +#include "word_wrap_view.h" + +#include "utility.h" + +#include + +namespace reader +{ +void WordWrapTextView::drawRect(KDContext * ctx, KDRect rect) const +{ + ctx->fillRect(KDRect(0, 0, bounds().width(), bounds().height()), m_backgroundColor); + + + const char * current = text(); + const char * startOfWord = current; + const char * endOfWord = UTF8Helper::EndOfWord(startOfWord); + KDPoint textPosition(0, 0); + + const int wordMaxLength = 128; + char word[wordMaxLength]; + + const int spaceWidth = m_font->stringSize(" ").width(); + + while(*startOfWord != 0) + { + + KDSize textSize = m_font->stringSizeUntil(startOfWord, endOfWord); + KDPoint nextTextPosition = KDPoint(textPosition.x()+textSize.width(), textPosition.y()); + + if(nextTextPosition.x() > m_frame.width()) + { + textPosition = KDPoint(0, textPosition.y() + textSize.height()); + nextTextPosition = KDPoint(textSize.width(), textPosition.y()); + } + + stringNCopy(word, wordMaxLength, startOfWord, endOfWord-startOfWord); + ctx->drawString(word, textPosition, m_font, m_textColor, m_backgroundColor); + + while(*endOfWord == ' ') + { + nextTextPosition = KDPoint(nextTextPosition.x() + spaceWidth, nextTextPosition.y()); + ++endOfWord; + } + if(nextTextPosition.x() > m_frame.width()) + { + nextTextPosition = KDPoint(0, nextTextPosition.y() + textSize.height()); + } + + while(*endOfWord == '\n') + { + nextTextPosition = KDPoint(0, nextTextPosition.y() + textSize.height()); + ++endOfWord; + } + + if(nextTextPosition.y() + textSize.height() > m_frame.height()) + { + break; + } + + textPosition = nextTextPosition; + startOfWord = endOfWord; + endOfWord = UTF8Helper::EndOfWord(startOfWord); + + std::cout< + +namespace reader +{ + +class WordWrapTextView : public PointerTextView { +public: + + void drawRect(KDContext * ctx, KDRect rect) const override; + +protected: + +}; + +} + +#endif \ No newline at end of file From 92ee632ce20a4838dd9d27bfaaeca755055964b8 Mon Sep 17 00:00:00 2001 From: Fournier Gabriel <> Date: Sat, 5 Dec 2020 22:24:13 +0100 Subject: [PATCH 11/48] word wrapping --- apps/reader/read_book_controller.cpp | 17 +++- apps/reader/read_book_controller.h | 2 +- apps/reader/word_wrap_view.cpp | 111 +++++++++++++++++++++++---- apps/reader/word_wrap_view.h | 14 +++- 4 files changed, 123 insertions(+), 21 deletions(-) diff --git a/apps/reader/read_book_controller.cpp b/apps/reader/read_book_controller.cpp index 75288029f..522491bfb 100644 --- a/apps/reader/read_book_controller.cpp +++ b/apps/reader/read_book_controller.cpp @@ -15,7 +15,22 @@ View * ReadBookController::view() void ReadBookController::setBook(const External::Archive::File& file) { - m_readerView.setText(reinterpret_cast(file.data)); + m_readerView.setText(reinterpret_cast(file.data), file.dataLength); +} + +bool ReadBookController::handleEvent(Ion::Events::Event event) +{ + if(event == Ion::Events::Down) + { + m_readerView.nextPage(); + return true; + } + if(event == Ion::Events::Up) + { + m_readerView.previousPage(); + return true; + } + return false; } } \ No newline at end of file diff --git a/apps/reader/read_book_controller.h b/apps/reader/read_book_controller.h index 3c734f1a4..d83be7f6e 100644 --- a/apps/reader/read_book_controller.h +++ b/apps/reader/read_book_controller.h @@ -13,7 +13,7 @@ public: View * view() override; void setBook(const External::Archive::File& file); - + bool handleEvent(Ion::Events::Event event) override; private: WordWrapTextView m_readerView; }; diff --git a/apps/reader/word_wrap_view.cpp b/apps/reader/word_wrap_view.cpp index f0f673a1a..9f4ed284f 100644 --- a/apps/reader/word_wrap_view.cpp +++ b/apps/reader/word_wrap_view.cpp @@ -6,33 +6,115 @@ namespace reader { + +void WordWrapTextView::nextPage() +{ + if(m_nextPageOffset >= m_length) + return; + m_pageOffset = m_nextPageOffset; + markRectAsDirty(bounds()); +} + +void WordWrapTextView::setText(const char* text, int length) +{ + PointerTextView::setText(text); + m_length = length; +} + +void WordWrapTextView::previousPage() +{ + if(m_pageOffset <= 0) + return; + + const int spaceWidth = m_font->stringSize(" ").width(); + + const char * endOfWord = text() + m_pageOffset; + const char * startOfWord = UTF8Helper::BeginningOfWord(text(), endOfWord); + + KDPoint textPosition(m_frame.width() - margin, m_frame.height() - margin); + + while(startOfWord>=text()) + { + endOfWord = UTF8Helper::EndOfWord(startOfWord); + KDSize textSize = m_font->stringSizeUntil(startOfWord, endOfWord); + KDPoint previousTextPosition = KDPoint(textPosition.x()-textSize.width(), textPosition.y()); + + if(previousTextPosition.x() < margin) + { + std::cout<<"|||\n"<= text() && *startOfWord == ' ') + { + previousTextPosition = KDPoint(previousTextPosition.x() - spaceWidth, previousTextPosition.y()); + --startOfWord; + } + if(previousTextPosition.x() < margin) + { + previousTextPosition = KDPoint(m_frame.width() - margin, previousTextPosition.y() - textSize.height()); + } + + + while(startOfWord >= text() && *startOfWord == '\n') + { + previousTextPosition = KDPoint(m_frame.width() - margin, previousTextPosition.y() - textSize.height()); + --startOfWord; + } + + if(previousTextPosition.y() - textSize.height() < margin) + { + break; + } + + textPosition = previousTextPosition; + endOfWord = startOfWord; + startOfWord = UTF8Helper::BeginningOfWord(text(), endOfWord); + } + if(startOfWord == text()) + m_pageOffset = 0; + else + m_pageOffset = UTF8Helper::EndOfWord(startOfWord) - text() + 1; + markRectAsDirty(bounds()); +} + void WordWrapTextView::drawRect(KDContext * ctx, KDRect rect) const { ctx->fillRect(KDRect(0, 0, bounds().width(), bounds().height()), m_backgroundColor); - - const char * current = text(); - const char * startOfWord = current; + const char * endOfFile = text() + m_length; + const char * startOfWord = text() + m_pageOffset; const char * endOfWord = UTF8Helper::EndOfWord(startOfWord); - KDPoint textPosition(0, 0); + KDPoint textPosition(margin, margin); const int wordMaxLength = 128; char word[wordMaxLength]; const int spaceWidth = m_font->stringSize(" ").width(); - while(*startOfWord != 0) + while(startOfWord < endOfFile) { KDSize textSize = m_font->stringSizeUntil(startOfWord, endOfWord); KDPoint nextTextPosition = KDPoint(textPosition.x()+textSize.width(), textPosition.y()); - if(nextTextPosition.x() > m_frame.width()) + if(nextTextPosition.x() > m_frame.width() - margin) { - textPosition = KDPoint(0, textPosition.y() + textSize.height()); - nextTextPosition = KDPoint(textSize.width(), textPosition.y()); + textPosition = KDPoint(margin, textPosition.y() + textSize.height()); + nextTextPosition = KDPoint(margin + textSize.width(), textPosition.y()); } - + if(textPosition.y() + textSize.height() > m_frame.height() - margin) + { + break; + } + stringNCopy(word, wordMaxLength, startOfWord, endOfWord-startOfWord); ctx->drawString(word, textPosition, m_font, m_textColor, m_backgroundColor); @@ -41,18 +123,18 @@ void WordWrapTextView::drawRect(KDContext * ctx, KDRect rect) const nextTextPosition = KDPoint(nextTextPosition.x() + spaceWidth, nextTextPosition.y()); ++endOfWord; } - if(nextTextPosition.x() > m_frame.width()) + if(nextTextPosition.x() > m_frame.width() - margin) { - nextTextPosition = KDPoint(0, nextTextPosition.y() + textSize.height()); + nextTextPosition = KDPoint(margin, nextTextPosition.y() + textSize.height()); } while(*endOfWord == '\n') { - nextTextPosition = KDPoint(0, nextTextPosition.y() + textSize.height()); + nextTextPosition = KDPoint(margin, nextTextPosition.y() + textSize.height()); ++endOfWord; } - if(nextTextPosition.y() + textSize.height() > m_frame.height()) + if(nextTextPosition.y() + textSize.height() > m_frame.height() - margin) { break; } @@ -60,9 +142,8 @@ void WordWrapTextView::drawRect(KDContext * ctx, KDRect rect) const textPosition = nextTextPosition; startOfWord = endOfWord; endOfWord = UTF8Helper::EndOfWord(startOfWord); - - std::cout< Date: Sun, 13 Dec 2020 21:55:03 +0100 Subject: [PATCH 12/48] saving --- apps/reader/list_book_controller.cpp | 27 +++++++++++++++++++++++ apps/reader/list_book_controller.h | 2 ++ apps/reader/read_book_controller.cpp | 33 ++++++++++++++++++++++++++++ apps/reader/read_book_controller.h | 4 ++++ apps/reader/word_wrap_view.cpp | 10 +++++++++ apps/reader/word_wrap_view.h | 3 ++- 6 files changed, 78 insertions(+), 1 deletion(-) diff --git a/apps/reader/list_book_controller.cpp b/apps/reader/list_book_controller.cpp index a963ad0ba..d2635129c 100644 --- a/apps/reader/list_book_controller.cpp +++ b/apps/reader/list_book_controller.cpp @@ -16,6 +16,7 @@ ListBookController::ListBookController(Responder * parentResponder): m_readBookController(this) { m_nbFiles = filesWithExtension(".txt", m_files, NB_FILES); + cleanRemovedBookRecord(); } int ListBookController::numberOfRows() const @@ -72,4 +73,30 @@ bool ListBookController::handleEvent(Ion::Events::Event event) return false; } + +bool ListBookController::hasBook(const char* filename) const +{ + for(int i=0;inumberOfRecordsWithExtension("txt"); + for(int i=0; irecordWithExtensionAtIndex("txt", i); + if(!hasBook(r.fullName())) + { + r.destroy(); + } + } +} + } \ No newline at end of file diff --git a/apps/reader/list_book_controller.h b/apps/reader/list_book_controller.h index 9fdd11262..82eecf207 100644 --- a/apps/reader/list_book_controller.h +++ b/apps/reader/list_book_controller.h @@ -22,6 +22,8 @@ public: void willDisplayCellForIndex(HighlightCell * cell, int index) override; void didBecomeFirstResponder() override; bool handleEvent(Ion::Events::Event event) override; + bool hasBook(const char* filename) const; + void cleanRemovedBookRecord(); private: SelectableTableView m_tableView; diff --git a/apps/reader/read_book_controller.cpp b/apps/reader/read_book_controller.cpp index 522491bfb..7bdd9380e 100644 --- a/apps/reader/read_book_controller.cpp +++ b/apps/reader/read_book_controller.cpp @@ -15,6 +15,8 @@ View * ReadBookController::view() void ReadBookController::setBook(const External::Archive::File& file) { + m_file = &file; + loadPosition(); m_readerView.setText(reinterpret_cast(file.data), file.dataLength); } @@ -30,7 +32,38 @@ bool ReadBookController::handleEvent(Ion::Events::Event event) m_readerView.previousPage(); return true; } + if(event == Ion::Events::Back || event == Ion::Events::Home) + { + savePosition(); + } return false; } +void ReadBookController::savePosition() const +{ + int pageOffset = m_readerView.getPageOffset(); + Ion::Storage::Record::ErrorStatus status = Ion::Storage::sharedStorage()->createRecordWithFullName(m_file->name, &pageOffset, sizeof(pageOffset)); + if(Ion::Storage::Record::ErrorStatus::NameTaken == status) + { + Ion::Storage::Record::Data data; + data.buffer = &pageOffset; + data.size = sizeof(pageOffset); + status = Ion::Storage::sharedStorage()->recordNamed(m_file->name).setValue(data); + } +} + +void ReadBookController::loadPosition() +{ + Ion::Storage::Record r = Ion::Storage::sharedStorage()->recordNamed(m_file->name); + if(Ion::Storage::sharedStorage()->hasRecord(r)) + { + int pageOffset = *(static_cast(r.value().buffer)); + m_readerView.setPageOffset(pageOffset); + } + else + { + m_readerView.setPageOffset(0); + } +} + } \ No newline at end of file diff --git a/apps/reader/read_book_controller.h b/apps/reader/read_book_controller.h index d83be7f6e..c4349be4e 100644 --- a/apps/reader/read_book_controller.h +++ b/apps/reader/read_book_controller.h @@ -14,8 +14,12 @@ public: void setBook(const External::Archive::File& file); bool handleEvent(Ion::Events::Event event) override; + + void savePosition() const; + void loadPosition(); private: WordWrapTextView m_readerView; + const External::Archive::File* m_file; }; } diff --git a/apps/reader/word_wrap_view.cpp b/apps/reader/word_wrap_view.cpp index 9f4ed284f..610b57128 100644 --- a/apps/reader/word_wrap_view.cpp +++ b/apps/reader/word_wrap_view.cpp @@ -146,4 +146,14 @@ void WordWrapTextView::drawRect(KDContext * ctx, KDRect rect) const m_nextPageOffset = startOfWord - text(); } +int WordWrapTextView::getPageOffset() const +{ + return m_pageOffset; +} + +void WordWrapTextView::setPageOffset(int o) +{ + m_pageOffset = o; +} + } \ No newline at end of file diff --git a/apps/reader/word_wrap_view.h b/apps/reader/word_wrap_view.h index d71fcb15e..4075ff4e2 100644 --- a/apps/reader/word_wrap_view.h +++ b/apps/reader/word_wrap_view.h @@ -12,7 +12,8 @@ public: void setText(const char*, int length); void nextPage(); void previousPage(); - + int getPageOffset() const; + void setPageOffset(int o); protected: int m_pageOffset = 0; mutable int m_nextPageOffset = 0; From 890592854a71abe9a7e691dc3da3a007007f3298 Mon Sep 17 00:00:00 2001 From: Fournier Gabriel <> Date: Sun, 13 Dec 2020 22:13:33 +0100 Subject: [PATCH 13/48] fix for device --- apps/reader/word_wrap_view.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/apps/reader/word_wrap_view.cpp b/apps/reader/word_wrap_view.cpp index 610b57128..9e4cccd62 100644 --- a/apps/reader/word_wrap_view.cpp +++ b/apps/reader/word_wrap_view.cpp @@ -2,8 +2,6 @@ #include "utility.h" -#include - namespace reader { @@ -41,7 +39,6 @@ void WordWrapTextView::previousPage() if(previousTextPosition.x() < margin) { - std::cout<<"|||\n"< Date: Fri, 29 Jan 2021 12:01:01 +0100 Subject: [PATCH 14/48] [.github/issue_template] Remove issue templates temporarily --- .github/ISSUE_TEMPLATE/bug_report.md | 14 -------------- .github/ISSUE_TEMPLATE/config.yml | 1 - .github/ISSUE_TEMPLATE/feature_request.md | 10 ---------- .github/ISSUE_TEMPLATE/other.md | 10 ---------- .../problems-during-installation.md | 19 ------------------- 5 files changed, 54 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/bug_report.md delete mode 100644 .github/ISSUE_TEMPLATE/config.yml delete mode 100644 .github/ISSUE_TEMPLATE/feature_request.md delete mode 100644 .github/ISSUE_TEMPLATE/other.md delete mode 100644 .github/ISSUE_TEMPLATE/problems-during-installation.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index 30b227d04..000000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -name: Bug report -about: Omega is not working like it should? Let us know! -title: '' -labels: Bug, Triage -assignees: '' - ---- - -#### Describe the bug - - -#### Environment - - Omega Version: {go to settings > about > Omega Version and type the version here} diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml deleted file mode 100644 index 3ba13e0ce..000000000 --- a/.github/ISSUE_TEMPLATE/config.yml +++ /dev/null @@ -1 +0,0 @@ -blank_issues_enabled: false diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index eee03aa6a..000000000 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -name: Feature request -about: Suggest an idea for an improvement of Omega -title: '' -labels: Feature, Triage -assignees: '' - ---- - -#### What I want to see in the next version of Omega diff --git a/.github/ISSUE_TEMPLATE/other.md b/.github/ISSUE_TEMPLATE/other.md deleted file mode 100644 index 38b47c71f..000000000 --- a/.github/ISSUE_TEMPLATE/other.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -name: Other -about: A question? A problem? ... -title: '' -labels: Triage -assignees: '' - ---- - - diff --git a/.github/ISSUE_TEMPLATE/problems-during-installation.md b/.github/ISSUE_TEMPLATE/problems-during-installation.md deleted file mode 100644 index 347b6d957..000000000 --- a/.github/ISSUE_TEMPLATE/problems-during-installation.md +++ /dev/null @@ -1,19 +0,0 @@ ---- -name: Problems during installation -about: Need help to install Omega? -title: '' -labels: Installation issue, Triage -assignees: '' - ---- - -#### Describe the problem - - -#### Logs -``` -Copy/paste the logs here (If you have some) -``` - -#### Environment - - Omega Version: {go to settings > about > Omega Version and type the version here} From f052ad7bf8fe1fb243c69f25f278b1c5d3dc3960 Mon Sep 17 00:00:00 2001 From: Quentin Date: Fri, 29 Jan 2021 12:22:16 +0100 Subject: [PATCH 15/48] [.github/issue_template] New issue templates --- .github/ISSUE_TEMPLATE/bug_report.md | 27 ++++++++++++++++++ .github/ISSUE_TEMPLATE/feature_request.md | 17 +++++++++++ .../omega-beta-only---bug-report.md | 28 +++++++++++++++++++ .github/ISSUE_TEMPLATE/other.md | 10 +++++++ .../problems-during-installation.md | 19 +++++++++++++ 5 files changed, 101 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md create mode 100644 .github/ISSUE_TEMPLATE/omega-beta-only---bug-report.md create mode 100644 .github/ISSUE_TEMPLATE/other.md create mode 100644 .github/ISSUE_TEMPLATE/problems-during-installation.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 000000000..d6e0dd686 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,27 @@ +--- +name: Bug report +about: Omega is not working like it should? Let us know! +title: '' +labels: 'Status: Triage, Type: Bug' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + - Omega Version: [go to settings > about > Omega Version and type the version here] diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 000000000..5e45c5032 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,17 @@ +--- +name: Feature request +about: Suggest an idea for Omega +title: '' +labels: 'Status: Triage, Type: Feature' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.github/ISSUE_TEMPLATE/omega-beta-only---bug-report.md b/.github/ISSUE_TEMPLATE/omega-beta-only---bug-report.md new file mode 100644 index 000000000..f97b2b86f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/omega-beta-only---bug-report.md @@ -0,0 +1,28 @@ +--- +name: OMEGA BETA ONLY - Bug report +about: Omega 1.21 is not working like it should? Let us know! +title: "[BETA-1.21] …" +labels: 'Status: Triage, Type: Bug' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + - Omega Version: [go to settings > about > Omega Version and type the version here] + - Discord username: ..........#.... diff --git a/.github/ISSUE_TEMPLATE/other.md b/.github/ISSUE_TEMPLATE/other.md new file mode 100644 index 000000000..30134e273 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/other.md @@ -0,0 +1,10 @@ +--- +name: Other +about: A question? A problem? … +title: '' +labels: 'Status: Triage' +assignees: '' + +--- + + diff --git a/.github/ISSUE_TEMPLATE/problems-during-installation.md b/.github/ISSUE_TEMPLATE/problems-during-installation.md new file mode 100644 index 000000000..baab16e64 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/problems-during-installation.md @@ -0,0 +1,19 @@ +--- +name: Problems during installation +about: Need help to install Omega? +title: '' +labels: 'Status: Triage, Type: Installation issue' +assignees: '' + +--- + +**Describe the problem** + + +**Logs** +``` +Copy/paste the logs here (If you have some) +``` + +**Environment** + - Omega Version: {go to settings > about > Omega Version and type the version here} From 4c29b05b53e0191bb0458f443849040354334275 Mon Sep 17 00:00:00 2001 From: ArtichOwO Date: Mon, 21 Jun 2021 19:34:26 +0200 Subject: [PATCH 16/48] [MPY/MOD/OS] Added "name" attribute, "getlogin()" function and "username" key in "uname()" "os.name" returns either "Omega" (only if OMEGA_VERSION is defined) or "Epsilon" "os.getlogin()" returns the calculator owner username "os.uname()" returns an object which has now the "username" attribute --- python/port/genhdr/qstrdefs.in.h | 2 ++ python/port/mod/os/modos.cpp | 11 +++++++++++ python/port/mod/os/modos.h | 1 + python/port/mod/os/modos_table.c | 13 +++++++++++++ 4 files changed, 27 insertions(+) diff --git a/python/port/genhdr/qstrdefs.in.h b/python/port/genhdr/qstrdefs.in.h index c580bcb8e..b385aa63b 100644 --- a/python/port/genhdr/qstrdefs.in.h +++ b/python/port/genhdr/qstrdefs.in.h @@ -537,11 +537,13 @@ Q(SEEK_END) // os QSTRs Q(os) Q(uname) +Q(getlogin) Q(sysname) Q(nodename) Q(release) Q(version) Q(machine) +Q(username) Q(rename) Q(listdir) diff --git a/python/port/mod/os/modos.cpp b/python/port/mod/os/modos.cpp index 6bf5124d1..b32d9a3d8 100644 --- a/python/port/mod/os/modos.cpp +++ b/python/port/mod/os/modos.cpp @@ -32,12 +32,19 @@ STATIC const MP_DEFINE_STR_OBJ(modos_uname_info_machine_obj, "NumWorks N0100"); STATIC const MP_DEFINE_STR_OBJ(modos_uname_info_machine_obj, "NumWorks Simulator"); #endif +#if defined(OMEGA_USERNAME) +STATIC const MP_DEFINE_STR_OBJ(modos_uname_info_username_obj, MP_STRINGIFY(OMEGA_USERNAME)); +#else +STATIC const MP_DEFINE_STR_OBJ(modos_uname_info_username_obj, ""); +#endif + STATIC const mp_rom_map_elem_t modos_uname_info_table[] = { { MP_ROM_QSTR(MP_QSTR_sysname), &modos_uname_info_sysname_obj }, { MP_ROM_QSTR(MP_QSTR_nodename), &modos_uname_info_nodename_obj }, { MP_ROM_QSTR(MP_QSTR_release), &modos_uname_info_release_obj }, { MP_ROM_QSTR(MP_QSTR_version), &modos_uname_info_version_obj }, { MP_ROM_QSTR(MP_QSTR_machine), &modos_uname_info_machine_obj }, + { MP_ROM_QSTR(MP_QSTR_username), &modos_uname_info_username_obj }, }; STATIC MP_DEFINE_CONST_DICT(modos_uname_info_obj, modos_uname_info_table); @@ -46,6 +53,10 @@ mp_obj_t modos_uname(void) { return (mp_obj_t)&modos_uname_info_obj; } +mp_obj_t modos_getlogin(void) { + return (mp_obj_t)&modos_uname_info_username_obj; +} + mp_obj_t modos_remove(mp_obj_t o_file_name) { size_t len; diff --git a/python/port/mod/os/modos.h b/python/port/mod/os/modos.h index 713c81309..0e9da4b98 100644 --- a/python/port/mod/os/modos.h +++ b/python/port/mod/os/modos.h @@ -1,6 +1,7 @@ #include mp_obj_t modos_uname(); +mp_obj_t modos_getlogin(); mp_obj_t modos_remove(mp_obj_t o_file_name); mp_obj_t modos_rename(mp_obj_t o_old_name, mp_obj_t o_new_name); mp_obj_t modos_listdir(); diff --git a/python/port/mod/os/modos_table.c b/python/port/mod/os/modos_table.c index 723720ce4..20106bb2d 100644 --- a/python/port/mod/os/modos_table.c +++ b/python/port/mod/os/modos_table.c @@ -1,16 +1,29 @@ #include "modos.h" +#include +#include +#include + MP_DEFINE_CONST_FUN_OBJ_0(modos_uname_obj, modos_uname); +MP_DEFINE_CONST_FUN_OBJ_0(modos_getlogin_obj, modos_getlogin); MP_DEFINE_CONST_FUN_OBJ_1(modos_remove_obj, modos_remove); MP_DEFINE_CONST_FUN_OBJ_2(modos_rename_obj, modos_rename); MP_DEFINE_CONST_FUN_OBJ_0(modos_listdir_obj, modos_listdir); +#ifdef OMEGA_VERSION +STATIC const MP_DEFINE_STR_OBJ(modos_sysname_obj, "Omega"); +#else +STATIC const MP_DEFINE_STR_OBJ(modos_sysname_obj, "Epsilon"); +#endif + STATIC const mp_rom_map_elem_t modos_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_os) }, { MP_ROM_QSTR(MP_QSTR_uname), &modos_uname_obj}, + { MP_ROM_QSTR(MP_QSTR_getlogin), &modos_getlogin_obj}, { MP_ROM_QSTR(MP_QSTR_remove), &modos_remove_obj}, { MP_ROM_QSTR(MP_QSTR_rename), &modos_rename_obj}, { MP_ROM_QSTR(MP_QSTR_listdir), &modos_listdir_obj}, + { MP_ROM_QSTR(MP_QSTR_name), &modos_sysname_obj} }; STATIC MP_DEFINE_CONST_DICT(modos_module_globals, modos_module_globals_table); From ba94c7db009411ce610bb844eb3dbcb8e21ecb18 Mon Sep 17 00:00:00 2001 From: ArtichOwO Date: Mon, 21 Jun 2021 23:24:50 +0200 Subject: [PATCH 17/48] [Code/Toolbox] Added "getlogin()" to code toolbox --- apps/code/catalog.de.i18n | 1 + apps/code/catalog.en.i18n | 1 + apps/code/catalog.es.i18n | 1 + apps/code/catalog.fr.i18n | 1 + apps/code/catalog.hu.i18n | 1 + apps/code/catalog.it.i18n | 1 + apps/code/catalog.nl.i18n | 1 + apps/code/catalog.pt.i18n | 1 + apps/code/catalog.universal.i18n | 1 + apps/code/python_toolbox.cpp | 1 + 10 files changed, 10 insertions(+) diff --git a/apps/code/catalog.de.i18n b/apps/code/catalog.de.i18n index 9cb321ab8..325dd7975 100644 --- a/apps/code/catalog.de.i18n +++ b/apps/code/catalog.de.i18n @@ -72,6 +72,7 @@ PythonImportMath = "math Modul importieren" PythonImportMatplotlibPyplot = "Import matplotlib.pyplot module" PythonImportOs = "os Modul importieren" PythonOsUname = "Informieren Sie sich über das System" +PythonOsGetlogin = "Get username" PythonOsRemove = "Datei namens Dateiname entfernen" PythonOsRename = "Datei mit altem Namen in neuen Namen umbenennen" PythonOsListdir = "Dateien im Speicher auflisten" diff --git a/apps/code/catalog.en.i18n b/apps/code/catalog.en.i18n index 819887dbb..a58ff5804 100644 --- a/apps/code/catalog.en.i18n +++ b/apps/code/catalog.en.i18n @@ -153,6 +153,7 @@ PythonUniform = "Floating point number in [a,b]" PythonImportTime = "Import time module" PythonImportOs = "Import os module" PythonOsUname = "Get infos about the system" +PythonOsGetlogin = "Get username" PythonOsRemove = "Remove file named filename" PythonOsRename = "Rename file oldname to newname" PythonOsListdir = "List files in memory" diff --git a/apps/code/catalog.es.i18n b/apps/code/catalog.es.i18n index 960dd7eb9..2dee2b928 100644 --- a/apps/code/catalog.es.i18n +++ b/apps/code/catalog.es.i18n @@ -153,6 +153,7 @@ PythonUniform = "Floating point number in [a,b]" PythonImportTime = "Import time module" PythonImportOs = "Import os module" PythonOsUname = " Información del sistema " +PythonOsGetlogin = "Get username" PythonOsRemove = "Eliminar un archivo" PythonOsRename = "Renombrar archivo" PythonOsListdir = "Archivos de la lista" diff --git a/apps/code/catalog.fr.i18n b/apps/code/catalog.fr.i18n index 756ef882e..4ac2560c9 100644 --- a/apps/code/catalog.fr.i18n +++ b/apps/code/catalog.fr.i18n @@ -153,6 +153,7 @@ PythonUniform = "Nombre décimal dans [a,b]" PythonImportTime = "Importation du module temps" PythonImportOs = "Importation du module os" PythonOsUname = "Donne des infos sur le système" +PythonOsGetlogin = "Donne le nom d'utilisateur" PythonOsRemove = "Supprime le fichier nommé filename" PythonOsRename = "Renomme oldname en newname" PythonOsListdir = "Liste les fichiers" diff --git a/apps/code/catalog.hu.i18n b/apps/code/catalog.hu.i18n index a837db91b..6959839aa 100644 --- a/apps/code/catalog.hu.i18n +++ b/apps/code/catalog.hu.i18n @@ -172,6 +172,7 @@ PythonFileReadable = "read-et lehete használni" PythonFileWritable = "write-ot lehete használni" PythonImportOs = "os modul importálása" PythonOsUname = "Rendszer informaciók" +PythonOsGetlogin = "Get username" PythonOsRemove = "Fájl törlése" PythonOsRename = "Fájl átnevezése" PythonOsListdir = "Fájlok listája" diff --git a/apps/code/catalog.it.i18n b/apps/code/catalog.it.i18n index 94eacc262..6c174b53c 100644 --- a/apps/code/catalog.it.i18n +++ b/apps/code/catalog.it.i18n @@ -74,6 +74,7 @@ PythonImportTurtle = "Importa del modulo turtle" PythonImportTime = "Importa del modulo time" PythonImportOs = "Importa modulo os" PythonOsUname = "Ottieni informazioni sul sistema" +PythonOsGetlogin = "Get username" PythonOsRemove = "Rimuovere un file" PythonOsRename = "Rinomina file" PythonOsListdir = "Elenca file" diff --git a/apps/code/catalog.nl.i18n b/apps/code/catalog.nl.i18n index 1ee1f667c..cc788ee13 100644 --- a/apps/code/catalog.nl.i18n +++ b/apps/code/catalog.nl.i18n @@ -73,6 +73,7 @@ PythonImportMatplotlibPyplot = "Importeer matplotlib.pyplot module" PythonImportTime = "Importeer time module" PythonImportOs = "Importeer os module" PythonOsUname = " Krijg systeeminfo" +PythonOsGetlogin = "Get username" PythonOsRemove = "Een bestand verwijderen" PythonOsRename = "Hernoem bestand" PythonOsListdir = "Lijstbestanden" diff --git a/apps/code/catalog.pt.i18n b/apps/code/catalog.pt.i18n index a17ea0ad5..d50630dda 100644 --- a/apps/code/catalog.pt.i18n +++ b/apps/code/catalog.pt.i18n @@ -153,6 +153,7 @@ PythonUniform = "Número decimal em [a,b]" PythonImportTime = "Import time module" PythonImportOs = "Import os module" PythonOsUname = " Obter informações do sistema" +PythonOsGetlogin = "Get username" PythonOsRemove = "Remover um ficheiro" PythonOsRename = "Renomear ficheiro" PythonOsListdir = "Listar ficheiros" diff --git a/apps/code/catalog.universal.i18n b/apps/code/catalog.universal.i18n index 39d1ff3b4..0c05ad6f4 100644 --- a/apps/code/catalog.universal.i18n +++ b/apps/code/catalog.universal.i18n @@ -216,6 +216,7 @@ PythonCommandUniform = "uniform(a,b)" PythonConstantE = "2.718281828459045" PythonConstantPi = "3.141592653589793" PythonOsCommandUname = "uname()" +PythonOsCommandGetlogin = "getlogin()" PythonOsCommandRemove = "remove(filename)" PythonOsCommandRename = "rename(oldname, newname)" PythonOsCommandRemoveWithoutArg = "remove(\x11)" diff --git a/apps/code/python_toolbox.cpp b/apps/code/python_toolbox.cpp index 568e1f22c..4adf62795 100644 --- a/apps/code/python_toolbox.cpp +++ b/apps/code/python_toolbox.cpp @@ -217,6 +217,7 @@ const ToolboxMessageTree OsModuleChildren[] = { ToolboxMessageTree::Leaf(I18n::Message::PythonCommandImportOs, I18n::Message::PythonImportOs, false), ToolboxMessageTree::Leaf(I18n::Message::PythonCommandImportFromOs, I18n::Message::PythonImportOs, false), ToolboxMessageTree::Leaf(I18n::Message::PythonOsCommandUname, I18n::Message::PythonOsUname, false), + ToolboxMessageTree::Leaf(I18n::Message::PythonOsCommandGetlogin, I18n::Message::PythonOsGetlogin, false), ToolboxMessageTree::Leaf(I18n::Message::PythonOsCommandRemove, I18n::Message::PythonOsRemove, false, I18n::Message::PythonOsCommandRemoveWithoutArg), ToolboxMessageTree::Leaf(I18n::Message::PythonOsCommandRename, I18n::Message::PythonOsRename, false, I18n::Message::PythonOsCommandRenameWithoutArg), ToolboxMessageTree::Leaf(I18n::Message::PythonOsCommandListdir, I18n::Message::PythonOsListdir, false) From b0befbdbc5c66e5f0eeb880d0e9afe6ff13e6d87 Mon Sep 17 00:00:00 2001 From: ArtichOwO Date: Wed, 23 Jun 2021 22:40:41 +0200 Subject: [PATCH 18/48] [MPY/MOD] Added uLab --- python/Makefile | 26 + python/port/genhdr/qstrdefs.in.h | 117 + python/port/mod/ulab/ndarray.c | 2180 +++++++++++++++++ python/port/mod/ulab/ndarray.h | 736 ++++++ python/port/mod/ulab/ndarray_operators.c | 807 ++++++ python/port/mod/ulab/ndarray_operators.h | 277 +++ python/port/mod/ulab/ndarray_properties.c | 100 + python/port/mod/ulab/ndarray_properties.h | 92 + python/port/mod/ulab/numpy/approx/approx.c | 221 ++ python/port/mod/ulab/numpy/approx/approx.h | 29 + python/port/mod/ulab/numpy/compare/compare.c | 417 ++++ python/port/mod/ulab/numpy/compare/compare.h | 150 ++ python/port/mod/ulab/numpy/fft/fft.c | 82 + python/port/mod/ulab/numpy/fft/fft.h | 24 + python/port/mod/ulab/numpy/fft/fft_tools.c | 165 ++ python/port/mod/ulab/numpy/fft/fft_tools.h | 23 + python/port/mod/ulab/numpy/filter/filter.c | 84 + python/port/mod/ulab/numpy/filter/filter.h | 20 + python/port/mod/ulab/numpy/linalg/linalg.c | 397 +++ python/port/mod/ulab/numpy/linalg/linalg.h | 26 + .../port/mod/ulab/numpy/linalg/linalg_tools.c | 171 ++ .../port/mod/ulab/numpy/linalg/linalg_tools.h | 28 + .../port/mod/ulab/numpy/numerical/numerical.c | 1338 ++++++++++ .../port/mod/ulab/numpy/numerical/numerical.h | 652 +++++ python/port/mod/ulab/numpy/numpy.c | 336 +++ python/port/mod/ulab/numpy/numpy.h | 21 + python/port/mod/ulab/numpy/poly/poly.c | 232 ++ python/port/mod/ulab/numpy/poly/poly.h | 21 + python/port/mod/ulab/numpy/stats/stats.c | 52 + python/port/mod/ulab/numpy/stats/stats.h | 20 + .../port/mod/ulab/numpy/transform/transform.c | 89 + .../port/mod/ulab/numpy/transform/transform.h | 28 + python/port/mod/ulab/numpy/vector/vector.c | 635 +++++ python/port/mod/ulab/numpy/vector/vector.h | 156 ++ python/port/mod/ulab/scipy/linalg/linalg.c | 279 +++ python/port/mod/ulab/scipy/linalg/linalg.h | 21 + .../port/mod/ulab/scipy/optimize/optimize.c | 414 ++++ .../port/mod/ulab/scipy/optimize/optimize.h | 41 + python/port/mod/ulab/scipy/scipy.c | 51 + python/port/mod/ulab/scipy/scipy.h | 21 + python/port/mod/ulab/scipy/signal/signal.c | 153 ++ python/port/mod/ulab/scipy/signal/signal.h | 24 + python/port/mod/ulab/scipy/special/special.c | 42 + python/port/mod/ulab/scipy/special/special.h | 21 + python/port/mod/ulab/ulab.c | 162 ++ python/port/mod/ulab/ulab.h | 658 +++++ python/port/mod/ulab/ulab_create.c | 706 ++++++ python/port/mod/ulab/ulab_create.h | 78 + python/port/mod/ulab/ulab_tools.c | 233 ++ python/port/mod/ulab/ulab_tools.h | 37 + python/port/mod/ulab/user/user.c | 95 + python/port/mod/ulab/user/user.h | 20 + python/port/mod/ulab/utils/utils.c | 215 ++ python/port/mod/ulab/utils/utils.h | 19 + python/port/mpconfigport.h | 2 + python/port/port.cpp | 1 + 56 files changed, 13045 insertions(+) create mode 100644 python/port/mod/ulab/ndarray.c create mode 100644 python/port/mod/ulab/ndarray.h create mode 100644 python/port/mod/ulab/ndarray_operators.c create mode 100644 python/port/mod/ulab/ndarray_operators.h create mode 100644 python/port/mod/ulab/ndarray_properties.c create mode 100644 python/port/mod/ulab/ndarray_properties.h create mode 100644 python/port/mod/ulab/numpy/approx/approx.c create mode 100644 python/port/mod/ulab/numpy/approx/approx.h create mode 100644 python/port/mod/ulab/numpy/compare/compare.c create mode 100644 python/port/mod/ulab/numpy/compare/compare.h create mode 100644 python/port/mod/ulab/numpy/fft/fft.c create mode 100644 python/port/mod/ulab/numpy/fft/fft.h create mode 100644 python/port/mod/ulab/numpy/fft/fft_tools.c create mode 100644 python/port/mod/ulab/numpy/fft/fft_tools.h create mode 100644 python/port/mod/ulab/numpy/filter/filter.c create mode 100644 python/port/mod/ulab/numpy/filter/filter.h create mode 100644 python/port/mod/ulab/numpy/linalg/linalg.c create mode 100644 python/port/mod/ulab/numpy/linalg/linalg.h create mode 100644 python/port/mod/ulab/numpy/linalg/linalg_tools.c create mode 100644 python/port/mod/ulab/numpy/linalg/linalg_tools.h create mode 100644 python/port/mod/ulab/numpy/numerical/numerical.c create mode 100644 python/port/mod/ulab/numpy/numerical/numerical.h create mode 100644 python/port/mod/ulab/numpy/numpy.c create mode 100644 python/port/mod/ulab/numpy/numpy.h create mode 100644 python/port/mod/ulab/numpy/poly/poly.c create mode 100644 python/port/mod/ulab/numpy/poly/poly.h create mode 100644 python/port/mod/ulab/numpy/stats/stats.c create mode 100644 python/port/mod/ulab/numpy/stats/stats.h create mode 100644 python/port/mod/ulab/numpy/transform/transform.c create mode 100644 python/port/mod/ulab/numpy/transform/transform.h create mode 100644 python/port/mod/ulab/numpy/vector/vector.c create mode 100644 python/port/mod/ulab/numpy/vector/vector.h create mode 100644 python/port/mod/ulab/scipy/linalg/linalg.c create mode 100644 python/port/mod/ulab/scipy/linalg/linalg.h create mode 100644 python/port/mod/ulab/scipy/optimize/optimize.c create mode 100644 python/port/mod/ulab/scipy/optimize/optimize.h create mode 100644 python/port/mod/ulab/scipy/scipy.c create mode 100644 python/port/mod/ulab/scipy/scipy.h create mode 100644 python/port/mod/ulab/scipy/signal/signal.c create mode 100644 python/port/mod/ulab/scipy/signal/signal.h create mode 100644 python/port/mod/ulab/scipy/special/special.c create mode 100644 python/port/mod/ulab/scipy/special/special.h create mode 100644 python/port/mod/ulab/ulab.c create mode 100644 python/port/mod/ulab/ulab.h create mode 100644 python/port/mod/ulab/ulab_create.c create mode 100644 python/port/mod/ulab/ulab_create.h create mode 100644 python/port/mod/ulab/ulab_tools.c create mode 100644 python/port/mod/ulab/ulab_tools.h create mode 100644 python/port/mod/ulab/user/user.c create mode 100644 python/port/mod/ulab/user/user.h create mode 100644 python/port/mod/ulab/utils/utils.c create mode 100644 python/port/mod/ulab/utils/utils.h diff --git a/python/Makefile b/python/Makefile index 6957ed586..15a770c73 100644 --- a/python/Makefile +++ b/python/Makefile @@ -154,6 +154,32 @@ port_src += $(addprefix python/port/,\ mod/turtle/modturtle.cpp \ mod/turtle/modturtle_table.c \ mod/turtle/turtle.cpp \ + mod/ulab/scipy/linalg/linalg.c \ + mod/ulab/scipy/optimize/optimize.c \ + mod/ulab/scipy/signal/signal.c \ + mod/ulab/scipy/special/special.c \ + mod/ulab/ndarray_operators.c \ + mod/ulab/ulab_tools.c \ + mod/ulab/ndarray.c \ + mod/ulab/ndarray_properties.c \ + mod/ulab/numpy/approx/approx.c \ + mod/ulab/numpy/compare/compare.c \ + mod/ulab/ulab_create.c \ + mod/ulab/numpy/fft/fft.c \ + mod/ulab/numpy/fft/fft_tools.c \ + mod/ulab/numpy/filter/filter.c \ + mod/ulab/numpy/linalg/linalg.c \ + mod/ulab/numpy/linalg/linalg_tools.c \ + mod/ulab/numpy/numerical/numerical.c \ + mod/ulab/numpy/poly/poly.c \ + mod/ulab/numpy/stats/stats.c \ + mod/ulab/numpy/transform/transform.c \ + mod/ulab/numpy/vector/vector.c \ + mod/ulab/numpy/numpy.c \ + mod/ulab/scipy/scipy.c \ + mod/ulab/user/user.c \ + mod/ulab/utils/utils.c \ + mod/ulab/ulab.c \ mphalport.c \ ) diff --git a/python/port/genhdr/qstrdefs.in.h b/python/port/genhdr/qstrdefs.in.h index c580bcb8e..f6d637bff 100644 --- a/python/port/genhdr/qstrdefs.in.h +++ b/python/port/genhdr/qstrdefs.in.h @@ -545,3 +545,120 @@ Q(machine) Q(rename) Q(listdir) +// ulab QSTRs +Q(threshold) +Q(edgeitems) +Q(inplace) +Q(dtype) +Q(order) +Q(C) +Q(shape) +Q(strides) +Q(itemsize) +Q(size) +Q(T) +Q(x) +Q(dx) +Q(fft) +Q(ifft) +Q(a) +Q(v) +Q(linalg) +Q(cholesky) +Q(det) +Q(eig) +Q(inv) +Q(norm) +Q(n) +Q(ddof) +Q(numpy) +Q(ndarray) +Q(array) +Q(frombuffer) +Q(inf) +Q(nan) +Q(uint8) +Q(int8) +Q(uint16) +Q(int16) +Q(set_printoptions) +Q(get_printoptions) +Q(ndinfo) +Q(arange) +Q(concatenate) +Q(diag) +Q(empty) +Q(eye) +Q(interp) +Q(trapz) +Q(full) +Q(linspace) +Q(logspace) +Q(ones) +Q(zeros) +Q(clip) +Q(equal) +Q(not_equal) +Q(maximum) +Q(minimum) +Q(where) +Q(convolve) +Q(argmax) +Q(argmin) +Q(argsort) +Q(cross) +Q(diff) +Q(dot) +Q(decimals) +Q(otypes) +Q(solve_triangular) +Q(cho_solve) +Q(trace) +Q(flip) +Q(mean) +Q(median) +Q(roll) +Q(std) +Q(polyfit) +Q(polyval) +Q(arctan2) +Q(around) +Q(vectorize) +Q(xtol) +Q(maxiter) +Q(xatol) +Q(fatol) +Q(tol) +Q(rtol) +Q(scipy) +Q(optimize) +Q(signal) +Q(bisect) +Q(special) +Q(fmin) +Q(newton) +Q(sos) +Q(zi) +Q(spectrogram) +Q(sosfilt) +Q(gammaln) +Q(reshape) +Q(transpose) +Q(byteswap) +Q(flatten) +Q(k) +Q(tobytes) +Q(M) +Q(ulab) +Q(num) +Q(endpoint) +Q(__version__) +Q(utils) +Q(retstep) +Q(base) +Q(offset) +Q(out) +Q(from_int16_buffer) +Q(from_uint16_buffer) +Q(from_int32_buffer) +Q(from_uint32_buffer) diff --git a/python/port/mod/ulab/ndarray.c b/python/port/mod/ulab/ndarray.c new file mode 100644 index 000000000..e37802efe --- /dev/null +++ b/python/port/mod/ulab/ndarray.c @@ -0,0 +1,2180 @@ + +/* + * This file is part of the micropython-ulab project, + * + * https://github.com/v923z/micropython-ulab + * + * The MIT License (MIT) + * + * Copyright (c) 2019-2021 Zoltán Vörös + * 2020 Jeff Epler for Adafruit Industries + * 2020 Taku Fukada +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ulab_tools.h" +#include "ndarray.h" +#include "ndarray_operators.h" + +mp_uint_t ndarray_print_threshold = NDARRAY_PRINT_THRESHOLD; +mp_uint_t ndarray_print_edgeitems = NDARRAY_PRINT_EDGEITEMS; + +//| """Manipulate numeric data similar to numpy +//| +//| `ulab` is a numpy-like module for micropython, meant to simplify and +//| speed up common mathematical operations on arrays. The primary goal was to +//| implement a small subset of numpy that might be useful in the context of a +//| microcontroller. This means low-level data processing of linear (array) and +//| two-dimensional (matrix) data. +//| +//| `ulab` is adapted from micropython-ulab, and the original project's +//| documentation can be found at +//| https://micropython-ulab.readthedocs.io/en/latest/ +//| +//| `ulab` is modeled after numpy, and aims to be a compatible subset where +//| possible. Numpy's documentation can be found at +//| https://docs.scipy.org/doc/numpy/index.html""" +//| +//| from typing import Dict +//| +//| _DType = int +//| """`ulab.int8`, `ulab.uint8`, `ulab.int16`, `ulab.uint16`, `ulab.float` or `ulab.bool`""" +//| +//| _float = float +//| """Type alias of the bulitin float""" +//| +//| _bool = bool +//| """Type alias of the bulitin bool""" +//| +//| _Index = Union[int, slice, ulab.ndarray, Tuple[Union[int, slice], ...]] +//| + +//| class ndarray: +//| """1- and 2- dimensional ndarray""" +//| +//| def __init__( +//| self, +//| values: Union[ndarray, Iterable[Union[_float, _bool, Iterable[Any]]]], +//| *, +//| dtype: _DType = ulab.float +//| ) -> None: +//| """:param sequence values: Sequence giving the initial content of the ndarray. +//| :param ~ulab._DType dtype: The type of ndarray values, `ulab.int8`, `ulab.uint8`, `ulab.int16`, `ulab.uint16`, `ulab.float` or `ulab.bool` +//| +//| The ``values`` sequence can either be another ~ulab.ndarray, sequence of numbers +//| (in which case a 1-dimensional ndarray is created), or a sequence where each +//| subsequence has the same length (in which case a 2-dimensional ndarray is +//| created). +//| +//| Passing a `ulab.ndarray` and a different dtype can be used to convert an ndarray +//| from one dtype to another. +//| +//| In many cases, it is more convenient to create an ndarray from a function +//| like `zeros` or `linspace`. +//| +//| `ulab.ndarray` implements the buffer protocol, so it can be used in many +//| places an `array.array` can be used.""" +//| ... +//| +//| shape: Tuple[int, ...] +//| """The size of the array, a tuple of length 1 or 2""" +//| +//| size: int +//| """The number of elements in the array""" +//| +//| itemsize: int +//| """The size of a single item in the array""" +//| +//| strides: Tuple[int, ...] +//| """Tuple of bytes to step in each dimension, a tuple of length 1 or 2""" +//| +//| def copy(self) -> ulab.ndarray: +//| """Return a copy of the array""" +//| ... +//| +//| def dtype(self) -> _DType: +//| """Returns the dtype of the array""" +//| ... +//| +//| def flatten(self, *, order: str = "C") -> ulab.ndarray: +//| """:param order: Whether to flatten by rows ('C') or columns ('F') +//| +//| Returns a new `ulab.ndarray` object which is always 1 dimensional. +//| If order is 'C' (the default", then the data is ordered in rows; +//| If it is 'F', then the data is ordered in columns. "C" and "F" refer +//| to the typical storage organization of the C and Fortran languages.""" +//| ... +//| +//| def reshape(self, shape: Tuple[int, ...]) -> ulab.ndarray: +//| """Returns an ndarray containing the same data with a new shape.""" +//| ... +//| +//| def sort(self, *, axis: Optional[int] = 1) -> None: +//| """:param axis: Whether to sort elements within rows (0), columns (1), or elements (None)""" +//| ... +//| +//| def tobytes(self) -> bytearray: +//| """Return the raw data bytes in the ndarray""" +//| ... +//| +//| def transpose(self) -> ulab.ndarray: +//| """Swap the rows and columns of a 2-dimensional ndarray""" +//| ... +//| +//| def __add__(self, other: Union[ndarray, _float]) -> ulab.ndarray: +//| """Adds corresponding elements of the two ndarrays, or adds a number to all +//| elements of the ndarray. If both arguments are ndarrays, their sizes must match.""" +//| ... +//| def __radd__(self, other: _float) -> ulab.ndarray: ... +//| +//| def __sub__(self, other: Union[ndarray, _float]) -> ulab.ndarray: +//| """Subtracts corresponding elements of the two ndarrays, or subtracts a number from all +//| elements of the ndarray. If both arguments are ndarrays, their sizes must match.""" +//| ... +//| def __rsub__(self, other: _float) -> ulab.ndarray: ... +//| +//| def __mul__(self, other: Union[ndarray, _float]) -> ulab.ndarray: +//| """Multiplies corresponding elements of the two ndarrays, or multiplies +//| all elements of the ndarray by a number. If both arguments are ndarrays, +//| their sizes must match.""" +//| ... +//| def __rmul__(self, other: _float) -> ulab.ndarray: ... +//| +//| def __div__(self, other: Union[ndarray, _float]) -> ulab.ndarray: +//| """Multiplies corresponding elements of the two ndarrays, or divides +//| all elements of the ndarray by a number. If both arguments are ndarrays, +//| their sizes must match.""" +//| ... +//| def __rdiv__(self, other: _float) -> ulab.ndarray: ... +//| +//| def __pow__(self, other: Union[ndarray, _float]) -> ulab.ndarray: +//| """Computes the power (x**y) of corresponding elements of the the two ndarrays, +//| or one number and one ndarray. If both arguments are ndarrays, their sizes +//| must match.""" +//| ... +//| def __rpow__(self, other: _float) -> ulab.ndarray: ... +//| +//| def __inv__(self) -> ulab.ndarray: +//| ... +//| def __neg__(self) -> ulab.ndarray: +//| ... +//| def __pos__(self) -> ulab.ndarray: +//| ... +//| def __abs__(self) -> ulab.ndarray: +//| ... +//| def __len__(self) -> int: +//| ... +//| def __lt__(self, other: Union[ndarray, _float]) -> ulab.ndarray: +//| ... +//| def __le__(self, other: Union[ndarray, _float]) -> ulab.ndarray: +//| ... +//| def __gt__(self, other: Union[ndarray, _float]) -> ulab.ndarray: +//| ... +//| def __ge__(self, other: Union[ndarray, _float]) -> ulab.ndarray: +//| ... +//| +//| def __iter__(self) -> Union[Iterator[ndarray], Iterator[_float]]: +//| ... +//| +//| def __getitem__(self, index: _Index) -> Union[ndarray, _float]: +//| """Retrieve an element of the ndarray.""" +//| ... +//| +//| def __setitem__(self, index: _Index, value: Union[ndarray, _float]) -> None: +//| """Set an element of the ndarray.""" +//| ... +//| +//| _ArrayLike = Union[ndarray, List[_float], Tuple[_float], range] +//| """`ulab.ndarray`, ``List[float]``, ``Tuple[float]`` or `range`""" +//| +//| int8: _DType +//| """Type code for signed integers in the range -128 .. 127 inclusive, like the 'b' typecode of `array.array`""" +//| +//| int16: _DType +//| """Type code for signed integers in the range -32768 .. 32767 inclusive, like the 'h' typecode of `array.array`""" +//| +//| float: _DType +//| """Type code for floating point values, like the 'f' typecode of `array.array`""" +//| +//| uint8: _DType +//| """Type code for unsigned integers in the range 0 .. 255 inclusive, like the 'H' typecode of `array.array`""" +//| +//| uint16: _DType +//| """Type code for unsigned integers in the range 0 .. 65535 inclusive, like the 'h' typecode of `array.array`""" +//| +//| bool: _DType +//| """Type code for boolean values""" +//| +//| def get_printoptions() -> Dict[str, int]: +//| """Get printing options""" +//| ... +//| +//| def set_printoptions(threshold: Optional[int] = None, edgeitems: Optional[int] = None) -> None: +//| """Set printing options""" +//| ... +//| +//| def ndinfo(array: ulab.ndarray) -> None: +//| ... +//| +//| def array( +//| values: Union[ndarray, Iterable[Union[_float, _bool, Iterable[Any]]]], +//| *, +//| dtype: _DType = ulab.float +//| ) -> ulab.ndarray: +//| """alternate constructor function for `ulab.ndarray`. Mirrors numpy.array""" +//| ... + + +#ifdef CIRCUITPY +void ndarray_set_value(char typecode, void *p, size_t index, mp_obj_t val_in) { + switch (typecode) { + case NDARRAY_INT8: + ((signed char *)p)[index] = mp_obj_get_int(val_in); + break; + case NDARRAY_UINT8: + ((unsigned char *)p)[index] = mp_obj_get_int(val_in); + break; + case NDARRAY_INT16: + ((short *)p)[index] = mp_obj_get_int(val_in); + break; + case NDARRAY_UINT16: + ((unsigned short *)p)[index] = mp_obj_get_int(val_in); + break; + case NDARRAY_FLOAT: + ((mp_float_t *)p)[index] = mp_obj_get_float(val_in); + break; + } +} +#endif + +#if defined(MICROPY_VERSION_MAJOR) && MICROPY_VERSION_MAJOR == 1 && MICROPY_VERSION_MINOR == 12 + +void mp_obj_slice_indices(mp_obj_t self_in, mp_int_t length, mp_bound_slice_t *result) { + mp_obj_slice_t *self = MP_OBJ_TO_PTR(self_in); + mp_int_t start, stop, step; + + if (self->step == mp_const_none) { + step = 1; + } else { + step = mp_obj_get_int(self->step); + if (step == 0) { + mp_raise_ValueError(translate("slice step can't be zero")); + } + } + + if (step > 0) { + // Positive step + if (self->start == mp_const_none) { + start = 0; + } else { + start = mp_obj_get_int(self->start); + if (start < 0) { + start += length; + } + start = MIN(length, MAX(start, 0)); + } + + if (self->stop == mp_const_none) { + stop = length; + } else { + stop = mp_obj_get_int(self->stop); + if (stop < 0) { + stop += length; + } + stop = MIN(length, MAX(stop, 0)); + } + } else { + // Negative step + if (self->start == mp_const_none) { + start = length - 1; + } else { + start = mp_obj_get_int(self->start); + if (start < 0) { + start += length; + } + start = MIN(length - 1, MAX(start, -1)); + } + + if (self->stop == mp_const_none) { + stop = -1; + } else { + stop = mp_obj_get_int(self->stop); + if (stop < 0) { + stop += length; + } + stop = MIN(length - 1, MAX(stop, -1)); + } + } + + result->start = start; + result->stop = stop; + result->step = step; +} +#endif /* MICROPY_VERSION v1.11 */ + +void ndarray_fill_array_iterable(mp_float_t *array, mp_obj_t iterable) { + mp_obj_iter_buf_t x_buf; + mp_obj_t x_item, x_iterable = mp_getiter(iterable, &x_buf); + while ((x_item = mp_iternext(x_iterable)) != MP_OBJ_STOP_ITERATION) { + *array++ = (mp_float_t)mp_obj_get_float(x_item); + } +} + +#if ULAB_HAS_FUNCTION_ITERATOR +size_t *ndarray_new_coords(uint8_t ndim) { + size_t *coords = m_new(size_t, ndim); + memset(coords, 0, ndim*sizeof(size_t)); + return coords; +} + +void ndarray_rewind_array(uint8_t ndim, uint8_t *array, size_t *shape, int32_t *strides, size_t *coords) { + // resets the data pointer of a single array, whenever an axis is full + // since we always iterate over the very last axis, we have to keep track of + // the last ndim-2 axes only + array -= shape[ULAB_MAX_DIMS - 1] * strides[ULAB_MAX_DIMS - 1]; + array += strides[ULAB_MAX_DIMS - 2]; + for(uint8_t i=1; i < ndim-1; i++) { + coords[ULAB_MAX_DIMS - 1 - i] += 1; + if(coords[ULAB_MAX_DIMS - 1 - i] == shape[ULAB_MAX_DIMS - 1 - i]) { // we are at a dimension boundary + array -= shape[ULAB_MAX_DIMS - 1 - i] * strides[ULAB_MAX_DIMS - 1 - i]; + array += strides[ULAB_MAX_DIMS - 2 - i]; + coords[ULAB_MAX_DIMS - 1 - i] = 0; + coords[ULAB_MAX_DIMS - 2 - i] += 1; + } else { // coordinates can change only, if the last coordinate changes + return; + } + } +} +#endif + +static int32_t *strides_from_shape(size_t *shape, uint8_t dtype) { + // returns a strides array that corresponds to a dense array with the prescribed shape + int32_t *strides = m_new(int32_t, ULAB_MAX_DIMS); + strides[ULAB_MAX_DIMS-1] = (int32_t)mp_binary_get_size('@', dtype, NULL); + for(uint8_t i=ULAB_MAX_DIMS; i > 1; i--) { + strides[i-2] = strides[i-1] * shape[i-1]; + } + return strides; +} + +size_t *ndarray_shape_vector(size_t a, size_t b, size_t c, size_t d) { + // returns a ULAB_MAX_DIMS-aware array of shapes + // WARNING: this assumes that the maximum possible dimension is 4! + size_t *shape = m_new(size_t, ULAB_MAX_DIMS); + shape[ULAB_MAX_DIMS - 1] = d; + #if ULAB_MAX_DIMS > 1 + shape[ULAB_MAX_DIMS - 2] = c; + #endif + #if ULAB_MAX_DIMS > 2 + shape[ULAB_MAX_DIMS - 3] = b; + #endif + #if ULAB_MAX_DIMS > 3 + shape[ULAB_MAX_DIMS - 4] = a; + #endif + return shape; +} + +bool ndarray_object_is_array_like(mp_obj_t o_in) { + if(mp_obj_is_type(o_in, &ulab_ndarray_type) || + mp_obj_is_type(o_in, &mp_type_tuple) || + mp_obj_is_type(o_in, &mp_type_list) || + mp_obj_is_type(o_in, &mp_type_range)) { + return true; + } + return false; +} + +void fill_array_iterable(mp_float_t *array, mp_obj_t iterable) { + mp_obj_iter_buf_t x_buf; + mp_obj_t x_item, x_iterable = mp_getiter(iterable, &x_buf); + size_t i=0; + while ((x_item = mp_iternext(x_iterable)) != MP_OBJ_STOP_ITERATION) { + array[i] = (mp_float_t)mp_obj_get_float(x_item); + i++; + } +} + +#if NDARRAY_HAS_DTYPE +#if ULAB_HAS_DTYPE_OBJECT +void ndarray_dtype_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + (void)kind; + dtype_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_print_str(print, "dtype('"); + if(self->dtype == NDARRAY_BOOLEAN) { + mp_print_str(print, "bool')"); + } else if(self->dtype == NDARRAY_UINT8) { + mp_print_str(print, "uint8')"); + } else if(self->dtype == NDARRAY_INT8) { + mp_print_str(print, "int8')"); + } else if(self->dtype == NDARRAY_UINT16) { + mp_print_str(print, "uint16')"); + } else if(self->dtype == NDARRAY_INT16) { + mp_print_str(print, "int16')"); + } else { + #if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT + mp_print_str(print, "float32')"); + #else + mp_print_str(print, "float64')"); + #endif + } +} + +mp_obj_t ndarray_dtype_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + (void) type; + mp_arg_check_num(n_args, n_kw, 0, 1, true); + mp_map_t kw_args; + mp_map_init_fixed_table(&kw_args, n_kw, args + n_args); + + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_OBJ, { .u_obj = mp_const_none } }, + }; + mp_arg_val_t _args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, args, &kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, _args); + + dtype_obj_t *dtype = m_new_obj(dtype_obj_t); + dtype->base.type = &ulab_dtype_type; + + if(mp_obj_is_type(args[0], &ulab_ndarray_type)) { + // return the dtype of the array + ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(args[0]); + dtype->dtype = ndarray->dtype; + } else { + uint8_t _dtype; + if(mp_obj_is_int(_args[0].u_obj)) { + _dtype = mp_obj_get_int(_args[0].u_obj); + if((_dtype != NDARRAY_BOOL) && (_dtype != NDARRAY_UINT8) + && (_dtype != NDARRAY_INT8) && (_dtype != NDARRAY_UINT16) + && (_dtype != NDARRAY_INT16) && (_dtype != NDARRAY_FLOAT)) { + mp_raise_TypeError(translate("data type not understood")); + } + } else { + GET_STR_DATA_LEN(_args[0].u_obj, _dtype_, len); + if(memcmp(_dtype_, "uint8", 5) == 0) { + _dtype = NDARRAY_UINT8; + } else if(memcmp(_dtype_, "int8", 4) == 0) { + _dtype = NDARRAY_INT8; + } else if(memcmp(_dtype_, "uint16", 6) == 0) { + _dtype = NDARRAY_UINT16; + } else if(memcmp(_dtype_, "int16", 5) == 0) { + _dtype = NDARRAY_INT16; + } else if(memcmp(_dtype_, "float", 5) == 0) { + _dtype = NDARRAY_FLOAT; + } else { + mp_raise_TypeError(translate("data type not understood")); + } + } + dtype->dtype = _dtype; + } + return dtype; +} + +mp_obj_t ndarray_dtype(mp_obj_t self_in) { + ndarray_obj_t *self = MP_OBJ_TO_PTR(self_in); + dtype_obj_t *dtype = m_new_obj(dtype_obj_t); + dtype->base.type = &ulab_dtype_type; + dtype->dtype = self->dtype; + return dtype; +} + +#else +// this is the cheap implementation of tbe dtype +mp_obj_t ndarray_dtype(mp_obj_t self_in) { + uint8_t dtype; + if(mp_obj_is_type(self_in, &ulab_ndarray_type)) { + ndarray_obj_t *self = MP_OBJ_TO_PTR(self_in); + dtype = self->dtype; + } else { // we assume here that the input is a single character + GET_STR_DATA_LEN(self_in, _dtype, len); + if((len != 1) || ((*_dtype != NDARRAY_BOOL) && (*_dtype != NDARRAY_UINT8) + && (*_dtype != NDARRAY_INT8) && (*_dtype != NDARRAY_UINT16) + && (*_dtype != NDARRAY_INT16) && (*_dtype != NDARRAY_FLOAT))) { + mp_raise_TypeError(translate("data type not understood")); + } + dtype = *_dtype; + } + return mp_obj_new_int(dtype); +} +#endif /* ULAB_HAS_DTYPE_OBJECT */ +#endif /* NDARRAY_HAS_DTYPE */ + +#if ULAB_HAS_PRINTOPTIONS +mp_obj_t ndarray_set_printoptions(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + static const mp_arg_t allowed_args[] = { + { MP_QSTR_threshold, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = mp_const_none} }, + { MP_QSTR_edgeitems, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = mp_const_none} }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + if(args[0].u_rom_obj != mp_const_none) { + ndarray_print_threshold = mp_obj_get_int(args[0].u_rom_obj); + } + if(args[1].u_rom_obj != mp_const_none) { + ndarray_print_edgeitems = mp_obj_get_int(args[1].u_rom_obj); + } + return mp_const_none; +} + +MP_DEFINE_CONST_FUN_OBJ_KW(ndarray_set_printoptions_obj, 0, ndarray_set_printoptions); + +mp_obj_t ndarray_get_printoptions(void) { + mp_obj_t dict = mp_obj_new_dict(2); + mp_obj_dict_store(MP_OBJ_FROM_PTR(dict), MP_OBJ_NEW_QSTR(MP_QSTR_threshold), mp_obj_new_int(ndarray_print_threshold)); + mp_obj_dict_store(MP_OBJ_FROM_PTR(dict), MP_OBJ_NEW_QSTR(MP_QSTR_edgeitems), mp_obj_new_int(ndarray_print_edgeitems)); + return dict; +} + +MP_DEFINE_CONST_FUN_OBJ_0(ndarray_get_printoptions_obj, ndarray_get_printoptions); +#endif + +mp_obj_t ndarray_get_item(ndarray_obj_t *ndarray, void *array) { + // returns a proper micropython object from an array + if(!ndarray->boolean) { + return mp_binary_get_val_array(ndarray->dtype, array, 0); + } else { + if(*(uint8_t *)array) { + return mp_const_true; + } else { + return mp_const_false; + } + } +} + +static void ndarray_print_row(const mp_print_t *print, ndarray_obj_t * ndarray, uint8_t *array, size_t stride, size_t n) { + if(n == 0) { + return; + } + mp_print_str(print, "["); + if((n <= ndarray_print_threshold) || (n <= 2*ndarray_print_edgeitems)) { // if the array is short, print everything + mp_obj_print_helper(print, ndarray_get_item(ndarray, array), PRINT_REPR); + array += stride; + for(size_t i=1; i < n; i++, array += stride) { + mp_print_str(print, ", "); + mp_obj_print_helper(print, ndarray_get_item(ndarray, array), PRINT_REPR); + } + } else { + mp_obj_print_helper(print, ndarray_get_item(ndarray, array), PRINT_REPR); + array += stride; + for(size_t i=1; i < ndarray_print_edgeitems; i++, array += stride) { + mp_print_str(print, ", "); + mp_obj_print_helper(print, ndarray_get_item(ndarray, array), PRINT_REPR); + } + mp_printf(print, ", ..., "); + array += stride * (n - 2 * ndarray_print_edgeitems); + mp_obj_print_helper(print, ndarray_get_item(ndarray, array), PRINT_REPR); + array += stride; + for(size_t i=1; i < ndarray_print_edgeitems; i++, array += stride) { + mp_print_str(print, ", "); + mp_obj_print_helper(print, ndarray_get_item(ndarray, array), PRINT_REPR); + } + } + mp_print_str(print, "]"); +} + +static void ndarray_print_bracket(const mp_print_t *print, const size_t condition, const size_t shape, const char *string) { + if(condition < shape) { + mp_print_str(print, string); + } +} + +void ndarray_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + (void)kind; + ndarray_obj_t *self = MP_OBJ_TO_PTR(self_in); + uint8_t *array = (uint8_t *)self->array; + mp_print_str(print, "array("); + if(self->len == 0) { + mp_print_str(print, "[]"); + if(self->ndim > 1) { + mp_print_str(print, ", shape=("); + for(uint8_t ndim = self->ndim; ndim > 1; ndim--) { + mp_printf(MP_PYTHON_PRINTER, "%d,", self->shape[ULAB_MAX_DIMS - ndim]); + } + mp_printf(MP_PYTHON_PRINTER, "%d)", self->shape[ULAB_MAX_DIMS - 1]); + } + } else { + #if ULAB_MAX_DIMS > 3 + size_t i=0; + ndarray_print_bracket(print, 0, self->shape[ULAB_MAX_DIMS-4], "["); + do { + #endif + #if ULAB_MAX_DIMS > 2 + size_t j = 0; + ndarray_print_bracket(print, 0, self->shape[ULAB_MAX_DIMS-3], "["); + do { + #endif + #if ULAB_MAX_DIMS > 1 + size_t k = 0; + ndarray_print_bracket(print, 0, self->shape[ULAB_MAX_DIMS-2], "["); + do { + #endif + ndarray_print_row(print, self, array, self->strides[ULAB_MAX_DIMS-1], self->shape[ULAB_MAX_DIMS-1]); + #if ULAB_MAX_DIMS > 1 + array += self->strides[ULAB_MAX_DIMS-2]; + k++; + ndarray_print_bracket(print, k, self->shape[ULAB_MAX_DIMS-2], ",\n "); + } while(k < self->shape[ULAB_MAX_DIMS-2]); + ndarray_print_bracket(print, 0, self->shape[ULAB_MAX_DIMS-2], "]"); + #endif + #if ULAB_MAX_DIMS > 2 + j++; + ndarray_print_bracket(print, j, self->shape[ULAB_MAX_DIMS-3], ",\n\n "); + array -= self->strides[ULAB_MAX_DIMS-2] * self->shape[ULAB_MAX_DIMS-2]; + array += self->strides[ULAB_MAX_DIMS-3]; + } while(j < self->shape[ULAB_MAX_DIMS-3]); + ndarray_print_bracket(print, 0, self->shape[ULAB_MAX_DIMS-3], "]"); + #endif + #if ULAB_MAX_DIMS > 3 + array -= self->strides[ULAB_MAX_DIMS-3] * self->shape[ULAB_MAX_DIMS-3]; + array += self->strides[ULAB_MAX_DIMS-4]; + i++; + ndarray_print_bracket(print, i, self->shape[ULAB_MAX_DIMS-4], ",\n\n "); + } while(i < self->shape[ULAB_MAX_DIMS-4]); + ndarray_print_bracket(print, 0, self->shape[ULAB_MAX_DIMS-4], "]"); + #endif + } + if(self->boolean) { + mp_print_str(print, ", dtype=bool)"); + } else if(self->dtype == NDARRAY_UINT8) { + mp_print_str(print, ", dtype=uint8)"); + } else if(self->dtype == NDARRAY_INT8) { + mp_print_str(print, ", dtype=int8)"); + } else if(self->dtype == NDARRAY_UINT16) { + mp_print_str(print, ", dtype=uint16)"); + } else if(self->dtype == NDARRAY_INT16) { + mp_print_str(print, ", dtype=int16)"); + } else { + #if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT + mp_print_str(print, ", dtype=float32)"); + #else + mp_print_str(print, ", dtype=float64)"); + #endif + } +} + +void ndarray_assign_elements(ndarray_obj_t *ndarray, mp_obj_t iterable, uint8_t dtype, size_t *idx) { + // assigns a single row in the tensor + mp_obj_t item; + if(ndarray->boolean) { + uint8_t *array = (uint8_t *)ndarray->array; + array += *idx; + while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) { + // TODO: this might be wrong here: we have to check for the trueness of item + if(mp_obj_is_true(item)) { + *array = 1; + } + array++; + (*idx)++; + } + } else { + while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) { + ndarray_set_value(dtype, ndarray->array, (*idx)++, item); + } + } +} + +bool ndarray_is_dense(ndarray_obj_t *ndarray) { + // returns true, if the array is dense, false otherwise + // the array should be dense, if the very first stride can be calculated from shape + int32_t stride = ndarray->itemsize; + for(uint8_t i = ULAB_MAX_DIMS - 1; i > ULAB_MAX_DIMS-ndarray->ndim; i--) { + stride *= ndarray->shape[i]; + } + return stride == ndarray->strides[ULAB_MAX_DIMS-ndarray->ndim] ? true : false; +} + + +ndarray_obj_t *ndarray_new_ndarray(uint8_t ndim, size_t *shape, int32_t *strides, uint8_t dtype) { + // Creates the base ndarray with shape, and initialises the values to straight 0s + ndarray_obj_t *ndarray = m_new_obj(ndarray_obj_t); + ndarray->base.type = &ulab_ndarray_type; + ndarray->dtype = dtype == NDARRAY_BOOL ? NDARRAY_UINT8 : dtype; + ndarray->boolean = dtype == NDARRAY_BOOL ? NDARRAY_BOOLEAN : NDARRAY_NUMERIC; + ndarray->ndim = ndim; + ndarray->len = ndim == 0 ? 0 : 1; + ndarray->itemsize = mp_binary_get_size('@', ndarray->dtype, NULL); + int32_t *_strides; + if(strides == NULL) { + _strides = strides_from_shape(shape, ndarray->dtype); + } else { + _strides = strides; + } + for(uint8_t i=ULAB_MAX_DIMS; i > ULAB_MAX_DIMS-ndim; i--) { + ndarray->shape[i-1] = shape[i-1]; + ndarray->strides[i-1] = _strides[i-1]; + ndarray->len *= shape[i-1]; + } + + // if the length is 0, still allocate a single item, so that contractions can be handled + size_t len = ndarray->itemsize * MAX(1, ndarray->len); + uint8_t *array = m_new(byte, len); + // this should set all elements to 0, irrespective of the of the dtype (all bits are zero) + // we could, perhaps, leave this step out, and initialise the array only, when needed + memset(array, 0, len); + ndarray->array = array; + ndarray->origin = array; + return ndarray; +} + +ndarray_obj_t *ndarray_new_dense_ndarray(uint8_t ndim, size_t *shape, uint8_t dtype) { + // creates a dense array, i.e., one, where the strides are derived directly from the shapes + // the function should work in the general n-dimensional case + int32_t *strides = m_new(int32_t, ULAB_MAX_DIMS); + strides[ULAB_MAX_DIMS-1] = dtype == NDARRAY_BOOL ? 1 : mp_binary_get_size('@', dtype, NULL); + for(size_t i=ULAB_MAX_DIMS; i > 1; i--) { + strides[i-2] = strides[i-1] * MAX(1, shape[i-1]); + } + return ndarray_new_ndarray(ndim, shape, strides, dtype); +} + +ndarray_obj_t *ndarray_new_ndarray_from_tuple(mp_obj_tuple_t *_shape, uint8_t dtype) { + // creates a dense array from a tuple + // the function should work in the general n-dimensional case + size_t *shape = m_new(size_t, ULAB_MAX_DIMS); + for(size_t i=0; i < ULAB_MAX_DIMS; i++) { + if(i < ULAB_MAX_DIMS - _shape->len) { + shape[i] = 0; + } else { + shape[i] = mp_obj_get_int(_shape->items[i]); + } + } + return ndarray_new_dense_ndarray(_shape->len, shape, dtype); +} + +void ndarray_copy_array(ndarray_obj_t *source, ndarray_obj_t *target) { + // TODO: if the array is dense, the content could be copied in a single pass + // copies the content of source->array into a new dense void pointer + // it is assumed that the dtypes in source and target are the same + // Since the target is a new array, it is supposed to be dense + uint8_t *sarray = (uint8_t *)source->array; + uint8_t *tarray = (uint8_t *)target->array; + + #if ULAB_MAX_DIMS > 3 + size_t i = 0; + do { + #endif + #if ULAB_MAX_DIMS > 2 + size_t j = 0; + do { + #endif + #if ULAB_MAX_DIMS > 1 + size_t k = 0; + do { + #endif + size_t l = 0; + do { + memcpy(tarray, sarray, source->itemsize); + tarray += target->itemsize; + sarray += source->strides[ULAB_MAX_DIMS - 1]; + l++; + } while(l < source->shape[ULAB_MAX_DIMS - 1]); + #if ULAB_MAX_DIMS > 1 + sarray -= source->strides[ULAB_MAX_DIMS - 1] * source->shape[ULAB_MAX_DIMS-1]; + sarray += source->strides[ULAB_MAX_DIMS - 2]; + k++; + } while(k < source->shape[ULAB_MAX_DIMS - 2]); + #endif + #if ULAB_MAX_DIMS > 2 + sarray -= source->strides[ULAB_MAX_DIMS - 2] * source->shape[ULAB_MAX_DIMS-2]; + sarray += source->strides[ULAB_MAX_DIMS - 3]; + j++; + } while(j < source->shape[ULAB_MAX_DIMS - 3]); + #endif + #if ULAB_MAX_DIMS > 3 + sarray -= source->strides[ULAB_MAX_DIMS - 3] * source->shape[ULAB_MAX_DIMS-3]; + sarray += source->strides[ULAB_MAX_DIMS - 4]; + i++; + } while(i < source->shape[ULAB_MAX_DIMS - 4]); + #endif +} + +ndarray_obj_t *ndarray_new_view(ndarray_obj_t *source, uint8_t ndim, size_t *shape, int32_t *strides, int32_t offset) { + // creates a new view from the input arguments + ndarray_obj_t *ndarray = m_new_obj(ndarray_obj_t); + ndarray->base.type = &ulab_ndarray_type; + ndarray->boolean = source->boolean; + ndarray->dtype = source->dtype; + ndarray->ndim = ndim; + ndarray->itemsize = source->itemsize; + ndarray->len = ndim == 0 ? 0 : 1; + for(uint8_t i=ULAB_MAX_DIMS; i > ULAB_MAX_DIMS-ndim; i--) { + ndarray->shape[i-1] = shape[i-1]; + ndarray->strides[i-1] = strides[i-1]; + ndarray->len *= shape[i-1]; + } + uint8_t *pointer = (uint8_t *)source->array; + pointer += offset; + ndarray->array = pointer; + ndarray->origin = source->origin; + return ndarray; +} + +ndarray_obj_t *ndarray_copy_view(ndarray_obj_t *source) { + // creates a one-to-one deep copy of the input ndarray or its view + // the function should work in the general n-dimensional case + // In order to make it dtype-agnostic, we copy the memory content + // instead of reading out the values + + int32_t *strides = strides_from_shape(source->shape, source->dtype); + + uint8_t dtype = source->dtype; + if(source->boolean) { + dtype = NDARRAY_BOOLEAN; + } + ndarray_obj_t *ndarray = ndarray_new_ndarray(source->ndim, source->shape, strides, dtype); + ndarray_copy_array(source, ndarray); + return ndarray; +} + +#if NDARRAY_HAS_BYTESWAP +mp_obj_t ndarray_byteswap(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + // changes the endiannes of an array + // if the dtype of the input uint8/int8/bool, simply return a copy or view + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = mp_const_none } }, + { MP_QSTR_inplace, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = mp_const_false } }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + ndarray_obj_t *self = MP_OBJ_TO_PTR(args[0].u_obj); + ndarray_obj_t *ndarray = NULL; + if(args[1].u_obj == mp_const_false) { + ndarray = ndarray_copy_view(self); + } else { + ndarray = ndarray_new_view(self, self->ndim, self->shape, self->strides, 0); + } + if((self->dtype == NDARRAY_BOOL) || (self->dtype == NDARRAY_UINT8) || (self->dtype == NDARRAY_INT8)) { + return MP_OBJ_FROM_PTR(ndarray); + } else { + uint8_t *array = (uint8_t *)ndarray->array; + #if ULAB_MAX_DIMS > 3 + size_t i = 0; + do { + #endif + #if ULAB_MAX_DIMS > 2 + size_t j = 0; + do { + #endif + #if ULAB_MAX_DIMS > 1 + size_t k = 0; + do { + #endif + size_t l = 0; + do { + if(self->dtype == NDARRAY_FLOAT) { + #if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT + SWAP(uint8_t, array[0], array[3]); + SWAP(uint8_t, array[1], array[2]); + #else + SWAP(uint8_t, array[0], array[7]); + SWAP(uint8_t, array[1], array[6]); + SWAP(uint8_t, array[2], array[5]); + SWAP(uint8_t, array[3], array[4]); + #endif + } else { + SWAP(uint8_t, array[0], array[1]); + } + array += ndarray->strides[ULAB_MAX_DIMS - 1]; + l++; + } while(l < ndarray->shape[ULAB_MAX_DIMS - 1]); + #if ULAB_MAX_DIMS > 1 + array -= ndarray->strides[ULAB_MAX_DIMS - 1] * ndarray->shape[ULAB_MAX_DIMS-1]; + array += ndarray->strides[ULAB_MAX_DIMS - 2]; + k++; + } while(k < ndarray->shape[ULAB_MAX_DIMS - 2]); + #endif + #if ULAB_MAX_DIMS > 2 + array -= ndarray->strides[ULAB_MAX_DIMS - 2] * ndarray->shape[ULAB_MAX_DIMS-2]; + array += ndarray->strides[ULAB_MAX_DIMS - 3]; + j++; + } while(j < ndarray->shape[ULAB_MAX_DIMS - 3]); + #endif + #if ULAB_MAX_DIMS > 3 + array -= ndarray->strides[ULAB_MAX_DIMS - 3] * ndarray->shape[ULAB_MAX_DIMS-3]; + array += ndarray->strides[ULAB_MAX_DIMS - 4]; + i++; + } while(i < ndarray->shape[ULAB_MAX_DIMS - 4]); + #endif + } + return MP_OBJ_FROM_PTR(ndarray); +} + +MP_DEFINE_CONST_FUN_OBJ_KW(ndarray_byteswap_obj, 1, ndarray_byteswap); +#endif + +#if NDARRAY_HAS_COPY +mp_obj_t ndarray_copy(mp_obj_t self_in) { + ndarray_obj_t *self = MP_OBJ_TO_PTR(self_in); + return MP_OBJ_FROM_PTR(ndarray_copy_view(self)); +} + +MP_DEFINE_CONST_FUN_OBJ_1(ndarray_copy_obj, ndarray_copy); +#endif + +ndarray_obj_t *ndarray_new_linear_array(size_t len, uint8_t dtype) { + size_t *shape = m_new(size_t, ULAB_MAX_DIMS); + if(len == 0) { + return ndarray_new_dense_ndarray(0, shape, dtype); + } + shape[ULAB_MAX_DIMS-1] = len; + return ndarray_new_dense_ndarray(1, shape, dtype); +} + +ndarray_obj_t *ndarray_from_iterable(mp_obj_t obj, uint8_t dtype) { + // returns an ndarray from an iterable micropython object + // if the input is an ndarray, returns the input... + if(mp_obj_is_type(obj, &ulab_ndarray_type)) { + return MP_OBJ_TO_PTR(obj); + } + // ... otherwise, takes the values from the iterable, and creates the corresponding ndarray + + // First, we have to figure out, whether the elements of the iterable are iterables themself + uint8_t ndim = 0; + size_t shape[ULAB_MAX_DIMS]; + mp_obj_iter_buf_t iter_buf[ULAB_MAX_DIMS]; + mp_obj_t iterable[ULAB_MAX_DIMS]; + // inspect only the very first element in each dimension; this is fast, + // but not completely safe, e.g., length compatibility is not checked + mp_obj_t item = obj; + + while(1) { + if(mp_obj_len_maybe(item) == MP_OBJ_NULL) { + break; + } + if(ndim == ULAB_MAX_DIMS) { + mp_raise_ValueError(translate("too many dimensions")); + } + shape[ndim] = MP_OBJ_SMALL_INT_VALUE(mp_obj_len_maybe(item)); + if(shape[ndim] == 0) { + ndim++; + break; + } + iterable[ndim] = mp_getiter(item, &iter_buf[ndim]); + item = mp_iternext(iterable[ndim]); + ndim++; + } + for(uint8_t i = 0; i < ndim; i++) { + // align all values to the right + shape[ULAB_MAX_DIMS - i - 1] = shape[ndim - 1 - i]; + } + + ndarray_obj_t *ndarray = ndarray_new_dense_ndarray(ndim, shape, dtype); + item = obj; + for(uint8_t i = 0; i < ndim - 1; i++) { + // if ndim > 1, descend into the hierarchy + iterable[ULAB_MAX_DIMS - ndim + i] = mp_getiter(item, &iter_buf[ULAB_MAX_DIMS - ndim + i]); + item = mp_iternext(iterable[ULAB_MAX_DIMS - ndim + i]); + } + + size_t idx = 0; + // TODO: this could surely be done in a more elegant way... + #if ULAB_MAX_DIMS > 3 + do { + #endif + #if ULAB_MAX_DIMS > 2 + do { + #endif + #if ULAB_MAX_DIMS > 1 + do { + #endif + iterable[ULAB_MAX_DIMS - 1] = mp_getiter(item, &iter_buf[ULAB_MAX_DIMS - 1]); + ndarray_assign_elements(ndarray, iterable[ULAB_MAX_DIMS - 1], ndarray->dtype, &idx); + #if ULAB_MAX_DIMS > 1 + item = ndim > 1 ? mp_iternext(iterable[ULAB_MAX_DIMS - 2]) : MP_OBJ_STOP_ITERATION; + } while(item != MP_OBJ_STOP_ITERATION); + #endif + #if ULAB_MAX_DIMS > 2 + item = ndim > 2 ? mp_iternext(iterable[ULAB_MAX_DIMS - 3]) : MP_OBJ_STOP_ITERATION; + if(item != MP_OBJ_STOP_ITERATION) { + iterable[ULAB_MAX_DIMS - 2] = mp_getiter(item, &iter_buf[ULAB_MAX_DIMS - 2]); + item = mp_iternext(iterable[ULAB_MAX_DIMS - 2]); + } else { + iterable[ULAB_MAX_DIMS - 2] = MP_OBJ_STOP_ITERATION; + } + } while(iterable[ULAB_MAX_DIMS - 2] != MP_OBJ_STOP_ITERATION); + #endif + #if ULAB_MAX_DIMS > 3 + item = ndim > 3 ? mp_iternext(iterable[ULAB_MAX_DIMS - 4]) : MP_OBJ_STOP_ITERATION; + if(item != MP_OBJ_STOP_ITERATION) { + iterable[ULAB_MAX_DIMS - 3] = mp_getiter(item, &iter_buf[ULAB_MAX_DIMS - 3]); + item = mp_iternext(iterable[ULAB_MAX_DIMS - 3]); + } else { + iterable[ULAB_MAX_DIMS - 3] = MP_OBJ_STOP_ITERATION; + } + } while(iterable[ULAB_MAX_DIMS - 3] != MP_OBJ_STOP_ITERATION); + #endif + + return ndarray; +} + +STATIC uint8_t ndarray_init_helper(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } }, + { MP_QSTR_dtype, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_ROM_INT(NDARRAY_FLOAT) } }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + uint8_t _dtype; + #if ULAB_HAS_DTYPE_OBJECT + if(mp_obj_is_type(args[1].u_obj, &ulab_dtype_type)) { + dtype_obj_t *dtype = MP_OBJ_TO_PTR(args[1].u_obj); + _dtype = dtype->dtype; + } else { // this must be an integer defined as a class constant (ulba.uint8 etc.) + _dtype = mp_obj_get_int(args[1].u_obj); + } + #else + _dtype = mp_obj_get_int(args[1].u_obj); + #endif + return _dtype; +} + +STATIC mp_obj_t ndarray_make_new_core(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args, mp_map_t *kw_args) { + uint8_t dtype = ndarray_init_helper(n_args, args, kw_args); + + if(mp_obj_is_type(args[0], &ulab_ndarray_type)) { + ndarray_obj_t *source = MP_OBJ_TO_PTR(args[0]); + if(dtype == source->dtype) { + return ndarray_copy_view(source); + } + ndarray_obj_t *target = ndarray_new_dense_ndarray(source->ndim, source->shape, dtype); + uint8_t *sarray = (uint8_t *)source->array; + uint8_t *tarray = (uint8_t *)target->array; + #if ULAB_MAX_DIMS > 3 + size_t i = 0; + do { + #endif + #if ULAB_MAX_DIMS > 2 + size_t j = 0; + do { + #endif + #if ULAB_MAX_DIMS > 1 + size_t k = 0; + do { + #endif + size_t l = 0; + do { + mp_obj_t item; + if((source->dtype == NDARRAY_FLOAT) && (dtype != NDARRAY_FLOAT)) { + // floats must be treated separately, because they can't directly be converted to integer types + mp_float_t f = ndarray_get_float_value(sarray, source->dtype); + item = mp_obj_new_int((int32_t)MICROPY_FLOAT_C_FUN(floor)(f)); + } else { + item = mp_binary_get_val_array(source->dtype, sarray, 0); + } + ndarray_set_value(dtype, tarray, 0, item); + tarray += target->itemsize; + sarray += source->strides[ULAB_MAX_DIMS - 1]; + l++; + } while(l < source->shape[ULAB_MAX_DIMS - 1]); + #if ULAB_MAX_DIMS > 1 + sarray -= source->strides[ULAB_MAX_DIMS - 1] * source->shape[ULAB_MAX_DIMS-1]; + sarray += source->strides[ULAB_MAX_DIMS - 2]; + k++; + } while(k < source->shape[ULAB_MAX_DIMS - 2]); + #endif + #if ULAB_MAX_DIMS > 2 + sarray -= source->strides[ULAB_MAX_DIMS - 2] * source->shape[ULAB_MAX_DIMS-2]; + sarray += source->strides[ULAB_MAX_DIMS - 3]; + j++; + } while(j < source->shape[ULAB_MAX_DIMS - 3]); + #endif + #if ULAB_MAX_DIMS > 3 + sarray -= source->strides[ULAB_MAX_DIMS - 3] * source->shape[ULAB_MAX_DIMS-3]; + sarray += source->strides[ULAB_MAX_DIMS - 4]; + i++; + } while(i < source->shape[ULAB_MAX_DIMS - 4]); + #endif + return MP_OBJ_FROM_PTR(target); + } else { + // assume that the input is an iterable + return MP_OBJ_FROM_PTR(ndarray_from_iterable(args[0], dtype)); + } +} + +mp_obj_t ndarray_array_constructor(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + // array constructor for ndarray, equivalent to numpy.array(...) + return ndarray_make_new_core(&ulab_ndarray_type, n_args, kw_args->used, pos_args, kw_args); +} +MP_DEFINE_CONST_FUN_OBJ_KW(ndarray_array_constructor_obj, 1, ndarray_array_constructor); + +#ifdef CIRCUITPY +mp_obj_t ndarray_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { + (void) type; + mp_arg_check_num(n_args, kw_args, 1, 2, true); + size_t n_kw = 0; + if (kw_args != 0) { + n_kw = kw_args->used; + } + mp_map_init_fixed_table(kw_args, n_kw, args + n_args); + return ndarray_make_new_core(type, n_args, n_kw, args, kw_args); +} +#else +mp_obj_t ndarray_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + (void) type; + mp_arg_check_num(n_args, n_kw, 1, 2, true); + mp_map_t kw_args; + mp_map_init_fixed_table(&kw_args, n_kw, args + n_args); + return ndarray_make_new_core(type, n_args, n_kw, args, &kw_args); +} +#endif + +// broadcasting is used at a number of places, always include +bool ndarray_can_broadcast(ndarray_obj_t *lhs, ndarray_obj_t *rhs, uint8_t *ndim, size_t *shape, int32_t *lstrides, int32_t *rstrides) { + // Returns true or false, depending on, whether the two arrays can be broadcast together + // with numpy's broadcasting rules. These are as follows: + // + // 1. the two shapes are either equal + // 2. one of the shapes is 1 + memset(lstrides, 0, sizeof(size_t)*ULAB_MAX_DIMS); + memset(rstrides, 0, sizeof(size_t)*ULAB_MAX_DIMS); + lstrides[ULAB_MAX_DIMS - 1] = lhs->strides[ULAB_MAX_DIMS - 1]; + rstrides[ULAB_MAX_DIMS - 1] = rhs->strides[ULAB_MAX_DIMS - 1]; + for(uint8_t i=ULAB_MAX_DIMS; i > 0; i--) { + if((lhs->shape[i-1] == rhs->shape[i-1]) || (lhs->shape[i-1] == 0) || (lhs->shape[i-1] == 1) || + (rhs->shape[i-1] == 0) || (rhs->shape[i-1] == 1)) { + shape[i-1] = MAX(lhs->shape[i-1], rhs->shape[i-1]); + if(shape[i-1] > 0) (*ndim)++; + if(lhs->shape[i-1] < 2) { + lstrides[i-1] = 0; + } else { + lstrides[i-1] = lhs->strides[i-1]; + } + if(rhs->shape[i-1] < 2) { + rstrides[i-1] = 0; + } else { + rstrides[i-1] = rhs->strides[i-1]; + } + } else { + return false; + } + } + return true; +} + +#if NDARRAY_HAS_INPLACE_OPS +bool ndarray_can_broadcast_inplace(ndarray_obj_t *lhs, ndarray_obj_t *rhs, int32_t *rstrides) { + // returns true or false, depending on, whether the two arrays can be broadcast together inplace + // this means that the right hand side always must be "smaller" than the left hand side, i.e. + // the broadcasting rules are as follows: + // + // 1. the two shapes are either equal + // 2. the shapes on the right hand side is 1 + memset(rstrides, 0, sizeof(size_t)*ULAB_MAX_DIMS); + rstrides[ULAB_MAX_DIMS - 1] = rhs->strides[ULAB_MAX_DIMS - 1]; + for(uint8_t i=ULAB_MAX_DIMS; i > 0; i--) { + if((lhs->shape[i-1] == rhs->shape[i-1]) || (rhs->shape[i-1] == 0) || (rhs->shape[i-1] == 1)) { + if(rhs->shape[i-1] < 2) { + rstrides[i-1] = 0; + } else { + rstrides[i-1] = rhs->strides[i-1]; + } + } else { + return false; + } + } + return true; +} +#endif + +#if NDARRAY_IS_SLICEABLE +static size_t slice_length(mp_bound_slice_t slice) { + ssize_t len, correction = 1; + if(slice.step > 0) correction = -1; + len = (ssize_t)(slice.stop - slice.start + (slice.step + correction)) / slice.step; + if(len < 0) return 0; + return (size_t)len; +} + +static mp_bound_slice_t generate_slice(mp_int_t n, mp_obj_t index) { + mp_bound_slice_t slice; + if(mp_obj_is_type(index, &mp_type_slice)) { + mp_obj_slice_indices(index, n, &slice); + } else if(mp_obj_is_int(index)) { + mp_int_t _index = mp_obj_get_int(index); + if(_index < 0) { + _index += n; + } + if((_index >= n) || (_index < 0)) { + mp_raise_msg(&mp_type_IndexError, translate("index is out of bounds")); + } + slice.start = _index; + slice.stop = _index + 1; + slice.step = 1; + } else { + mp_raise_msg(&mp_type_IndexError, translate("indices must be integers, slices, or Boolean lists")); + } + return slice; +} + +static ndarray_obj_t *ndarray_view_from_slices(ndarray_obj_t *ndarray, mp_obj_tuple_t *tuple) { + size_t *shape = m_new(size_t, ULAB_MAX_DIMS); + memset(shape, 0, sizeof(size_t)*ULAB_MAX_DIMS); + int32_t *strides = m_new(int32_t, ULAB_MAX_DIMS); + memset(strides, 0, sizeof(size_t)*ULAB_MAX_DIMS); + + uint8_t ndim = ndarray->ndim; + + for(uint8_t i=0; i < ndim; i++) { + // copy from the end + shape[ULAB_MAX_DIMS - 1 - i] = ndarray->shape[ULAB_MAX_DIMS - 1 - i]; + strides[ULAB_MAX_DIMS - 1 - i] = ndarray->strides[ULAB_MAX_DIMS - 1 - i]; + } + int32_t offset = 0; + for(uint8_t i=0; i < tuple->len; i++) { + if(mp_obj_is_int(tuple->items[i])) { + // if item is an int, the dimension will first be reduced ... + ndim--; + int32_t k = mp_obj_get_int(tuple->items[i]); + if(k < 0) { + k += ndarray->shape[ULAB_MAX_DIMS - ndarray->ndim + i]; + } + if((k >= (int32_t)ndarray->shape[ULAB_MAX_DIMS - ndarray->ndim + i]) || (k < 0)) { + mp_raise_msg(&mp_type_IndexError, translate("index is out of bounds")); + } + offset += ndarray->strides[ULAB_MAX_DIMS - ndarray->ndim + i] * k; + // ... and then we have to shift the shapes to the right + for(uint8_t j=0; j < i; j++) { + shape[ULAB_MAX_DIMS - ndarray->ndim + i - j] = shape[ULAB_MAX_DIMS - ndarray->ndim + i - j - 1]; + strides[ULAB_MAX_DIMS - ndarray->ndim + i - j] = strides[ULAB_MAX_DIMS - ndarray->ndim + i - j - 1]; + } + } else { + mp_bound_slice_t slice = generate_slice(shape[ULAB_MAX_DIMS - ndarray->ndim + i], tuple->items[i]); + shape[ULAB_MAX_DIMS - ndarray->ndim + i] = slice_length(slice); + offset += ndarray->strides[ULAB_MAX_DIMS - ndarray->ndim + i] * (int32_t)slice.start; + strides[ULAB_MAX_DIMS - ndarray->ndim + i] = (int32_t)slice.step * ndarray->strides[ULAB_MAX_DIMS - ndarray->ndim + i]; + } + } + return ndarray_new_view(ndarray, ndim, shape, strides, offset); +} + +void ndarray_assign_view(ndarray_obj_t *view, ndarray_obj_t *values) { + if(values->len == 0) { + return; + } + uint8_t ndim = 0; + size_t *shape = m_new(size_t, ULAB_MAX_DIMS); + int32_t *lstrides = m_new(int32_t, ULAB_MAX_DIMS); + int32_t *rstrides = m_new(int32_t, ULAB_MAX_DIMS); + if(!ndarray_can_broadcast(view, values, &ndim, shape, lstrides, rstrides)) { + mp_raise_ValueError(translate("operands could not be broadcast together")); + m_del(size_t, shape, ULAB_MAX_DIMS); + m_del(int32_t, lstrides, ULAB_MAX_DIMS); + m_del(int32_t, rstrides, ULAB_MAX_DIMS); + } + + uint8_t *rarray = (uint8_t *)values->array; + // since in ASSIGNMENT_LOOP the array has a type, we have to divide the strides by the itemsize + for(uint8_t i=0; i < ULAB_MAX_DIMS; i++) { + lstrides[i] /= view->itemsize; + } + + if(view->dtype == NDARRAY_UINT8) { + if(values->dtype == NDARRAY_UINT8) { + ASSIGNMENT_LOOP(view, uint8_t, uint8_t, lstrides, rarray, rstrides); + } else if(values->dtype == NDARRAY_INT8) { + ASSIGNMENT_LOOP(view, uint8_t, int8_t, lstrides, rarray, rstrides); + } else if(values->dtype == NDARRAY_UINT16) { + ASSIGNMENT_LOOP(view, uint8_t, uint16_t, lstrides, rarray, rstrides); + } else if(values->dtype == NDARRAY_INT16) { + ASSIGNMENT_LOOP(view, uint8_t, int16_t, lstrides, rarray, rstrides); + } else if(values->dtype == NDARRAY_FLOAT) { + ASSIGNMENT_LOOP(view, uint8_t, mp_float_t, lstrides, rarray, rstrides); + } + } else if(view->dtype == NDARRAY_INT8) { + if(values->dtype == NDARRAY_UINT8) { + ASSIGNMENT_LOOP(view, int8_t, uint8_t, lstrides, rarray, rstrides); + } else if(values->dtype == NDARRAY_INT8) { + ASSIGNMENT_LOOP(view, int8_t, int8_t, lstrides, rarray, rstrides); + } else if(values->dtype == NDARRAY_UINT16) { + ASSIGNMENT_LOOP(view, int8_t, uint16_t, lstrides, rarray, rstrides); + } else if(values->dtype == NDARRAY_INT16) { + ASSIGNMENT_LOOP(view, int8_t, int16_t, lstrides, rarray, rstrides); + } else if(values->dtype == NDARRAY_FLOAT) { + ASSIGNMENT_LOOP(view, int8_t, mp_float_t, lstrides, rarray, rstrides); + } + } else if(view->dtype == NDARRAY_UINT16) { + if(values->dtype == NDARRAY_UINT8) { + ASSIGNMENT_LOOP(view, uint16_t, uint8_t, lstrides, rarray, rstrides); + } else if(values->dtype == NDARRAY_INT8) { + ASSIGNMENT_LOOP(view, uint16_t, int8_t, lstrides, rarray, rstrides); + } else if(values->dtype == NDARRAY_UINT16) { + ASSIGNMENT_LOOP(view, uint16_t, uint16_t, lstrides, rarray, rstrides); + } else if(values->dtype == NDARRAY_INT16) { + ASSIGNMENT_LOOP(view, uint16_t, int16_t, lstrides, rarray, rstrides); + } else if(values->dtype == NDARRAY_FLOAT) { + ASSIGNMENT_LOOP(view, uint16_t, mp_float_t, lstrides, rarray, rstrides); + } + } else if(view->dtype == NDARRAY_INT16) { + if(values->dtype == NDARRAY_UINT8) { + ASSIGNMENT_LOOP(view, int16_t, uint8_t, lstrides, rarray, rstrides); + } else if(values->dtype == NDARRAY_INT8) { + ASSIGNMENT_LOOP(view, int16_t, int8_t, lstrides, rarray, rstrides); + } else if(values->dtype == NDARRAY_UINT16) { + ASSIGNMENT_LOOP(view, int16_t, uint16_t, lstrides, rarray, rstrides); + } else if(values->dtype == NDARRAY_INT16) { + ASSIGNMENT_LOOP(view, int16_t, int16_t, lstrides, rarray, rstrides); + } else if(values->dtype == NDARRAY_FLOAT) { + ASSIGNMENT_LOOP(view, int16_t, mp_float_t, lstrides, rarray, rstrides); + } + } else { // the dtype must be an mp_float_t now + if(values->dtype == NDARRAY_UINT8) { + ASSIGNMENT_LOOP(view, mp_float_t, uint8_t, lstrides, rarray, rstrides); + } else if(values->dtype == NDARRAY_INT8) { + ASSIGNMENT_LOOP(view, mp_float_t, int8_t, lstrides, rarray, rstrides); + } else if(values->dtype == NDARRAY_UINT16) { + ASSIGNMENT_LOOP(view, mp_float_t, uint16_t, lstrides, rarray, rstrides); + } else if(values->dtype == NDARRAY_INT16) { + ASSIGNMENT_LOOP(view, mp_float_t, int16_t, lstrides, rarray, rstrides); + } else if(values->dtype == NDARRAY_FLOAT) { + ASSIGNMENT_LOOP(view, mp_float_t, mp_float_t, lstrides, rarray, rstrides); + } + } +} + +static mp_obj_t ndarray_from_boolean_index(ndarray_obj_t *ndarray, ndarray_obj_t *index) { + // returns a 1D array, indexed by a Boolean array + if(ndarray->len != index->len) { + mp_raise_ValueError(translate("array and index length must be equal")); + } + uint8_t *iarray = (uint8_t *)index->array; + // first we have to find out how many trues there are + size_t count = 0; + for(size_t i=0; i < index->len; i++) { + count += *iarray; + iarray += index->strides[ULAB_MAX_DIMS - 1]; + } + ndarray_obj_t *results = ndarray_new_linear_array(count, ndarray->dtype); + uint8_t *rarray = (uint8_t *)results->array; + uint8_t *array = (uint8_t *)ndarray->array; + // re-wind the index array + iarray = index->array; + for(size_t i=0; i < index->len; i++) { + if(*iarray) { + memcpy(rarray, array, results->itemsize); + rarray += results->itemsize; + count++; + } + array += ndarray->strides[ULAB_MAX_DIMS - 1]; + iarray += index->strides[ULAB_MAX_DIMS - 1]; + } + return MP_OBJ_FROM_PTR(results); +} + +static mp_obj_t ndarray_assign_from_boolean_index(ndarray_obj_t *ndarray, ndarray_obj_t *index, ndarray_obj_t *values) { + // assigns values to a Boolean-indexed array + // first we have to find out how many trues there are + uint8_t *iarray = (uint8_t *)index->array; + size_t count = 0; + for(size_t i=0; i < index->len; i++) { + count += *iarray; + iarray += index->strides[ULAB_MAX_DIMS - 1]; + } + // re-wind the index array + iarray = index->array; + uint8_t *varray = (uint8_t *)values->array; + size_t vstride; + size_t istride = index->strides[ULAB_MAX_DIMS - 1]; + + if(count == values->len) { + // there are as many values as true indices + vstride = values->strides[ULAB_MAX_DIMS - 1]; + } else { + // there is a single value + vstride = 0; + } + if(ndarray->dtype == NDARRAY_UINT8) { + if(values->dtype == NDARRAY_UINT8) { + BOOLEAN_ASSIGNMENT_LOOP(uint8_t, uint8_t, ndarray, iarray, istride, varray, vstride); + } else if(values->dtype == NDARRAY_INT8) { + BOOLEAN_ASSIGNMENT_LOOP(uint8_t, int8_t, ndarray, iarray, istride, varray, vstride); + } else if(values->dtype == NDARRAY_UINT16) { + BOOLEAN_ASSIGNMENT_LOOP(uint8_t, uint16_t, ndarray, iarray, istride, varray, vstride); + } else if(values->dtype == NDARRAY_INT16) { + BOOLEAN_ASSIGNMENT_LOOP(uint8_t, int16_t, ndarray, iarray, istride, varray, vstride); + } else if(values->dtype == NDARRAY_FLOAT) { + BOOLEAN_ASSIGNMENT_LOOP(uint8_t, mp_float_t, ndarray, iarray, istride, varray, vstride); + } + } else if(ndarray->dtype == NDARRAY_INT8) { + if(values->dtype == NDARRAY_UINT8) { + BOOLEAN_ASSIGNMENT_LOOP(int8_t, uint8_t, ndarray, iarray, istride, varray, vstride); + } else if(values->dtype == NDARRAY_INT8) { + BOOLEAN_ASSIGNMENT_LOOP(int8_t, int8_t, ndarray, iarray, istride, varray, vstride); + } else if(values->dtype == NDARRAY_UINT16) { + BOOLEAN_ASSIGNMENT_LOOP(int8_t, uint16_t, ndarray, iarray, istride, varray, vstride); + } else if(values->dtype == NDARRAY_INT16) { + BOOLEAN_ASSIGNMENT_LOOP(int8_t, int16_t, ndarray, iarray, istride, varray, vstride); + } else if(values->dtype == NDARRAY_FLOAT) { + BOOLEAN_ASSIGNMENT_LOOP(int8_t, mp_float_t, ndarray, iarray, istride, varray, vstride); + } + } else if(ndarray->dtype == NDARRAY_UINT16) { + if(values->dtype == NDARRAY_UINT8) { + BOOLEAN_ASSIGNMENT_LOOP(uint16_t, uint8_t, ndarray, iarray, istride, varray, vstride); + } else if(values->dtype == NDARRAY_INT8) { + BOOLEAN_ASSIGNMENT_LOOP(uint16_t, int8_t, ndarray, iarray, istride, varray, vstride); + } else if(values->dtype == NDARRAY_UINT16) { + BOOLEAN_ASSIGNMENT_LOOP(uint16_t, uint16_t, ndarray, iarray, istride, varray, vstride); + } else if(values->dtype == NDARRAY_INT16) { + BOOLEAN_ASSIGNMENT_LOOP(uint16_t, int16_t, ndarray, iarray, istride, varray, vstride); + } else if(values->dtype == NDARRAY_FLOAT) { + BOOLEAN_ASSIGNMENT_LOOP(uint16_t, mp_float_t, ndarray, iarray, istride, varray, vstride); + } + } else if(ndarray->dtype == NDARRAY_INT16) { + if(values->dtype == NDARRAY_UINT8) { + BOOLEAN_ASSIGNMENT_LOOP(int16_t, uint8_t, ndarray, iarray, istride, varray, vstride); + } else if(values->dtype == NDARRAY_INT8) { + BOOLEAN_ASSIGNMENT_LOOP(int16_t, int8_t, ndarray, iarray, istride, varray, vstride); + } else if(values->dtype == NDARRAY_UINT16) { + BOOLEAN_ASSIGNMENT_LOOP(int16_t, uint16_t, ndarray, iarray, istride, varray, vstride); + } else if(values->dtype == NDARRAY_INT16) { + BOOLEAN_ASSIGNMENT_LOOP(int16_t, int16_t, ndarray, iarray, istride, varray, vstride); + } else if(values->dtype == NDARRAY_FLOAT) { + BOOLEAN_ASSIGNMENT_LOOP(int16_t, mp_float_t, ndarray, iarray, istride, varray, vstride); + } + } else { + if(values->dtype == NDARRAY_UINT8) { + BOOLEAN_ASSIGNMENT_LOOP(mp_float_t, uint8_t, ndarray, iarray, istride, varray, vstride); + } else if(values->dtype == NDARRAY_INT8) { + BOOLEAN_ASSIGNMENT_LOOP(mp_float_t, int8_t, ndarray, iarray, istride, varray, vstride); + } else if(values->dtype == NDARRAY_UINT16) { + BOOLEAN_ASSIGNMENT_LOOP(mp_float_t, uint16_t, ndarray, iarray, istride, varray, vstride); + } else if(values->dtype == NDARRAY_INT16) { + BOOLEAN_ASSIGNMENT_LOOP(mp_float_t, int16_t, ndarray, iarray, istride, varray, vstride); + } else if(values->dtype == NDARRAY_FLOAT) { + BOOLEAN_ASSIGNMENT_LOOP(mp_float_t, mp_float_t, ndarray, iarray, istride, varray, vstride); + } + } + return MP_OBJ_FROM_PTR(ndarray); +} + +static mp_obj_t ndarray_get_slice(ndarray_obj_t *ndarray, mp_obj_t index, ndarray_obj_t *values) { + if(mp_obj_is_type(index, &ulab_ndarray_type)) { + ndarray_obj_t *nindex = MP_OBJ_TO_PTR(index); + if((nindex->ndim > 1) || (nindex->boolean == false)) { + mp_raise_NotImplementedError(translate("operation is implemented for 1D Boolean arrays only")); + } + if(values == NULL) { // return value(s) + return ndarray_from_boolean_index(ndarray, nindex); + } else { // assign value(s) + ndarray_assign_from_boolean_index(ndarray, index, values); + } + } + if(mp_obj_is_type(index, &mp_type_tuple) || mp_obj_is_int(index) || mp_obj_is_type(index, &mp_type_slice)) { + mp_obj_tuple_t *tuple; + if(mp_obj_is_type(index, &mp_type_tuple)) { + tuple = MP_OBJ_TO_PTR(index); + if(tuple->len > ndarray->ndim) { + mp_raise_msg(&mp_type_IndexError, translate("too many indices")); + } + } else { + mp_obj_t *items = m_new(mp_obj_t, 1); + items[0] = index; + tuple = mp_obj_new_tuple(1, items); + } + ndarray_obj_t *view = ndarray_view_from_slices(ndarray, tuple); + if(values == NULL) { // return value(s) + // if the view has been reduced to nothing, return a single value + if(view->ndim == 0) { + return mp_binary_get_val_array(view->dtype, view->array, 0); + } else { + return MP_OBJ_FROM_PTR(view); + } + } else { // assign value(s) + ndarray_assign_view(view, values); + } + } + return mp_const_none; +} + +mp_obj_t ndarray_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) { + ndarray_obj_t *self = MP_OBJ_TO_PTR(self_in); + + if (value == MP_OBJ_SENTINEL) { // return value(s) + return ndarray_get_slice(self, index, NULL); + } else { // assignment to slices; the value must be an ndarray, or a scalar + ndarray_obj_t *values = ndarray_from_mp_obj(value, 0); + return ndarray_get_slice(self, index, values); + } + return mp_const_none; +} +#endif /* NDARRAY_IS_SLICEABLE */ + +#if NDARRAY_IS_ITERABLE + +// itarray iterator +mp_obj_t ndarray_getiter(mp_obj_t o_in, mp_obj_iter_buf_t *iter_buf) { + return ndarray_new_ndarray_iterator(o_in, iter_buf); +} + +typedef struct _mp_obj_ndarray_it_t { + mp_obj_base_t base; + mp_fun_1_t iternext; + mp_obj_t ndarray; + size_t cur; +} mp_obj_ndarray_it_t; + +mp_obj_t ndarray_iternext(mp_obj_t self_in) { + mp_obj_ndarray_it_t *self = MP_OBJ_TO_PTR(self_in); + ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(self->ndarray); + uint8_t *array = (uint8_t *)ndarray->array; + + size_t iter_end = ndarray->shape[ULAB_MAX_DIMS-ndarray->ndim]; + if(self->cur < iter_end) { + // separating this case out saves 50 bytes for 1D arrays + #if ULAB_MAX_DIMS == 1 + array += self->cur * ndarray->strides[0]; + self->cur++; + return ndarray_get_item(ndarray, array); + #else + if(ndarray->ndim == 1) { // we have a linear array + array += self->cur * ndarray->strides[ULAB_MAX_DIMS - 1]; + self->cur++; + return ndarray_get_item(ndarray, array); + } else { // we have a tensor, return the reduced view + size_t offset = self->cur * ndarray->strides[ULAB_MAX_DIMS - ndarray->ndim]; + self->cur++; + return MP_OBJ_FROM_PTR(ndarray_new_view(ndarray, ndarray->ndim-1, ndarray->shape, ndarray->strides, offset)); + } + #endif + } else { + return MP_OBJ_STOP_ITERATION; + } +} + +mp_obj_t ndarray_new_ndarray_iterator(mp_obj_t ndarray, mp_obj_iter_buf_t *iter_buf) { + assert(sizeof(mp_obj_ndarray_it_t) <= sizeof(mp_obj_iter_buf_t)); + mp_obj_ndarray_it_t *o = (mp_obj_ndarray_it_t*)iter_buf; + o->base.type = &mp_type_polymorph_iter; + o->iternext = ndarray_iternext; + o->ndarray = ndarray; + o->cur = 0; + return MP_OBJ_FROM_PTR(o); +} +#endif /* NDARRAY_IS_ITERABLE */ + +#if NDARRAY_HAS_FLATTEN +mp_obj_t ndarray_flatten(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + static const mp_arg_t allowed_args[] = { + { MP_QSTR_order, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_QSTR(MP_QSTR_C)} }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + ndarray_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + GET_STR_DATA_LEN(args[0].u_obj, order, len); + if((len != 1) || ((memcmp(order, "C", 1) != 0) && (memcmp(order, "F", 1) != 0))) { + mp_raise_ValueError(translate("flattening order must be either 'C', or 'F'")); + } + + uint8_t *sarray = (uint8_t *)self->array; + ndarray_obj_t *ndarray = ndarray_new_linear_array(self->len, self->dtype); + uint8_t *array = (uint8_t *)ndarray->array; + + if(memcmp(order, "C", 1) == 0) { // C-type ordering + #if ULAB_MAX_DIMS > 3 + size_t i = 0; + do { + #endif + #if ULAB_MAX_DIMS > 2 + size_t j = 0; + do { + #endif + #if ULAB_MAX_DIMS > 1 + size_t k = 0; + do { + #endif + size_t l = 0; + do { + memcpy(array, sarray, self->itemsize); + array += ndarray->strides[ULAB_MAX_DIMS - 1]; + sarray += self->strides[ULAB_MAX_DIMS - 1]; + l++; + } while(l < self->shape[ULAB_MAX_DIMS - 1]); + #if ULAB_MAX_DIMS > 1 + sarray -= self->strides[ULAB_MAX_DIMS - 1] * self->shape[ULAB_MAX_DIMS-1]; + sarray += self->strides[ULAB_MAX_DIMS - 2]; + k++; + } while(k < self->shape[ULAB_MAX_DIMS - 2]); + #endif + #if ULAB_MAX_DIMS > 2 + sarray -= self->strides[ULAB_MAX_DIMS - 2] * self->shape[ULAB_MAX_DIMS-2]; + sarray += self->strides[ULAB_MAX_DIMS - 3]; + j++; + } while(j < self->shape[ULAB_MAX_DIMS - 3]); + #endif + #if ULAB_MAX_DIMS > 3 + sarray -= self->strides[ULAB_MAX_DIMS - 3] * self->shape[ULAB_MAX_DIMS-3]; + sarray += self->strides[ULAB_MAX_DIMS - 4]; + i++; + } while(i < self->shape[ULAB_MAX_DIMS - 4]); + #endif + } else { // 'F', Fortran-type ordering + #if ULAB_MAX_DIMS > 3 + size_t i = 0; + do { + #endif + #if ULAB_MAX_DIMS > 2 + size_t j = 0; + do { + #endif + #if ULAB_MAX_DIMS > 1 + size_t k = 0; + do { + #endif + size_t l = 0; + do { + memcpy(array, sarray, self->itemsize); + array += ndarray->strides[ULAB_MAX_DIMS - 1]; + sarray += self->strides[0]; + l++; + } while(l < self->shape[0]); + #if ULAB_MAX_DIMS > 1 + sarray -= self->strides[0] * self->shape[0]; + sarray += self->strides[1]; + k++; + } while(k < self->shape[1]); + #endif + #if ULAB_MAX_DIMS > 2 + sarray -= self->strides[1] * self->shape[1]; + sarray += self->strides[2]; + j++; + } while(j < self->shape[2]); + #endif + #if ULAB_MAX_DIMS > 3 + sarray -= self->strides[2] * self->shape[2]; + sarray += self->strides[3]; + i++; + } while(i < self->shape[3]); + #endif + } + return MP_OBJ_FROM_PTR(ndarray); +} + +MP_DEFINE_CONST_FUN_OBJ_KW(ndarray_flatten_obj, 1, ndarray_flatten); +#endif + +#if NDARRAY_HAS_ITEMSIZE +mp_obj_t ndarray_itemsize(mp_obj_t self_in) { + ndarray_obj_t *self = MP_OBJ_TO_PTR(self_in); + return MP_OBJ_NEW_SMALL_INT(self->itemsize); +} +#endif + +#if NDARRAY_HAS_SHAPE +mp_obj_t ndarray_shape(mp_obj_t self_in) { + ndarray_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_obj_t *items = m_new(mp_obj_t, self->ndim); + for(uint8_t i=0; i < self->ndim; i++) { + items[self->ndim - i - 1] = mp_obj_new_int(self->shape[ULAB_MAX_DIMS - i - 1]); + } + mp_obj_t tuple = mp_obj_new_tuple(self->ndim, items); + m_del(mp_obj_t, items, self->ndim); + return tuple; +} +#endif + +#if NDARRAY_HAS_SIZE +mp_obj_t ndarray_size(mp_obj_t self_in) { + ndarray_obj_t *self = MP_OBJ_TO_PTR(self_in); + return mp_obj_new_int(self->len); +} +#endif + +#if NDARRAY_HAS_STRIDES +mp_obj_t ndarray_strides(mp_obj_t self_in) { + ndarray_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_obj_t *items = m_new(mp_obj_t, self->ndim); + for(int8_t i=0; i < self->ndim; i++) { + items[i] = mp_obj_new_int(self->strides[ULAB_MAX_DIMS - self->ndim + i]); + } + mp_obj_t tuple = mp_obj_new_tuple(self->ndim, items); + m_del(mp_obj_t, items, self->ndim); + return tuple; +} +#endif + +#if NDARRAY_HAS_TOBYTES +mp_obj_t ndarray_tobytes(mp_obj_t self_in) { + // As opposed to numpy, this function returns a bytearray object with the data pointer (i.e., not a copy) + ndarray_obj_t *self = MP_OBJ_TO_PTR(self_in); + // Piping into a bytearray makes sense for dense arrays only, + // so bail out, if that is not the case + if(!ndarray_is_dense(self)) { + mp_raise_ValueError(translate("tobytes can be invoked for dense arrays only")); + } + return mp_obj_new_bytearray_by_ref(self->itemsize * self->len, self->array); +} + +MP_DEFINE_CONST_FUN_OBJ_1(ndarray_tobytes_obj, ndarray_tobytes); +#endif + +// Binary operations +ndarray_obj_t *ndarray_from_mp_obj(mp_obj_t obj, uint8_t other_type) { + // creates an ndarray from a micropython int or float + // if the input is an ndarray, it is returned + // if other_type is 0, return the smallest type that can accommodate the object + ndarray_obj_t *ndarray; + + if(mp_obj_is_int(obj)) { + int32_t ivalue = mp_obj_get_int(obj); + if((ivalue < -32767) || (ivalue > 32767)) { + // the integer value clearly does not fit the ulab integer types, so move on to float + ndarray = ndarray_new_linear_array(1, NDARRAY_FLOAT); + mp_float_t *array = (mp_float_t *)ndarray->array; + array[0] = (mp_float_t)ivalue; + } else { + uint8_t dtype; + if(ivalue < 0) { + if(ivalue > -128) { + dtype = NDARRAY_INT8; + } else { + dtype = NDARRAY_INT16; + } + } else { // ivalue >= 0 + if((other_type == NDARRAY_INT8) || (other_type == NDARRAY_INT16)) { + if(ivalue < 128) { + dtype = NDARRAY_INT8; + } else { + dtype = NDARRAY_INT16; + } + } else { // other_type = 0 is also included here + if(ivalue < 256) { + dtype = NDARRAY_UINT8; + } else { + dtype = NDARRAY_UINT16; + } + } + } + ndarray = ndarray_new_linear_array(1, dtype); + ndarray_set_value(dtype, ndarray->array, 0, obj); + } + } else if(mp_obj_is_float(obj)) { + ndarray = ndarray_new_linear_array(1, NDARRAY_FLOAT); + mp_float_t *array = (mp_float_t *)ndarray->array; + array[0] = mp_obj_get_float(obj); + } else if(mp_obj_is_type(obj, &ulab_ndarray_type)){ + return obj; + } else { + // assume that the input is an iterable (raises an exception, if it is not the case) + ndarray = ndarray_from_iterable(obj, NDARRAY_FLOAT); + } + return ndarray; +} + +#if NDARRAY_HAS_BINARY_OPS || NDARRAY_HAS_INPLACE_OPS +mp_obj_t ndarray_binary_op(mp_binary_op_t _op, mp_obj_t lobj, mp_obj_t robj) { + // TODO: implement in-place operators + // if the ndarray stands on the right hand side of the expression, simply swap the operands + ndarray_obj_t *lhs, *rhs; + mp_binary_op_t op = _op; + if((op == MP_BINARY_OP_REVERSE_ADD) || (op == MP_BINARY_OP_REVERSE_MULTIPLY) || + (op == MP_BINARY_OP_REVERSE_POWER) || (op == MP_BINARY_OP_REVERSE_SUBTRACT) || + (op == MP_BINARY_OP_REVERSE_TRUE_DIVIDE)) { + lhs = ndarray_from_mp_obj(robj, 0); + rhs = ndarray_from_mp_obj(lobj, lhs->dtype); + } else { + lhs = ndarray_from_mp_obj(lobj, 0); + rhs = ndarray_from_mp_obj(robj, lhs->dtype); + } + if(op == MP_BINARY_OP_REVERSE_ADD) { + op = MP_BINARY_OP_ADD; + } else if(op == MP_BINARY_OP_REVERSE_MULTIPLY) { + op = MP_BINARY_OP_MULTIPLY; + } else if(op == MP_BINARY_OP_REVERSE_POWER) { + op = MP_BINARY_OP_POWER; + } else if(op == MP_BINARY_OP_REVERSE_SUBTRACT) { + op = MP_BINARY_OP_SUBTRACT; + } else if(op == MP_BINARY_OP_REVERSE_TRUE_DIVIDE) { + op = MP_BINARY_OP_TRUE_DIVIDE; + } + + uint8_t ndim = 0; + size_t *shape = m_new(size_t, ULAB_MAX_DIMS); + int32_t *lstrides = m_new(int32_t, ULAB_MAX_DIMS); + int32_t *rstrides = m_new(int32_t, ULAB_MAX_DIMS); + uint8_t broadcastable; + if((op == MP_BINARY_OP_INPLACE_ADD) || (op == MP_BINARY_OP_INPLACE_MULTIPLY) || (op == MP_BINARY_OP_INPLACE_POWER) || + (op == MP_BINARY_OP_INPLACE_SUBTRACT) || (op == MP_BINARY_OP_INPLACE_TRUE_DIVIDE)) { + broadcastable = ndarray_can_broadcast_inplace(lhs, rhs, rstrides); + } else { + broadcastable = ndarray_can_broadcast(lhs, rhs, &ndim, shape, lstrides, rstrides); + } + if(!broadcastable) { + mp_raise_ValueError(translate("operands could not be broadcast together")); + m_del(size_t, shape, ULAB_MAX_DIMS); + m_del(int32_t, lstrides, ULAB_MAX_DIMS); + m_del(int32_t, rstrides, ULAB_MAX_DIMS); + } + // the empty arrays have to be treated separately + uint8_t dtype = NDARRAY_INT16; + ndarray_obj_t *nd; + if((lhs->ndim == 0) || (rhs->ndim == 0)) { + switch(op) { + case MP_BINARY_OP_INPLACE_ADD: + case MP_BINARY_OP_INPLACE_MULTIPLY: + case MP_BINARY_OP_INPLACE_SUBTRACT: + case MP_BINARY_OP_ADD: + case MP_BINARY_OP_MULTIPLY: + case MP_BINARY_OP_SUBTRACT: + // here we don't have to list those cases that result in an int16, + // because dtype is initialised with that NDARRAY_INT16 + if(lhs->dtype == rhs->dtype) { + dtype = rhs->dtype; + } else if((lhs->dtype == NDARRAY_FLOAT) || (rhs->dtype == NDARRAY_FLOAT)) { + dtype = NDARRAY_FLOAT; + } else if(((lhs->dtype == NDARRAY_UINT8) && (rhs->dtype == NDARRAY_UINT16)) || + ((lhs->dtype == NDARRAY_INT8) && (rhs->dtype == NDARRAY_UINT16)) || + ((rhs->dtype == NDARRAY_UINT8) && (lhs->dtype == NDARRAY_UINT16)) || + ((rhs->dtype == NDARRAY_INT8) && (lhs->dtype == NDARRAY_UINT16))) { + dtype = NDARRAY_UINT16; + } + return MP_OBJ_FROM_PTR(ndarray_new_linear_array(0, dtype)); + break; + + case MP_BINARY_OP_INPLACE_POWER: + case MP_BINARY_OP_INPLACE_TRUE_DIVIDE: + case MP_BINARY_OP_POWER: + case MP_BINARY_OP_TRUE_DIVIDE: + return MP_OBJ_FROM_PTR(ndarray_new_linear_array(0, NDARRAY_FLOAT)); + break; + + case MP_BINARY_OP_LESS: + case MP_BINARY_OP_LESS_EQUAL: + case MP_BINARY_OP_MORE: + case MP_BINARY_OP_MORE_EQUAL: + case MP_BINARY_OP_EQUAL: + case MP_BINARY_OP_NOT_EQUAL: + nd = ndarray_new_linear_array(0, NDARRAY_UINT8); + nd->boolean = true; + return MP_OBJ_FROM_PTR(nd); + + default: + return mp_const_none; + break; + } + } + + switch(op) { + // first the in-place operators + #if NDARRAY_HAS_INPLACE_ADD + case MP_BINARY_OP_INPLACE_ADD: + return ndarray_inplace_ams(lhs, rhs, rstrides, op); + break; + #endif + #if NDARRAY_HAS_INPLACE_MULTIPLY + case MP_BINARY_OP_INPLACE_MULTIPLY: + return ndarray_inplace_ams(lhs, rhs, rstrides, op); + break; + #endif + #if NDARRAY_HAS_INPLACE_POWER + case MP_BINARY_OP_INPLACE_POWER: + return ndarray_inplace_power(lhs, rhs, rstrides); + break; + #endif + #if NDARRAY_HAS_INPLACE_SUBTRACT + case MP_BINARY_OP_INPLACE_SUBTRACT: + return ndarray_inplace_ams(lhs, rhs, rstrides, op); + break; + #endif + #if NDARRAY_HAS_INPLACE_TRUE_DIVIDE + case MP_BINARY_OP_INPLACE_TRUE_DIVIDE: + return ndarray_inplace_divide(lhs, rhs, rstrides); + break; + #endif + // end if in-place operators + + #if NDARRAY_HAS_BINARY_OP_LESS + case MP_BINARY_OP_LESS: + // here we simply swap the operands + return ndarray_binary_more(rhs, lhs, ndim, shape, rstrides, lstrides, MP_BINARY_OP_MORE); + break; + #endif + #if NDARRAY_HAS_BINARY_OP_LESS_EQUAL + case MP_BINARY_OP_LESS_EQUAL: + // here we simply swap the operands + return ndarray_binary_more(rhs, lhs, ndim, shape, rstrides, lstrides, MP_BINARY_OP_MORE_EQUAL); + break; + #endif + #if NDARRAY_HAS_BINARY_OP_EQUAL + case MP_BINARY_OP_EQUAL: + return ndarray_binary_equality(lhs, rhs, ndim, shape, lstrides, rstrides, MP_BINARY_OP_EQUAL); + break; + #endif + #if NDARRAY_HAS_BINARY_OP_NOT_EQUAL + case MP_BINARY_OP_NOT_EQUAL: + return ndarray_binary_equality(lhs, rhs, ndim, shape, lstrides, rstrides, MP_BINARY_OP_NOT_EQUAL); + break; + #endif + #if NDARRAY_HAS_BINARY_OP_ADD + case MP_BINARY_OP_ADD: + return ndarray_binary_add(lhs, rhs, ndim, shape, lstrides, rstrides); + break; + #endif + #if NDARRAY_HAS_BINARY_OP_MULTIPLY + case MP_BINARY_OP_MULTIPLY: + return ndarray_binary_multiply(lhs, rhs, ndim, shape, lstrides, rstrides); + break; + #endif + #if NDARRAY_HAS_BINARY_OP_MORE + case MP_BINARY_OP_MORE: + return ndarray_binary_more(lhs, rhs, ndim, shape, lstrides, rstrides, MP_BINARY_OP_MORE); + break; + #endif + #if NDARRAY_HAS_BINARY_OP_MORE_EQUAL + case MP_BINARY_OP_MORE_EQUAL: + return ndarray_binary_more(lhs, rhs, ndim, shape, lstrides, rstrides, MP_BINARY_OP_MORE_EQUAL); + break; + #endif + #if NDARRAY_HAS_BINARY_OP_SUBTRACT + case MP_BINARY_OP_SUBTRACT: + return ndarray_binary_subtract(lhs, rhs, ndim, shape, lstrides, rstrides); + break; + #endif + #if NDARRAY_HAS_BINARY_OP_TRUE_DIVIDE + case MP_BINARY_OP_TRUE_DIVIDE: + return ndarray_binary_true_divide(lhs, rhs, ndim, shape, lstrides, rstrides); + break; + #endif + #if NDARRAY_HAS_BINARY_OP_POWER + case MP_BINARY_OP_POWER: + return ndarray_binary_power(lhs, rhs, ndim, shape, lstrides, rstrides); + break; + #endif + default: + return MP_OBJ_NULL; // op not supported + break; + } + return MP_OBJ_NULL; +} +#endif /* NDARRAY_HAS_BINARY_OPS || NDARRAY_HAS_INPLACE_OPS */ + +#if NDARRAY_HAS_UNARY_OPS +mp_obj_t ndarray_unary_op(mp_unary_op_t op, mp_obj_t self_in) { + ndarray_obj_t *self = MP_OBJ_TO_PTR(self_in); + ndarray_obj_t *ndarray = NULL; + + switch (op) { + #if NDARRAY_HAS_UNARY_OP_ABS + case MP_UNARY_OP_ABS: + ndarray = ndarray_copy_view(self); + // if Boolean, NDARRAY_UINT8, or NDARRAY_UINT16, there is nothing to do + if(self->dtype == NDARRAY_INT8) { + int8_t *array = (int8_t *)ndarray->array; + for(size_t i=0; i < self->len; i++, array++) { + if(*array < 0) *array = -(*array); + } + } else if(self->dtype == NDARRAY_INT16) { + int16_t *array = (int16_t *)ndarray->array; + for(size_t i=0; i < self->len; i++, array++) { + if(*array < 0) *array = -(*array); + } + } else { + mp_float_t *array = (mp_float_t *)ndarray->array; + for(size_t i=0; i < self->len; i++, array++) { + if(*array < 0) *array = -(*array); + } + } + return MP_OBJ_FROM_PTR(ndarray); + break; + #endif + #if NDARRAY_HAS_UNARY_OP_INVERT + case MP_UNARY_OP_INVERT: + if(self->dtype == NDARRAY_FLOAT) { + mp_raise_ValueError(translate("operation is not supported for given type")); + } + // we can invert the content byte by byte, no need to distinguish between different dtypes + ndarray = ndarray_copy_view(self); // from this point, this is a dense copy + uint8_t *array = (uint8_t *)ndarray->array; + if(ndarray->boolean) { + for(size_t i=0; i < ndarray->len; i++, array++) *array = *array ^ 0x01; + } else { + uint8_t itemsize = mp_binary_get_size('@', self->dtype, NULL); + for(size_t i=0; i < ndarray->len*itemsize; i++, array++) *array ^= 0xFF; + } + return MP_OBJ_FROM_PTR(ndarray); + break; + #endif + #if NDARRAY_HAS_UNARY_OP_LEN + case MP_UNARY_OP_LEN: + return mp_obj_new_int(self->shape[ULAB_MAX_DIMS - self->ndim]); + break; + #endif + #if NDARRAY_HAS_UNARY_OP_NEGATIVE + case MP_UNARY_OP_NEGATIVE: + ndarray = ndarray_copy_view(self); // from this point, this is a dense copy + if(self->dtype == NDARRAY_UINT8) { + uint8_t *array = (uint8_t *)ndarray->array; + for(size_t i=0; i < self->len; i++, array++) *array = -(*array); + } else if(self->dtype == NDARRAY_INT8) { + int8_t *array = (int8_t *)ndarray->array; + for(size_t i=0; i < self->len; i++, array++) *array = -(*array); + } else if(self->dtype == NDARRAY_UINT16) { + uint16_t *array = (uint16_t *)ndarray->array; + for(size_t i=0; i < self->len; i++, array++) *array = -(*array); + } else if(self->dtype == NDARRAY_INT16) { + int16_t *array = (int16_t *)ndarray->array; + for(size_t i=0; i < self->len; i++, array++) *array = -(*array); + } else { + mp_float_t *array = (mp_float_t *)ndarray->array; + for(size_t i=0; i < self->len; i++, array++) *array = -(*array); + } + return MP_OBJ_FROM_PTR(ndarray); + break; + #endif + #if NDARRAY_HAS_UNARY_OP_POSITIVE + case MP_UNARY_OP_POSITIVE: + return MP_OBJ_FROM_PTR(ndarray_copy_view(self)); + #endif + + default: + return MP_OBJ_NULL; // operator not supported + break; + } +} +#endif /* NDARRAY_HAS_UNARY_OPS */ + +#if NDARRAY_HAS_TRANSPOSE +mp_obj_t ndarray_transpose(mp_obj_t self_in) { + #if ULAB_MAX_DIMS == 1 + return self_in; + #endif + // TODO: check, what happens to the offset here, if we have a view + ndarray_obj_t *self = MP_OBJ_TO_PTR(self_in); + if(self->ndim == 1) { + return self_in; + } + size_t *shape = m_new(size_t, self->ndim); + int32_t *strides = m_new(int32_t, self->ndim); + for(uint8_t i=0; i < self->ndim; i++) { + shape[ULAB_MAX_DIMS - 1 - i] = self->shape[ULAB_MAX_DIMS - self->ndim + i]; + strides[ULAB_MAX_DIMS - 1 - i] = self->strides[ULAB_MAX_DIMS - self->ndim + i]; + } + // TODO: I am not sure ndarray_new_view is OK here... + // should be deep copy... + ndarray_obj_t *ndarray = ndarray_new_view(self, self->ndim, shape, strides, 0); + return MP_OBJ_FROM_PTR(ndarray); +} + +MP_DEFINE_CONST_FUN_OBJ_1(ndarray_transpose_obj, ndarray_transpose); +#endif /* NDARRAY_HAS_TRANSPOSE */ + +#if ULAB_MAX_DIMS > 1 +#if NDARRAY_HAS_RESHAPE +mp_obj_t ndarray_reshape_core(mp_obj_t oin, mp_obj_t _shape, bool inplace) { + ndarray_obj_t *source = MP_OBJ_TO_PTR(oin); + if(!mp_obj_is_type(_shape, &mp_type_tuple)) { + mp_raise_TypeError(translate("shape must be a tuple")); + } + + mp_obj_tuple_t *shape = MP_OBJ_TO_PTR(_shape); + if(shape->len > ULAB_MAX_DIMS) { + mp_raise_ValueError(translate("maximum number of dimensions is 4")); + } + size_t *new_shape = m_new(size_t, ULAB_MAX_DIMS); + memset(new_shape, 0, sizeof(size_t)*ULAB_MAX_DIMS); + size_t new_length = 1; + for(uint8_t i=0; i < shape->len; i++) { + new_shape[ULAB_MAX_DIMS - i - 1] = mp_obj_get_int(shape->items[shape->len - i - 1]); + new_length *= new_shape[ULAB_MAX_DIMS - i - 1]; + } + if(source->len != new_length) { + mp_raise_ValueError(translate("input and output shapes are not compatible")); + } + ndarray_obj_t *ndarray; + if(ndarray_is_dense(source)) { + int32_t *new_strides = strides_from_shape(new_shape, source->dtype); + if(inplace) { + for(uint8_t i = 0; i < ULAB_MAX_DIMS; i++) { + source->shape[i] = new_shape[i]; + source->strides[i] = new_strides[i]; + } + return MP_OBJ_FROM_PTR(oin); + } else { + ndarray = ndarray_new_view(source, shape->len, new_shape, new_strides, 0); + } + } else { + if(inplace) { + mp_raise_ValueError(translate("cannot assign new shape")); + } + ndarray = ndarray_new_ndarray_from_tuple(shape, source->dtype); + ndarray_copy_array(source, ndarray); + } + return MP_OBJ_FROM_PTR(ndarray); +} + +mp_obj_t ndarray_reshape(mp_obj_t oin, mp_obj_t _shape) { + return ndarray_reshape_core(oin, _shape, 0); +} + +MP_DEFINE_CONST_FUN_OBJ_2(ndarray_reshape_obj, ndarray_reshape); +#endif /* NDARRAY_HAS_RESHAPE */ +#endif /* ULAB_MAX_DIMS > 1 */ + +#if ULAB_NUMPY_HAS_NDINFO +mp_obj_t ndarray_info(mp_obj_t obj_in) { + ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(obj_in); + if(!mp_obj_is_type(ndarray, &ulab_ndarray_type)) { + mp_raise_TypeError(translate("function is defined for ndarrays only")); + } + mp_printf(MP_PYTHON_PRINTER, "class: ndarray\n"); + mp_printf(MP_PYTHON_PRINTER, "shape: ("); + if(ndarray->ndim == 1) { + mp_printf(MP_PYTHON_PRINTER, "%d,", ndarray->shape[ULAB_MAX_DIMS-1]); + } else { + for(uint8_t i=0; i < ndarray->ndim-1; i++) mp_printf(MP_PYTHON_PRINTER, "%d, ", ndarray->shape[i]); + mp_printf(MP_PYTHON_PRINTER, "%d", ndarray->shape[ULAB_MAX_DIMS-1]); + } + mp_printf(MP_PYTHON_PRINTER, ")\n"); + mp_printf(MP_PYTHON_PRINTER, "strides: ("); + if(ndarray->ndim == 1) { + mp_printf(MP_PYTHON_PRINTER, "%d,", ndarray->strides[ULAB_MAX_DIMS-1]); + } else { + for(uint8_t i=0; i < ndarray->ndim-1; i++) mp_printf(MP_PYTHON_PRINTER, "%d, ", ndarray->strides[i]); + mp_printf(MP_PYTHON_PRINTER, "%d", ndarray->strides[ULAB_MAX_DIMS-1]); + } + mp_printf(MP_PYTHON_PRINTER, ")\n"); + mp_printf(MP_PYTHON_PRINTER, "itemsize: %d\n", ndarray->itemsize); + mp_printf(MP_PYTHON_PRINTER, "data pointer: 0x%p\n", ndarray->array); + mp_printf(MP_PYTHON_PRINTER, "type: "); + if(ndarray->boolean) { + mp_printf(MP_PYTHON_PRINTER, "bool\n"); + } else if(ndarray->dtype == NDARRAY_UINT8) { + mp_printf(MP_PYTHON_PRINTER, "uint8\n"); + } else if(ndarray->dtype == NDARRAY_INT8) { + mp_printf(MP_PYTHON_PRINTER, "int8\n"); + } else if(ndarray->dtype == NDARRAY_UINT16) { + mp_printf(MP_PYTHON_PRINTER, "uint16\n"); + } else if(ndarray->dtype == NDARRAY_INT16) { + mp_printf(MP_PYTHON_PRINTER, "int16\n"); + } else if(ndarray->dtype == NDARRAY_FLOAT) { + mp_printf(MP_PYTHON_PRINTER, "float\n"); + } + return mp_const_none; +} + +MP_DEFINE_CONST_FUN_OBJ_1(ndarray_info_obj, ndarray_info); +#endif + +// (the get_buffer protocol returns 0 for success, 1 for failure) +mp_int_t ndarray_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags) { + ndarray_obj_t *self = MP_OBJ_TO_PTR(self_in); + if(!ndarray_is_dense(self)) { + return 1; + } + bufinfo->len = self->itemsize * self->len; + bufinfo->buf = self->array; + bufinfo->typecode = self->dtype; + return 0; +} diff --git a/python/port/mod/ulab/ndarray.h b/python/port/mod/ulab/ndarray.h new file mode 100644 index 000000000..aa7f499af --- /dev/null +++ b/python/port/mod/ulab/ndarray.h @@ -0,0 +1,736 @@ + +/* + * This file is part of the micropython-ulab project, + * + * https://github.com/v923z/micropython-ulab + * + * The MIT License (MIT) + * + * Copyright (c) 2019-2021 Zoltán Vörös + * 2020 Jeff Epler for Adafruit Industries +*/ + +#ifndef _NDARRAY_ +#define _NDARRAY_ + +#include "py/objarray.h" +#include "py/binary.h" +#include "py/objstr.h" +#include "py/objlist.h" + +#include "ulab.h" + +#ifndef MP_PI +#define MP_PI MICROPY_FLOAT_CONST(3.14159265358979323846) +#endif +#ifndef MP_E +#define MP_E MICROPY_FLOAT_CONST(2.71828182845904523536) +#endif + +#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT +#define FLOAT_TYPECODE 'f' +#elif MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE +#define FLOAT_TYPECODE 'd' +#endif + +// this typedef is lifted from objfloat.c, because mp_obj_float_t is not exposed +typedef struct _mp_obj_float_t { + mp_obj_base_t base; + mp_float_t value; +} mp_obj_float_t; + +#if defined(MICROPY_VERSION_MAJOR) && MICROPY_VERSION_MAJOR == 1 && MICROPY_VERSION_MINOR == 12 +typedef struct _mp_obj_slice_t { + mp_obj_base_t base; + mp_obj_t start; + mp_obj_t stop; + mp_obj_t step; +} mp_obj_slice_t; +#define MP_ERROR_TEXT(x) x +#endif + +#if !CIRCUITPY +#define translate(x) MP_ERROR_TEXT(x) +#define ndarray_set_value(a, b, c, d) mp_binary_set_val_array(a, b, c, d) +#else +void ndarray_set_value(char , void *, size_t , mp_obj_t ); +#endif + +#define NDARRAY_NUMERIC 0 +#define NDARRAY_BOOLEAN 1 + +#define NDARRAY_NDARRAY_TYPE 1 +#define NDARRAY_ITERABLE_TYPE 2 + +extern const mp_obj_type_t ulab_ndarray_type; + +enum NDARRAY_TYPE { + NDARRAY_BOOL = '?', // this must never be assigned to the dtype! + NDARRAY_UINT8 = 'B', + NDARRAY_INT8 = 'b', + NDARRAY_UINT16 = 'H', + NDARRAY_INT16 = 'h', + NDARRAY_FLOAT = FLOAT_TYPECODE, +}; + +typedef struct _ndarray_obj_t { + mp_obj_base_t base; + uint8_t dtype; + uint8_t itemsize; + uint8_t boolean; + uint8_t ndim; + size_t len; + size_t shape[ULAB_MAX_DIMS]; + int32_t strides[ULAB_MAX_DIMS]; + void *array; + void *origin; +} ndarray_obj_t; + +#if ULAB_HAS_DTYPE_OBJECT +extern const mp_obj_type_t ulab_dtype_type; + +typedef struct _dtype_obj_t { + mp_obj_base_t base; + uint8_t dtype; +} dtype_obj_t; + +void ndarray_dtype_print(const mp_print_t *, mp_obj_t , mp_print_kind_t ); + +#ifdef CIRCUITPY +mp_obj_t ndarray_dtype_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *args, mp_map_t *kw_args); +#else +mp_obj_t ndarray_dtype_make_new(const mp_obj_type_t *, size_t , size_t , const mp_obj_t *); +#endif /* CIRCUITPY */ +#endif /* ULAB_HAS_DTYPE_OBJECT */ + +mp_obj_t ndarray_new_ndarray_iterator(mp_obj_t , mp_obj_iter_buf_t *); + +mp_float_t ndarray_get_float_value(void *, uint8_t ); +mp_float_t ndarray_get_float_index(void *, uint8_t , size_t ); +bool ndarray_object_is_array_like(mp_obj_t ); +void fill_array_iterable(mp_float_t *, mp_obj_t ); +size_t *ndarray_shape_vector(size_t , size_t , size_t , size_t ); + +void ndarray_print(const mp_print_t *, mp_obj_t , mp_print_kind_t ); + +#if ULAB_HAS_PRINTOPTIONS +mp_obj_t ndarray_set_printoptions(size_t , const mp_obj_t *, mp_map_t *); +MP_DECLARE_CONST_FUN_OBJ_KW(ndarray_set_printoptions_obj); + +mp_obj_t ndarray_get_printoptions(void); +MP_DECLARE_CONST_FUN_OBJ_0(ndarray_get_printoptions_obj); +#endif + +void ndarray_assign_elements(ndarray_obj_t *, mp_obj_t , uint8_t , size_t *); +size_t *ndarray_contract_shape(ndarray_obj_t *, uint8_t ); +int32_t *ndarray_contract_strides(ndarray_obj_t *, uint8_t ); + +ndarray_obj_t *ndarray_new_dense_ndarray(uint8_t , size_t *, uint8_t ); +ndarray_obj_t *ndarray_new_ndarray_from_tuple(mp_obj_tuple_t *, uint8_t ); +ndarray_obj_t *ndarray_new_ndarray(uint8_t , size_t *, int32_t *, uint8_t ); +ndarray_obj_t *ndarray_new_linear_array(size_t , uint8_t ); +ndarray_obj_t *ndarray_new_view(ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t ); +bool ndarray_is_dense(ndarray_obj_t *); +ndarray_obj_t *ndarray_copy_view(ndarray_obj_t *); +void ndarray_copy_array(ndarray_obj_t *, ndarray_obj_t *); + +MP_DECLARE_CONST_FUN_OBJ_KW(ndarray_array_constructor_obj); +#ifdef CIRCUITPY +mp_obj_t ndarray_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *args, mp_map_t *kw_args); +#else +mp_obj_t ndarray_make_new(const mp_obj_type_t *, size_t , size_t , const mp_obj_t *); +#endif +mp_obj_t ndarray_subscr(mp_obj_t , mp_obj_t , mp_obj_t ); +mp_obj_t ndarray_getiter(mp_obj_t , mp_obj_iter_buf_t *); +bool ndarray_can_broadcast(ndarray_obj_t *, ndarray_obj_t *, uint8_t *, size_t *, int32_t *, int32_t *); +bool ndarray_can_broadcast_inplace(ndarray_obj_t *, ndarray_obj_t *, int32_t *); +mp_obj_t ndarray_binary_op(mp_binary_op_t , mp_obj_t , mp_obj_t ); +mp_obj_t ndarray_unary_op(mp_unary_op_t , mp_obj_t ); + +size_t *ndarray_new_coords(uint8_t ); +void ndarray_rewind_array(uint8_t , uint8_t *, size_t *, int32_t *, size_t *); + +// various ndarray methods +#if NDARRAY_HAS_BYTESWAP +mp_obj_t ndarray_byteswap(size_t , const mp_obj_t *, mp_map_t *); +MP_DECLARE_CONST_FUN_OBJ_KW(ndarray_byteswap_obj); +#endif + +#if NDARRAY_HAS_COPY +mp_obj_t ndarray_copy(mp_obj_t ); +MP_DECLARE_CONST_FUN_OBJ_1(ndarray_copy_obj); +#endif + +#if NDARRAY_HAS_FLATTEN +mp_obj_t ndarray_flatten(size_t , const mp_obj_t *, mp_map_t *); +MP_DECLARE_CONST_FUN_OBJ_KW(ndarray_flatten_obj); +#endif + +mp_obj_t ndarray_dtype(mp_obj_t ); +mp_obj_t ndarray_itemsize(mp_obj_t ); +mp_obj_t ndarray_size(mp_obj_t ); +mp_obj_t ndarray_shape(mp_obj_t ); +mp_obj_t ndarray_strides(mp_obj_t ); + +#if NDARRAY_HAS_RESHAPE +mp_obj_t ndarray_reshape_core(mp_obj_t , mp_obj_t , bool ); +mp_obj_t ndarray_reshape(mp_obj_t , mp_obj_t ); +MP_DECLARE_CONST_FUN_OBJ_2(ndarray_reshape_obj); +#endif + +#if NDARRAY_HAS_TOBYTES +mp_obj_t ndarray_tobytes(mp_obj_t ); +MP_DECLARE_CONST_FUN_OBJ_1(ndarray_tobytes_obj); +#endif + +#if NDARRAY_HAS_TRANSPOSE +mp_obj_t ndarray_transpose(mp_obj_t ); +MP_DECLARE_CONST_FUN_OBJ_1(ndarray_transpose_obj); +#endif + +#if ULAB_NUMPY_HAS_NDINFO +mp_obj_t ndarray_info(mp_obj_t ); +MP_DECLARE_CONST_FUN_OBJ_1(ndarray_info_obj); +#endif + +mp_int_t ndarray_get_buffer(mp_obj_t , mp_buffer_info_t *, mp_uint_t ); +//void ndarray_attributes(mp_obj_t , qstr , mp_obj_t *); + +ndarray_obj_t *ndarray_from_mp_obj(mp_obj_t , uint8_t ); + + +#define BOOLEAN_ASSIGNMENT_LOOP(type_left, type_right, ndarray, iarray, istride, varray, vstride)\ + type_left *array = (type_left *)(ndarray)->array;\ + for(size_t i=0; i < (ndarray)->len; i++) {\ + if(*(iarray)) {\ + *array = (type_left)(*((type_right *)(varray)));\ + }\ + array += (ndarray)->strides[ULAB_MAX_DIMS - 1] / (ndarray)->itemsize;\ + (iarray) += (istride);\ + (varray) += (vstride);\ + } while(0) + +#if ULAB_HAS_FUNCTION_ITERATOR +#define BINARY_LOOP(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides, OPERATOR)\ + type_out *array = (type_out *)(results)->array;\ + size_t *lcoords = ndarray_new_coords((results)->ndim);\ + size_t *rcoords = ndarray_new_coords((results)->ndim);\ + for(size_t i=0; i < (results)->len/(results)->shape[ULAB_MAX_DIMS -1]; i++) {\ + size_t l = 0;\ + do {\ + *array++ = *((type_left *)(larray)) OPERATOR *((type_right *)(rarray));\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\ + l++;\ + } while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\ + ndarray_rewind_array((results)->ndim, (larray), (results)->shape, (lstrides), lcoords);\ + ndarray_rewind_array((results)->ndim, (rarray), (results)->shape, (rstrides), rcoords);\ + } while(0) + +#define INPLACE_LOOP(results, type_left, type_right, larray, rarray, rstrides, OPERATOR)\ + size_t *lcoords = ndarray_new_coords((results)->ndim);\ + size_t *rcoords = ndarray_new_coords((results)->ndim);\ + for(size_t i=0; i < (results)->len/(results)->shape[ULAB_MAX_DIMS -1]; i++) {\ + size_t l = 0;\ + do {\ + *((type_left *)(larray)) OPERATOR *((type_right *)(rarray));\ + (larray) += (results)->strides[ULAB_MAX_DIMS - 1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\ + l++;\ + } while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\ + ndarray_rewind_array((results)->ndim, (larray), (results)->shape, (results)->strides, lcoords);\ + ndarray_rewind_array((results)->ndim, (rarray), (results)->shape, (rstrides), rcoords);\ + } while(0) + +#define EQUALITY_LOOP(results, array, type_left, type_right, larray, lstrides, rarray, rstrides, OPERATOR)\ + size_t *lcoords = ndarray_new_coords((results)->ndim);\ + size_t *rcoords = ndarray_new_coords((results)->ndim);\ + for(size_t i=0; i < (results)->len/(results)->shape[ULAB_MAX_DIMS -1]; i++) {\ + size_t l = 0;\ + do {\ + *(array)++ = *((type_left *)(larray)) OPERATOR *((type_right *)(rarray)) ? 1 : 0;\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\ + l++;\ + } while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\ + ndarray_rewind_array((results)->ndim, (larray), (results)->shape, (lstrides), lcoords);\ + ndarray_rewind_array((results)->ndim, (rarray), (results)->shape, (rstrides), rcoords);\ + } while(0) + +#define POWER_LOOP(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides)\ + type_out *array = (type_out *)(results)->array;\ + size_t *lcoords = ndarray_new_coords((results)->ndim);\ + size_t *rcoords = ndarray_new_coords((results)->ndim);\ + for(size_t i=0; i < (results)->len/(results)->shape[ULAB_MAX_DIMS -1]; i++) {\ + size_t l = 0;\ + do {\ + *array++ = MICROPY_FLOAT_C_FUN(pow)(*((type_left *)(larray)), *((type_right *)(rarray)));\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\ + l++;\ + } while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\ + ndarray_rewind_array((results)->ndim, (larray), (results)->shape, (lstrides), lcoords);\ + ndarray_rewind_array((results)->ndim, (rarray), (results)->shape, (rstrides), rcoords);\ + } while(0) + +#else + +#if ULAB_MAX_DIMS == 1 +#define BINARY_LOOP(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides, OPERATOR)\ + type_out *array = (type_out *)results->array;\ + size_t l = 0;\ + do {\ + *array++ = *((type_left *)(larray)) OPERATOR *((type_right *)(rarray));\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\ + l++;\ + } while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\ + +#define INPLACE_LOOP(results, type_left, type_right, larray, rarray, rstrides, OPERATOR)\ + size_t l = 0;\ + do {\ + *((type_left *)(larray)) OPERATOR *((type_right *)(rarray));\ + (larray) += (results)->strides[ULAB_MAX_DIMS - 1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\ + l++;\ + } while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\ + +#define EQUALITY_LOOP(results, array, type_left, type_right, larray, lstrides, rarray, rstrides, OPERATOR)\ + size_t l = 0;\ + do {\ + *(array)++ = *((type_left *)(larray)) OPERATOR *((type_right *)(rarray)) ? 1 : 0;\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\ + l++;\ + } while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\ + +#define POWER_LOOP(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides)\ + type_out *array = (type_out *)results->array;\ + size_t l = 0;\ + do {\ + *array++ = MICROPY_FLOAT_C_FUN(pow)(*((type_left *)(larray)), *((type_right *)(rarray)));\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\ + l++;\ + } while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\ + +#endif /* ULAB_MAX_DIMS == 1 */ + +#if ULAB_MAX_DIMS == 2 +#define BINARY_LOOP(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides, OPERATOR)\ + type_out *array = (type_out *)(results)->array;\ + size_t k = 0;\ + do {\ + size_t l = 0;\ + do {\ + *array++ = *((type_left *)(larray)) OPERATOR *((type_right *)(rarray));\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\ + l++;\ + } while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\ + (larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 2];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\ + k++;\ + } while(k < (results)->shape[ULAB_MAX_DIMS - 2]);\ + +#define INPLACE_LOOP(results, type_left, type_right, larray, rarray, rstrides, OPERATOR)\ + size_t k = 0;\ + do {\ + size_t l = 0;\ + do {\ + *((type_left *)(larray)) OPERATOR *((type_right *)(rarray));\ + (larray) += (results)->strides[ULAB_MAX_DIMS - 1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\ + l++;\ + } while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\ + (larray) -= (results)->strides[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\ + (larray) += (results)->strides[ULAB_MAX_DIMS - 2];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\ + k++;\ + } while(k < (results)->shape[ULAB_MAX_DIMS - 2]);\ + +#define EQUALITY_LOOP(results, array, type_left, type_right, larray, lstrides, rarray, rstrides, OPERATOR)\ + size_t k = 0;\ + do {\ + size_t l = 0;\ + do {\ + *(array)++ = *((type_left *)(larray)) OPERATOR *((type_right *)(rarray)) ? 1 : 0;\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\ + l++;\ + } while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\ + (larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 2];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\ + k++;\ + } while(k < (results)->shape[ULAB_MAX_DIMS - 2]);\ + +#define POWER_LOOP(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides)\ + type_out *array = (type_out *)(results)->array;\ + size_t k = 0;\ + do {\ + size_t l = 0;\ + do {\ + *array++ = MICROPY_FLOAT_C_FUN(pow)(*((type_left *)(larray)), *((type_right *)(rarray)));\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\ + l++;\ + } while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\ + (larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 2];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\ + k++;\ + } while(k < (results)->shape[ULAB_MAX_DIMS - 2]);\ + +#endif /* ULAB_MAX_DIMS == 2 */ + +#if ULAB_MAX_DIMS == 3 +#define BINARY_LOOP(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides, OPERATOR)\ + type_out *array = (type_out *)results->array;\ + size_t j = 0;\ + do {\ + size_t k = 0;\ + do {\ + size_t l = 0;\ + do {\ + *array++ = *((type_left *)(larray)) OPERATOR *((type_right *)(rarray));\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\ + l++;\ + } while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\ + (larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 2];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\ + k++;\ + } while(k < (results)->shape[ULAB_MAX_DIMS - 2]);\ + (larray) -= (lstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS-2];\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 3];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS-2];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\ + j++;\ + } while(j < (results)->shape[ULAB_MAX_DIMS - 3]);\ + +#define INPLACE_LOOP(results, type_left, type_right, larray, rarray, rstrides, OPERATOR)\ + size_t j = 0;\ + do {\ + size_t k = 0;\ + do {\ + size_t l = 0;\ + do {\ + *((type_left *)(larray)) OPERATOR *((type_right *)(rarray));\ + (larray) += (results)->strides[ULAB_MAX_DIMS - 1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\ + l++;\ + } while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\ + (larray) -= (results)->strides[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\ + (larray) += (results)->strides[ULAB_MAX_DIMS - 2];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\ + k++;\ + } while(k < (results)->shape[ULAB_MAX_DIMS - 2]);\ + (larray) -= (results)->strides[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS-2];\ + (larray) += (results)->strides[ULAB_MAX_DIMS - 3];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS-2];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\ + j++;\ + } while(j < (results)->shape[ULAB_MAX_DIMS - 3]);\ + +#define EQUALITY_LOOP(results, array, type_left, type_right, larray, lstrides, rarray, rstrides, OPERATOR)\ + size_t j = 0;\ + do {\ + size_t k = 0;\ + do {\ + size_t l = 0;\ + do {\ + *(array)++ = *((type_left *)(larray)) OPERATOR *((type_right *)(rarray)) ? 1 : 0;\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\ + l++;\ + } while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\ + (larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 2];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\ + k++;\ + } while(k < (results)->shape[ULAB_MAX_DIMS - 2]);\ + (larray) -= (lstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS-2];\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 3];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS-2];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\ + j++;\ + } while(j < (results)->shape[ULAB_MAX_DIMS - 3]);\ + +#define POWER_LOOP(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides)\ + type_out *array = (type_out *)results->array;\ + size_t j = 0;\ + do {\ + size_t k = 0;\ + do {\ + size_t l = 0;\ + do {\ + *array++ = MICROPY_FLOAT_C_FUN(pow)(*((type_left *)(larray)), *((type_right *)(rarray)));\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\ + l++;\ + } while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\ + (larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 2];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\ + k++;\ + } while(k < (results)->shape[ULAB_MAX_DIMS - 2]);\ + (larray) -= (lstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS-2];\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 3];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS-2];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\ + j++;\ + } while(j < (results)->shape[ULAB_MAX_DIMS - 3]);\ + +#endif /* ULAB_MAX_DIMS == 3 */ + +#if ULAB_MAX_DIMS == 4 +#define BINARY_LOOP(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides, OPERATOR)\ + type_out *array = (type_out *)results->array;\ + size_t i = 0;\ + do {\ + size_t j = 0;\ + do {\ + size_t k = 0;\ + do {\ + size_t l = 0;\ + do {\ + *array++ = *((type_left *)(larray)) OPERATOR *((type_right *)(rarray));\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\ + l++;\ + } while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\ + (larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 2];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\ + k++;\ + } while(k < (results)->shape[ULAB_MAX_DIMS - 2]);\ + (larray) -= (lstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS-2];\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 3];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS-2];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\ + j++;\ + } while(j < (results)->shape[ULAB_MAX_DIMS - 3]);\ + (larray) -= (lstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS-3];\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 4];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS-3];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 4];\ + i++;\ + } while(i < (results)->shape[ULAB_MAX_DIMS - 4]);\ + +#define INPLACE_LOOP(results, type_left, type_right, larray, rarray, rstrides, OPERATOR)\ + size_t i = 0;\ + do {\ + size_t j = 0;\ + do {\ + size_t k = 0;\ + do {\ + size_t l = 0;\ + do {\ + *((type_left *)(larray)) OPERATOR *((type_right *)(rarray));\ + (larray) += (results)->strides[ULAB_MAX_DIMS - 1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\ + l++;\ + } while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\ + (larray) -= (results)->strides[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\ + (larray) += (results)->strides[ULAB_MAX_DIMS - 2];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\ + k++;\ + } while(k < (results)->shape[ULAB_MAX_DIMS - 2]);\ + (larray) -= (results)->strides[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS-2];\ + (larray) += (results)->strides[ULAB_MAX_DIMS - 3];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS-2];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\ + j++;\ + } while(j < (results)->shape[ULAB_MAX_DIMS - 3]);\ + (larray) -= (results)->strides[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS-3];\ + (larray) += (results)->strides[ULAB_MAX_DIMS - 4];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS-3];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 4];\ + i++;\ + } while(i < (results)->shape[ULAB_MAX_DIMS - 4]);\ + +#define EQUALITY_LOOP(results, array, type_left, type_right, larray, lstrides, rarray, rstrides, OPERATOR)\ + size_t i = 0;\ + do {\ + size_t j = 0;\ + do {\ + size_t k = 0;\ + do {\ + size_t l = 0;\ + do {\ + *(array)++ = *((type_left *)(larray)) OPERATOR *((type_right *)(rarray)) ? 1 : 0;\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\ + l++;\ + } while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\ + (larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * results->shape[ULAB_MAX_DIMS-1];\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 2];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * results->shape[ULAB_MAX_DIMS-1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\ + k++;\ + } while(k < (results)->shape[ULAB_MAX_DIMS - 2]);\ + (larray) -= (lstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS-2];\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 3];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS-2];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\ + j++;\ + } while(j < (results)->shape[ULAB_MAX_DIMS - 3]);\ + (larray) -= (lstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS-3];\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 4];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS-3];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 4];\ + i++;\ + } while(i < (results)->shape[ULAB_MAX_DIMS - 4]);\ + +#define POWER_LOOP(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides)\ + type_out *array = (type_out *)results->array;\ + size_t i = 0;\ + do {\ + size_t j = 0;\ + do {\ + size_t k = 0;\ + do {\ + size_t l = 0;\ + do {\ + *array++ = MICROPY_FLOAT_C_FUN(pow)(*((type_left *)(larray)), *((type_right *)(rarray)));\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\ + l++;\ + } while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\ + (larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 2];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\ + k++;\ + } while(k < (results)->shape[ULAB_MAX_DIMS - 2]);\ + (larray) -= (lstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS-2];\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 3];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS-2];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\ + j++;\ + } while(j < (results)->shape[ULAB_MAX_DIMS - 3]);\ + (larray) -= (lstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS-3];\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 4];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS-3];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 4];\ + i++;\ + } while(i < (results)->shape[ULAB_MAX_DIMS - 4]);\ + +#endif /* ULAB_MAX_DIMS == 4 */ +#endif /* ULAB_HAS_FUNCTION_ITERATOR */ + + +#if ULAB_MAX_DIMS == 1 +#define ASSIGNMENT_LOOP(results, type_left, type_right, lstrides, rarray, rstrides)\ + type_left *larray = (type_left *)(results)->array;\ + size_t l = 0;\ + do {\ + *larray = (type_left)(*((type_right *)(rarray)));\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\ + l++;\ + } while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\ + +#endif /* ULAB_MAX_DIMS == 1 */ + +#if ULAB_MAX_DIMS == 2 +#define ASSIGNMENT_LOOP(results, type_left, type_right, lstrides, rarray, rstrides)\ + type_left *larray = (type_left *)(results)->array;\ + size_t k = 0;\ + do {\ + size_t l = 0;\ + do {\ + *larray = (type_left)(*((type_right *)(rarray)));\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\ + l++;\ + } while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\ + (larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 2];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\ + k++;\ + } while(k < (results)->shape[ULAB_MAX_DIMS - 2]);\ + +#endif /* ULAB_MAX_DIMS == 2 */ + +#if ULAB_MAX_DIMS == 3 +#define ASSIGNMENT_LOOP(results, type_left, type_right, lstrides, rarray, rstrides)\ + type_left *larray = (type_left *)(results)->array;\ + size_t j = 0;\ + do {\ + size_t k = 0;\ + do {\ + size_t l = 0;\ + do {\ + *larray = (type_left)(*((type_right *)(rarray)));\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\ + l++;\ + } while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\ + (larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 2];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\ + k++;\ + } while(k < (results)->shape[ULAB_MAX_DIMS - 2]);\ + (larray) -= (lstrides)[ULAB_MAX_DIMS - 2] * results->shape[ULAB_MAX_DIMS-2];\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 3];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * results->shape[ULAB_MAX_DIMS-2];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\ + j++;\ + } while(j < (results)->shape[ULAB_MAX_DIMS - 3]);\ + +#endif /* ULAB_MAX_DIMS == 3 */ + +#if ULAB_MAX_DIMS == 4 +#define ASSIGNMENT_LOOP(results, type_left, type_right, lstrides, rarray, rstrides)\ + type_left *larray = (type_left *)(results)->array;\ + size_t i = 0;\ + do {\ + size_t j = 0;\ + do {\ + size_t k = 0;\ + do {\ + size_t l = 0;\ + do {\ + *larray = (type_left)(*((type_right *)(rarray)));\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\ + l++;\ + } while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\ + (larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 2];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\ + k++;\ + } while(k < (results)->shape[ULAB_MAX_DIMS - 2]);\ + (larray) -= (lstrides)[ULAB_MAX_DIMS - 2] * results->shape[ULAB_MAX_DIMS-2];\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 3];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * results->shape[ULAB_MAX_DIMS-2];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\ + j++;\ + } while(j < (results)->shape[ULAB_MAX_DIMS - 3]);\ + (larray) -= (lstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS-3];\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 4];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS-3];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 4];\ + i++;\ + } while(i < (results)->shape[ULAB_MAX_DIMS - 4]);\ + +#endif /* ULAB_MAX_DIMS == 4 */ + +#endif diff --git a/python/port/mod/ulab/ndarray_operators.c b/python/port/mod/ulab/ndarray_operators.c new file mode 100644 index 000000000..ac2386053 --- /dev/null +++ b/python/port/mod/ulab/ndarray_operators.c @@ -0,0 +1,807 @@ +/* + * This file is part of the micropython-ulab project, + * + * https://github.com/v923z/micropython-ulab + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Zoltán Vörös +*/ + + +#include + +#include +#include +#include "ndarray.h" +#include "ndarray_operators.h" +#include "ulab.h" +#include "ulab_tools.h" + +/* + This file contains the actual implementations of the various + ndarray operators. + + These are the upcasting rules of the binary operators + + - if one of the operarands is a float, the result is always float + - operation on identical types preserves type + + uint8 + int8 => int16 + uint8 + int16 => int16 + uint8 + uint16 => uint16 + int8 + int16 => int16 + int8 + uint16 => uint16 + uint16 + int16 => float +*/ + +#if NDARRAY_HAS_BINARY_OP_EQUAL | NDARRAY_HAS_BINARY_OP_NOT_EQUAL +mp_obj_t ndarray_binary_equality(ndarray_obj_t *lhs, ndarray_obj_t *rhs, + uint8_t ndim, size_t *shape, int32_t *lstrides, int32_t *rstrides, mp_binary_op_t op) { + + ndarray_obj_t *results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT8); + results->boolean = 1; + uint8_t *array = (uint8_t *)results->array; + uint8_t *larray = (uint8_t *)lhs->array; + uint8_t *rarray = (uint8_t *)rhs->array; + + #if NDARRAY_HAS_BINARY_OP_EQUAL + if(op == MP_BINARY_OP_EQUAL) { + if(lhs->dtype == NDARRAY_UINT8) { + if(rhs->dtype == NDARRAY_UINT8) { + EQUALITY_LOOP(results, array, uint8_t, uint8_t, larray, lstrides, rarray, rstrides, ==); + } else if(rhs->dtype == NDARRAY_INT8) { + EQUALITY_LOOP(results, array, uint8_t, int8_t, larray, lstrides, rarray, rstrides, ==); + } else if(rhs->dtype == NDARRAY_UINT16) { + EQUALITY_LOOP(results, array, uint8_t, uint16_t, larray, lstrides, rarray, rstrides, ==); + } else if(rhs->dtype == NDARRAY_INT16) { + EQUALITY_LOOP(results, array, uint8_t, int16_t, larray, lstrides, rarray, rstrides, ==); + } else if(rhs->dtype == NDARRAY_FLOAT) { + EQUALITY_LOOP(results, array, uint8_t, mp_float_t, larray, lstrides, rarray, rstrides, ==); + } + } else if(lhs->dtype == NDARRAY_INT8) { + if(rhs->dtype == NDARRAY_INT8) { + EQUALITY_LOOP(results, array, int8_t, int8_t, larray, lstrides, rarray, rstrides, ==); + } else if(rhs->dtype == NDARRAY_UINT16) { + EQUALITY_LOOP(results, array, int8_t, uint16_t, larray, lstrides, rarray, rstrides, ==); + } else if(rhs->dtype == NDARRAY_INT16) { + EQUALITY_LOOP(results, array, int8_t, int16_t, larray, lstrides, rarray, rstrides, ==); + } else if(rhs->dtype == NDARRAY_FLOAT) { + EQUALITY_LOOP(results, array, int8_t, mp_float_t, larray, lstrides, rarray, rstrides, ==); + } else { + return ndarray_binary_op(op, rhs, lhs); + } + } else if(lhs->dtype == NDARRAY_UINT16) { + if(rhs->dtype == NDARRAY_UINT16) { + EQUALITY_LOOP(results, array, uint16_t, uint16_t, larray, lstrides, rarray, rstrides, ==); + } else if(rhs->dtype == NDARRAY_INT16) { + EQUALITY_LOOP(results, array, uint16_t, int16_t, larray, lstrides, rarray, rstrides, ==); + } else if(rhs->dtype == NDARRAY_FLOAT) { + EQUALITY_LOOP(results, array, uint16_t, mp_float_t, larray, lstrides, rarray, rstrides, ==); + } else { + return ndarray_binary_op(op, rhs, lhs); + } + } else if(lhs->dtype == NDARRAY_INT16) { + if(rhs->dtype == NDARRAY_INT16) { + EQUALITY_LOOP(results, array, int16_t, int16_t, larray, lstrides, rarray, rstrides, ==); + } else if(rhs->dtype == NDARRAY_FLOAT) { + EQUALITY_LOOP(results, array, int16_t, mp_float_t, larray, lstrides, rarray, rstrides, ==); + } else { + return ndarray_binary_op(op, rhs, lhs); + } + } else if(lhs->dtype == NDARRAY_FLOAT) { + if(rhs->dtype == NDARRAY_FLOAT) { + EQUALITY_LOOP(results, array, mp_float_t, mp_float_t, larray, lstrides, rarray, rstrides, ==); + } else { + return ndarray_binary_op(op, rhs, lhs); + } + } + } + #endif /* NDARRAY_HAS_BINARY_OP_EQUAL */ + + #if NDARRAY_HAS_BINARY_OP_NOT_EQUAL + if(op == MP_BINARY_OP_NOT_EQUAL) { + if(lhs->dtype == NDARRAY_UINT8) { + if(rhs->dtype == NDARRAY_UINT8) { + EQUALITY_LOOP(results, array, uint8_t, uint8_t, larray, lstrides, rarray, rstrides, !=); + } else if(rhs->dtype == NDARRAY_INT8) { + EQUALITY_LOOP(results, array, uint8_t, int8_t, larray, lstrides, rarray, rstrides, !=); + } else if(rhs->dtype == NDARRAY_UINT16) { + EQUALITY_LOOP(results, array, uint8_t, uint16_t, larray, lstrides, rarray, rstrides, !=); + } else if(rhs->dtype == NDARRAY_INT16) { + EQUALITY_LOOP(results, array, uint8_t, int16_t, larray, lstrides, rarray, rstrides, !=); + } else if(rhs->dtype == NDARRAY_FLOAT) { + EQUALITY_LOOP(results, array, uint8_t, mp_float_t, larray, lstrides, rarray, rstrides, !=); + } + } else if(lhs->dtype == NDARRAY_INT8) { + if(rhs->dtype == NDARRAY_INT8) { + EQUALITY_LOOP(results, array, int8_t, int8_t, larray, lstrides, rarray, rstrides, !=); + } else if(rhs->dtype == NDARRAY_UINT16) { + EQUALITY_LOOP(results, array, int8_t, uint16_t, larray, lstrides, rarray, rstrides, !=); + } else if(rhs->dtype == NDARRAY_INT16) { + EQUALITY_LOOP(results, array, int8_t, int16_t, larray, lstrides, rarray, rstrides, !=); + } else if(rhs->dtype == NDARRAY_FLOAT) { + EQUALITY_LOOP(results, array, int8_t, mp_float_t, larray, lstrides, rarray, rstrides, !=); + } else { + return ndarray_binary_op(op, rhs, lhs); + } + } else if(lhs->dtype == NDARRAY_UINT16) { + if(rhs->dtype == NDARRAY_UINT16) { + EQUALITY_LOOP(results, array, uint16_t, uint16_t, larray, lstrides, rarray, rstrides, !=); + } else if(rhs->dtype == NDARRAY_INT16) { + EQUALITY_LOOP(results, array, uint16_t, int16_t, larray, lstrides, rarray, rstrides, !=); + } else if(rhs->dtype == NDARRAY_FLOAT) { + EQUALITY_LOOP(results, array, uint16_t, mp_float_t, larray, lstrides, rarray, rstrides, !=); + } else { + return ndarray_binary_op(op, rhs, lhs); + } + } else if(lhs->dtype == NDARRAY_INT16) { + if(rhs->dtype == NDARRAY_INT16) { + EQUALITY_LOOP(results, array, int16_t, int16_t, larray, lstrides, rarray, rstrides, !=); + } else if(rhs->dtype == NDARRAY_FLOAT) { + EQUALITY_LOOP(results, array, int16_t, mp_float_t, larray, lstrides, rarray, rstrides, !=); + } else { + return ndarray_binary_op(op, rhs, lhs); + } + } else if(lhs->dtype == NDARRAY_FLOAT) { + if(rhs->dtype == NDARRAY_FLOAT) { + EQUALITY_LOOP(results, array, mp_float_t, mp_float_t, larray, lstrides, rarray, rstrides, !=); + } else { + return ndarray_binary_op(op, rhs, lhs); + } + } + } + #endif /* NDARRAY_HAS_BINARY_OP_NOT_EQUAL */ + + return MP_OBJ_FROM_PTR(results); +} +#endif /* NDARRAY_HAS_BINARY_OP_EQUAL | NDARRAY_HAS_BINARY_OP_NOT_EQUAL */ + +#if NDARRAY_HAS_BINARY_OP_ADD +mp_obj_t ndarray_binary_add(ndarray_obj_t *lhs, ndarray_obj_t *rhs, + uint8_t ndim, size_t *shape, int32_t *lstrides, int32_t *rstrides) { + + ndarray_obj_t *results = NULL; + uint8_t *larray = (uint8_t *)lhs->array; + uint8_t *rarray = (uint8_t *)rhs->array; + + if(lhs->dtype == NDARRAY_UINT8) { + if(rhs->dtype == NDARRAY_UINT8) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16); + BINARY_LOOP(results, uint16_t, uint8_t, uint8_t, larray, lstrides, rarray, rstrides, +); + } else if(rhs->dtype == NDARRAY_INT8) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, uint8_t, int8_t, larray, lstrides, rarray, rstrides, +); + } else if(rhs->dtype == NDARRAY_UINT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16); + BINARY_LOOP(results, uint16_t, uint8_t, uint16_t, larray, lstrides, rarray, rstrides, +); + } else if(rhs->dtype == NDARRAY_INT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, uint8_t, int16_t, larray, lstrides, rarray, rstrides, +); + } else if(rhs->dtype == NDARRAY_FLOAT) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT); + BINARY_LOOP(results, mp_float_t, uint8_t, mp_float_t, larray, lstrides, rarray, rstrides, +); + } + } else if(lhs->dtype == NDARRAY_INT8) { + if(rhs->dtype == NDARRAY_INT8) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT8); + BINARY_LOOP(results, int8_t, int8_t, int8_t, larray, lstrides, rarray, rstrides, +); + } else if(rhs->dtype == NDARRAY_UINT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, int8_t, uint16_t, larray, lstrides, rarray, rstrides, +); + } else if(rhs->dtype == NDARRAY_INT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, int8_t, int16_t, larray, lstrides, rarray, rstrides, +); + } else if(rhs->dtype == NDARRAY_FLOAT) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT); + BINARY_LOOP(results, mp_float_t, int8_t, mp_float_t, larray, lstrides, rarray, rstrides, +); + } else { + return ndarray_binary_op(MP_BINARY_OP_ADD, rhs, lhs); + } + } else if(lhs->dtype == NDARRAY_UINT16) { + if(rhs->dtype == NDARRAY_UINT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16); + BINARY_LOOP(results, uint16_t, uint16_t, uint16_t, larray, lstrides, rarray, rstrides, +); + } else if(rhs->dtype == NDARRAY_INT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT); + BINARY_LOOP(results, mp_float_t, uint16_t, int16_t, larray, lstrides, rarray, rstrides, +); + } else if(rhs->dtype == NDARRAY_FLOAT) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT); + BINARY_LOOP(results, mp_float_t, uint16_t, mp_float_t, larray, lstrides, rarray, rstrides, +); + } else { + return ndarray_binary_op(MP_BINARY_OP_ADD, rhs, lhs); + } + } else if(lhs->dtype == NDARRAY_INT16) { + if(rhs->dtype == NDARRAY_INT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, int16_t, int16_t, larray, lstrides, rarray, rstrides, +); + } else if(rhs->dtype == NDARRAY_FLOAT) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT); + BINARY_LOOP(results, mp_float_t, int16_t, mp_float_t, larray, lstrides, rarray, rstrides, +); + } else { + return ndarray_binary_op(MP_BINARY_OP_ADD, rhs, lhs); + } + } else if(lhs->dtype == NDARRAY_FLOAT) { + if(rhs->dtype == NDARRAY_FLOAT) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT); + BINARY_LOOP(results, mp_float_t, mp_float_t, mp_float_t, larray, lstrides, rarray, rstrides, +); + } else { + return ndarray_binary_op(MP_BINARY_OP_ADD, rhs, lhs); + } + } + + return MP_OBJ_FROM_PTR(results); +} +#endif /* NDARRAY_HAS_BINARY_OP_ADD */ + +#if NDARRAY_HAS_BINARY_OP_MULTIPLY +mp_obj_t ndarray_binary_multiply(ndarray_obj_t *lhs, ndarray_obj_t *rhs, + uint8_t ndim, size_t *shape, int32_t *lstrides, int32_t *rstrides) { + + ndarray_obj_t *results = NULL; + uint8_t *larray = (uint8_t *)lhs->array; + uint8_t *rarray = (uint8_t *)rhs->array; + + if(lhs->dtype == NDARRAY_UINT8) { + if(rhs->dtype == NDARRAY_UINT8) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16); + BINARY_LOOP(results, uint16_t, uint8_t, uint8_t, larray, lstrides, rarray, rstrides, *); + } else if(rhs->dtype == NDARRAY_INT8) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, uint8_t, int8_t, larray, lstrides, rarray, rstrides, *); + } else if(rhs->dtype == NDARRAY_UINT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16); + BINARY_LOOP(results, uint16_t, uint8_t, uint16_t, larray, lstrides, rarray, rstrides, *); + } else if(rhs->dtype == NDARRAY_INT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, uint8_t, int16_t, larray, lstrides, rarray, rstrides, *); + } else if(rhs->dtype == NDARRAY_FLOAT) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT); + BINARY_LOOP(results, mp_float_t, uint8_t, mp_float_t, larray, lstrides, rarray, rstrides, *); + } + } else if(lhs->dtype == NDARRAY_INT8) { + if(rhs->dtype == NDARRAY_INT8) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT8); + BINARY_LOOP(results, int8_t, int8_t, int8_t, larray, lstrides, rarray, rstrides, *); + } else if(rhs->dtype == NDARRAY_UINT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, int8_t, uint16_t, larray, lstrides, rarray, rstrides, *); + } else if(rhs->dtype == NDARRAY_INT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, int8_t, int16_t, larray, lstrides, rarray, rstrides, *); + } else if(rhs->dtype == NDARRAY_FLOAT) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT); + BINARY_LOOP(results, mp_float_t, int8_t, mp_float_t, larray, lstrides, rarray, rstrides, *); + } else { + return ndarray_binary_op(MP_BINARY_OP_MULTIPLY, rhs, lhs); + } + } else if(lhs->dtype == NDARRAY_UINT16) { + if(rhs->dtype == NDARRAY_UINT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16); + BINARY_LOOP(results, uint16_t, uint16_t, uint16_t, larray, lstrides, rarray, rstrides, *); + } else if(rhs->dtype == NDARRAY_INT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT); + BINARY_LOOP(results, mp_float_t, uint16_t, int16_t, larray, lstrides, rarray, rstrides, *); + } else if(rhs->dtype == NDARRAY_FLOAT) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT); + BINARY_LOOP(results, mp_float_t, uint16_t, mp_float_t, larray, lstrides, rarray, rstrides, *); + } else { + return ndarray_binary_op(MP_BINARY_OP_MULTIPLY, rhs, lhs); + } + } else if(lhs->dtype == NDARRAY_INT16) { + if(rhs->dtype == NDARRAY_INT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, int16_t, int16_t, larray, lstrides, rarray, rstrides, *); + } else if(rhs->dtype == NDARRAY_FLOAT) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT); + BINARY_LOOP(results, mp_float_t, int16_t, mp_float_t, larray, lstrides, rarray, rstrides, *); + } else { + return ndarray_binary_op(MP_BINARY_OP_MULTIPLY, rhs, lhs); + } + } else if(lhs->dtype == NDARRAY_FLOAT) { + if(rhs->dtype == NDARRAY_FLOAT) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT); + BINARY_LOOP(results, mp_float_t, mp_float_t, mp_float_t, larray, lstrides, rarray, rstrides, *); + } else { + return ndarray_binary_op(MP_BINARY_OP_MULTIPLY, rhs, lhs); + } + } + + return MP_OBJ_FROM_PTR(results); +} +#endif /* NDARRAY_HAS_BINARY_OP_MULTIPLY */ + +#if NDARRAY_HAS_BINARY_OP_MORE | NDARRAY_HAS_BINARY_OP_MORE_EQUAL | NDARRAY_HAS_BINARY_OP_LESS | NDARRAY_HAS_BINARY_OP_LESS_EQUAL +mp_obj_t ndarray_binary_more(ndarray_obj_t *lhs, ndarray_obj_t *rhs, + uint8_t ndim, size_t *shape, int32_t *lstrides, int32_t *rstrides, mp_binary_op_t op) { + + ndarray_obj_t *results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT8); + results->boolean = 1; + uint8_t *array = (uint8_t *)results->array; + uint8_t *larray = (uint8_t *)lhs->array; + uint8_t *rarray = (uint8_t *)rhs->array; + + #if NDARRAY_HAS_BINARY_OP_MORE | NDARRAY_HAS_BINARY_OP_LESS + if(op == MP_BINARY_OP_MORE) { + if(lhs->dtype == NDARRAY_UINT8) { + if(rhs->dtype == NDARRAY_UINT8) { + EQUALITY_LOOP(results, array, uint8_t, uint8_t, larray, lstrides, rarray, rstrides, >); + } else if(rhs->dtype == NDARRAY_INT8) { + EQUALITY_LOOP(results, array, uint8_t, int8_t, larray, lstrides, rarray, rstrides, >); + } else if(rhs->dtype == NDARRAY_UINT16) { + EQUALITY_LOOP(results, array, uint8_t, uint16_t, larray, lstrides, rarray, rstrides, >); + } else if(rhs->dtype == NDARRAY_INT16) { + EQUALITY_LOOP(results, array, uint8_t, int16_t, larray, lstrides, rarray, rstrides, >); + } else if(rhs->dtype == NDARRAY_FLOAT) { + EQUALITY_LOOP(results, array, uint8_t, mp_float_t, larray, lstrides, rarray, rstrides, >); + } + } else if(lhs->dtype == NDARRAY_INT8) { + if(rhs->dtype == NDARRAY_UINT8) { + EQUALITY_LOOP(results, array, int8_t, uint8_t, larray, lstrides, rarray, rstrides, >); + } else if(rhs->dtype == NDARRAY_INT8) { + EQUALITY_LOOP(results, array, int8_t, int8_t, larray, lstrides, rarray, rstrides, >); + } else if(rhs->dtype == NDARRAY_UINT16) { + EQUALITY_LOOP(results, array, int8_t, uint16_t, larray, lstrides, rarray, rstrides, >); + } else if(rhs->dtype == NDARRAY_INT16) { + EQUALITY_LOOP(results, array, int8_t, int16_t, larray, lstrides, rarray, rstrides, >); + } else if(rhs->dtype == NDARRAY_FLOAT) { + EQUALITY_LOOP(results, array, int8_t, mp_float_t, larray, lstrides, rarray, rstrides, >); + } + } else if(lhs->dtype == NDARRAY_UINT16) { + if(rhs->dtype == NDARRAY_UINT8) { + EQUALITY_LOOP(results, array, uint16_t, uint8_t, larray, lstrides, rarray, rstrides, >); + } else if(rhs->dtype == NDARRAY_INT8) { + EQUALITY_LOOP(results, array, uint16_t, int8_t, larray, lstrides, rarray, rstrides, >); + } else if(rhs->dtype == NDARRAY_UINT16) { + EQUALITY_LOOP(results, array, uint16_t, uint16_t, larray, lstrides, rarray, rstrides, >); + } else if(rhs->dtype == NDARRAY_INT16) { + EQUALITY_LOOP(results, array, uint16_t, int16_t, larray, lstrides, rarray, rstrides, >); + } else if(rhs->dtype == NDARRAY_FLOAT) { + EQUALITY_LOOP(results, array, uint16_t, mp_float_t, larray, lstrides, rarray, rstrides, >); + } + } else if(lhs->dtype == NDARRAY_INT16) { + if(rhs->dtype == NDARRAY_UINT8) { + EQUALITY_LOOP(results, array, int16_t, uint8_t, larray, lstrides, rarray, rstrides, >); + } else if(rhs->dtype == NDARRAY_INT8) { + EQUALITY_LOOP(results, array, int16_t, int8_t, larray, lstrides, rarray, rstrides, >); + } else if(rhs->dtype == NDARRAY_UINT16) { + EQUALITY_LOOP(results, array, int16_t, uint16_t, larray, lstrides, rarray, rstrides, >); + } else if(rhs->dtype == NDARRAY_INT16) { + EQUALITY_LOOP(results, array, int16_t, int16_t, larray, lstrides, rarray, rstrides, >); + } else if(rhs->dtype == NDARRAY_FLOAT) { + EQUALITY_LOOP(results, array, uint16_t, mp_float_t, larray, lstrides, rarray, rstrides, >); + } + } else if(lhs->dtype == NDARRAY_FLOAT) { + if(rhs->dtype == NDARRAY_UINT8) { + EQUALITY_LOOP(results, array, mp_float_t, uint8_t, larray, lstrides, rarray, rstrides, >); + } else if(rhs->dtype == NDARRAY_INT8) { + EQUALITY_LOOP(results, array, mp_float_t, int8_t, larray, lstrides, rarray, rstrides, >); + } else if(rhs->dtype == NDARRAY_UINT16) { + EQUALITY_LOOP(results, array, mp_float_t, uint16_t, larray, lstrides, rarray, rstrides, >); + } else if(rhs->dtype == NDARRAY_INT16) { + EQUALITY_LOOP(results, array, mp_float_t, int16_t, larray, lstrides, rarray, rstrides, >); + } else if(rhs->dtype == NDARRAY_FLOAT) { + EQUALITY_LOOP(results, array, mp_float_t, mp_float_t, larray, lstrides, rarray, rstrides, >); + } + } + } + #endif /* NDARRAY_HAS_BINARY_OP_MORE | NDARRAY_HAS_BINARY_OP_LESS*/ + #if NDARRAY_HAS_BINARY_OP_MORE_EQUAL | NDARRAY_HAS_BINARY_OP_LESS_EQUAL + if(op == MP_BINARY_OP_MORE_EQUAL) { + if(lhs->dtype == NDARRAY_UINT8) { + if(rhs->dtype == NDARRAY_UINT8) { + EQUALITY_LOOP(results, array, uint8_t, uint8_t, larray, lstrides, rarray, rstrides, >=); + } else if(rhs->dtype == NDARRAY_INT8) { + EQUALITY_LOOP(results, array, uint8_t, int8_t, larray, lstrides, rarray, rstrides, >=); + } else if(rhs->dtype == NDARRAY_UINT16) { + EQUALITY_LOOP(results, array, uint8_t, uint16_t, larray, lstrides, rarray, rstrides, >=); + } else if(rhs->dtype == NDARRAY_INT16) { + EQUALITY_LOOP(results, array, uint8_t, int16_t, larray, lstrides, rarray, rstrides, >=); + } else if(rhs->dtype == NDARRAY_FLOAT) { + EQUALITY_LOOP(results, array, uint8_t, mp_float_t, larray, lstrides, rarray, rstrides, >=); + } + } else if(lhs->dtype == NDARRAY_INT8) { + if(rhs->dtype == NDARRAY_UINT8) { + EQUALITY_LOOP(results, array, int8_t, uint8_t, larray, lstrides, rarray, rstrides, >=); + } else if(rhs->dtype == NDARRAY_INT8) { + EQUALITY_LOOP(results, array, int8_t, int8_t, larray, lstrides, rarray, rstrides, >=); + } else if(rhs->dtype == NDARRAY_UINT16) { + EQUALITY_LOOP(results, array, int8_t, uint16_t, larray, lstrides, rarray, rstrides, >=); + } else if(rhs->dtype == NDARRAY_INT16) { + EQUALITY_LOOP(results, array, int8_t, int16_t, larray, lstrides, rarray, rstrides, >=); + } else if(rhs->dtype == NDARRAY_FLOAT) { + EQUALITY_LOOP(results, array, int8_t, mp_float_t, larray, lstrides, rarray, rstrides, >=); + } + } else if(lhs->dtype == NDARRAY_UINT16) { + if(rhs->dtype == NDARRAY_UINT8) { + EQUALITY_LOOP(results, array, uint16_t, uint8_t, larray, lstrides, rarray, rstrides, >=); + } else if(rhs->dtype == NDARRAY_INT8) { + EQUALITY_LOOP(results, array, uint16_t, int8_t, larray, lstrides, rarray, rstrides, >=); + } else if(rhs->dtype == NDARRAY_UINT16) { + EQUALITY_LOOP(results, array, uint16_t, uint16_t, larray, lstrides, rarray, rstrides, >=); + } else if(rhs->dtype == NDARRAY_INT16) { + EQUALITY_LOOP(results, array, uint16_t, int16_t, larray, lstrides, rarray, rstrides, >=); + } else if(rhs->dtype == NDARRAY_FLOAT) { + EQUALITY_LOOP(results, array, uint16_t, mp_float_t, larray, lstrides, rarray, rstrides, >=); + } + } else if(lhs->dtype == NDARRAY_INT16) { + if(rhs->dtype == NDARRAY_UINT8) { + EQUALITY_LOOP(results, array, int16_t, uint8_t, larray, lstrides, rarray, rstrides, >=); + } else if(rhs->dtype == NDARRAY_INT8) { + EQUALITY_LOOP(results, array, int16_t, int8_t, larray, lstrides, rarray, rstrides, >=); + } else if(rhs->dtype == NDARRAY_UINT16) { + EQUALITY_LOOP(results, array, int16_t, uint16_t, larray, lstrides, rarray, rstrides, >=); + } else if(rhs->dtype == NDARRAY_INT16) { + EQUALITY_LOOP(results, array, int16_t, int16_t, larray, lstrides, rarray, rstrides, >=); + } else if(rhs->dtype == NDARRAY_FLOAT) { + EQUALITY_LOOP(results, array, uint16_t, mp_float_t, larray, lstrides, rarray, rstrides, >=); + } + } else if(lhs->dtype == NDARRAY_FLOAT) { + if(rhs->dtype == NDARRAY_UINT8) { + EQUALITY_LOOP(results, array, mp_float_t, uint8_t, larray, lstrides, rarray, rstrides, >=); + } else if(rhs->dtype == NDARRAY_INT8) { + EQUALITY_LOOP(results, array, mp_float_t, int8_t, larray, lstrides, rarray, rstrides, >=); + } else if(rhs->dtype == NDARRAY_UINT16) { + EQUALITY_LOOP(results, array, mp_float_t, uint16_t, larray, lstrides, rarray, rstrides, >=); + } else if(rhs->dtype == NDARRAY_INT16) { + EQUALITY_LOOP(results, array, mp_float_t, int16_t, larray, lstrides, rarray, rstrides, >=); + } else if(rhs->dtype == NDARRAY_FLOAT) { + EQUALITY_LOOP(results, array, mp_float_t, mp_float_t, larray, lstrides, rarray, rstrides, >=); + } + } + } + #endif /* NDARRAY_HAS_BINARY_OP_MORE_EQUAL | NDARRAY_HAS_BINARY_OP_LESS_EQUAL */ + + return MP_OBJ_FROM_PTR(results); +} +#endif /* NDARRAY_HAS_BINARY_OP_MORE | NDARRAY_HAS_BINARY_OP_MORE_EQUAL | NDARRAY_HAS_BINARY_OP_LESS | NDARRAY_HAS_BINARY_OP_LESS_EQUAL */ + +#if NDARRAY_HAS_BINARY_OP_SUBTRACT +mp_obj_t ndarray_binary_subtract(ndarray_obj_t *lhs, ndarray_obj_t *rhs, + uint8_t ndim, size_t *shape, int32_t *lstrides, int32_t *rstrides) { + + ndarray_obj_t *results = NULL; + uint8_t *larray = (uint8_t *)lhs->array; + uint8_t *rarray = (uint8_t *)rhs->array; + + if(lhs->dtype == NDARRAY_UINT8) { + if(rhs->dtype == NDARRAY_UINT8) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT8); + BINARY_LOOP(results, uint8_t, uint8_t, uint8_t, larray, lstrides, rarray, rstrides, -); + } else if(rhs->dtype == NDARRAY_INT8) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, uint8_t, int8_t, larray, lstrides, rarray, rstrides, -); + } else if(rhs->dtype == NDARRAY_UINT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16); + BINARY_LOOP(results, uint16_t, uint8_t, uint16_t, larray, lstrides, rarray, rstrides, -); + } else if(rhs->dtype == NDARRAY_INT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, uint8_t, int16_t, larray, lstrides, rarray, rstrides, -); + } else if(rhs->dtype == NDARRAY_FLOAT) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT); + BINARY_LOOP(results, mp_float_t, uint8_t, mp_float_t, larray, lstrides, rarray, rstrides, -); + } + } else if(lhs->dtype == NDARRAY_INT8) { + if(rhs->dtype == NDARRAY_UINT8) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, int8_t, uint8_t, larray, lstrides, rarray, rstrides, -); + } else if(rhs->dtype == NDARRAY_INT8) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT8); + BINARY_LOOP(results, int8_t, int8_t, int8_t, larray, lstrides, rarray, rstrides, -); + } else if(rhs->dtype == NDARRAY_UINT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, int8_t, uint16_t, larray, lstrides, rarray, rstrides, -); + } else if(rhs->dtype == NDARRAY_INT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, int8_t, int16_t, larray, lstrides, rarray, rstrides, -); + } else if(rhs->dtype == NDARRAY_FLOAT) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT); + BINARY_LOOP(results, mp_float_t, int8_t, mp_float_t, larray, lstrides, rarray, rstrides, -); + } + } else if(lhs->dtype == NDARRAY_UINT16) { + if(rhs->dtype == NDARRAY_UINT8) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16); + BINARY_LOOP(results, uint16_t, uint16_t, uint8_t, larray, lstrides, rarray, rstrides, -); + } else if(rhs->dtype == NDARRAY_INT8) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16); + BINARY_LOOP(results, uint16_t, uint16_t, int8_t, larray, lstrides, rarray, rstrides, -); + } else if(rhs->dtype == NDARRAY_UINT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16); + BINARY_LOOP(results, uint16_t, uint16_t, uint16_t, larray, lstrides, rarray, rstrides, -); + } else if(rhs->dtype == NDARRAY_INT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT); + BINARY_LOOP(results, mp_float_t, uint16_t, int16_t, larray, lstrides, rarray, rstrides, -); + } else if(rhs->dtype == NDARRAY_FLOAT) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT); + BINARY_LOOP(results, mp_float_t, uint16_t, mp_float_t, larray, lstrides, rarray, rstrides, -); + } + } else if(lhs->dtype == NDARRAY_INT16) { + if(rhs->dtype == NDARRAY_UINT8) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, int16_t, uint8_t, larray, lstrides, rarray, rstrides, -); + } else if(rhs->dtype == NDARRAY_INT8) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, int16_t, int8_t, larray, lstrides, rarray, rstrides, -); + } else if(rhs->dtype == NDARRAY_UINT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT); + BINARY_LOOP(results, mp_float_t, int16_t, uint16_t, larray, lstrides, rarray, rstrides, -); + } else if(rhs->dtype == NDARRAY_INT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, int16_t, int16_t, larray, lstrides, rarray, rstrides, -); + } else if(rhs->dtype == NDARRAY_FLOAT) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT); + BINARY_LOOP(results, mp_float_t, uint16_t, mp_float_t, larray, lstrides, rarray, rstrides, -); + } + } else if(lhs->dtype == NDARRAY_FLOAT) { + if(rhs->dtype == NDARRAY_UINT8) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT); + BINARY_LOOP(results, mp_float_t, mp_float_t, uint8_t, larray, lstrides, rarray, rstrides, -); + } else if(rhs->dtype == NDARRAY_INT8) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT); + BINARY_LOOP(results, mp_float_t, mp_float_t, int8_t, larray, lstrides, rarray, rstrides, -); + } else if(rhs->dtype == NDARRAY_UINT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT); + BINARY_LOOP(results, mp_float_t, mp_float_t, uint16_t, larray, lstrides, rarray, rstrides, -); + } else if(rhs->dtype == NDARRAY_INT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT); + BINARY_LOOP(results, mp_float_t, mp_float_t, int16_t, larray, lstrides, rarray, rstrides, -); + } else if(rhs->dtype == NDARRAY_FLOAT) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT); + BINARY_LOOP(results, mp_float_t, mp_float_t, mp_float_t, larray, lstrides, rarray, rstrides, -); + } + } + + return MP_OBJ_FROM_PTR(results); +} +#endif /* NDARRAY_HAS_BINARY_OP_SUBTRACT */ + +#if NDARRAY_HAS_BINARY_OP_TRUE_DIVIDE +mp_obj_t ndarray_binary_true_divide(ndarray_obj_t *lhs, ndarray_obj_t *rhs, + uint8_t ndim, size_t *shape, int32_t *lstrides, int32_t *rstrides) { + + ndarray_obj_t *results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT); + uint8_t *larray = (uint8_t *)lhs->array; + uint8_t *rarray = (uint8_t *)rhs->array; + + #if NDARRAY_BINARY_USES_FUN_POINTER + mp_float_t (*get_lhs)(void *) = ndarray_get_float_function(lhs->dtype); + mp_float_t (*get_rhs)(void *) = ndarray_get_float_function(rhs->dtype); + + uint8_t *array = (uint8_t *)results->array; + void (*set_result)(void *, mp_float_t ) = ndarray_set_float_function(NDARRAY_FLOAT); + + // Note that lvalue and rvalue are local variables in the macro itself + FUNC_POINTER_LOOP(results, array, get_lhs, get_rhs, larray, lstrides, rarray, rstrides, lvalue/rvalue); + + #else + if(lhs->dtype == NDARRAY_UINT8) { + if(rhs->dtype == NDARRAY_UINT8) { + BINARY_LOOP(results, mp_float_t, uint8_t, uint8_t, larray, lstrides, rarray, rstrides, /); + } else if(rhs->dtype == NDARRAY_INT8) { + BINARY_LOOP(results, mp_float_t, uint8_t, int8_t, larray, lstrides, rarray, rstrides, /); + } else if(rhs->dtype == NDARRAY_UINT16) { + BINARY_LOOP(results, mp_float_t, uint8_t, uint16_t, larray, lstrides, rarray, rstrides, /); + } else if(rhs->dtype == NDARRAY_INT16) { + BINARY_LOOP(results, mp_float_t, uint8_t, int16_t, larray, lstrides, rarray, rstrides, /); + } else if(rhs->dtype == NDARRAY_FLOAT) { + BINARY_LOOP(results, mp_float_t, uint8_t, mp_float_t, larray, lstrides, rarray, rstrides, /); + } + } else if(lhs->dtype == NDARRAY_INT8) { + if(rhs->dtype == NDARRAY_UINT8) { + BINARY_LOOP(results, mp_float_t, int8_t, uint8_t, larray, lstrides, rarray, rstrides, /); + } else if(rhs->dtype == NDARRAY_INT8) { + BINARY_LOOP(results, mp_float_t, int8_t, int8_t, larray, lstrides, rarray, rstrides, /); + } else if(rhs->dtype == NDARRAY_UINT16) { + BINARY_LOOP(results, mp_float_t, int8_t, uint16_t, larray, lstrides, rarray, rstrides, /); + } else if(rhs->dtype == NDARRAY_INT16) { + BINARY_LOOP(results, mp_float_t, int8_t, int16_t, larray, lstrides, rarray, rstrides, /); + } else if(rhs->dtype == NDARRAY_FLOAT) { + BINARY_LOOP(results, mp_float_t, int8_t, mp_float_t, larray, lstrides, rarray, rstrides, /); + } + } else if(lhs->dtype == NDARRAY_UINT16) { + if(rhs->dtype == NDARRAY_UINT8) { + BINARY_LOOP(results, mp_float_t, uint16_t, uint8_t, larray, lstrides, rarray, rstrides, /); + } else if(rhs->dtype == NDARRAY_INT8) { + BINARY_LOOP(results, mp_float_t, uint16_t, int8_t, larray, lstrides, rarray, rstrides, /); + } else if(rhs->dtype == NDARRAY_UINT16) { + BINARY_LOOP(results, mp_float_t, uint16_t, uint16_t, larray, lstrides, rarray, rstrides, /); + } else if(rhs->dtype == NDARRAY_INT16) { + BINARY_LOOP(results, mp_float_t, uint16_t, int16_t, larray, lstrides, rarray, rstrides, /); + } else if(rhs->dtype == NDARRAY_FLOAT) { + BINARY_LOOP(results, mp_float_t, uint16_t, mp_float_t, larray, lstrides, rarray, rstrides, /); + } + } else if(lhs->dtype == NDARRAY_INT16) { + if(rhs->dtype == NDARRAY_UINT8) { + BINARY_LOOP(results, mp_float_t, int16_t, uint8_t, larray, lstrides, rarray, rstrides, /); + } else if(rhs->dtype == NDARRAY_INT8) { + BINARY_LOOP(results, mp_float_t, int16_t, int8_t, larray, lstrides, rarray, rstrides, /); + } else if(rhs->dtype == NDARRAY_UINT16) { + BINARY_LOOP(results, mp_float_t, int16_t, uint16_t, larray, lstrides, rarray, rstrides, /); + } else if(rhs->dtype == NDARRAY_INT16) { + BINARY_LOOP(results, mp_float_t, int16_t, int16_t, larray, lstrides, rarray, rstrides, /); + } else if(rhs->dtype == NDARRAY_FLOAT) { + BINARY_LOOP(results, mp_float_t, uint16_t, mp_float_t, larray, lstrides, rarray, rstrides, /); + } + } else if(lhs->dtype == NDARRAY_FLOAT) { + if(rhs->dtype == NDARRAY_UINT8) { + BINARY_LOOP(results, mp_float_t, mp_float_t, uint8_t, larray, lstrides, rarray, rstrides, /); + } else if(rhs->dtype == NDARRAY_INT8) { + BINARY_LOOP(results, mp_float_t, mp_float_t, int8_t, larray, lstrides, rarray, rstrides, /); + } else if(rhs->dtype == NDARRAY_UINT16) { + BINARY_LOOP(results, mp_float_t, mp_float_t, uint16_t, larray, lstrides, rarray, rstrides, /); + } else if(rhs->dtype == NDARRAY_INT16) { + BINARY_LOOP(results, mp_float_t, mp_float_t, int16_t, larray, lstrides, rarray, rstrides, /); + } else if(rhs->dtype == NDARRAY_FLOAT) { + BINARY_LOOP(results, mp_float_t, mp_float_t, mp_float_t, larray, lstrides, rarray, rstrides, /); + } + } + #endif /* NDARRAY_BINARY_USES_FUN_POINTER */ + + return MP_OBJ_FROM_PTR(results); +} +#endif /* NDARRAY_HAS_BINARY_OP_TRUE_DIVIDE */ + +#if NDARRAY_HAS_BINARY_OP_POWER +mp_obj_t ndarray_binary_power(ndarray_obj_t *lhs, ndarray_obj_t *rhs, + uint8_t ndim, size_t *shape, int32_t *lstrides, int32_t *rstrides) { + + // Note that numpy upcasts the results to int64, if the inputs are of integer type, + // while we always return a float array. + ndarray_obj_t *results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT); + uint8_t *larray = (uint8_t *)lhs->array; + uint8_t *rarray = (uint8_t *)rhs->array; + + #if NDARRAY_BINARY_USES_FUN_POINTER + mp_float_t (*get_lhs)(void *) = ndarray_get_float_function(lhs->dtype); + mp_float_t (*get_rhs)(void *) = ndarray_get_float_function(rhs->dtype); + + uint8_t *array = (uint8_t *)results->array; + void (*set_result)(void *, mp_float_t ) = ndarray_set_float_function(NDARRAY_FLOAT); + + // Note that lvalue and rvalue are local variables in the macro itself + FUNC_POINTER_LOOP(results, array, get_lhs, get_rhs, larray, lstrides, rarray, rstrides, MICROPY_FLOAT_C_FUN(pow)(lvalue, rvalue)); + + #else + if(lhs->dtype == NDARRAY_UINT8) { + if(rhs->dtype == NDARRAY_UINT8) { + POWER_LOOP(results, mp_float_t, uint8_t, uint8_t, larray, lstrides, rarray, rstrides); + } else if(rhs->dtype == NDARRAY_INT8) { + POWER_LOOP(results, mp_float_t, uint8_t, int8_t, larray, lstrides, rarray, rstrides); + } else if(rhs->dtype == NDARRAY_UINT16) { + POWER_LOOP(results, mp_float_t, uint8_t, uint16_t, larray, lstrides, rarray, rstrides); + } else if(rhs->dtype == NDARRAY_INT16) { + POWER_LOOP(results, mp_float_t, uint8_t, int16_t, larray, lstrides, rarray, rstrides); + } else if(rhs->dtype == NDARRAY_FLOAT) { + POWER_LOOP(results, mp_float_t, uint8_t, mp_float_t, larray, lstrides, rarray, rstrides); + } + } else if(lhs->dtype == NDARRAY_INT8) { + if(rhs->dtype == NDARRAY_UINT8) { + POWER_LOOP(results, mp_float_t, int8_t, uint8_t, larray, lstrides, rarray, rstrides); + } else if(rhs->dtype == NDARRAY_INT8) { + POWER_LOOP(results, mp_float_t, int8_t, int8_t, larray, lstrides, rarray, rstrides); + } else if(rhs->dtype == NDARRAY_UINT16) { + POWER_LOOP(results, mp_float_t, int8_t, uint16_t, larray, lstrides, rarray, rstrides); + } else if(rhs->dtype == NDARRAY_INT16) { + POWER_LOOP(results, mp_float_t, int8_t, int16_t, larray, lstrides, rarray, rstrides); + } else if(rhs->dtype == NDARRAY_FLOAT) { + POWER_LOOP(results, mp_float_t, int8_t, mp_float_t, larray, lstrides, rarray, rstrides); + } + } else if(lhs->dtype == NDARRAY_UINT16) { + if(rhs->dtype == NDARRAY_UINT8) { + POWER_LOOP(results, mp_float_t, uint16_t, uint8_t, larray, lstrides, rarray, rstrides); + } else if(rhs->dtype == NDARRAY_INT8) { + POWER_LOOP(results, mp_float_t, uint16_t, int8_t, larray, lstrides, rarray, rstrides); + } else if(rhs->dtype == NDARRAY_UINT16) { + POWER_LOOP(results, mp_float_t, uint16_t, uint16_t, larray, lstrides, rarray, rstrides); + } else if(rhs->dtype == NDARRAY_INT16) { + POWER_LOOP(results, mp_float_t, uint16_t, int16_t, larray, lstrides, rarray, rstrides); + } else if(rhs->dtype == NDARRAY_FLOAT) { + POWER_LOOP(results, mp_float_t, uint16_t, mp_float_t, larray, lstrides, rarray, rstrides); + } + } else if(lhs->dtype == NDARRAY_INT16) { + if(rhs->dtype == NDARRAY_UINT8) { + POWER_LOOP(results, mp_float_t, int16_t, uint8_t, larray, lstrides, rarray, rstrides); + } else if(rhs->dtype == NDARRAY_INT8) { + POWER_LOOP(results, mp_float_t, int16_t, int8_t, larray, lstrides, rarray, rstrides); + } else if(rhs->dtype == NDARRAY_UINT16) { + POWER_LOOP(results, mp_float_t, int16_t, uint16_t, larray, lstrides, rarray, rstrides); + } else if(rhs->dtype == NDARRAY_INT16) { + POWER_LOOP(results, mp_float_t, int16_t, int16_t, larray, lstrides, rarray, rstrides); + } else if(rhs->dtype == NDARRAY_FLOAT) { + POWER_LOOP(results, mp_float_t, uint16_t, mp_float_t, larray, lstrides, rarray, rstrides); + } + } else if(lhs->dtype == NDARRAY_FLOAT) { + if(rhs->dtype == NDARRAY_UINT8) { + POWER_LOOP(results, mp_float_t, mp_float_t, uint8_t, larray, lstrides, rarray, rstrides); + } else if(rhs->dtype == NDARRAY_INT8) { + POWER_LOOP(results, mp_float_t, mp_float_t, int8_t, larray, lstrides, rarray, rstrides); + } else if(rhs->dtype == NDARRAY_UINT16) { + POWER_LOOP(results, mp_float_t, mp_float_t, uint16_t, larray, lstrides, rarray, rstrides); + } else if(rhs->dtype == NDARRAY_INT16) { + POWER_LOOP(results, mp_float_t, mp_float_t, int16_t, larray, lstrides, rarray, rstrides); + } else if(rhs->dtype == NDARRAY_FLOAT) { + POWER_LOOP(results, mp_float_t, mp_float_t, mp_float_t, larray, lstrides, rarray, rstrides); + } + } + #endif /* NDARRAY_BINARY_USES_FUN_POINTER */ + + return MP_OBJ_FROM_PTR(results); +} +#endif /* NDARRAY_HAS_BINARY_OP_POWER */ + +#if NDARRAY_HAS_INPLACE_ADD || NDARRAY_HAS_INPLACE_MULTIPLY || NDARRAY_HAS_INPLACE_SUBTRACT +mp_obj_t ndarray_inplace_ams(ndarray_obj_t *lhs, ndarray_obj_t *rhs, int32_t *rstrides, uint8_t optype) { + + if((lhs->dtype != NDARRAY_FLOAT) && (rhs->dtype == NDARRAY_FLOAT)) { + mp_raise_TypeError(translate("cannot cast output with casting rule")); + } + uint8_t *larray = (uint8_t *)lhs->array; + uint8_t *rarray = (uint8_t *)rhs->array; + + #if NDARRAY_HAS_INPLACE_ADD + if(optype == MP_BINARY_OP_INPLACE_ADD) { + UNWRAP_INPLACE_OPERATOR(lhs, larray, rarray, rstrides, +=); + } + #endif + #if NDARRAY_HAS_INPLACE_ADD + if(optype == MP_BINARY_OP_INPLACE_MULTIPLY) { + UNWRAP_INPLACE_OPERATOR(lhs, larray, rarray, rstrides, *=); + } + #endif + #if NDARRAY_HAS_INPLACE_SUBTRACT + if(optype == MP_BINARY_OP_INPLACE_SUBTRACT) { + UNWRAP_INPLACE_OPERATOR(lhs, larray, rarray, rstrides, -=); + } + #endif + + return MP_OBJ_FROM_PTR(lhs); +} +#endif /* NDARRAY_HAS_INPLACE_ADD || NDARRAY_HAS_INPLACE_MULTIPLY || NDARRAY_HAS_INPLACE_SUBTRACT */ + +#if NDARRAY_HAS_INPLACE_TRUE_DIVIDE +mp_obj_t ndarray_inplace_divide(ndarray_obj_t *lhs, ndarray_obj_t *rhs, int32_t *rstrides) { + + if((lhs->dtype != NDARRAY_FLOAT)) { + mp_raise_TypeError(translate("results cannot be cast to specified type")); + } + uint8_t *larray = (uint8_t *)lhs->array; + uint8_t *rarray = (uint8_t *)rhs->array; + + if(rhs->dtype == NDARRAY_UINT8) { + INPLACE_LOOP(lhs, mp_float_t, uint8_t, larray, rarray, rstrides, /=); + } else if(rhs->dtype == NDARRAY_INT8) { + INPLACE_LOOP(lhs, mp_float_t, int8_t, larray, rarray, rstrides, /=); + } else if(rhs->dtype == NDARRAY_UINT16) { + INPLACE_LOOP(lhs, mp_float_t, uint16_t, larray, rarray, rstrides, /=); + } else if(rhs->dtype == NDARRAY_INT16) { + INPLACE_LOOP(lhs, mp_float_t, int16_t, larray, rarray, rstrides, /=); + } else if(lhs->dtype == NDARRAY_FLOAT) { + INPLACE_LOOP(lhs, mp_float_t, mp_float_t, larray, rarray, rstrides, /=); + } + return MP_OBJ_FROM_PTR(lhs); +} +#endif /* NDARRAY_HAS_INPLACE_DIVIDE */ + +#if NDARRAY_HAS_INPLACE_POWER +mp_obj_t ndarray_inplace_power(ndarray_obj_t *lhs, ndarray_obj_t *rhs, int32_t *rstrides) { + + if((lhs->dtype != NDARRAY_FLOAT)) { + mp_raise_TypeError(translate("results cannot be cast to specified type")); + } + uint8_t *larray = (uint8_t *)lhs->array; + uint8_t *rarray = (uint8_t *)rhs->array; + + if(rhs->dtype == NDARRAY_UINT8) { + INPLACE_POWER(lhs, mp_float_t, uint8_t, larray, rarray, rstrides); + } else if(rhs->dtype == NDARRAY_INT8) { + INPLACE_POWER(lhs, mp_float_t, int8_t, larray, rarray, rstrides); + } else if(rhs->dtype == NDARRAY_UINT16) { + INPLACE_POWER(lhs, mp_float_t, uint16_t, larray, rarray, rstrides); + } else if(rhs->dtype == NDARRAY_INT16) { + INPLACE_POWER(lhs, mp_float_t, int16_t, larray, rarray, rstrides); + } else if(lhs->dtype == NDARRAY_FLOAT) { + INPLACE_POWER(lhs, mp_float_t, mp_float_t, larray, rarray, rstrides); + } + return MP_OBJ_FROM_PTR(lhs); +} +#endif /* NDARRAY_HAS_INPLACE_POWER */ diff --git a/python/port/mod/ulab/ndarray_operators.h b/python/port/mod/ulab/ndarray_operators.h new file mode 100644 index 000000000..7849e0309 --- /dev/null +++ b/python/port/mod/ulab/ndarray_operators.h @@ -0,0 +1,277 @@ +/* + * This file is part of the micropython-ulab project, + * + * https://github.com/v923z/micropython-ulab + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Zoltán Vörös +*/ + +#include "ndarray.h" + +mp_obj_t ndarray_binary_equality(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *, mp_binary_op_t ); +mp_obj_t ndarray_binary_add(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *); +mp_obj_t ndarray_binary_multiply(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *); +mp_obj_t ndarray_binary_more(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *, mp_binary_op_t ); +mp_obj_t ndarray_binary_power(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *); +mp_obj_t ndarray_binary_subtract(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *); +mp_obj_t ndarray_binary_true_divide(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *); + +mp_obj_t ndarray_inplace_ams(ndarray_obj_t *, ndarray_obj_t *, int32_t *, uint8_t ); +mp_obj_t ndarray_inplace_power(ndarray_obj_t *, ndarray_obj_t *, int32_t *); +mp_obj_t ndarray_inplace_divide(ndarray_obj_t *, ndarray_obj_t *, int32_t *); + +#define UNWRAP_INPLACE_OPERATOR(lhs, larray, rarray, rstrides, OPERATOR)\ +({\ + if((lhs)->dtype == NDARRAY_UINT8) {\ + if((rhs)->dtype == NDARRAY_UINT8) {\ + INPLACE_LOOP((lhs), uint8_t, uint8_t, (larray), (rarray), (rstrides), OPERATOR);\ + } else if(rhs->dtype == NDARRAY_INT8) {\ + INPLACE_LOOP((lhs), uint8_t, int8_t, (larray), (rarray), (rstrides), OPERATOR);\ + } else if(rhs->dtype == NDARRAY_UINT16) {\ + INPLACE_LOOP((lhs), uint8_t, uint16_t, (larray), (rarray), (rstrides), OPERATOR);\ + } else {\ + INPLACE_LOOP((lhs), uint8_t, int16_t, (larray), (rarray), (rstrides), OPERATOR);\ + }\ + } else if(lhs->dtype == NDARRAY_INT8) {\ + if(rhs->dtype == NDARRAY_UINT8) {\ + INPLACE_LOOP((lhs), int8_t, uint8_t, (larray), (rarray), (rstrides), OPERATOR);\ + } else if(rhs->dtype == NDARRAY_INT8) {\ + INPLACE_LOOP((lhs), int8_t, int8_t, (larray), (rarray), (rstrides), OPERATOR);\ + } else if(rhs->dtype == NDARRAY_UINT16) {\ + INPLACE_LOOP((lhs), int8_t, uint16_t, (larray), (rarray), (rstrides), OPERATOR);\ + } else {\ + INPLACE_LOOP((lhs), int8_t, int16_t, (larray), (rarray), (rstrides), OPERATOR);\ + }\ + } else if(lhs->dtype == NDARRAY_UINT16) {\ + if(rhs->dtype == NDARRAY_UINT8) {\ + INPLACE_LOOP((lhs), uint16_t, uint8_t, (larray), (rarray), (rstrides), OPERATOR);\ + } else if(rhs->dtype == NDARRAY_INT8) {\ + INPLACE_LOOP((lhs), uint16_t, int8_t, (larray), (rarray), (rstrides), OPERATOR);\ + } else if(rhs->dtype == NDARRAY_UINT16) {\ + INPLACE_LOOP((lhs), uint16_t, uint16_t, (larray), (rarray), (rstrides), OPERATOR);\ + } else {\ + INPLACE_LOOP((lhs), uint16_t, int16_t, (larray), (rarray), (rstrides), OPERATOR);\ + }\ + } else if(lhs->dtype == NDARRAY_INT16) {\ + if(rhs->dtype == NDARRAY_UINT8) {\ + INPLACE_LOOP((lhs), int16_t, uint8_t, (larray), (rarray), (rstrides), OPERATOR);\ + } else if(rhs->dtype == NDARRAY_INT8) {\ + INPLACE_LOOP((lhs), int16_t, int8_t, (larray), (rarray), (rstrides), OPERATOR);\ + } else if(rhs->dtype == NDARRAY_UINT16) {\ + INPLACE_LOOP((lhs), int16_t, uint16_t, (larray), (rarray), (rstrides), OPERATOR);\ + } else {\ + INPLACE_LOOP((lhs), int16_t, int16_t, (larray), (rarray), (rstrides), OPERATOR);\ + }\ + } else if(lhs->dtype == NDARRAY_FLOAT) {\ + if(rhs->dtype == NDARRAY_UINT8) {\ + INPLACE_LOOP((lhs), mp_float_t, uint8_t, (larray), (rarray), (rstrides), OPERATOR);\ + } else if(rhs->dtype == NDARRAY_INT8) {\ + INPLACE_LOOP((lhs), mp_float_t, int8_t, (larray), (rarray), (rstrides), OPERATOR);\ + } else if(rhs->dtype == NDARRAY_UINT16) {\ + INPLACE_LOOP((lhs), mp_float_t, uint16_t, (larray), (rarray), (rstrides), OPERATOR);\ + } else if(rhs->dtype == NDARRAY_INT16) {\ + INPLACE_LOOP((lhs), mp_float_t, int16_t, (larray), (rarray), (rstrides), OPERATOR);\ + } else {\ + INPLACE_LOOP((lhs), mp_float_t, mp_float_t, (larray), (rarray), (rstrides), OPERATOR);\ + }\ + }\ +}) + +#if ULAB_MAX_DIMS == 1 +#define INPLACE_POWER(results, type_left, type_right, larray, rarray, rstrides)\ +({ size_t l = 0;\ + do {\ + *((type_left *)(larray)) = MICROPY_FLOAT_C_FUN(pow)(*((type_left *)(larray)), *((type_right *)(rarray)));\ + (larray) += (results)->strides[ULAB_MAX_DIMS - 1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\ + l++;\ + } while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\ +}) + +#define FUNC_POINTER_LOOP(results, array, get_lhs, get_rhs, larray, lstrides, rarray, rstrides, OPERATION)\ +({ size_t l = 0;\ + do {\ + mp_float_t lvalue = (get_lhs)((larray));\ + mp_float_t rvalue = (get_rhs)((rarray));\ + (set_result)((array), OPERATION);\ + (array) += (results)->itemsize;\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\ + l++;\ + } while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\ +}) +#endif /* ULAB_MAX_DIMS == 1 */ + +#if ULAB_MAX_DIMS == 2 +#define INPLACE_POWER(results, type_left, type_right, larray, rarray, rstrides)\ +({ size_t k = 0;\ + do {\ + size_t l = 0;\ + do {\ + *((type_left *)(larray)) = MICROPY_FLOAT_C_FUN(pow)(*((type_left *)(larray)), *((type_right *)(rarray)));\ + (larray) += (results)->strides[ULAB_MAX_DIMS - 1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\ + l++;\ + } while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\ + (larray) -= (results)->strides[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\ + (larray) += (results)->strides[ULAB_MAX_DIMS - 2];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\ + k++;\ + } while(k < (results)->shape[ULAB_MAX_DIMS - 2]);\ +}) + +#define FUNC_POINTER_LOOP(results, array, get_lhs, get_rhs, larray, lstrides, rarray, rstrides, OPERATION)\ +({ size_t k = 0;\ + do {\ + size_t l = 0;\ + do {\ + mp_float_t lvalue = (get_lhs)((larray));\ + mp_float_t rvalue = (get_rhs)((rarray));\ + (set_result)((array), OPERATION);\ + (array) += (results)->itemsize;\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\ + l++;\ + } while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\ + (larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 2];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\ + k++;\ + } while(k < results->shape[ULAB_MAX_DIMS - 2]);\ +}) +#endif /* ULAB_MAX_DIMS == 2 */ + +#if ULAB_MAX_DIMS == 3 +#define INPLACE_POWER(results, type_left, type_right, larray, rarray, rstrides)\ +({ size_t j = 0;\ + do {\ + size_t k = 0;\ + do {\ + size_t l = 0;\ + do {\ + *((type_left *)(larray)) = MICROPY_FLOAT_C_FUN(pow)(*((type_left *)(larray)), *((type_right *)(rarray)));\ + (larray) += (results)->strides[ULAB_MAX_DIMS - 1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\ + l++;\ + } while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\ + (larray) -= (results)->strides[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\ + (larray) += (results)->strides[ULAB_MAX_DIMS - 2];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\ + k++;\ + } while(k < (results)->shape[ULAB_MAX_DIMS - 2]);\ + (larray) -= (results)->strides[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS-2];\ + (larray) += (results)->strides[ULAB_MAX_DIMS - 3];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS-2];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\ + j++;\ + } while(j < (results)->shape[ULAB_MAX_DIMS - 3]);\ +}) + + +#define FUNC_POINTER_LOOP(results, array, get_lhs, get_rhs, larray, lstrides, rarray, rstrides, OPERATION)\ +({ size_t j = 0;\ + do {\ + size_t k = 0;\ + do {\ + size_t l = 0;\ + do {\ + mp_float_t lvalue = (get_lhs)((larray));\ + mp_float_t rvalue = (get_rhs)((rarray));\ + (set_result)((array), OPERATION);\ + (array) += (results)->itemsize;\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\ + l++;\ + } while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\ + (larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 2];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\ + k++;\ + } while(k < results->shape[ULAB_MAX_DIMS - 2]);\ + (larray) -= (results)->strides[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS-2];\ + (larray) += (results)->strides[ULAB_MAX_DIMS - 3];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS-2];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\ + j++;\ + } while(j < (results)->shape[ULAB_MAX_DIMS - 3]);\ +}) +#endif /* ULAB_MAX_DIMS == 3 */ + +#if ULAB_MAX_DIMS == 4 +#define INPLACE_POWER(results, type_left, type_right, larray, rarray, rstrides)\ +({ size_t i = 0;\ + do {\ + size_t j = 0;\ + do {\ + size_t k = 0;\ + do {\ + size_t l = 0;\ + do {\ + *((type_left *)(larray)) = MICROPY_FLOAT_C_FUN(pow)(*((type_left *)(larray)), *((type_right *)(rarray)));\ + (larray) += (results)->strides[ULAB_MAX_DIMS - 1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\ + l++;\ + } while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\ + (larray) -= (results)->strides[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\ + (larray) += (results)->strides[ULAB_MAX_DIMS - 2];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\ + k++;\ + } while(k < (results)->shape[ULAB_MAX_DIMS - 2]);\ + (larray) -= (results)->strides[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS-2];\ + (larray) += (results)->strides[ULAB_MAX_DIMS - 3];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS-2];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\ + j++;\ + } while(j < (results)->shape[ULAB_MAX_DIMS - 3]);\ + (larray) -= (results)->strides[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS-3];\ + (larray) += (results)->strides[ULAB_MAX_DIMS - 4];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS-3];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 4];\ + i++;\ + } while(i < (results)->shape[ULAB_MAX_DIMS - 4]);\ +}) + +#define FUNC_POINTER_LOOP(results, array, get_lhs, get_rhs, larray, lstrides, rarray, rstrides, OPERATION)\ +({ size_t i = 0;\ + do {\ + size_t j = 0;\ + do {\ + size_t k = 0;\ + do {\ + size_t l = 0;\ + do {\ + mp_float_t lvalue = (get_lhs)((larray));\ + mp_float_t rvalue = (get_rhs)((rarray));\ + (set_result)((array), OPERATION);\ + (array) += (results)->itemsize;\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\ + l++;\ + } while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\ + (larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 2];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\ + k++;\ + } while(k < results->shape[ULAB_MAX_DIMS - 2]);\ + (larray) -= (results)->strides[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS-2];\ + (larray) += (results)->strides[ULAB_MAX_DIMS - 3];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS-2];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\ + j++;\ + } while(j < (results)->shape[ULAB_MAX_DIMS - 3]);\ + (larray) -= (results)->strides[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS-3];\ + (larray) += (results)->strides[ULAB_MAX_DIMS - 4];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS-3];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 4];\ + i++;\ + } while(i < (results)->shape[ULAB_MAX_DIMS - 4]);\ +}) +#endif /* ULAB_MAX_DIMS == 4 */ diff --git a/python/port/mod/ulab/ndarray_properties.c b/python/port/mod/ulab/ndarray_properties.c new file mode 100644 index 000000000..0fcf83bca --- /dev/null +++ b/python/port/mod/ulab/ndarray_properties.c @@ -0,0 +1,100 @@ + +/* + * This file is part of the micropython-ulab project, + * + * https://github.com/v923z/micropython-ulab + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Zoltán Vörös + * +*/ + +#include +#include +#include +#include +#include +#include + +#include "ulab.h" +#include "ndarray.h" + +#ifndef CIRCUITPY + +// a somewhat hackish implementation of property getters/setters; +// this functions is hooked into the attr member of ndarray + +STATIC void call_local_method(mp_obj_t obj, qstr attr, mp_obj_t *dest) { + const mp_obj_type_t *type = mp_obj_get_type(obj); + while (type->locals_dict != NULL) { + assert(type->locals_dict->base.type == &mp_type_dict); // MicroPython restriction, for now + mp_map_t *locals_map = &type->locals_dict->map; + mp_map_elem_t *elem = mp_map_lookup(locals_map, MP_OBJ_NEW_QSTR(attr), MP_MAP_LOOKUP); + if (elem != NULL) { + mp_convert_member_lookup(obj, type, elem->value, dest); + break; + } + if (type->parent == NULL) { + break; + } + type = type->parent; + } +} + + +void ndarray_properties_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { + if (dest[0] == MP_OBJ_NULL) { + switch(attr) { + #if NDARRAY_HAS_DTYPE + case MP_QSTR_dtype: + dest[0] = ndarray_dtype(self_in); + break; + #endif + #if NDARRAY_HAS_ITEMSIZE + case MP_QSTR_itemsize: + dest[0] = ndarray_itemsize(self_in); + break; + #endif + #if NDARRAY_HAS_SHAPE + case MP_QSTR_shape: + dest[0] = ndarray_shape(self_in); + break; + #endif + #if NDARRAY_HAS_SIZE + case MP_QSTR_size: + dest[0] = ndarray_size(self_in); + break; + #endif + #if NDARRAY_HAS_STRIDES + case MP_QSTR_strides: + dest[0] = ndarray_strides(self_in); + break; + #endif + #if NDARRAY_HAS_TRANSPOSE + case MP_QSTR_T: + dest[0] = ndarray_transpose(self_in); + break; + #endif + default: + call_local_method(self_in, attr, dest); + break; + } + } else { + if(dest[1]) { + switch(attr) { + #if NDARRAY_HAS_RESHAPE + case MP_QSTR_shape: + ndarray_reshape_core(self_in, dest[1], 1); + break; + #endif + default: + return; + break; + } + dest[0] = MP_OBJ_NULL; + } + } +} + +#endif /* CIRCUITPY */ \ No newline at end of file diff --git a/python/port/mod/ulab/ndarray_properties.h b/python/port/mod/ulab/ndarray_properties.h new file mode 100644 index 000000000..9d3b4ee9b --- /dev/null +++ b/python/port/mod/ulab/ndarray_properties.h @@ -0,0 +1,92 @@ + +/* + * This file is part of the micropython-ulab project, + * + * https://github.com/v923z/micropython-ulab + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jeff Epler for Adafruit Industries + * 2020 Zoltán Vörös +*/ + +#ifndef _NDARRAY_PROPERTIES_ +#define _NDARRAY_PROPERTIES_ + +#include +#include +#include +#include + +#include "ulab.h" +#include "ndarray.h" + +#if CIRCUITPY +typedef struct _mp_obj_property_t { + mp_obj_base_t base; + mp_obj_t proxy[3]; // getter, setter, deleter +} mp_obj_property_t; + +#if NDARRAY_HAS_DTYPE +MP_DEFINE_CONST_FUN_OBJ_1(ndarray_get_dtype_obj, ndarray_dtype); +STATIC const mp_obj_property_t ndarray_dtype_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&ndarray_get_dtype_obj, + mp_const_none, + mp_const_none }, +}; +#endif /* NDARRAY_HAS_DTYPE */ + +#if NDARRAY_HAS_ITEMSIZE +MP_DEFINE_CONST_FUN_OBJ_1(ndarray_get_itemsize_obj, ndarray_itemsize); +STATIC const mp_obj_property_t ndarray_itemsize_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&ndarray_get_itemsize_obj, + mp_const_none, + mp_const_none }, +}; +#endif /* NDARRAY_HAS_ITEMSIZE */ + +#if NDARRAY_HAS_SHAPE +MP_DEFINE_CONST_FUN_OBJ_1(ndarray_get_shape_obj, ndarray_shape); +STATIC const mp_obj_property_t ndarray_shape_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&ndarray_get_shape_obj, + mp_const_none, + mp_const_none }, +}; +#endif /* NDARRAY_HAS_SHAPE */ + +#if NDARRAY_HAS_SIZE +MP_DEFINE_CONST_FUN_OBJ_1(ndarray_get_size_obj, ndarray_size); +STATIC const mp_obj_property_t ndarray_size_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&ndarray_get_size_obj, + mp_const_none, + mp_const_none }, +}; +#endif /* NDARRAY_HAS_SIZE */ + +#if NDARRAY_HAS_STRIDES +MP_DEFINE_CONST_FUN_OBJ_1(ndarray_get_strides_obj, ndarray_strides); +STATIC const mp_obj_property_t ndarray_strides_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&ndarray_get_strides_obj, + mp_const_none, + mp_const_none }, +}; +#endif /* NDARRAY_HAS_STRIDES */ + +#else + +void ndarray_properties_attr(mp_obj_t , qstr , mp_obj_t *); + +MP_DEFINE_CONST_FUN_OBJ_1(ndarray_dtype_obj, ndarray_dtype); +MP_DEFINE_CONST_FUN_OBJ_1(ndarray_itemsize_obj, ndarray_itemsize); +MP_DEFINE_CONST_FUN_OBJ_1(ndarray_shape_obj, ndarray_shape); +MP_DEFINE_CONST_FUN_OBJ_1(ndarray_size_obj, ndarray_size); +MP_DEFINE_CONST_FUN_OBJ_1(ndarray_strides_obj, ndarray_strides); + +#endif /* CIRCUITPY */ + +#endif diff --git a/python/port/mod/ulab/numpy/approx/approx.c b/python/port/mod/ulab/numpy/approx/approx.c new file mode 100644 index 000000000..706a969a7 --- /dev/null +++ b/python/port/mod/ulab/numpy/approx/approx.c @@ -0,0 +1,221 @@ +/* + * This file is part of the micropython-ulab project, + * + * https://github.com/v923z/micropython-ulab + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Zoltán Vörös + * 2020 Diego Elio Pettenò + * 2020 Taku Fukada +*/ + +#include +#include +#include +#include +#include +#include + +#include "../../ulab.h" +#include "../../ulab_tools.h" +#include "approx.h" + +//| """Numerical approximation methods""" +//| + +const mp_obj_float_t approx_trapz_dx = {{&mp_type_float}, MICROPY_FLOAT_CONST(1.0)}; + +#if ULAB_NUMPY_HAS_INTERP +//| def interp( +//| x: ulab.ndarray, +//| xp: ulab.ndarray, +//| fp: ulab.ndarray, +//| *, +//| left: Optional[float] = None, +//| right: Optional[float] = None +//| ) -> ulab.ndarray: +//| """ +//| :param ulab.ndarray x: The x-coordinates at which to evaluate the interpolated values. +//| :param ulab.ndarray xp: The x-coordinates of the data points, must be increasing +//| :param ulab.ndarray fp: The y-coordinates of the data points, same length as xp +//| :param left: Value to return for ``x < xp[0]``, default is ``fp[0]``. +//| :param right: Value to return for ``x > xp[-1]``, default is ``fp[-1]``. +//| +//| Returns the one-dimensional piecewise linear interpolant to a function with given discrete data points (xp, fp), evaluated at x.""" +//| ... +//| + +STATIC mp_obj_t approx_interp(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } }, + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } }, + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } }, + { MP_QSTR_left, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = mp_const_none} }, + { MP_QSTR_right, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = mp_const_none} }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + ndarray_obj_t *x = ndarray_from_mp_obj(args[0].u_obj, 0); + ndarray_obj_t *xp = ndarray_from_mp_obj(args[1].u_obj, 0); // xp must hold an increasing sequence of independent values + ndarray_obj_t *fp = ndarray_from_mp_obj(args[2].u_obj, 0); + if((xp->ndim != 1) || (fp->ndim != 1) || (xp->len < 2) || (fp->len < 2) || (xp->len != fp->len)) { + mp_raise_ValueError(translate("interp is defined for 1D iterables of equal length")); + } + + ndarray_obj_t *y = ndarray_new_linear_array(x->len, NDARRAY_FLOAT); + mp_float_t left_value, right_value; + uint8_t *xparray = (uint8_t *)xp->array; + + mp_float_t xp_left = ndarray_get_float_value(xparray, xp->dtype); + xparray += (xp->len-1) * xp->strides[ULAB_MAX_DIMS - 1]; + mp_float_t xp_right = ndarray_get_float_value(xparray, xp->dtype); + + uint8_t *fparray = (uint8_t *)fp->array; + + if(args[3].u_obj == mp_const_none) { + left_value = ndarray_get_float_value(fparray, fp->dtype); + } else { + left_value = mp_obj_get_float(args[3].u_obj); + } + if(args[4].u_obj == mp_const_none) { + fparray += (fp->len-1) * fp->strides[ULAB_MAX_DIMS - 1]; + right_value = ndarray_get_float_value(fparray, fp->dtype); + } else { + right_value = mp_obj_get_float(args[4].u_obj); + } + + xparray = xp->array; + fparray = fp->array; + + uint8_t *xarray = (uint8_t *)x->array; + mp_float_t *yarray = (mp_float_t *)y->array; + uint8_t *temp; + + for(size_t i=0; i < x->len; i++, yarray++) { + mp_float_t x_value = ndarray_get_float_value(xarray, x->dtype); + xarray += x->strides[ULAB_MAX_DIMS - 1]; + if(x_value < xp_left) { + *yarray = left_value; + } else if(x_value > xp_right) { + *yarray = right_value; + } else { // do the binary search here + mp_float_t xp_left_, xp_right_; + mp_float_t fp_left, fp_right; + size_t left_index = 0, right_index = xp->len - 1, middle_index; + while(right_index - left_index > 1) { + middle_index = left_index + (right_index - left_index) / 2; + temp = xparray + middle_index * xp->strides[ULAB_MAX_DIMS - 1]; + mp_float_t xp_middle = ndarray_get_float_value(temp, xp->dtype); + if(x_value <= xp_middle) { + right_index = middle_index; + } else { + left_index = middle_index; + } + } + temp = xparray + left_index * xp->strides[ULAB_MAX_DIMS - 1]; + xp_left_ = ndarray_get_float_value(temp, xp->dtype); + + temp = xparray + right_index * xp->strides[ULAB_MAX_DIMS - 1]; + xp_right_ = ndarray_get_float_value(temp, xp->dtype); + + temp = fparray + left_index * fp->strides[ULAB_MAX_DIMS - 1]; + fp_left = ndarray_get_float_value(temp, fp->dtype); + + temp = fparray + right_index * fp->strides[ULAB_MAX_DIMS - 1]; + fp_right = ndarray_get_float_value(temp, fp->dtype); + + *yarray = fp_left + (x_value - xp_left_) * (fp_right - fp_left) / (xp_right_ - xp_left_); + } + } + return MP_OBJ_FROM_PTR(y); +} + +MP_DEFINE_CONST_FUN_OBJ_KW(approx_interp_obj, 2, approx_interp); +#endif + +#if ULAB_NUMPY_HAS_TRAPZ +//| def trapz(y: ulab.ndarray, x: Optional[ulab.ndarray] = None, dx: float = 1.0) -> float: +//| """ +//| :param 1D ulab.ndarray y: the values of the dependent variable +//| :param 1D ulab.ndarray x: optional, the coordinates of the independent variable. Defaults to uniformly spaced values. +//| :param float dx: the spacing between sample points, if x=None +//| +//| Returns the integral of y(x) using the trapezoidal rule. +//| """ +//| ... +//| + +STATIC mp_obj_t approx_trapz(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } }, + { MP_QSTR_x, MP_ARG_OBJ, {.u_rom_obj = mp_const_none } }, + { MP_QSTR_dx, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&approx_trapz_dx)} }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + ndarray_obj_t *y = ndarray_from_mp_obj(args[0].u_obj, 0); + ndarray_obj_t *x; + mp_float_t mean = MICROPY_FLOAT_CONST(0.0); + if(y->len < 2) { + return mp_obj_new_float(mean); + } + if((y->ndim != 1)) { + mp_raise_ValueError(translate("trapz is defined for 1D iterables")); + } + + mp_float_t (*funcy)(void *) = ndarray_get_float_function(y->dtype); + uint8_t *yarray = (uint8_t *)y->array; + + size_t count = 1; + mp_float_t y1, y2, m; + + if(args[1].u_obj != mp_const_none) { + x = ndarray_from_mp_obj(args[1].u_obj, 0); // x must hold an increasing sequence of independent values + if((x->ndim != 1) || (y->len != x->len)) { + mp_raise_ValueError(translate("trapz is defined for 1D arrays of equal length")); + } + + mp_float_t (*funcx)(void *) = ndarray_get_float_function(x->dtype); + uint8_t *xarray = (uint8_t *)x->array; + mp_float_t x1, x2; + + y1 = funcy(yarray); + yarray += y->strides[ULAB_MAX_DIMS - 1]; + x1 = funcx(xarray); + xarray += x->strides[ULAB_MAX_DIMS - 1]; + + for(size_t i=1; i < y->len; i++) { + y2 = funcy(yarray); + yarray += y->strides[ULAB_MAX_DIMS - 1]; + x2 = funcx(xarray); + xarray += x->strides[ULAB_MAX_DIMS - 1]; + mp_float_t value = (x2 - x1) * (y2 + y1); + m = mean + (value - mean) / (mp_float_t)count; + mean = m; + x1 = x2; + y1 = y2; + count++; + } + } else { + mp_float_t dx = mp_obj_get_float(args[2].u_obj); + y1 = funcy(yarray); + yarray += y->strides[ULAB_MAX_DIMS - 1]; + + for(size_t i=1; i < y->len; i++) { + y2 = ndarray_get_float_index(y->array, y->dtype, i); + mp_float_t value = (y2 + y1); + m = mean + (value - mean) / (mp_float_t)count; + mean = m; + y1 = y2; + count++; + } + mean *= dx; + } + return mp_obj_new_float(MICROPY_FLOAT_CONST(0.5)*mean*(y->len-1)); +} + +MP_DEFINE_CONST_FUN_OBJ_KW(approx_trapz_obj, 1, approx_trapz); +#endif diff --git a/python/port/mod/ulab/numpy/approx/approx.h b/python/port/mod/ulab/numpy/approx/approx.h new file mode 100644 index 000000000..7708bb789 --- /dev/null +++ b/python/port/mod/ulab/numpy/approx/approx.h @@ -0,0 +1,29 @@ + +/* + * This file is part of the micropython-ulab project, + * + * https://github.com/v923z/micropython-ulab + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Zoltán Vörös +*/ + +#ifndef _APPROX_ +#define _APPROX_ + +#include "../../ulab.h" +#include "../../ndarray.h" + +#define APPROX_EPS MICROPY_FLOAT_CONST(1.0e-4) +#define APPROX_NONZDELTA MICROPY_FLOAT_CONST(0.05) +#define APPROX_ZDELTA MICROPY_FLOAT_CONST(0.00025) +#define APPROX_ALPHA MICROPY_FLOAT_CONST(1.0) +#define APPROX_BETA MICROPY_FLOAT_CONST(2.0) +#define APPROX_GAMMA MICROPY_FLOAT_CONST(0.5) +#define APPROX_DELTA MICROPY_FLOAT_CONST(0.5) + +MP_DECLARE_CONST_FUN_OBJ_KW(approx_interp_obj); +MP_DECLARE_CONST_FUN_OBJ_KW(approx_trapz_obj); + +#endif /* _APPROX_ */ diff --git a/python/port/mod/ulab/numpy/compare/compare.c b/python/port/mod/ulab/numpy/compare/compare.c new file mode 100644 index 000000000..73dfaa135 --- /dev/null +++ b/python/port/mod/ulab/numpy/compare/compare.c @@ -0,0 +1,417 @@ + +/* + * This file is part of the micropython-ulab project, + * + * https://github.com/v923z/micropython-ulab + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Zoltán Vörös + * 2020 Jeff Epler for Adafruit Industries +*/ + +#include +#include +#include +#include +#include +#include + +#include "../../ulab.h" +#include "../../ndarray_operators.h" +#include "../../ulab_tools.h" +#include "compare.h" + +static mp_obj_t compare_function(mp_obj_t x1, mp_obj_t x2, uint8_t op) { + ndarray_obj_t *lhs = ndarray_from_mp_obj(x1, 0); + ndarray_obj_t *rhs = ndarray_from_mp_obj(x2, 0); + uint8_t ndim = 0; + size_t *shape = m_new(size_t, ULAB_MAX_DIMS); + int32_t *lstrides = m_new(int32_t, ULAB_MAX_DIMS); + int32_t *rstrides = m_new(int32_t, ULAB_MAX_DIMS); + if(!ndarray_can_broadcast(lhs, rhs, &ndim, shape, lstrides, rstrides)) { + mp_raise_ValueError(translate("operands could not be broadcast together")); + m_del(size_t, shape, ULAB_MAX_DIMS); + m_del(int32_t, lstrides, ULAB_MAX_DIMS); + m_del(int32_t, rstrides, ULAB_MAX_DIMS); + } + + uint8_t *larray = (uint8_t *)lhs->array; + uint8_t *rarray = (uint8_t *)rhs->array; + + if(op == COMPARE_EQUAL) { + return ndarray_binary_equality(lhs, rhs, ndim, shape, lstrides, rstrides, MP_BINARY_OP_EQUAL); + } else if(op == COMPARE_NOT_EQUAL) { + return ndarray_binary_equality(lhs, rhs, ndim, shape, lstrides, rstrides, MP_BINARY_OP_NOT_EQUAL); + } + // These are the upcasting rules + // float always becomes float + // operation on identical types preserves type + // uint8 + int8 => int16 + // uint8 + int16 => int16 + // uint8 + uint16 => uint16 + // int8 + int16 => int16 + // int8 + uint16 => uint16 + // uint16 + int16 => float + // The parameters of RUN_COMPARE_LOOP are + // typecode of result, type_out, type_left, type_right, lhs operand, rhs operand, operator + if(lhs->dtype == NDARRAY_UINT8) { + if(rhs->dtype == NDARRAY_UINT8) { + RUN_COMPARE_LOOP(NDARRAY_UINT8, uint8_t, uint8_t, uint8_t, larray, lstrides, rarray, rstrides, ndim, shape, op); + } else if(rhs->dtype == NDARRAY_INT8) { + RUN_COMPARE_LOOP(NDARRAY_INT16, int16_t, uint8_t, int8_t, larray, lstrides, rarray, rstrides, ndim, shape, op); + } else if(rhs->dtype == NDARRAY_UINT16) { + RUN_COMPARE_LOOP(NDARRAY_UINT16, uint16_t, uint8_t, uint16_t, larray, lstrides, rarray, rstrides, ndim, shape, op); + } else if(rhs->dtype == NDARRAY_INT16) { + RUN_COMPARE_LOOP(NDARRAY_INT16, int16_t, uint8_t, int16_t, larray, lstrides, rarray, rstrides, ndim, shape, op); + } else if(rhs->dtype == NDARRAY_FLOAT) { + RUN_COMPARE_LOOP(NDARRAY_FLOAT, mp_float_t, uint8_t, mp_float_t, larray, lstrides, rarray, rstrides, ndim, shape, op); + } + } else if(lhs->dtype == NDARRAY_INT8) { + if(rhs->dtype == NDARRAY_UINT8) { + RUN_COMPARE_LOOP(NDARRAY_INT16, int16_t, int8_t, uint8_t, larray, lstrides, rarray, rstrides, ndim, shape, op); + } else if(rhs->dtype == NDARRAY_INT8) { + RUN_COMPARE_LOOP(NDARRAY_INT8, int8_t, int8_t, int8_t, larray, lstrides, rarray, rstrides, ndim, shape, op); + } else if(rhs->dtype == NDARRAY_UINT16) { + RUN_COMPARE_LOOP(NDARRAY_INT16, int16_t, int8_t, uint16_t, larray, lstrides, rarray, rstrides, ndim, shape, op); + } else if(rhs->dtype == NDARRAY_INT16) { + RUN_COMPARE_LOOP(NDARRAY_INT16, int16_t, int8_t, int16_t, larray, lstrides, rarray, rstrides, ndim, shape, op); + } else if(rhs->dtype == NDARRAY_FLOAT) { + RUN_COMPARE_LOOP(NDARRAY_FLOAT, mp_float_t, int8_t, mp_float_t, larray, lstrides, rarray, rstrides, ndim, shape, op); + } + } else if(lhs->dtype == NDARRAY_UINT16) { + if(rhs->dtype == NDARRAY_UINT8) { + RUN_COMPARE_LOOP(NDARRAY_UINT16, uint16_t, uint16_t, uint8_t, larray, lstrides, rarray, rstrides, ndim, shape, op); + } else if(rhs->dtype == NDARRAY_INT8) { + RUN_COMPARE_LOOP(NDARRAY_UINT16, uint16_t, uint16_t, int8_t, larray, lstrides, rarray, rstrides, ndim, shape, op); + } else if(rhs->dtype == NDARRAY_UINT16) { + RUN_COMPARE_LOOP(NDARRAY_UINT16, uint16_t, uint16_t, uint16_t, larray, lstrides, rarray, rstrides, ndim, shape, op); + } else if(rhs->dtype == NDARRAY_INT16) { + RUN_COMPARE_LOOP(NDARRAY_FLOAT, mp_float_t, uint16_t, int16_t, larray, lstrides, rarray, rstrides, ndim, shape, op); + } else if(rhs->dtype == NDARRAY_FLOAT) { + RUN_COMPARE_LOOP(NDARRAY_FLOAT, mp_float_t, uint8_t, mp_float_t, larray, lstrides, rarray, rstrides, ndim, shape, op); + } + } else if(lhs->dtype == NDARRAY_INT16) { + if(rhs->dtype == NDARRAY_UINT8) { + RUN_COMPARE_LOOP(NDARRAY_INT16, int16_t, int16_t, uint8_t, larray, lstrides, rarray, rstrides, ndim, shape, op); + } else if(rhs->dtype == NDARRAY_INT8) { + RUN_COMPARE_LOOP(NDARRAY_INT16, int16_t, int16_t, int8_t, larray, lstrides, rarray, rstrides, ndim, shape, op); + } else if(rhs->dtype == NDARRAY_UINT16) { + RUN_COMPARE_LOOP(NDARRAY_FLOAT, mp_float_t, int16_t, uint16_t, larray, lstrides, rarray, rstrides, ndim, shape, op); + } else if(rhs->dtype == NDARRAY_INT16) { + RUN_COMPARE_LOOP(NDARRAY_INT16, int16_t, int16_t, int16_t, larray, lstrides, rarray, rstrides, ndim, shape, op); + } else if(rhs->dtype == NDARRAY_FLOAT) { + RUN_COMPARE_LOOP(NDARRAY_FLOAT, mp_float_t, uint16_t, mp_float_t, larray, lstrides, rarray, rstrides, ndim, shape, op); + } + } else if(lhs->dtype == NDARRAY_FLOAT) { + if(rhs->dtype == NDARRAY_UINT8) { + RUN_COMPARE_LOOP(NDARRAY_FLOAT, mp_float_t, mp_float_t, uint8_t, larray, lstrides, rarray, rstrides, ndim, shape, op); + } else if(rhs->dtype == NDARRAY_INT8) { + RUN_COMPARE_LOOP(NDARRAY_FLOAT, mp_float_t, mp_float_t, int8_t, larray, lstrides, rarray, rstrides, ndim, shape, op); + } else if(rhs->dtype == NDARRAY_UINT16) { + RUN_COMPARE_LOOP(NDARRAY_FLOAT, mp_float_t, mp_float_t, uint16_t, larray, lstrides, rarray, rstrides, ndim, shape, op); + } else if(rhs->dtype == NDARRAY_INT16) { + RUN_COMPARE_LOOP(NDARRAY_FLOAT, mp_float_t, mp_float_t, int16_t, larray, lstrides, rarray, rstrides, ndim, shape, op); + } else if(rhs->dtype == NDARRAY_FLOAT) { + RUN_COMPARE_LOOP(NDARRAY_FLOAT, mp_float_t, mp_float_t, mp_float_t, larray, lstrides, rarray, rstrides, ndim, shape, op); + } + } + return mp_const_none; // we should never reach this point +} + +static mp_obj_t compare_equal_helper(mp_obj_t x1, mp_obj_t x2, uint8_t comptype) { + // scalar comparisons should return a single object of mp_obj_t type + mp_obj_t result = compare_function(x1, x2, comptype); + if((mp_obj_is_int(x1) || mp_obj_is_float(x1)) && (mp_obj_is_int(x2) || mp_obj_is_float(x2))) { + mp_obj_iter_buf_t iter_buf; + mp_obj_t iterable = mp_getiter(result, &iter_buf); + mp_obj_t item = mp_iternext(iterable); + return item; + } + return result; +} + +#if ULAB_NUMPY_HAS_CLIP + +mp_obj_t compare_clip(mp_obj_t x1, mp_obj_t x2, mp_obj_t x3) { + // Note: this function could be made faster by implementing a single-loop comparison in + // RUN_COMPARE_LOOP. However, that would add around 2 kB of compile size, while we + // would not gain a factor of two in speed, since the two comparisons should still be + // evaluated. In contrast, calling the function twice adds only 140 bytes to the firmware + if(mp_obj_is_int(x1) || mp_obj_is_float(x1)) { + mp_float_t v1 = mp_obj_get_float(x1); + mp_float_t v2 = mp_obj_get_float(x2); + mp_float_t v3 = mp_obj_get_float(x3); + if(v1 < v2) { + return x2; + } else if(v1 > v3) { + return x3; + } else { + return x1; + } + } else { // assume ndarrays + return compare_function(x2, compare_function(x1, x3, COMPARE_MINIMUM), COMPARE_MAXIMUM); + } +} + +MP_DEFINE_CONST_FUN_OBJ_3(compare_clip_obj, compare_clip); +#endif + +#if ULAB_NUMPY_HAS_EQUAL + +mp_obj_t compare_equal(mp_obj_t x1, mp_obj_t x2) { + return compare_equal_helper(x1, x2, COMPARE_EQUAL); +} + +MP_DEFINE_CONST_FUN_OBJ_2(compare_equal_obj, compare_equal); +#endif + +#if ULAB_NUMPY_HAS_NOTEQUAL + +mp_obj_t compare_not_equal(mp_obj_t x1, mp_obj_t x2) { + return compare_equal_helper(x1, x2, COMPARE_NOT_EQUAL); +} + +MP_DEFINE_CONST_FUN_OBJ_2(compare_not_equal_obj, compare_not_equal); +#endif + +#if ULAB_NUMPY_HAS_ISFINITE | ULAB_NUMPY_HAS_ISINF +static mp_obj_t compare_isinf_isfinite(mp_obj_t _x, uint8_t mask) { + // mask should signify, whether the function is called from isinf (mask = 1), + // or from isfinite (mask = 0) + if(mp_obj_is_int(_x)) { + if(mask) { + return mp_const_false; + } else { + return mp_const_true; + } + } else if(mp_obj_is_float(_x)) { + mp_float_t x = mp_obj_get_float(_x); + if(isnan(x)) { + return mp_const_false; + } + if(mask) { // called from isinf + return isinf(x) ? mp_const_true : mp_const_false; + } else { // called from isfinite + return isinf(x) ? mp_const_false : mp_const_true; + } + } else if(mp_obj_is_type(_x, &ulab_ndarray_type)) { + ndarray_obj_t *x = MP_OBJ_TO_PTR(_x); + ndarray_obj_t *results = ndarray_new_dense_ndarray(x->ndim, x->shape, NDARRAY_BOOL); + // At this point, results is all False + uint8_t *rarray = (uint8_t *)results->array; + if(x->dtype != NDARRAY_FLOAT) { + // int types can never be infinite... + if(!mask) { + // ...so flip all values in the array, if the function was called from isfinite + memset(rarray, 1, results->len); + } + return results; + } + uint8_t *xarray = (uint8_t *)x->array; + + #if ULAB_MAX_DIMS > 3 + size_t i = 0; + do { + #endif + #if ULAB_MAX_DIMS > 2 + size_t j = 0; + do { + #endif + #if ULAB_MAX_DIMS > 1 + size_t k = 0; + do { + #endif + size_t l = 0; + do { + mp_float_t value = *(mp_float_t *)xarray; + if(isnan(value)) { + *rarray++ = 0; + } else { + *rarray++ = isinf(value) ? mask : 1 - mask; + } + xarray += x->strides[ULAB_MAX_DIMS - 1]; + l++; + } while(l < x->shape[ULAB_MAX_DIMS - 1]); + #if ULAB_MAX_DIMS > 1 + xarray -= x->strides[ULAB_MAX_DIMS - 1] * x->shape[ULAB_MAX_DIMS-1]; + xarray += x->strides[ULAB_MAX_DIMS - 2]; + k++; + } while(k < x->shape[ULAB_MAX_DIMS - 2]); + #endif + #if ULAB_MAX_DIMS > 2 + xarray -= x->strides[ULAB_MAX_DIMS - 2] * x->shape[ULAB_MAX_DIMS-2]; + xarray += x->strides[ULAB_MAX_DIMS - 3]; + j++; + } while(j < x->shape[ULAB_MAX_DIMS - 3]); + #endif + #if ULAB_MAX_DIMS > 3 + xarray -= x->strides[ULAB_MAX_DIMS - 3] * x->shape[ULAB_MAX_DIMS-3]; + xarray += x->strides[ULAB_MAX_DIMS - 4]; + i++; + } while(i < x->shape[ULAB_MAX_DIMS - 4]); + #endif + + return results; + } else { + mp_raise_TypeError(translate("wrong input type")); + } + return mp_const_none; +} +#endif + +#if ULAB_NUMPY_HAS_ISFINITE +mp_obj_t compare_isfinite(mp_obj_t _x) { + return compare_isinf_isfinite(_x, 0); +} + +MP_DEFINE_CONST_FUN_OBJ_1(compare_isfinite_obj, compare_isfinite); +#endif + +#if ULAB_NUMPY_HAS_ISINF +mp_obj_t compare_isinf(mp_obj_t _x) { + return compare_isinf_isfinite(_x, 1); +} + +MP_DEFINE_CONST_FUN_OBJ_1(compare_isinf_obj, compare_isinf); +#endif + +#if ULAB_NUMPY_HAS_MAXIMUM +mp_obj_t compare_maximum(mp_obj_t x1, mp_obj_t x2) { + // extra round, so that we can return maximum(3, 4) properly + mp_obj_t result = compare_function(x1, x2, COMPARE_MAXIMUM); + if((mp_obj_is_int(x1) || mp_obj_is_float(x1)) && (mp_obj_is_int(x2) || mp_obj_is_float(x2))) { + ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(result); + return mp_binary_get_val_array(ndarray->dtype, ndarray->array, 0); + } + return result; +} + +MP_DEFINE_CONST_FUN_OBJ_2(compare_maximum_obj, compare_maximum); +#endif + +#if ULAB_NUMPY_HAS_MINIMUM + +mp_obj_t compare_minimum(mp_obj_t x1, mp_obj_t x2) { + // extra round, so that we can return minimum(3, 4) properly + mp_obj_t result = compare_function(x1, x2, COMPARE_MINIMUM); + if((mp_obj_is_int(x1) || mp_obj_is_float(x1)) && (mp_obj_is_int(x2) || mp_obj_is_float(x2))) { + ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(result); + return mp_binary_get_val_array(ndarray->dtype, ndarray->array, 0); + } + return result; +} + +MP_DEFINE_CONST_FUN_OBJ_2(compare_minimum_obj, compare_minimum); +#endif + +#if ULAB_NUMPY_HAS_WHERE + +mp_obj_t compare_where(mp_obj_t _condition, mp_obj_t _x, mp_obj_t _y) { + // this implementation will work with ndarrays, and scalars only + ndarray_obj_t *c = ndarray_from_mp_obj(_condition, 0); + ndarray_obj_t *x = ndarray_from_mp_obj(_x, 0); + ndarray_obj_t *y = ndarray_from_mp_obj(_y, 0); + + int32_t *cstrides = m_new(int32_t, ULAB_MAX_DIMS); + int32_t *xstrides = m_new(int32_t, ULAB_MAX_DIMS); + int32_t *ystrides = m_new(int32_t, ULAB_MAX_DIMS); + + size_t *oshape = m_new(size_t, ULAB_MAX_DIMS); + + uint8_t ndim; + + // establish the broadcasting conditions first + // if any two of the arrays can be broadcast together, then + // the three arrays can also be broadcast together + if(!ndarray_can_broadcast(c, x, &ndim, oshape, cstrides, ystrides) || + !ndarray_can_broadcast(c, y, &ndim, oshape, cstrides, ystrides) || + !ndarray_can_broadcast(x, y, &ndim, oshape, xstrides, ystrides)) { + mp_raise_ValueError(translate("operands could not be broadcast together")); + } + + ndim = MAX(MAX(c->ndim, x->ndim), y->ndim); + + for(uint8_t i = 1; i <= ndim; i++) { + cstrides[ULAB_MAX_DIMS - i] = c->shape[ULAB_MAX_DIMS - i] < 2 ? 0 : c->strides[ULAB_MAX_DIMS - i]; + xstrides[ULAB_MAX_DIMS - i] = x->shape[ULAB_MAX_DIMS - i] < 2 ? 0 : x->strides[ULAB_MAX_DIMS - i]; + ystrides[ULAB_MAX_DIMS - i] = y->shape[ULAB_MAX_DIMS - i] < 2 ? 0 : y->strides[ULAB_MAX_DIMS - i]; + oshape[ULAB_MAX_DIMS - i] = MAX(MAX(c->shape[ULAB_MAX_DIMS - i], x->shape[ULAB_MAX_DIMS - i]), y->shape[ULAB_MAX_DIMS - i]); + } + + uint8_t out_dtype = ndarray_upcast_dtype(x->dtype, y->dtype); + ndarray_obj_t *out = ndarray_new_dense_ndarray(ndim, oshape, out_dtype); + + mp_float_t (*cfunc)(void *) = ndarray_get_float_function(c->dtype); + mp_float_t (*xfunc)(void *) = ndarray_get_float_function(x->dtype); + mp_float_t (*yfunc)(void *) = ndarray_get_float_function(y->dtype); + mp_float_t (*ofunc)(void *, mp_float_t ) = ndarray_set_float_function(out->dtype); + + uint8_t *oarray = (uint8_t *)out->array; + uint8_t *carray = (uint8_t *)c->array; + uint8_t *xarray = (uint8_t *)x->array; + uint8_t *yarray = (uint8_t *)y->array; + + #if ULAB_MAX_DIMS > 3 + size_t i = 0; + do { + #endif + #if ULAB_MAX_DIMS > 2 + size_t j = 0; + do { + #endif + #if ULAB_MAX_DIMS > 1 + size_t k = 0; + do { + #endif + size_t l = 0; + do { + mp_float_t value; + mp_float_t cvalue = cfunc(carray); + if(cvalue != MICROPY_FLOAT_CONST(0.0)) { + value = xfunc(xarray); + } else { + value = yfunc(yarray); + } + ofunc(oarray, value); + oarray += out->itemsize; + carray += cstrides[ULAB_MAX_DIMS - 1]; + xarray += xstrides[ULAB_MAX_DIMS - 1]; + yarray += ystrides[ULAB_MAX_DIMS - 1]; + l++; + } while(l < out->shape[ULAB_MAX_DIMS - 1]); + #if ULAB_MAX_DIMS > 1 + carray -= cstrides[ULAB_MAX_DIMS - 1] * c->shape[ULAB_MAX_DIMS-1]; + carray += cstrides[ULAB_MAX_DIMS - 2]; + xarray -= xstrides[ULAB_MAX_DIMS - 1] * x->shape[ULAB_MAX_DIMS-1]; + xarray += xstrides[ULAB_MAX_DIMS - 2]; + yarray -= ystrides[ULAB_MAX_DIMS - 1] * y->shape[ULAB_MAX_DIMS-1]; + yarray += ystrides[ULAB_MAX_DIMS - 2]; + k++; + } while(k < out->shape[ULAB_MAX_DIMS - 2]); + #endif + #if ULAB_MAX_DIMS > 2 + carray -= cstrides[ULAB_MAX_DIMS - 2] * c->shape[ULAB_MAX_DIMS-2]; + carray += cstrides[ULAB_MAX_DIMS - 3]; + xarray -= xstrides[ULAB_MAX_DIMS - 2] * x->shape[ULAB_MAX_DIMS-2]; + xarray += xstrides[ULAB_MAX_DIMS - 3]; + yarray -= ystrides[ULAB_MAX_DIMS - 2] * y->shape[ULAB_MAX_DIMS-2]; + yarray += ystrides[ULAB_MAX_DIMS - 3]; + j++; + } while(j < out->shape[ULAB_MAX_DIMS - 3]); + #endif + #if ULAB_MAX_DIMS > 3 + carray -= cstrides[ULAB_MAX_DIMS - 3] * c->shape[ULAB_MAX_DIMS-3]; + carray += cstrides[ULAB_MAX_DIMS - 4]; + xarray -= xstrides[ULAB_MAX_DIMS - 3] * x->shape[ULAB_MAX_DIMS-3]; + xarray += xstrides[ULAB_MAX_DIMS - 4]; + yarray -= ystrides[ULAB_MAX_DIMS - 3] * y->shape[ULAB_MAX_DIMS-3]; + yarray += ystrides[ULAB_MAX_DIMS - 4]; + i++; + } while(i < out->shape[ULAB_MAX_DIMS - 4]); + #endif + return MP_OBJ_FROM_PTR(out); +} + +MP_DEFINE_CONST_FUN_OBJ_3(compare_where_obj, compare_where); +#endif diff --git a/python/port/mod/ulab/numpy/compare/compare.h b/python/port/mod/ulab/numpy/compare/compare.h new file mode 100644 index 000000000..12a559e0a --- /dev/null +++ b/python/port/mod/ulab/numpy/compare/compare.h @@ -0,0 +1,150 @@ + +/* + * This file is part of the micropython-ulab project, + * + * https://github.com/v923z/micropython-ulab + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Zoltán Vörös +*/ + +#ifndef _COMPARE_ +#define _COMPARE_ + +#include "../../ulab.h" +#include "../../ndarray.h" + +enum COMPARE_FUNCTION_TYPE { + COMPARE_EQUAL, + COMPARE_NOT_EQUAL, + COMPARE_MINIMUM, + COMPARE_MAXIMUM, + COMPARE_CLIP, +}; + +MP_DECLARE_CONST_FUN_OBJ_3(compare_clip_obj); +MP_DECLARE_CONST_FUN_OBJ_2(compare_equal_obj); +MP_DECLARE_CONST_FUN_OBJ_2(compare_isfinite_obj); +MP_DECLARE_CONST_FUN_OBJ_2(compare_isinf_obj); +MP_DECLARE_CONST_FUN_OBJ_2(compare_minimum_obj); +MP_DECLARE_CONST_FUN_OBJ_2(compare_maximum_obj); +MP_DECLARE_CONST_FUN_OBJ_2(compare_not_equal_obj); +MP_DECLARE_CONST_FUN_OBJ_3(compare_where_obj); + +#if ULAB_MAX_DIMS == 1 +#define COMPARE_LOOP(results, array, type_out, type_left, type_right, larray, lstrides, rarray, rstrides, OPERATOR)\ + size_t l = 0;\ + do {\ + *((type_out *)(array)) = *((type_left *)(larray)) OPERATOR *((type_right *)(rarray)) ? (type_out)(*((type_left *)(larray))) : (type_out)(*((type_right *)(rarray)));\ + (array) += (results)->strides[ULAB_MAX_DIMS - 1];\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\ + l++;\ + } while(l < results->shape[ULAB_MAX_DIMS - 1]);\ + return MP_OBJ_FROM_PTR(results);\ + +#endif // ULAB_MAX_DIMS == 1 + +#if ULAB_MAX_DIMS == 2 +#define COMPARE_LOOP(results, array, type_out, type_left, type_right, larray, lstrides, rarray, rstrides, OPERATOR)\ + size_t k = 0;\ + do {\ + size_t l = 0;\ + do {\ + *((type_out *)(array)) = *((type_left *)(larray)) OPERATOR *((type_right *)(rarray)) ? (type_out)(*((type_left *)(larray))) : (type_out)(*((type_right *)(rarray)));\ + (array) += (results)->strides[ULAB_MAX_DIMS - 1];\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\ + l++;\ + } while(l < results->shape[ULAB_MAX_DIMS - 1]);\ + (larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * results->shape[ULAB_MAX_DIMS-1];\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 2];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * results->shape[ULAB_MAX_DIMS-1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\ + k++;\ + } while(k < results->shape[ULAB_MAX_DIMS - 2]);\ + return MP_OBJ_FROM_PTR(results);\ + +#endif // ULAB_MAX_DIMS == 2 + +#if ULAB_MAX_DIMS == 3 +#define COMPARE_LOOP(results, array, type_out, type_left, type_right, larray, lstrides, rarray, rstrides, OPERATOR)\ + size_t j = 0;\ + do {\ + size_t k = 0;\ + do {\ + size_t l = 0;\ + do {\ + *((type_out *)(array)) = *((type_left *)(larray)) OPERATOR *((type_right *)(rarray)) ? (type_out)(*((type_left *)(larray))) : (type_out)(*((type_right *)(rarray)));\ + (array) += (results)->strides[ULAB_MAX_DIMS - 1];\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\ + l++;\ + } while(l < results->shape[ULAB_MAX_DIMS - 1]);\ + (larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * results->shape[ULAB_MAX_DIMS-1];\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 2];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * results->shape[ULAB_MAX_DIMS-1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\ + k++;\ + } while(k < results->shape[ULAB_MAX_DIMS - 2]);\ + (larray) -= (lstrides)[ULAB_MAX_DIMS - 2] * results->shape[ULAB_MAX_DIMS-2];\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 3];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * results->shape[ULAB_MAX_DIMS-2];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\ + j++;\ + } while(j < results->shape[ULAB_MAX_DIMS - 3]);\ + return MP_OBJ_FROM_PTR(results);\ + +#endif // ULAB_MAX_DIMS == 3 + +#if ULAB_MAX_DIMS == 4 +#define COMPARE_LOOP(results, array, type_out, type_left, type_right, larray, lstrides, rarray, rstrides, OPERATOR)\ + size_t i = 0;\ + do {\ + size_t j = 0;\ + do {\ + size_t k = 0;\ + do {\ + size_t l = 0;\ + do {\ + *((type_out *)(array)) = *((type_left *)(larray)) OPERATOR *((type_right *)(rarray)) ? (type_out)(*((type_left *)(larray))) : (type_out)(*((type_right *)(rarray)));\ + (array) += (results)->strides[ULAB_MAX_DIMS - 1];\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\ + l++;\ + } while(l < results->shape[ULAB_MAX_DIMS - 1]);\ + (larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * results->shape[ULAB_MAX_DIMS-1];\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 2];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * results->shape[ULAB_MAX_DIMS-1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\ + k++;\ + } while(k < results->shape[ULAB_MAX_DIMS - 2]);\ + (larray) -= (lstrides)[ULAB_MAX_DIMS - 2] * results->shape[ULAB_MAX_DIMS-2];\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 3];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * results->shape[ULAB_MAX_DIMS-2];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\ + j++;\ + } while(j < results->shape[ULAB_MAX_DIMS - 3]);\ + (larray) -= (lstrides)[ULAB_MAX_DIMS - 3] * results->shape[ULAB_MAX_DIMS-3];\ + (larray) += (lstrides)[ULAB_MAX_DIMS - 4];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 3] * results->shape[ULAB_MAX_DIMS-3];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 4];\ + i++;\ + } while(i < results->shape[ULAB_MAX_DIMS - 4]);\ + return MP_OBJ_FROM_PTR(results);\ + +#endif // ULAB_MAX_DIMS == 4 + +#define RUN_COMPARE_LOOP(dtype, type_out, type_left, type_right, larray, lstrides, rarray, rstrides, ndim, shape, op) do {\ + ndarray_obj_t *results = ndarray_new_dense_ndarray((ndim), (shape), (dtype));\ + uint8_t *array = (uint8_t *)results->array;\ + if((op) == COMPARE_MINIMUM) {\ + COMPARE_LOOP(results, array, type_out, type_left, type_right, larray, lstrides, rarray, rstrides, <);\ + }\ + if((op) == COMPARE_MAXIMUM) {\ + COMPARE_LOOP(results, array, type_out, type_left, type_right, larray, lstrides, rarray, rstrides, >);\ + }\ +} while(0) + +#endif diff --git a/python/port/mod/ulab/numpy/fft/fft.c b/python/port/mod/ulab/numpy/fft/fft.c new file mode 100644 index 000000000..ff2373fe0 --- /dev/null +++ b/python/port/mod/ulab/numpy/fft/fft.c @@ -0,0 +1,82 @@ +/* + * This file is part of the micropython-ulab project, + * + * https://github.com/v923z/micropython-ulab + * + * The MIT License (MIT) + * + * Copyright (c) 2019-2021 Zoltán Vörös + * 2020 Scott Shawcroft for Adafruit Industries + * 2020 Taku Fukada +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fft.h" + +//| """Frequency-domain functions""" +//| + + +//| def fft(r: ulab.ndarray, c: Optional[ulab.ndarray] = None) -> Tuple[ulab.ndarray, ulab.ndarray]: +//| """ +//| :param ulab.ndarray r: A 1-dimension array of values whose size is a power of 2 +//| :param ulab.ndarray c: An optional 1-dimension array of values whose size is a power of 2, giving the complex part of the value +//| :return tuple (r, c): The real and complex parts of the FFT +//| +//| Perform a Fast Fourier Transform from the time domain into the frequency domain +//| +//| See also ~ulab.extras.spectrum, which computes the magnitude of the fft, +//| rather than separately returning its real and imaginary parts.""" +//| ... +//| +static mp_obj_t fft_fft(size_t n_args, const mp_obj_t *args) { + if(n_args == 2) { + return fft_fft_ifft_spectrogram(n_args, args[0], args[1], FFT_FFT); + } else { + return fft_fft_ifft_spectrogram(n_args, args[0], mp_const_none, FFT_FFT); + } +} + +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(fft_fft_obj, 1, 2, fft_fft); + +//| def ifft(r: ulab.ndarray, c: Optional[ulab.ndarray] = None) -> Tuple[ulab.ndarray, ulab.ndarray]: +//| """ +//| :param ulab.ndarray r: A 1-dimension array of values whose size is a power of 2 +//| :param ulab.ndarray c: An optional 1-dimension array of values whose size is a power of 2, giving the complex part of the value +//| :return tuple (r, c): The real and complex parts of the inverse FFT +//| +//| Perform an Inverse Fast Fourier Transform from the frequeny domain into the time domain""" +//| ... +//| + +static mp_obj_t fft_ifft(size_t n_args, const mp_obj_t *args) { + if(n_args == 2) { + return fft_fft_ifft_spectrogram(n_args, args[0], args[1], FFT_IFFT); + } else { + return fft_fft_ifft_spectrogram(n_args, args[0], mp_const_none, FFT_IFFT); + } +} + +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(fft_ifft_obj, 1, 2, fft_ifft); + +STATIC const mp_rom_map_elem_t ulab_fft_globals_table[] = { + { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_fft) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_fft), (mp_obj_t)&fft_fft_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_ifft), (mp_obj_t)&fft_ifft_obj }, +}; + +STATIC MP_DEFINE_CONST_DICT(mp_module_ulab_fft_globals, ulab_fft_globals_table); + +mp_obj_module_t ulab_fft_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t*)&mp_module_ulab_fft_globals, +}; diff --git a/python/port/mod/ulab/numpy/fft/fft.h b/python/port/mod/ulab/numpy/fft/fft.h new file mode 100644 index 000000000..66acafe11 --- /dev/null +++ b/python/port/mod/ulab/numpy/fft/fft.h @@ -0,0 +1,24 @@ + +/* + * This file is part of the micropython-ulab project, + * + * https://github.com/v923z/micropython-ulab + * + * The MIT License (MIT) + * + * Copyright (c) 2019-2021 Zoltán Vörös +*/ + +#ifndef _FFT_ +#define _FFT_ + +#include "../../ulab.h" +#include "../../ulab_tools.h" +#include "../../ndarray.h" +#include "fft_tools.h" + +extern mp_obj_module_t ulab_fft_module; + +MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(fft_fft_obj); +MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(fft_ifft_obj); +#endif diff --git a/python/port/mod/ulab/numpy/fft/fft_tools.c b/python/port/mod/ulab/numpy/fft/fft_tools.c new file mode 100644 index 000000000..ab737a850 --- /dev/null +++ b/python/port/mod/ulab/numpy/fft/fft_tools.c @@ -0,0 +1,165 @@ +/* + * This file is part of the micropython-ulab project, + * + * https://github.com/v923z/micropython-ulab + * + * The MIT License (MIT) + * + * Copyright (c) 2019-2021 Zoltán Vörös +*/ + +#include +#include + +#include "../../ndarray.h" +#include "../../ulab_tools.h" +#include "fft_tools.h" + +#ifndef MP_PI +#define MP_PI MICROPY_FLOAT_CONST(3.14159265358979323846) +#endif +#ifndef MP_E +#define MP_E MICROPY_FLOAT_CONST(2.71828182845904523536) +#endif + +/* + * The following function takes two arrays, namely, the real and imaginary + * parts of a complex array, and calculates the Fourier transform in place. + * + * The function is basically a modification of four1 from Numerical Recipes, + * has no dependencies beyond micropython itself (for the definition of mp_float_t), + * and can be used independent of ulab. + */ + +void fft_kernel(mp_float_t *real, mp_float_t *imag, size_t n, int isign) { + size_t j, m, mmax, istep; + mp_float_t tempr, tempi; + mp_float_t wtemp, wr, wpr, wpi, wi, theta; + + j = 0; + for(size_t i = 0; i < n; i++) { + if (j > i) { + SWAP(mp_float_t, real[i], real[j]); + SWAP(mp_float_t, imag[i], imag[j]); + } + m = n >> 1; + while (j >= m && m > 0) { + j -= m; + m >>= 1; + } + j += m; + } + + mmax = 1; + while (n > mmax) { + istep = mmax << 1; + theta = MICROPY_FLOAT_CONST(-2.0)*isign*MP_PI/istep; + wtemp = MICROPY_FLOAT_C_FUN(sin)(MICROPY_FLOAT_CONST(0.5) * theta); + wpr = MICROPY_FLOAT_CONST(-2.0) * wtemp * wtemp; + wpi = MICROPY_FLOAT_C_FUN(sin)(theta); + wr = MICROPY_FLOAT_CONST(1.0); + wi = MICROPY_FLOAT_CONST(0.0); + for(m = 0; m < mmax; m++) { + for(size_t i = m; i < n; i += istep) { + j = i + mmax; + tempr = wr * real[j] - wi * imag[j]; + tempi = wr * imag[j] + wi * real[j]; + real[j] = real[i] - tempr; + imag[j] = imag[i] - tempi; + real[i] += tempr; + imag[i] += tempi; + } + wtemp = wr; + wr = wr*wpr - wi*wpi + wr; + wi = wi*wpr + wtemp*wpi + wi; + } + mmax = istep; + } +} + +/* + * The following function is a helper interface to the python side. + * It has been factored out from fft.c, so that the same argument parsing + * routine can be called from scipy.signal.spectrogram. + */ + +mp_obj_t fft_fft_ifft_spectrogram(size_t n_args, mp_obj_t arg_re, mp_obj_t arg_im, uint8_t type) { + if(!mp_obj_is_type(arg_re, &ulab_ndarray_type)) { + mp_raise_NotImplementedError(translate("FFT is defined for ndarrays only")); + } + if(n_args == 2) { + if(!mp_obj_is_type(arg_im, &ulab_ndarray_type)) { + mp_raise_NotImplementedError(translate("FFT is defined for ndarrays only")); + } + } + ndarray_obj_t *re = MP_OBJ_TO_PTR(arg_re); + #if ULAB_MAX_DIMS > 1 + if(re->ndim != 1) { + mp_raise_TypeError(translate("FFT is implemented for linear arrays only")); + } + #endif + size_t len = re->len; + // Check if input is of length of power of 2 + if((len & (len-1)) != 0) { + mp_raise_ValueError(translate("input array length must be power of 2")); + } + + ndarray_obj_t *out_re = ndarray_new_linear_array(len, NDARRAY_FLOAT); + mp_float_t *data_re = (mp_float_t *)out_re->array; + + uint8_t *array = (uint8_t *)re->array; + mp_float_t (*func)(void *) = ndarray_get_float_function(re->dtype); + + for(size_t i=0; i < len; i++) { + *data_re++ = func(array); + array += re->strides[ULAB_MAX_DIMS - 1]; + } + data_re -= len; + ndarray_obj_t *out_im = ndarray_new_linear_array(len, NDARRAY_FLOAT); + mp_float_t *data_im = (mp_float_t *)out_im->array; + + if(n_args == 2) { + ndarray_obj_t *im = MP_OBJ_TO_PTR(arg_im); + #if ULAB_MAX_DIMS > 1 + if(im->ndim != 1) { + mp_raise_TypeError(translate("FFT is implemented for linear arrays only")); + } + #endif + if (re->len != im->len) { + mp_raise_ValueError(translate("real and imaginary parts must be of equal length")); + } + array = (uint8_t *)im->array; + func = ndarray_get_float_function(im->dtype); + for(size_t i=0; i < len; i++) { + *data_im++ = func(array); + array += im->strides[ULAB_MAX_DIMS - 1]; + } + data_im -= len; + } + + if((type == FFT_FFT) || (type == FFT_SPECTROGRAM)) { + fft_kernel(data_re, data_im, len, 1); + if(type == FFT_SPECTROGRAM) { + for(size_t i=0; i < len; i++) { + *data_re = MICROPY_FLOAT_C_FUN(sqrt)(*data_re * *data_re + *data_im * *data_im); + data_re++; + data_im++; + } + } + } else { // inverse transform + fft_kernel(data_re, data_im, len, -1); + // TODO: numpy accepts the norm keyword argument + for(size_t i=0; i < len; i++) { + *data_re++ /= len; + *data_im++ /= len; + } + } + if(type == FFT_SPECTROGRAM) { + return MP_OBJ_TO_PTR(out_re); + } else { + mp_obj_t tuple[2]; + tuple[0] = out_re; + tuple[1] = out_im; + return mp_obj_new_tuple(2, tuple); + } +} diff --git a/python/port/mod/ulab/numpy/fft/fft_tools.h b/python/port/mod/ulab/numpy/fft/fft_tools.h new file mode 100644 index 000000000..d3b856d07 --- /dev/null +++ b/python/port/mod/ulab/numpy/fft/fft_tools.h @@ -0,0 +1,23 @@ +/* + * This file is part of the micropython-ulab project, + * + * https://github.com/v923z/micropython-ulab + * + * The MIT License (MIT) + * + * Copyright (c) 2019-2021 Zoltán Vörös +*/ + +#ifndef _FFT_TOOLS_ +#define _FFT_TOOLS_ + +enum FFT_TYPE { + FFT_FFT, + FFT_IFFT, + FFT_SPECTROGRAM, +}; + +void fft_kernel(mp_float_t *, mp_float_t *, size_t , int ); +mp_obj_t fft_fft_ifft_spectrogram(size_t , mp_obj_t , mp_obj_t , uint8_t ); + +#endif /* _FFT_TOOLS_ */ diff --git a/python/port/mod/ulab/numpy/filter/filter.c b/python/port/mod/ulab/numpy/filter/filter.c new file mode 100644 index 000000000..9be43946b --- /dev/null +++ b/python/port/mod/ulab/numpy/filter/filter.c @@ -0,0 +1,84 @@ + +/* + * This file is part of the micropython-ulab project, + * + * https://github.com/v923z/micropython-ulab + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jeff Epler for Adafruit Industries + * 2020 Scott Shawcroft for Adafruit Industries + * 2020-2021 Zoltán Vörös + * 2020 Taku Fukada +*/ + +#include +#include +#include +#include +#include +#include + +#include "../../ulab.h" +#include "../../scipy/signal/signal.h" +#include "filter.h" + +#if ULAB_NUMPY_HAS_CONVOLVE + +mp_obj_t filter_convolve(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + static const mp_arg_t allowed_args[] = { + { MP_QSTR_a, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } }, + { MP_QSTR_v, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + if(!mp_obj_is_type(args[0].u_obj, &ulab_ndarray_type) || !mp_obj_is_type(args[1].u_obj, &ulab_ndarray_type)) { + mp_raise_TypeError(translate("convolve arguments must be ndarrays")); + } + + ndarray_obj_t *a = MP_OBJ_TO_PTR(args[0].u_obj); + ndarray_obj_t *c = MP_OBJ_TO_PTR(args[1].u_obj); + // deal with linear arrays only + #if ULAB_MAX_DIMS > 1 + if((a->ndim != 1) || (c->ndim != 1)) { + mp_raise_TypeError(translate("convolve arguments must be linear arrays")); + } + #endif + size_t len_a = a->len; + size_t len_c = c->len; + if(len_a == 0 || len_c == 0) { + mp_raise_TypeError(translate("convolve arguments must not be empty")); + } + + int len = len_a + len_c - 1; // convolve mode "full" + ndarray_obj_t *out = ndarray_new_linear_array(len, NDARRAY_FLOAT); + mp_float_t *outptr = (mp_float_t *)out->array; + uint8_t *aarray = (uint8_t *)a->array; + uint8_t *carray = (uint8_t *)c->array; + + int32_t off = len_c - 1; + int32_t as = a->strides[ULAB_MAX_DIMS - 1] / a->itemsize; + int32_t cs = c->strides[ULAB_MAX_DIMS - 1] / c->itemsize; + + for(int32_t k=-off; k < len-off; k++) { + mp_float_t accum = (mp_float_t)0.0; + int32_t top_n = MIN(len_c, len_a - k); + int32_t bot_n = MAX(-k, 0); + for(int32_t n=bot_n; n < top_n; n++) { + int32_t idx_c = (len_c - n - 1) * cs; + int32_t idx_a = (n + k) * as; + mp_float_t ai = ndarray_get_float_index(aarray, a->dtype, idx_a); + mp_float_t ci = ndarray_get_float_index(carray, c->dtype, idx_c); + accum += ai * ci; + } + *outptr++ = accum; + } + + return out; +} + +MP_DEFINE_CONST_FUN_OBJ_KW(filter_convolve_obj, 2, filter_convolve); + +#endif diff --git a/python/port/mod/ulab/numpy/filter/filter.h b/python/port/mod/ulab/numpy/filter/filter.h new file mode 100644 index 000000000..9b1ad1875 --- /dev/null +++ b/python/port/mod/ulab/numpy/filter/filter.h @@ -0,0 +1,20 @@ + +/* + * This file is part of the micropython-ulab project, + * + * https://github.com/v923z/micropython-ulab + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jeff Epler for Adafruit Industries + * 2020-2021 Zoltán Vörös +*/ + +#ifndef _FILTER_ +#define _FILTER_ + +#include "../../ulab.h" +#include "../../ndarray.h" + +MP_DECLARE_CONST_FUN_OBJ_KW(filter_convolve_obj); +#endif diff --git a/python/port/mod/ulab/numpy/linalg/linalg.c b/python/port/mod/ulab/numpy/linalg/linalg.c new file mode 100644 index 000000000..209330f86 --- /dev/null +++ b/python/port/mod/ulab/numpy/linalg/linalg.c @@ -0,0 +1,397 @@ + +/* + * This file is part of the micropython-ulab project, + * + * https://github.com/v923z/micropython-ulab + * + * The MIT License (MIT) + * + * Copyright (c) 2019-2021 Zoltán Vörös + * 2020 Scott Shawcroft for Adafruit Industries + * 2020 Roberto Colistete Jr. + * 2020 Taku Fukada + * +*/ + +#include +#include +#include +#include +#include +#include + +#include "../../ulab.h" +#include "../../ulab_tools.h" +#include "linalg.h" + +#if ULAB_NUMPY_HAS_LINALG_MODULE +//| +//| import ulab.numpy +//| +//| """Linear algebra functions""" +//| + +#if ULAB_MAX_DIMS > 1 +//| def cholesky(A: ulab.numpy.ndarray) -> ulab.numpy.ndarray: +//| """ +//| :param ~ulab.numpy.ndarray A: a positive definite, symmetric square matrix +//| :return ~ulab.numpy.ndarray L: a square root matrix in the lower triangular form +//| :raises ValueError: If the input does not fulfill the necessary conditions +//| +//| The returned matrix satisfies the equation m=LL*""" +//| ... +//| + +static mp_obj_t linalg_cholesky(mp_obj_t oin) { + ndarray_obj_t *ndarray = tools_object_is_square(oin); + ndarray_obj_t *L = ndarray_new_dense_ndarray(2, ndarray_shape_vector(0, 0, ndarray->shape[ULAB_MAX_DIMS - 1], ndarray->shape[ULAB_MAX_DIMS - 1]), NDARRAY_FLOAT); + mp_float_t *Larray = (mp_float_t *)L->array; + + size_t N = ndarray->shape[ULAB_MAX_DIMS - 1]; + uint8_t *array = (uint8_t *)ndarray->array; + mp_float_t (*func)(void *) = ndarray_get_float_function(ndarray->dtype); + + for(size_t m=0; m < N; m++) { // rows + for(size_t n=0; n < N; n++) { // columns + *Larray++ = func(array); + array += ndarray->strides[ULAB_MAX_DIMS - 1]; + } + array -= ndarray->strides[ULAB_MAX_DIMS - 1] * N; + array += ndarray->strides[ULAB_MAX_DIMS - 2]; + } + Larray -= N*N; + // make sure the matrix is symmetric + for(size_t m=0; m < N; m++) { // rows + for(size_t n=m+1; n < N; n++) { // columns + // compare entry (m, n) to (n, m) + if(LINALG_EPSILON < MICROPY_FLOAT_C_FUN(fabs)(Larray[m * N + n] - Larray[n * N + m])) { + mp_raise_ValueError(translate("input matrix is asymmetric")); + } + } + } + + // this is actually not needed, but Cholesky in numpy returns the lower triangular matrix + for(size_t i=0; i < N; i++) { // rows + for(size_t j=i+1; j < N; j++) { // columns + Larray[i*N + j] = MICROPY_FLOAT_CONST(0.0); + } + } + mp_float_t sum = 0.0; + for(size_t i=0; i < N; i++) { // rows + for(size_t j=0; j <= i; j++) { // columns + sum = Larray[i * N + j]; + for(size_t k=0; k < j; k++) { + sum -= Larray[i * N + k] * Larray[j * N + k]; + } + if(i == j) { + if(sum <= MICROPY_FLOAT_CONST(0.0)) { + mp_raise_ValueError(translate("matrix is not positive definite")); + } else { + Larray[i * N + i] = MICROPY_FLOAT_C_FUN(sqrt)(sum); + } + } else { + Larray[i * N + j] = sum / Larray[j * N + j]; + } + } + } + return MP_OBJ_FROM_PTR(L); +} + +MP_DEFINE_CONST_FUN_OBJ_1(linalg_cholesky_obj, linalg_cholesky); + +//| def det(m: ulab.numpy.ndarray) -> float: +//| """ +//| :param: m, a square matrix +//| :return float: The determinant of the matrix +//| +//| Computes the eigenvalues and eigenvectors of a square matrix""" +//| ... +//| + +static mp_obj_t linalg_det(mp_obj_t oin) { + ndarray_obj_t *ndarray = tools_object_is_square(oin); + uint8_t *array = (uint8_t *)ndarray->array; + size_t N = ndarray->shape[ULAB_MAX_DIMS - 1]; + mp_float_t *tmp = m_new(mp_float_t, N * N); + for(size_t m=0; m < N; m++) { // rows + for(size_t n=0; n < N; n++) { // columns + *tmp++ = ndarray_get_float_value(array, ndarray->dtype); + array += ndarray->strides[ULAB_MAX_DIMS - 1]; + } + array -= ndarray->strides[ULAB_MAX_DIMS - 1] * N; + array += ndarray->strides[ULAB_MAX_DIMS - 2]; + } + + // re-wind the pointer + tmp -= N*N; + + mp_float_t c; + mp_float_t det_sign = 1.0; + + for(size_t m=0; m < N-1; m++){ + if(MICROPY_FLOAT_C_FUN(fabs)(tmp[m * (N+1)]) < LINALG_EPSILON) { + size_t m1 = m + 1; + for(; m1 < N; m1++) { + if(!(MICROPY_FLOAT_C_FUN(fabs)(tmp[m1*N+m]) < LINALG_EPSILON)) { + //look for a line to swap + for(size_t m2=0; m2 < N; m2++) { + mp_float_t swapVal = tmp[m*N+m2]; + tmp[m*N+m2] = tmp[m1*N+m2]; + tmp[m1*N+m2] = swapVal; + } + det_sign = -det_sign; + break; + } + } + if (m1 >= N) { + m_del(mp_float_t, tmp, N * N); + return mp_obj_new_float(0.0); + } + } + for(size_t n=0; n < N; n++) { + if(m != n) { + c = tmp[N * n + m] / tmp[m * (N+1)]; + for(size_t k=0; k < N; k++){ + tmp[N * n + k] -= c * tmp[N * m + k]; + } + } + } + } + mp_float_t det = det_sign; + + for(size_t m=0; m < N; m++){ + det *= tmp[m * (N+1)]; + } + m_del(mp_float_t, tmp, N * N); + return mp_obj_new_float(det); +} + +MP_DEFINE_CONST_FUN_OBJ_1(linalg_det_obj, linalg_det); + +#endif + +#if ULAB_MAX_DIMS > 1 +//| def eig(m: ulab.numpy.ndarray) -> Tuple[ulab.numpy.ndarray, ulab.numpy.ndarray]: +//| """ +//| :param m: a square matrix +//| :return tuple (eigenvectors, eigenvalues): +//| +//| Computes the eigenvalues and eigenvectors of a square matrix""" +//| ... +//| + +static mp_obj_t linalg_eig(mp_obj_t oin) { + ndarray_obj_t *in = tools_object_is_square(oin); + uint8_t *iarray = (uint8_t *)in->array; + size_t S = in->shape[ULAB_MAX_DIMS - 1]; + mp_float_t *array = m_new(mp_float_t, S*S); + for(size_t i=0; i < S; i++) { // rows + for(size_t j=0; j < S; j++) { // columns + *array++ = ndarray_get_float_value(iarray, in->dtype); + iarray += in->strides[ULAB_MAX_DIMS - 1]; + } + iarray -= in->strides[ULAB_MAX_DIMS - 1] * S; + iarray += in->strides[ULAB_MAX_DIMS - 2]; + } + array -= S * S; + // make sure the matrix is symmetric + for(size_t m=0; m < S; m++) { + for(size_t n=m+1; n < S; n++) { + // compare entry (m, n) to (n, m) + // TODO: this must probably be scaled! + if(LINALG_EPSILON < MICROPY_FLOAT_C_FUN(fabs)(array[m * S + n] - array[n * S + m])) { + mp_raise_ValueError(translate("input matrix is asymmetric")); + } + } + } + + // if we got this far, then the matrix will be symmetric + + ndarray_obj_t *eigenvectors = ndarray_new_dense_ndarray(2, ndarray_shape_vector(0, 0, S, S), NDARRAY_FLOAT); + mp_float_t *eigvectors = (mp_float_t *)eigenvectors->array; + + size_t iterations = linalg_jacobi_rotations(array, eigvectors, S); + + if(iterations == 0) { + // the computation did not converge; numpy raises LinAlgError + m_del(mp_float_t, array, in->len); + mp_raise_ValueError(translate("iterations did not converge")); + } + ndarray_obj_t *eigenvalues = ndarray_new_linear_array(S, NDARRAY_FLOAT); + mp_float_t *eigvalues = (mp_float_t *)eigenvalues->array; + for(size_t i=0; i < S; i++) { + eigvalues[i] = array[i * (S + 1)]; + } + m_del(mp_float_t, array, in->len); + + mp_obj_tuple_t *tuple = MP_OBJ_TO_PTR(mp_obj_new_tuple(2, NULL)); + tuple->items[0] = MP_OBJ_FROM_PTR(eigenvalues); + tuple->items[1] = MP_OBJ_FROM_PTR(eigenvectors); + return tuple; +} + +MP_DEFINE_CONST_FUN_OBJ_1(linalg_eig_obj, linalg_eig); + +//| def inv(m: ulab.numpy.ndarray) -> ulab.numpy.ndarray: +//| """ +//| :param ~ulab.numpy.ndarray m: a square matrix +//| :return: The inverse of the matrix, if it exists +//| :raises ValueError: if the matrix is not invertible +//| +//| Computes the inverse of a square matrix""" +//| ... +//| +static mp_obj_t linalg_inv(mp_obj_t o_in) { + ndarray_obj_t *ndarray = tools_object_is_square(o_in); + uint8_t *array = (uint8_t *)ndarray->array; + size_t N = ndarray->shape[ULAB_MAX_DIMS - 1]; + ndarray_obj_t *inverted = ndarray_new_dense_ndarray(2, ndarray_shape_vector(0, 0, N, N), NDARRAY_FLOAT); + mp_float_t *iarray = (mp_float_t *)inverted->array; + + mp_float_t (*func)(void *) = ndarray_get_float_function(ndarray->dtype); + + for(size_t i=0; i < N; i++) { // rows + for(size_t j=0; j < N; j++) { // columns + *iarray++ = func(array); + array += ndarray->strides[ULAB_MAX_DIMS - 1]; + } + array -= ndarray->strides[ULAB_MAX_DIMS - 1] * N; + array += ndarray->strides[ULAB_MAX_DIMS - 2]; + } + // re-wind the pointer + iarray -= N*N; + + if(!linalg_invert_matrix(iarray, N)) { + mp_raise_ValueError(translate("input matrix is singular")); + } + return MP_OBJ_FROM_PTR(inverted); +} + +MP_DEFINE_CONST_FUN_OBJ_1(linalg_inv_obj, linalg_inv); +#endif + +//| def norm(x: ulab.numpy.ndarray) -> float: +//| """ +//| :param ~ulab.numpy.ndarray x: a vector or a matrix +//| +//| Computes the 2-norm of a vector or a matrix, i.e., ``sqrt(sum(x*x))``, however, without the RAM overhead.""" +//| ... +//| + +static mp_obj_t linalg_norm(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = mp_const_none} } , + { MP_QSTR_axis, MP_ARG_OBJ, { .u_rom_obj = mp_const_none } }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + mp_obj_t x = args[0].u_obj; + mp_obj_t axis = args[1].u_obj; + + mp_float_t dot = 0.0, value; + size_t count = 1; + + if(mp_obj_is_type(x, &mp_type_tuple) || mp_obj_is_type(x, &mp_type_list) || mp_obj_is_type(x, &mp_type_range)) { + mp_obj_iter_buf_t iter_buf; + mp_obj_t item, iterable = mp_getiter(x, &iter_buf); + while((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) { + value = mp_obj_get_float(item); + // we could simply take the sum of value ** 2, + // but this method is numerically stable + dot = dot + (value * value - dot) / count++; + } + return mp_obj_new_float(MICROPY_FLOAT_C_FUN(sqrt)(dot * (count - 1))); + } else if(mp_obj_is_type(x, &ulab_ndarray_type)) { + ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(x); + uint8_t *array = (uint8_t *)ndarray->array; + // always get a float, so that we don't have to resolve the dtype later + mp_float_t (*func)(void *) = ndarray_get_float_function(ndarray->dtype); + shape_strides _shape_strides = tools_reduce_axes(ndarray, axis); + ndarray_obj_t *results = ndarray_new_dense_ndarray(_shape_strides.ndim, _shape_strides.shape, NDARRAY_FLOAT); + mp_float_t *rarray = (mp_float_t *)results->array; + + #if ULAB_MAX_DIMS > 3 + size_t i = 0; + do { + #endif + #if ULAB_MAX_DIMS > 2 + size_t j = 0; + do { + #endif + #if ULAB_MAX_DIMS > 1 + size_t k = 0; + do { + #endif + size_t l = 0; + if(axis != mp_const_none) { + count = 1; + dot = 0.0; + } + do { + value = func(array); + dot = dot + (value * value - dot) / count++; + array += _shape_strides.strides[0]; + l++; + } while(l < _shape_strides.shape[0]); + *rarray = MICROPY_FLOAT_C_FUN(sqrt)(dot * (count - 1)); + #if ULAB_MAX_DIMS > 1 + rarray += _shape_strides.increment; + array -= _shape_strides.strides[0] * _shape_strides.shape[0]; + array += _shape_strides.strides[ULAB_MAX_DIMS - 1]; + k++; + } while(k < _shape_strides.shape[ULAB_MAX_DIMS - 1]); + #endif + #if ULAB_MAX_DIMS > 2 + array -= _shape_strides.strides[ULAB_MAX_DIMS - 1] * _shape_strides.shape[ULAB_MAX_DIMS - 1]; + array += _shape_strides.strides[ULAB_MAX_DIMS - 2]; + j++; + } while(j < _shape_strides.shape[ULAB_MAX_DIMS - 2]); + #endif + #if ULAB_MAX_DIMS > 3 + array -= _shape_strides.strides[ULAB_MAX_DIMS - 2] * _shape_strides.shape[ULAB_MAX_DIMS - 2]; + array += _shape_strides.strides[ULAB_MAX_DIMS - 3]; + i++; + } while(i < _shape_strides.shape[ULAB_MAX_DIMS - 3]); + #endif + if(results->ndim == 0) { + return mp_obj_new_float(*rarray); + } + return results; + } + return mp_const_none; // we should never reach this point +} + +MP_DEFINE_CONST_FUN_OBJ_KW(linalg_norm_obj, 1, linalg_norm); +// MP_DEFINE_CONST_FUN_OBJ_1(linalg_norm_obj, linalg_norm); + +STATIC const mp_rom_map_elem_t ulab_linalg_globals_table[] = { + { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_linalg) }, + #if ULAB_MAX_DIMS > 1 + #if ULAB_LINALG_HAS_CHOLESKY + { MP_ROM_QSTR(MP_QSTR_cholesky), (mp_obj_t)&linalg_cholesky_obj }, + #endif + #if ULAB_LINALG_HAS_DET + { MP_ROM_QSTR(MP_QSTR_det), (mp_obj_t)&linalg_det_obj }, + #endif + #if ULAB_LINALG_HAS_EIG + { MP_ROM_QSTR(MP_QSTR_eig), (mp_obj_t)&linalg_eig_obj }, + #endif + #if ULAB_LINALG_HAS_INV + { MP_ROM_QSTR(MP_QSTR_inv), (mp_obj_t)&linalg_inv_obj }, + #endif + #endif + #if ULAB_LINALG_HAS_NORM + { MP_ROM_QSTR(MP_QSTR_norm), (mp_obj_t)&linalg_norm_obj }, + #endif +}; + +STATIC MP_DEFINE_CONST_DICT(mp_module_ulab_linalg_globals, ulab_linalg_globals_table); + +mp_obj_module_t ulab_linalg_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t*)&mp_module_ulab_linalg_globals, +}; + +#endif diff --git a/python/port/mod/ulab/numpy/linalg/linalg.h b/python/port/mod/ulab/numpy/linalg/linalg.h new file mode 100644 index 000000000..fc1867fb8 --- /dev/null +++ b/python/port/mod/ulab/numpy/linalg/linalg.h @@ -0,0 +1,26 @@ + +/* + * This file is part of the micropython-ulab project, + * + * https://github.com/v923z/micropython-ulab + * + * The MIT License (MIT) + * + * Copyright (c) 2019-2021 Zoltán Vörös +*/ + +#ifndef _LINALG_ +#define _LINALG_ + +#include "../../ulab.h" +#include "../../ndarray.h" +#include "linalg_tools.h" + +extern mp_obj_module_t ulab_linalg_module; + +MP_DECLARE_CONST_FUN_OBJ_1(linalg_cholesky_obj); +MP_DECLARE_CONST_FUN_OBJ_1(linalg_det_obj); +MP_DECLARE_CONST_FUN_OBJ_1(linalg_eig_obj); +MP_DECLARE_CONST_FUN_OBJ_1(linalg_inv_obj); +MP_DECLARE_CONST_FUN_OBJ_KW(linalg_norm_obj); +#endif diff --git a/python/port/mod/ulab/numpy/linalg/linalg_tools.c b/python/port/mod/ulab/numpy/linalg/linalg_tools.c new file mode 100644 index 000000000..cf48f1140 --- /dev/null +++ b/python/port/mod/ulab/numpy/linalg/linalg_tools.c @@ -0,0 +1,171 @@ +/* + * This file is part of the micropython-ulab project, + * + * https://github.com/v923z/micropython-ulab + * + * The MIT License (MIT) + * + * Copyright (c) 2019-2010 Zoltán Vörös +*/ + +#include +#include +#include + +#include "linalg_tools.h" + +/* + * The following function inverts a matrix, whose entries are given in the input array + * The function has no dependencies beyond micropython itself (for the definition of mp_float_t), + * and can be used independent of ulab. + */ + +bool linalg_invert_matrix(mp_float_t *data, size_t N) { + // returns true, of the inversion was successful, + // false, if the matrix is singular + + // initially, this is the unit matrix: the contents of this matrix is what + // will be returned after all the transformations + mp_float_t *unit = m_new(mp_float_t, N*N); + mp_float_t elem = 1.0; + // initialise the unit matrix + memset(unit, 0, sizeof(mp_float_t)*N*N); + for(size_t m=0; m < N; m++) { + memcpy(&unit[m * (N+1)], &elem, sizeof(mp_float_t)); + } + for(size_t m=0; m < N; m++){ + // this could be faster with ((c < epsilon) && (c > -epsilon)) + if(MICROPY_FLOAT_C_FUN(fabs)(data[m * (N+1)]) < LINALG_EPSILON) { + //look for a line to swap + size_t m1 = m + 1; + for(; m1 < N; m1++) { + if(!(MICROPY_FLOAT_C_FUN(fabs)(data[m1*N + m]) < LINALG_EPSILON)) { + for(size_t m2=0; m2 < N; m2++) { + mp_float_t swapVal = data[m*N+m2]; + data[m*N+m2] = data[m1*N+m2]; + data[m1*N+m2] = swapVal; + swapVal = unit[m*N+m2]; + unit[m*N+m2] = unit[m1*N+m2]; + unit[m1*N+m2] = swapVal; + } + break; + } + } + if (m1 >= N) { + m_del(mp_float_t, unit, N*N); + return false; + } + } + for(size_t n=0; n < N; n++) { + if(m != n){ + elem = data[N * n + m] / data[m * (N+1)]; + for(size_t k=0; k < N; k++) { + data[N * n + k] -= elem * data[N * m + k]; + unit[N * n + k] -= elem * unit[N * m + k]; + } + } + } + } + for(size_t m=0; m < N; m++) { + elem = data[m * (N+1)]; + for(size_t n=0; n < N; n++) { + data[N * m + n] /= elem; + unit[N * m + n] /= elem; + } + } + memcpy(data, unit, sizeof(mp_float_t)*N*N); + m_del(mp_float_t, unit, N * N); + return true; +} + +/* + * The following function calculates the eigenvalues and eigenvectors of a symmetric + * real matrix, whose entries are given in the input array. + * The function has no dependencies beyond micropython itself (for the definition of mp_float_t), + * and can be used independent of ulab. + */ + +size_t linalg_jacobi_rotations(mp_float_t *array, mp_float_t *eigvectors, size_t S) { + // eigvectors should be a 0-array; start out with the unit matrix + for(size_t m=0; m < S; m++) { + eigvectors[m * (S+1)] = 1.0; + } + mp_float_t largest, w, t, c, s, tau, aMk, aNk, vm, vn; + size_t M, N; + size_t iterations = JACOBI_MAX * S * S; + do { + iterations--; + // find the pivot here + M = 0; + N = 0; + largest = 0.0; + for(size_t m=0; m < S-1; m++) { // -1: no need to inspect last row + for(size_t n=m+1; n < S; n++) { + w = MICROPY_FLOAT_C_FUN(fabs)(array[m * S + n]); + if((largest < w) && (LINALG_EPSILON < w)) { + M = m; + N = n; + largest = w; + } + } + } + if(M + N == 0) { // all entries are smaller than epsilon, there is not much we can do... + break; + } + // at this point, we have the pivot, and it is the entry (M, N) + // now we have to find the rotation angle + w = (array[N * S + N] - array[M * S + M]) / (MICROPY_FLOAT_CONST(2.0)*array[M * S + N]); + // The following if/else chooses the smaller absolute value for the tangent + // of the rotation angle. Going with the smaller should be numerically stabler. + if(w > 0) { + t = MICROPY_FLOAT_C_FUN(sqrt)(w*w + MICROPY_FLOAT_CONST(1.0)) - w; + } else { + t = MICROPY_FLOAT_CONST(-1.0)*(MICROPY_FLOAT_C_FUN(sqrt)(w*w + MICROPY_FLOAT_CONST(1.0)) + w); + } + s = t / MICROPY_FLOAT_C_FUN(sqrt)(t*t + MICROPY_FLOAT_CONST(1.0)); // the sine of the rotation angle + c = MICROPY_FLOAT_CONST(1.0) / MICROPY_FLOAT_C_FUN(sqrt)(t*t + MICROPY_FLOAT_CONST(1.0)); // the cosine of the rotation angle + tau = (MICROPY_FLOAT_CONST(1.0)-c)/s; // this is equal to the tangent of the half of the rotation angle + + // at this point, we have the rotation angles, so we can transform the matrix + // first the two diagonal elements + // a(M, M) = a(M, M) - t*a(M, N) + array[M * S + M] = array[M * S + M] - t * array[M * S + N]; + // a(N, N) = a(N, N) + t*a(M, N) + array[N * S + N] = array[N * S + N] + t * array[M * S + N]; + // after the rotation, the a(M, N), and a(N, M) entries should become zero + array[M * S + N] = array[N * S + M] = MICROPY_FLOAT_CONST(0.0); + // then all other elements in the column + for(size_t k=0; k < S; k++) { + if((k == M) || (k == N)) { + continue; + } + aMk = array[M * S + k]; + aNk = array[N * S + k]; + // a(M, k) = a(M, k) - s*(a(N, k) + tau*a(M, k)) + array[M * S + k] -= s * (aNk + tau * aMk); + // a(N, k) = a(N, k) + s*(a(M, k) - tau*a(N, k)) + array[N * S + k] += s * (aMk - tau * aNk); + // a(k, M) = a(M, k) + array[k * S + M] = array[M * S + k]; + // a(k, N) = a(N, k) + array[k * S + N] = array[N * S + k]; + } + // now we have to update the eigenvectors + // the rotation matrix, R, multiplies from the right + // R is the unit matrix, except for the + // R(M,M) = R(N, N) = c + // R(N, M) = s + // (M, N) = -s + // entries. This means that only the Mth, and Nth columns will change + for(size_t m=0; m < S; m++) { + vm = eigvectors[m * S + M]; + vn = eigvectors[m * S + N]; + // the new value of eigvectors(m, M) + eigvectors[m * S + M] = c * vm - s * vn; + // the new value of eigvectors(m, N) + eigvectors[m * S + N] = s * vm + c * vn; + } + } while(iterations > 0); + + return iterations; +} diff --git a/python/port/mod/ulab/numpy/linalg/linalg_tools.h b/python/port/mod/ulab/numpy/linalg/linalg_tools.h new file mode 100644 index 000000000..942da001c --- /dev/null +++ b/python/port/mod/ulab/numpy/linalg/linalg_tools.h @@ -0,0 +1,28 @@ +/* + * This file is part of the micropython-ulab project, + * + * https://github.com/v923z/micropython-ulab + * + * The MIT License (MIT) + * + * Copyright (c) 2019-2021 Zoltán Vörös +*/ + +#ifndef _TOOLS_TOOLS_ +#define _TOOLS_TOOLS_ + +#ifndef LINALG_EPSILON +#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT +#define LINALG_EPSILON MICROPY_FLOAT_CONST(1.2e-7) +#elif MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE +#define LINALG_EPSILON MICROPY_FLOAT_CONST(2.3e-16) +#endif +#endif /* LINALG_EPSILON */ + +#define JACOBI_MAX 20 + +bool linalg_invert_matrix(mp_float_t *, size_t ); +size_t linalg_jacobi_rotations(mp_float_t *, mp_float_t *, size_t ); + +#endif /* _TOOLS_TOOLS_ */ + diff --git a/python/port/mod/ulab/numpy/numerical/numerical.c b/python/port/mod/ulab/numpy/numerical/numerical.c new file mode 100644 index 000000000..af9ff1c02 --- /dev/null +++ b/python/port/mod/ulab/numpy/numerical/numerical.c @@ -0,0 +1,1338 @@ + +/* + * This file is part of the micropython-ulab project, + * + * https://github.com/v923z/micropython-ulab + * + * The MIT License (MIT) + * + * Copyright (c) 2019-2021 Zoltán Vörös + * 2020 Scott Shawcroft for Adafruit Industries + * 2020 Taku Fukada +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../../ulab.h" +#include "../../ulab_tools.h" +#include "numerical.h" + +enum NUMERICAL_FUNCTION_TYPE { + NUMERICAL_ALL, + NUMERICAL_ANY, + NUMERICAL_ARGMAX, + NUMERICAL_ARGMIN, + NUMERICAL_MAX, + NUMERICAL_MEAN, + NUMERICAL_MIN, + NUMERICAL_STD, + NUMERICAL_SUM, +}; + +//| """Numerical and Statistical functions +//| +//| Most of these functions take an "axis" argument, which indicates whether to +//| operate over the flattened array (None), or a particular axis (integer).""" +//| +//| from ulab import _ArrayLike +//| + +static void numerical_reduce_axes(ndarray_obj_t *ndarray, int8_t axis, size_t *shape, int32_t *strides) { + // removes the values corresponding to a single axis from the shape and strides array + uint8_t index = ULAB_MAX_DIMS - ndarray->ndim + axis; + if((ndarray->ndim == 1) && (axis == 0)) { + index = 0; + shape[ULAB_MAX_DIMS - 1] = 1; + return; + } + for(uint8_t i = ULAB_MAX_DIMS - 1; i > 0; i--) { + if(i > index) { + shape[i] = ndarray->shape[i]; + strides[i] = ndarray->strides[i]; + } else { + shape[i] = ndarray->shape[i-1]; + strides[i] = ndarray->strides[i-1]; + } + } +} + +#if ULAB_NUMPY_HAS_ALL | ULAB_NUMPY_HAS_ANY +static mp_obj_t numerical_all_any(mp_obj_t oin, mp_obj_t axis, uint8_t optype) { + bool anytype = optype == NUMERICAL_ALL ? 1 : 0; + if(mp_obj_is_type(oin, &ulab_ndarray_type)) { + ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(oin); + uint8_t *array = (uint8_t *)ndarray->array; + if(ndarray->len == 0) { // return immediately with empty arrays + if(optype == NUMERICAL_ALL) { + return mp_const_true; + } else { + return mp_const_false; + } + } + // always get a float, so that we don't have to resolve the dtype later + mp_float_t (*func)(void *) = ndarray_get_float_function(ndarray->dtype); + ndarray_obj_t *results = NULL; + uint8_t *rarray = NULL; + shape_strides _shape_strides = tools_reduce_axes(ndarray, axis); + if(axis != mp_const_none) { + results = ndarray_new_dense_ndarray(_shape_strides.ndim, _shape_strides.shape, NDARRAY_BOOL); + rarray = results->array; + if(optype == NUMERICAL_ALL) { + memset(rarray, 1, results->len); + } + } + + #if ULAB_MAX_DIMS > 3 + size_t i = 0; + do { + #endif + #if ULAB_MAX_DIMS > 2 + size_t j = 0; + do { + #endif + #if ULAB_MAX_DIMS > 1 + size_t k = 0; + do { + #endif + size_t l = 0; + if(axis == mp_const_none) { + do { + mp_float_t value = func(array); + if((value != MICROPY_FLOAT_CONST(0.0)) & !anytype) { + // optype = NUMERICAL_ANY + return mp_const_true; + } else if((value == MICROPY_FLOAT_CONST(0.0)) & anytype) { + // optype == NUMERICAL_ALL + return mp_const_false; + } + array += _shape_strides.strides[0]; + l++; + } while(l < _shape_strides.shape[0]); + } else { // a scalar axis keyword was supplied + do { + mp_float_t value = func(array); + if((value != MICROPY_FLOAT_CONST(0.0)) & !anytype) { + // optype == NUMERICAL_ANY + *rarray = 1; + // since we are breaking out of the loop, move the pointer forward + array += _shape_strides.strides[0] * (_shape_strides.shape[0] - l); + break; + } else if((value == MICROPY_FLOAT_CONST(0.0)) & anytype) { + // optype == NUMERICAL_ALL + *rarray = 0; + // since we are breaking out of the loop, move the pointer forward + array += _shape_strides.strides[0] * (_shape_strides.shape[0] - l); + break; + } + array += _shape_strides.strides[0]; + l++; + } while(l < _shape_strides.shape[0]); + } + #if ULAB_MAX_DIMS > 1 + rarray += _shape_strides.increment; + array -= _shape_strides.strides[0] * _shape_strides.shape[0]; + array += _shape_strides.strides[ULAB_MAX_DIMS - 1]; + k++; + } while(k < _shape_strides.shape[ULAB_MAX_DIMS - 1]); + #endif + #if ULAB_MAX_DIMS > 2 + array -= _shape_strides.strides[ULAB_MAX_DIMS - 1] * _shape_strides.shape[ULAB_MAX_DIMS - 1]; + array += _shape_strides.strides[ULAB_MAX_DIMS - 2]; + j++; + } while(j < _shape_strides.shape[ULAB_MAX_DIMS - 2]); + #endif + #if ULAB_MAX_DIMS > 3 + array -= _shape_strides.strides[ULAB_MAX_DIMS - 2] * _shape_strides.shape[ULAB_MAX_DIMS - 2]; + array += _shape_strides.strides[ULAB_MAX_DIMS - 3]; + i++; + } while(i < _shape_strides.shape[ULAB_MAX_DIMS - 3]) + #endif + return results; + } else if(mp_obj_is_int(oin) || mp_obj_is_float(oin)) { + return mp_obj_is_true(oin) ? mp_const_true : mp_const_false; + } else { + mp_obj_iter_buf_t iter_buf; + mp_obj_t item, iterable = mp_getiter(oin, &iter_buf); + while((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) { + if(!mp_obj_is_true(item) & !anytype) { + return mp_const_false; + } else if(mp_obj_is_true(item) & anytype) { + return mp_const_true; + } + } + } + return anytype ? mp_const_true : mp_const_false; +} +#endif + +#if ULAB_NUMPY_HAS_SUM | ULAB_NUMPY_HAS_MEAN | ULAB_NUMPY_HAS_STD +static mp_obj_t numerical_sum_mean_std_iterable(mp_obj_t oin, uint8_t optype, size_t ddof) { + mp_float_t value = MICROPY_FLOAT_CONST(0.0); + mp_float_t M = MICROPY_FLOAT_CONST(0.0); + mp_float_t m = MICROPY_FLOAT_CONST(0.0); + mp_float_t S = MICROPY_FLOAT_CONST(0.0); + mp_float_t s = MICROPY_FLOAT_CONST(0.0); + size_t count = 0; + mp_obj_iter_buf_t iter_buf; + mp_obj_t item, iterable = mp_getiter(oin, &iter_buf); + while((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) { + value = mp_obj_get_float(item); + m = M + (value - M) / (count + 1); + s = S + (value - M) * (value - m); + M = m; + S = s; + count++; + } + if(optype == NUMERICAL_SUM) { + return mp_obj_new_float(m * count); + } else if(optype == NUMERICAL_MEAN) { + return count > 0 ? mp_obj_new_float(m) : mp_obj_new_float(MICROPY_FLOAT_CONST(0.0)); + } else { // this should be the case of the standard deviation + return count > ddof ? mp_obj_new_float(MICROPY_FLOAT_C_FUN(sqrt)(s / (count - ddof))) : mp_obj_new_float(MICROPY_FLOAT_CONST(0.0)); + } +} + +static mp_obj_t numerical_sum_mean_std_ndarray(ndarray_obj_t *ndarray, mp_obj_t axis, uint8_t optype, size_t ddof) { + uint8_t *array = (uint8_t *)ndarray->array; + shape_strides _shape_strides = tools_reduce_axes(ndarray, axis); + + if(axis == mp_const_none) { + // work with the flattened array + if((optype == NUMERICAL_STD) && (ddof > ndarray->len)) { + // if there are too many degrees of freedom, there is no point in calculating anything + return mp_obj_new_float(MICROPY_FLOAT_CONST(0.0)); + } + mp_float_t (*func)(void *) = ndarray_get_float_function(ndarray->dtype); + mp_float_t M =MICROPY_FLOAT_CONST(0.0); + mp_float_t m = MICROPY_FLOAT_CONST(0.0); + mp_float_t S = MICROPY_FLOAT_CONST(0.0); + mp_float_t s = MICROPY_FLOAT_CONST(0.0); + size_t count = 0; + + #if ULAB_MAX_DIMS > 3 + size_t i = 0; + do { + #endif + #if ULAB_MAX_DIMS > 2 + size_t j = 0; + do { + #endif + #if ULAB_MAX_DIMS > 1 + size_t k = 0; + do { + #endif + size_t l = 0; + do { + count++; + mp_float_t value = func(array); + m = M + (value - M) / (mp_float_t)count; + if(optype == NUMERICAL_STD) { + s = S + (value - M) * (value - m); + S = s; + } + M = m; + array += _shape_strides.strides[ULAB_MAX_DIMS - 1]; + l++; + } while(l < _shape_strides.shape[ULAB_MAX_DIMS - 1]); + #if ULAB_MAX_DIMS > 1 + array -= _shape_strides.strides[ULAB_MAX_DIMS - 1] * _shape_strides.shape[ULAB_MAX_DIMS - 1]; + array += _shape_strides.strides[ULAB_MAX_DIMS - 2]; + k++; + } while(k < _shape_strides.shape[ULAB_MAX_DIMS - 2]); + #endif + #if ULAB_MAX_DIMS > 2 + array -= _shape_strides.strides[ULAB_MAX_DIMS - 2] * _shape_strides.shape[ULAB_MAX_DIMS - 2]; + array += _shape_strides.strides[ULAB_MAX_DIMS - 3]; + j++; + } while(j < _shape_strides.shape[ULAB_MAX_DIMS - 3]); + #endif + #if ULAB_MAX_DIMS > 3 + array -= _shape_strides.strides[ULAB_MAX_DIMS - 3] * _shape_strides.shape[ULAB_MAX_DIMS - 3]; + array += _shape_strides.strides[ULAB_MAX_DIMS - 4]; + i++; + } while(i < _shape_strides.shape[ULAB_MAX_DIMS - 4]); + #endif + if(optype == NUMERICAL_SUM) { + // numpy returns an integer for integer input types + if(ndarray->dtype == NDARRAY_FLOAT) { + return mp_obj_new_float(M * ndarray->len); + } else { + return mp_obj_new_int((int32_t)(M * ndarray->len)); + } + } else if(optype == NUMERICAL_MEAN) { + return mp_obj_new_float(M); + } else { // this must be the case of the standard deviation + // we have already made certain that ddof < ndarray->len holds + return mp_obj_new_float(MICROPY_FLOAT_C_FUN(sqrt)(S / (ndarray->len - ddof))); + } + } else { + ndarray_obj_t *results = NULL; + uint8_t *rarray = NULL; + mp_float_t *farray = NULL; + if(optype == NUMERICAL_SUM) { + results = ndarray_new_dense_ndarray(_shape_strides.ndim, _shape_strides.shape, ndarray->dtype); + rarray = (uint8_t *)results->array; + // TODO: numpy promotes the output to the highest integer type + if(ndarray->dtype == NDARRAY_UINT8) { + RUN_SUM(uint8_t, array, results, rarray, _shape_strides); + } else if(ndarray->dtype == NDARRAY_INT8) { + RUN_SUM(int8_t, array, results, rarray, _shape_strides); + } else if(ndarray->dtype == NDARRAY_UINT16) { + RUN_SUM(uint16_t, array, results, rarray, _shape_strides); + } else if(ndarray->dtype == NDARRAY_INT16) { + RUN_SUM(int16_t, array, results, rarray, _shape_strides); + } else { + // for floats, the sum might be inaccurate with the naive summation + // call mean, and multiply with the number of samples + farray = (mp_float_t *)results->array; + RUN_MEAN_STD(mp_float_t, array, farray, _shape_strides, MICROPY_FLOAT_CONST(0.0), 0); + mp_float_t norm = (mp_float_t)_shape_strides.shape[0]; + // re-wind the array here + farray = (mp_float_t *)results->array; + for(size_t i=0; i < results->len; i++) { + *farray++ *= norm; + } + } + } else { + bool isStd = optype == NUMERICAL_STD ? 1 : 0; + results = ndarray_new_dense_ndarray(_shape_strides.ndim, _shape_strides.shape, NDARRAY_FLOAT); + farray = (mp_float_t *)results->array; + // we can return the 0 array here, if the degrees of freedom is larger than the length of the axis + if((optype == NUMERICAL_STD) && (_shape_strides.shape[0] <= ddof)) { + return MP_OBJ_FROM_PTR(results); + } + mp_float_t div = optype == NUMERICAL_STD ? (mp_float_t)(_shape_strides.shape[0] - ddof) : MICROPY_FLOAT_CONST(0.0); + if(ndarray->dtype == NDARRAY_UINT8) { + RUN_MEAN_STD(uint8_t, array, farray, _shape_strides, div, isStd); + } else if(ndarray->dtype == NDARRAY_INT8) { + RUN_MEAN_STD(int8_t, array, farray, _shape_strides, div, isStd); + } else if(ndarray->dtype == NDARRAY_UINT16) { + RUN_MEAN_STD(uint16_t, array, farray, _shape_strides, div, isStd); + } else if(ndarray->dtype == NDARRAY_INT16) { + RUN_MEAN_STD(int16_t, array, farray, _shape_strides, div, isStd); + } else { + RUN_MEAN_STD(mp_float_t, array, farray, _shape_strides, div, isStd); + } + } + if(results->ndim == 0) { // return a scalar here + return mp_binary_get_val_array(results->dtype, results->array, 0); + } + return MP_OBJ_FROM_PTR(results); + } + return mp_const_none; +} +#endif + +#if ULAB_NUMPY_HAS_ARGMINMAX +static mp_obj_t numerical_argmin_argmax_iterable(mp_obj_t oin, uint8_t optype) { + if(MP_OBJ_SMALL_INT_VALUE(mp_obj_len_maybe(oin)) == 0) { + mp_raise_ValueError(translate("attempt to get argmin/argmax of an empty sequence")); + } + size_t idx = 0, best_idx = 0; + mp_obj_iter_buf_t iter_buf; + mp_obj_t iterable = mp_getiter(oin, &iter_buf); + mp_obj_t item; + uint8_t op = 0; // argmin, min + if((optype == NUMERICAL_ARGMAX) || (optype == NUMERICAL_MAX)) op = 1; + item = mp_iternext(iterable); + mp_obj_t best_obj = item; + mp_float_t value, best_value = mp_obj_get_float(item); + value = best_value; + while((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) { + idx++; + value = mp_obj_get_float(item); + if((op == 0) && (value < best_value)) { + best_obj = item; + best_idx = idx; + best_value = value; + } else if((op == 1) && (value > best_value)) { + best_obj = item; + best_idx = idx; + best_value = value; + } + } + if((optype == NUMERICAL_ARGMIN) || (optype == NUMERICAL_ARGMAX)) { + return MP_OBJ_NEW_SMALL_INT(best_idx); + } else { + return best_obj; + } +} + +static mp_obj_t numerical_argmin_argmax_ndarray(ndarray_obj_t *ndarray, mp_obj_t axis, uint8_t optype) { + // TODO: treat the flattened array + if(ndarray->len == 0) { + mp_raise_ValueError(translate("attempt to get (arg)min/(arg)max of empty sequence")); + } + + if(axis == mp_const_none) { + // work with the flattened array + mp_float_t (*func)(void *) = ndarray_get_float_function(ndarray->dtype); + uint8_t *array = (uint8_t *)ndarray->array; + mp_float_t best_value = func(array); + mp_float_t value; + size_t index = 0, best_index = 0; + + #if ULAB_MAX_DIMS > 3 + size_t i = 0; + do { + #endif + #if ULAB_MAX_DIMS > 2 + size_t j = 0; + do { + #endif + #if ULAB_MAX_DIMS > 1 + size_t k = 0; + do { + #endif + size_t l = 0; + do { + value = func(array); + if((optype == NUMERICAL_ARGMAX) || (optype == NUMERICAL_MAX)) { + if(best_value < value) { + best_value = value; + best_index = index; + } + } else { + if(best_value > value) { + best_value = value; + best_index = index; + } + } + array += ndarray->strides[ULAB_MAX_DIMS - 1]; + l++; + index++; + } while(l < ndarray->shape[ULAB_MAX_DIMS - 1]); + #if ULAB_MAX_DIMS > 1 + array -= ndarray->strides[ULAB_MAX_DIMS - 1] * ndarray->shape[ULAB_MAX_DIMS-1]; + array += ndarray->strides[ULAB_MAX_DIMS - 2]; + k++; + } while(k < ndarray->shape[ULAB_MAX_DIMS - 2]); + #endif + #if ULAB_MAX_DIMS > 2 + array -= ndarray->strides[ULAB_MAX_DIMS - 2] * ndarray->shape[ULAB_MAX_DIMS-2]; + array += ndarray->strides[ULAB_MAX_DIMS - 3]; + j++; + } while(j < ndarray->shape[ULAB_MAX_DIMS - 3]); + #endif + #if ULAB_MAX_DIMS > 3 + array -= ndarray->strides[ULAB_MAX_DIMS - 3] * ndarray->shape[ULAB_MAX_DIMS-3]; + array += ndarray->strides[ULAB_MAX_DIMS - 4]; + i++; + } while(i < ndarray->shape[ULAB_MAX_DIMS - 4]); + #endif + + if((optype == NUMERICAL_ARGMIN) || (optype == NUMERICAL_ARGMAX)) { + return mp_obj_new_int(best_index); + } else { + if(ndarray->dtype == NDARRAY_FLOAT) { + return mp_obj_new_float(best_value); + } else { + return MP_OBJ_NEW_SMALL_INT((int32_t)best_value); + } + } + } else { + int8_t ax = mp_obj_get_int(axis); + if(ax < 0) ax += ndarray->ndim; + if((ax < 0) || (ax > ndarray->ndim - 1)) { + mp_raise_ValueError(translate("axis is out of bounds")); + } + + uint8_t *array = (uint8_t *)ndarray->array; + size_t *shape = m_new(size_t, ULAB_MAX_DIMS); + memset(shape, 0, sizeof(size_t)*ULAB_MAX_DIMS); + int32_t *strides = m_new(int32_t, ULAB_MAX_DIMS); + memset(strides, 0, sizeof(uint32_t)*ULAB_MAX_DIMS); + numerical_reduce_axes(ndarray, ax, shape, strides); + uint8_t index = ULAB_MAX_DIMS - ndarray->ndim + ax; + + ndarray_obj_t *results = NULL; + + if((optype == NUMERICAL_ARGMIN) || (optype == NUMERICAL_ARGMAX)) { + results = ndarray_new_dense_ndarray(MAX(1, ndarray->ndim-1), shape, NDARRAY_INT16); + } else { + results = ndarray_new_dense_ndarray(MAX(1, ndarray->ndim-1), shape, ndarray->dtype); + } + + uint8_t *rarray = (uint8_t *)results->array; + + if(ndarray->dtype == NDARRAY_UINT8) { + RUN_ARGMIN(ndarray, uint8_t, array, results, rarray, shape, strides, index, optype); + } else if(ndarray->dtype == NDARRAY_INT8) { + RUN_ARGMIN(ndarray, int8_t, array, results, rarray, shape, strides, index, optype); + } else if(ndarray->dtype == NDARRAY_UINT16) { + RUN_ARGMIN(ndarray, uint16_t, array, results, rarray, shape, strides, index, optype); + } else if(ndarray->dtype == NDARRAY_INT16) { + RUN_ARGMIN(ndarray, int16_t, array, results, rarray, shape, strides, index, optype); + } else { + RUN_ARGMIN(ndarray, mp_float_t, array, results, rarray, shape, strides, index, optype); + } + if(results->len == 1) { + return mp_binary_get_val_array(results->dtype, results->array, 0); + } + return MP_OBJ_FROM_PTR(results); + } + return mp_const_none; +} +#endif + +static mp_obj_t numerical_function(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args, uint8_t optype) { + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = mp_const_none} } , + { MP_QSTR_axis, MP_ARG_OBJ, { .u_rom_obj = mp_const_none } }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + mp_obj_t oin = args[0].u_obj; + mp_obj_t axis = args[1].u_obj; + if((axis != mp_const_none) && (!mp_obj_is_int(axis))) { + mp_raise_TypeError(translate("axis must be None, or an integer")); + } + + if((optype == NUMERICAL_ALL) || (optype == NUMERICAL_ANY)) { + return numerical_all_any(oin, axis, optype); + } + if(mp_obj_is_type(oin, &mp_type_tuple) || mp_obj_is_type(oin, &mp_type_list) || + mp_obj_is_type(oin, &mp_type_range)) { + switch(optype) { + case NUMERICAL_MIN: + case NUMERICAL_ARGMIN: + case NUMERICAL_MAX: + case NUMERICAL_ARGMAX: + return numerical_argmin_argmax_iterable(oin, optype); + case NUMERICAL_SUM: + case NUMERICAL_MEAN: + return numerical_sum_mean_std_iterable(oin, optype, 0); + default: // we should never reach this point, but whatever + return mp_const_none; + } + } else if(mp_obj_is_type(oin, &ulab_ndarray_type)) { + ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(oin); + switch(optype) { + case NUMERICAL_MIN: + case NUMERICAL_MAX: + case NUMERICAL_ARGMIN: + case NUMERICAL_ARGMAX: + return numerical_argmin_argmax_ndarray(ndarray, axis, optype); + case NUMERICAL_SUM: + case NUMERICAL_MEAN: + return numerical_sum_mean_std_ndarray(ndarray, axis, optype, 0); + default: + mp_raise_NotImplementedError(translate("operation is not implemented on ndarrays")); + } + } else { + mp_raise_TypeError(translate("input must be tuple, list, range, or ndarray")); + } + return mp_const_none; +} + +#if ULAB_NUMPY_HAS_SORT | NDARRAY_HAS_SORT +static mp_obj_t numerical_sort_helper(mp_obj_t oin, mp_obj_t axis, uint8_t inplace) { + if(!mp_obj_is_type(oin, &ulab_ndarray_type)) { + mp_raise_TypeError(translate("sort argument must be an ndarray")); + } + + ndarray_obj_t *ndarray; + if(inplace == 1) { + ndarray = MP_OBJ_TO_PTR(oin); + } else { + ndarray = ndarray_copy_view(MP_OBJ_TO_PTR(oin)); + } + + int8_t ax = 0; + if(axis == mp_const_none) { + // flatten the array + for(uint8_t i=0; i < ULAB_MAX_DIMS - 1; i++) { + ndarray->shape[i] = 0; + ndarray->strides[i] = 0; + } + ndarray->shape[ULAB_MAX_DIMS - 1] = ndarray->len; + ndarray->strides[ULAB_MAX_DIMS - 1] = ndarray->itemsize; + ndarray->ndim = 1; + } else { + ax = mp_obj_get_int(axis); + if(ax < 0) ax += ndarray->ndim; + if((ax < 0) || (ax > ndarray->ndim - 1)) { + mp_raise_ValueError(translate("index out of range")); + } + } + + size_t *shape = m_new(size_t, ULAB_MAX_DIMS); + memset(shape, 0, sizeof(size_t)*ULAB_MAX_DIMS); + int32_t *strides = m_new(int32_t, ULAB_MAX_DIMS); + memset(strides, 0, sizeof(uint32_t)*ULAB_MAX_DIMS); + numerical_reduce_axes(ndarray, ax, shape, strides); + ax = ULAB_MAX_DIMS - ndarray->ndim + ax; + // we work with the typed array, so re-scale the stride + int32_t increment = ndarray->strides[ax] / ndarray->itemsize; + + uint8_t *array = (uint8_t *)ndarray->array; + if((ndarray->dtype == NDARRAY_UINT8) || (ndarray->dtype == NDARRAY_INT8)) { + HEAPSORT(ndarray, uint8_t, array, shape, strides, ax, increment, ndarray->shape[ax]); + } else if((ndarray->dtype == NDARRAY_INT16) || (ndarray->dtype == NDARRAY_INT16)) { + HEAPSORT(ndarray, uint16_t, array, shape, strides, ax, increment, ndarray->shape[ax]); + } else { + HEAPSORT(ndarray, mp_float_t, array, shape, strides, ax, increment, ndarray->shape[ax]); + } + if(inplace == 1) { + return mp_const_none; + } else { + return MP_OBJ_FROM_PTR(ndarray); + } +} +#endif /* ULAB_NUMERICAL_HAS_SORT | NDARRAY_HAS_SORT */ + +#if ULAB_NUMPY_HAS_ALL +mp_obj_t numerical_all(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + return numerical_function(n_args, pos_args, kw_args, NUMERICAL_ALL); +} +MP_DEFINE_CONST_FUN_OBJ_KW(numerical_all_obj, 1, numerical_all); +#endif + +#if ULAB_NUMPY_HAS_ANY +mp_obj_t numerical_any(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + return numerical_function(n_args, pos_args, kw_args, NUMERICAL_ANY); +} +MP_DEFINE_CONST_FUN_OBJ_KW(numerical_any_obj, 1, numerical_any); +#endif + +#if ULAB_NUMPY_HAS_ARGMINMAX +//| def argmax(array: _ArrayLike, *, axis: Optional[int] = None) -> int: +//| """Return the index of the maximum element of the 1D array""" +//| ... +//| + +mp_obj_t numerical_argmax(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + return numerical_function(n_args, pos_args, kw_args, NUMERICAL_ARGMAX); +} + +MP_DEFINE_CONST_FUN_OBJ_KW(numerical_argmax_obj, 1, numerical_argmax); + +//| def argmin(array: _ArrayLike, *, axis: Optional[int] = None) -> int: +//| """Return the index of the minimum element of the 1D array""" +//| ... +//| + +static mp_obj_t numerical_argmin(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + return numerical_function(n_args, pos_args, kw_args, NUMERICAL_ARGMIN); +} + +MP_DEFINE_CONST_FUN_OBJ_KW(numerical_argmin_obj, 1, numerical_argmin); +#endif + +#if ULAB_NUMPY_HAS_ARGSORT +//| def argsort(array: ulab.ndarray, *, axis: int = -1) -> ulab.ndarray: +//| """Returns an array which gives indices into the input array from least to greatest.""" +//| ... +//| + +mp_obj_t numerical_argsort(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = mp_const_none } }, + { MP_QSTR_axis, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = mp_const_none } }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + if(!mp_obj_is_type(args[0].u_obj, &ulab_ndarray_type)) { + mp_raise_TypeError(translate("argsort argument must be an ndarray")); + } + + ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(args[0].u_obj); + if(args[1].u_obj == mp_const_none) { + // bail out, though dense arrays could still be sorted + mp_raise_NotImplementedError(translate("argsort is not implemented for flattened arrays")); + } + // Since we are returning an NDARRAY_UINT16 array, bail out, + // if the axis is longer than what we can hold + for(uint8_t i=0; i < ULAB_MAX_DIMS; i++) { + if(ndarray->shape[i] > 65535) { + mp_raise_ValueError(translate("axis too long")); + } + } + int8_t ax = mp_obj_get_int(args[1].u_obj); + if(ax < 0) ax += ndarray->ndim; + if((ax < 0) || (ax > ndarray->ndim - 1)) { + mp_raise_ValueError(translate("index out of range")); + } + size_t *shape = m_new(size_t, ULAB_MAX_DIMS); + memset(shape, 0, sizeof(size_t)*ULAB_MAX_DIMS); + int32_t *strides = m_new(int32_t, ULAB_MAX_DIMS); + memset(strides, 0, sizeof(uint32_t)*ULAB_MAX_DIMS); + numerical_reduce_axes(ndarray, ax, shape, strides); + + // We could return an NDARRAY_UINT8 array, if all lengths are shorter than 256 + ndarray_obj_t *indices = ndarray_new_ndarray(ndarray->ndim, ndarray->shape, NULL, NDARRAY_UINT16); + int32_t *istrides = m_new(int32_t, ULAB_MAX_DIMS); + memset(istrides, 0, sizeof(uint32_t)*ULAB_MAX_DIMS); + numerical_reduce_axes(indices, ax, shape, istrides); + for(uint8_t i=0; i < ULAB_MAX_DIMS; i++) { + istrides[i] /= sizeof(uint16_t); + } + + ax = ULAB_MAX_DIMS - ndarray->ndim + ax; + // we work with the typed array, so re-scale the stride + int32_t increment = ndarray->strides[ax] / ndarray->itemsize; + uint16_t iincrement = indices->strides[ax] / sizeof(uint16_t); + + uint8_t *array = (uint8_t *)ndarray->array; + uint16_t *iarray = (uint16_t *)indices->array; + + // fill in the index values + #if ULAB_MAX_DIMS > 3 + size_t j = 0; + do { + #endif + #if ULAB_MAX_DIMS > 2 + size_t k = 0; + do { + #endif + #if ULAB_MAX_DIMS > 1 + size_t l = 0; + do { + #endif + uint16_t m = 0; + do { + *iarray = m++; + iarray += iincrement; + } while(m < indices->shape[ax]); + #if ULAB_MAX_DIMS > 1 + iarray -= iincrement * indices->shape[ax]; + iarray += istrides[ULAB_MAX_DIMS - 1]; + l++; + } while(l < shape[ULAB_MAX_DIMS - 1]); + iarray -= istrides[ULAB_MAX_DIMS - 1] * shape[ULAB_MAX_DIMS - 1]; + iarray += istrides[ULAB_MAX_DIMS - 2]; + #endif + #if ULAB_MAX_DIMS > 2 + k++; + } while(k < shape[ULAB_MAX_DIMS - 2]); + iarray -= istrides[ULAB_MAX_DIMS - 2] * shape[ULAB_MAX_DIMS - 2]; + iarray += istrides[ULAB_MAX_DIMS - 3]; + #endif + #if ULAB_MAX_DIMS > 3 + j++; + } while(j < shape[ULAB_MAX_DIMS - 3]); + #endif + // reset the array + iarray = indices->array; + + if((ndarray->dtype == NDARRAY_UINT8) || (ndarray->dtype == NDARRAY_INT8)) { + HEAP_ARGSORT(ndarray, uint8_t, array, shape, strides, ax, increment, ndarray->shape[ax], iarray, istrides, iincrement); + } else if((ndarray->dtype == NDARRAY_UINT16) || (ndarray->dtype == NDARRAY_INT16)) { + HEAP_ARGSORT(ndarray, uint16_t, array, shape, strides, ax, increment, ndarray->shape[ax], iarray, istrides, iincrement); + } else { + HEAP_ARGSORT(ndarray, mp_float_t, array, shape, strides, ax, increment, ndarray->shape[ax], iarray, istrides, iincrement); + } + return MP_OBJ_FROM_PTR(indices); +} + +MP_DEFINE_CONST_FUN_OBJ_KW(numerical_argsort_obj, 1, numerical_argsort); +#endif + +#if ULAB_NUMPY_HAS_CROSS +//| def cross(a: ulab.ndarray, b: ulab.ndarray) -> ulab.ndarray: +//| """Return the cross product of two vectors of length 3""" +//| ... +//| + +static mp_obj_t numerical_cross(mp_obj_t _a, mp_obj_t _b) { + if (!mp_obj_is_type(_a, &ulab_ndarray_type) || !mp_obj_is_type(_b, &ulab_ndarray_type)) { + mp_raise_TypeError(translate("arguments must be ndarrays")); + } + ndarray_obj_t *a = MP_OBJ_TO_PTR(_a); + ndarray_obj_t *b = MP_OBJ_TO_PTR(_b); + if((a->ndim != 1) || (b->ndim != 1) || (a->len != b->len) || (a->len != 3)) { + mp_raise_ValueError(translate("cross is defined for 1D arrays of length 3")); + } + + mp_float_t *results = m_new(mp_float_t, 3); + results[0] = ndarray_get_float_index(a->array, a->dtype, 1) * ndarray_get_float_index(b->array, b->dtype, 2); + results[0] -= ndarray_get_float_index(a->array, a->dtype, 2) * ndarray_get_float_index(b->array, b->dtype, 1); + results[1] = -ndarray_get_float_index(a->array, a->dtype, 0) * ndarray_get_float_index(b->array, b->dtype, 2); + results[1] += ndarray_get_float_index(a->array, a->dtype, 2) * ndarray_get_float_index(b->array, b->dtype, 0); + results[2] = ndarray_get_float_index(a->array, a->dtype, 0) * ndarray_get_float_index(b->array, b->dtype, 1); + results[2] -= ndarray_get_float_index(a->array, a->dtype, 1) * ndarray_get_float_index(b->array, b->dtype, 0); + + /* The upcasting happens here with the rules + + - if one of the operarands is a float, the result is always float + - operation on identical types preserves type + + uint8 + int8 => int16 + uint8 + int16 => int16 + uint8 + uint16 => uint16 + int8 + int16 => int16 + int8 + uint16 => uint16 + uint16 + int16 => float + + */ + + uint8_t dtype = NDARRAY_FLOAT; + if(a->dtype == b->dtype) { + dtype = a->dtype; + } else if(((a->dtype == NDARRAY_UINT8) && (b->dtype == NDARRAY_INT8)) || ((a->dtype == NDARRAY_INT8) && (b->dtype == NDARRAY_UINT8))) { + dtype = NDARRAY_INT16; + } else if(((a->dtype == NDARRAY_UINT8) && (b->dtype == NDARRAY_INT16)) || ((a->dtype == NDARRAY_INT16) && (b->dtype == NDARRAY_UINT8))) { + dtype = NDARRAY_INT16; + } else if(((a->dtype == NDARRAY_UINT8) && (b->dtype == NDARRAY_UINT16)) || ((a->dtype == NDARRAY_UINT16) && (b->dtype == NDARRAY_UINT8))) { + dtype = NDARRAY_UINT16; + } else if(((a->dtype == NDARRAY_INT8) && (b->dtype == NDARRAY_INT16)) || ((a->dtype == NDARRAY_INT16) && (b->dtype == NDARRAY_INT8))) { + dtype = NDARRAY_INT16; + } else if(((a->dtype == NDARRAY_INT8) && (b->dtype == NDARRAY_UINT16)) || ((a->dtype == NDARRAY_UINT16) && (b->dtype == NDARRAY_INT8))) { + dtype = NDARRAY_UINT16; + } + + ndarray_obj_t *ndarray = ndarray_new_linear_array(3, dtype); + if(dtype == NDARRAY_UINT8) { + uint8_t *array = (uint8_t *)ndarray->array; + for(uint8_t i=0; i < 3; i++) array[i] = (uint8_t)results[i]; + } else if(dtype == NDARRAY_INT8) { + int8_t *array = (int8_t *)ndarray->array; + for(uint8_t i=0; i < 3; i++) array[i] = (int8_t)results[i]; + } else if(dtype == NDARRAY_UINT16) { + uint16_t *array = (uint16_t *)ndarray->array; + for(uint8_t i=0; i < 3; i++) array[i] = (uint16_t)results[i]; + } else if(dtype == NDARRAY_INT16) { + int16_t *array = (int16_t *)ndarray->array; + for(uint8_t i=0; i < 3; i++) array[i] = (int16_t)results[i]; + } else { + mp_float_t *array = (mp_float_t *)ndarray->array; + for(uint8_t i=0; i < 3; i++) array[i] = results[i]; + } + m_del(mp_float_t, results, 3); + return MP_OBJ_FROM_PTR(ndarray); +} + +MP_DEFINE_CONST_FUN_OBJ_2(numerical_cross_obj, numerical_cross); + +#endif /* ULAB_NUMERICAL_HAS_CROSS */ + +#if ULAB_NUMPY_HAS_DIFF +//| def diff(array: ulab.ndarray, *, n: int = 1, axis: int = -1) -> ulab.ndarray: +//| """Return the numerical derivative of successive elements of the array, as +//| an array. axis=None is not supported.""" +//| ... +//| + +mp_obj_t numerical_diff(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } }, + { MP_QSTR_n, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 1 } }, + { MP_QSTR_axis, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1 } }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + if(!mp_obj_is_type(args[0].u_obj, &ulab_ndarray_type)) { + mp_raise_TypeError(translate("diff argument must be an ndarray")); + } + + ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(args[0].u_obj); + int8_t ax = args[2].u_int; + if(ax < 0) ax += ndarray->ndim; + + if((ax < 0) || (ax > ndarray->ndim - 1)) { + mp_raise_ValueError(translate("index out of range")); + } + + if((args[1].u_int < 0) || (args[1].u_int > 9)) { + mp_raise_ValueError(translate("differentiation order out of range")); + } + uint8_t N = (uint8_t)args[1].u_int; + uint8_t index = ULAB_MAX_DIMS - ndarray->ndim + ax; + if(N > ndarray->shape[index]) { + mp_raise_ValueError(translate("differentiation order out of range")); + } + + int8_t *stencil = m_new(int8_t, N+1); + stencil[0] = 1; + for(uint8_t i=1; i < N+1; i++) { + stencil[i] = -stencil[i-1]*(N-i+1)/i; + } + + size_t *shape = m_new(size_t, ULAB_MAX_DIMS); + memset(shape, 0, sizeof(size_t)*ULAB_MAX_DIMS); + for(uint8_t i=0; i < ULAB_MAX_DIMS; i++) { + shape[i] = ndarray->shape[i]; + if(i == index) { + shape[i] -= N; + } + } + uint8_t *array = (uint8_t *)ndarray->array; + ndarray_obj_t *results = ndarray_new_dense_ndarray(ndarray->ndim, shape, ndarray->dtype); + uint8_t *rarray = (uint8_t *)results->array; + + memset(shape, 0, sizeof(size_t)*ULAB_MAX_DIMS); + int32_t *strides = m_new(int32_t, ULAB_MAX_DIMS); + memset(strides, 0, sizeof(int32_t)*ULAB_MAX_DIMS); + numerical_reduce_axes(ndarray, ax, shape, strides); + + if(ndarray->dtype == NDARRAY_UINT8) { + RUN_DIFF(ndarray, uint8_t, array, results, rarray, shape, strides, index, stencil, N); + } else if(ndarray->dtype == NDARRAY_INT8) { + RUN_DIFF(ndarray, int8_t, array, results, rarray, shape, strides, index, stencil, N); + } else if(ndarray->dtype == NDARRAY_UINT16) { + RUN_DIFF(ndarray, uint16_t, array, results, rarray, shape, strides, index, stencil, N); + } else if(ndarray->dtype == NDARRAY_INT16) { + RUN_DIFF(ndarray, int16_t, array, results, rarray, shape, strides, index, stencil, N); + } else { + RUN_DIFF(ndarray, mp_float_t, array, results, rarray, shape, strides, index, stencil, N); + } + m_del(int8_t, stencil, N+1); + m_del(size_t, shape, ULAB_MAX_DIMS); + m_del(int32_t, strides, ULAB_MAX_DIMS); + return MP_OBJ_FROM_PTR(results); +} + +MP_DEFINE_CONST_FUN_OBJ_KW(numerical_diff_obj, 1, numerical_diff); +#endif + +#if ULAB_NUMPY_HAS_FLIP +//| def flip(array: ulab.ndarray, *, axis: Optional[int] = None) -> ulab.ndarray: +//| """Returns a new array that reverses the order of the elements along the +//| given axis, or along all axes if axis is None.""" +//| ... +//| + +mp_obj_t numerical_flip(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } }, + { MP_QSTR_axis, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + if(!mp_obj_is_type(args[0].u_obj, &ulab_ndarray_type)) { + mp_raise_TypeError(translate("flip argument must be an ndarray")); + } + + ndarray_obj_t *results = NULL; + ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(args[0].u_obj); + if(args[1].u_obj == mp_const_none) { // flip the flattened array + results = ndarray_new_linear_array(ndarray->len, ndarray->dtype); + ndarray_copy_array(ndarray, results); + uint8_t *rarray = (uint8_t *)results->array; + rarray += (results->len - 1) * results->itemsize; + results->array = rarray; + results->strides[ULAB_MAX_DIMS - 1] = -results->strides[ULAB_MAX_DIMS - 1]; + } else if(mp_obj_is_int(args[1].u_obj)){ + int8_t ax = mp_obj_get_int(args[1].u_obj); + if(ax < 0) ax += ndarray->ndim; + if((ax < 0) || (ax > ndarray->ndim - 1)) { + mp_raise_ValueError(translate("index out of range")); + } + ax = ULAB_MAX_DIMS - ndarray->ndim + ax; + int32_t offset = (ndarray->shape[ax] - 1) * ndarray->strides[ax]; + results = ndarray_new_view(ndarray, ndarray->ndim, ndarray->shape, ndarray->strides, offset); + results->strides[ax] = -results->strides[ax]; + } else { + mp_raise_TypeError(translate("wrong axis index")); + } + return results; +} + +MP_DEFINE_CONST_FUN_OBJ_KW(numerical_flip_obj, 1, numerical_flip); +#endif + +#if ULAB_NUMPY_HAS_MINMAX +//| def max(array: _ArrayLike, *, axis: Optional[int] = None) -> float: +//| """Return the maximum element of the 1D array""" +//| ... +//| + +mp_obj_t numerical_max(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + return numerical_function(n_args, pos_args, kw_args, NUMERICAL_MAX); +} + +MP_DEFINE_CONST_FUN_OBJ_KW(numerical_max_obj, 1, numerical_max); +#endif + +#if ULAB_NUMPY_HAS_MEAN +//| def mean(array: _ArrayLike, *, axis: Optional[int] = None) -> float: +//| """Return the mean element of the 1D array, as a number if axis is None, otherwise as an array.""" +//| ... +//| + +mp_obj_t numerical_mean(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + return numerical_function(n_args, pos_args, kw_args, NUMERICAL_MEAN); +} + +MP_DEFINE_CONST_FUN_OBJ_KW(numerical_mean_obj, 1, numerical_mean); +#endif + +#if ULAB_NUMPY_HAS_MEDIAN +//| def median(array: ulab.ndarray, *, axis: int = -1) -> ulab.ndarray: +//| """Find the median value in an array along the given axis, or along all axes if axis is None.""" +//| ... +//| + +mp_obj_t numerical_median(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } }, + { MP_QSTR_axis, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = mp_const_none } }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + if(!mp_obj_is_type(args[0].u_obj, &ulab_ndarray_type)) { + mp_raise_TypeError(translate("median argument must be an ndarray")); + } + + ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(args[0].u_obj); + if(ndarray->len == 0) { + return mp_obj_new_float(MICROPY_FLOAT_C_FUN(nan)("")); + } + + ndarray = numerical_sort_helper(args[0].u_obj, args[1].u_obj, 0); + + if((args[1].u_obj == mp_const_none) || (ndarray->ndim == 1)) { + // at this point, the array holding the sorted values should be flat + uint8_t *array = (uint8_t *)ndarray->array; + size_t len = ndarray->len; + array += (len >> 1) * ndarray->itemsize; + mp_float_t median = ndarray_get_float_value(array, ndarray->dtype); + if(!(len & 0x01)) { // len is an even number + array -= ndarray->itemsize; + median += ndarray_get_float_value(array, ndarray->dtype); + median *= MICROPY_FLOAT_CONST(0.5); + } + return mp_obj_new_float(median); + } else { + int8_t ax = mp_obj_get_int(args[1].u_obj); + if(ax < 0) ax += ndarray->ndim; + // here we can save the exception, because if the axis is out of range, + // then numerical_sort_helper has already taken care of the issue + size_t *shape = m_new(size_t, ULAB_MAX_DIMS); + memset(shape, 0, sizeof(size_t)*ULAB_MAX_DIMS); + int32_t *strides = m_new(int32_t, ULAB_MAX_DIMS); + memset(strides, 0, sizeof(uint32_t)*ULAB_MAX_DIMS); + numerical_reduce_axes(ndarray, ax, shape, strides); + ax = ULAB_MAX_DIMS - ndarray->ndim + ax; + ndarray_obj_t *results = ndarray_new_dense_ndarray(ndarray->ndim-1, shape, NDARRAY_FLOAT); + mp_float_t *rarray = (mp_float_t *)results->array; + + uint8_t *array = (uint8_t *)ndarray->array; + + size_t len = ndarray->shape[ax]; + + #if ULAB_MAX_DIMS > 3 + size_t i = 0; + do { + #endif + #if ULAB_MAX_DIMS > 2 + size_t j = 0; + do { + #endif + size_t k = 0; + do { + array += ndarray->strides[ax] * (len >> 1); + mp_float_t median = ndarray_get_float_value(array, ndarray->dtype); + if(!(len & 0x01)) { // len is an even number + array -= ndarray->strides[ax]; + median += ndarray_get_float_value(array, ndarray->dtype); + median *= MICROPY_FLOAT_CONST(0.5); + array += ndarray->strides[ax]; + } + array -= ndarray->strides[ax] * (len >> 1); + array += strides[ULAB_MAX_DIMS - 1]; + *rarray = median; + rarray++; + k++; + } while(k < shape[ULAB_MAX_DIMS - 1]); + #if ULAB_MAX_DIMS > 2 + array -= strides[ULAB_MAX_DIMS - 1] * shape[ULAB_MAX_DIMS - 1]; + array += strides[ULAB_MAX_DIMS - 2]; + j++; + } while(j < shape[ULAB_MAX_DIMS - 2]); + #endif + #if ULAB_MAX_DIMS > 3 + array -= strides[ULAB_MAX_DIMS - 2] * shape[ULAB_MAX_DIMS-2]; + array += strides[ULAB_MAX_DIMS - 3]; + i++; + } while(i < shape[ULAB_MAX_DIMS - 3]); + #endif + + return MP_OBJ_FROM_PTR(results); + } + return mp_const_none; +} + +MP_DEFINE_CONST_FUN_OBJ_KW(numerical_median_obj, 1, numerical_median); +#endif + +#if ULAB_NUMPY_HAS_MINMAX +//| def min(array: _ArrayLike, *, axis: Optional[int] = None) -> float: +//| """Return the minimum element of the 1D array""" +//| ... +//| + +mp_obj_t numerical_min(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + return numerical_function(n_args, pos_args, kw_args, NUMERICAL_MIN); +} + +MP_DEFINE_CONST_FUN_OBJ_KW(numerical_min_obj, 1, numerical_min); +#endif + +#if ULAB_NUMPY_HAS_ROLL +//| def roll(array: ulab.ndarray, distance: int, *, axis: Optional[int] = None) -> None: +//| """Shift the content of a vector by the positions given as the second +//| argument. If the ``axis`` keyword is supplied, the shift is applied to +//| the given axis. The array is modified in place.""" +//| ... +//| + +mp_obj_t numerical_roll(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } }, + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } }, + { MP_QSTR_axis, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + if(!mp_obj_is_type(args[0].u_obj, &ulab_ndarray_type)) { + mp_raise_TypeError(translate("roll argument must be an ndarray")); + } + ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(args[0].u_obj); + uint8_t *array = ndarray->array; + ndarray_obj_t *results = ndarray_new_dense_ndarray(ndarray->ndim, ndarray->shape, ndarray->dtype); + + int32_t shift = mp_obj_get_int(args[1].u_obj); + int32_t _shift = shift < 0 ? -shift : shift; + + size_t counter; + uint8_t *rarray = (uint8_t *)results->array; + + if(args[2].u_obj == mp_const_none) { // roll the flattened array + _shift = _shift % results->len; + if(shift > 0) { // shift to the right + rarray += _shift * results->itemsize; + counter = results->len - _shift; + } else { // shift to the left + rarray += (results->len - _shift) * results->itemsize; + counter = _shift; + } + #if ULAB_MAX_DIMS > 3 + size_t i = 0; + do { + #endif + #if ULAB_MAX_DIMS > 2 + size_t j = 0; + do { + #endif + #if ULAB_MAX_DIMS > 1 + size_t k = 0; + do { + #endif + size_t l = 0; + do { + memcpy(rarray, array, ndarray->itemsize); + rarray += results->itemsize; + array += ndarray->strides[ULAB_MAX_DIMS - 1]; + l++; + if(--counter == 0) { + rarray = results->array; + } + } while(l < ndarray->shape[ULAB_MAX_DIMS - 1]); + #if ULAB_MAX_DIMS > 1 + array -= ndarray->strides[ULAB_MAX_DIMS - 1] * ndarray->shape[ULAB_MAX_DIMS-1]; + array += ndarray->strides[ULAB_MAX_DIMS - 2]; + k++; + } while(k < ndarray->shape[ULAB_MAX_DIMS - 2]); + #endif + #if ULAB_MAX_DIMS > 2 + array -= ndarray->strides[ULAB_MAX_DIMS - 2] * ndarray->shape[ULAB_MAX_DIMS-2]; + array += ndarray->strides[ULAB_MAX_DIMS - 3]; + j++; + } while(j < ndarray->shape[ULAB_MAX_DIMS - 3]); + #endif + #if ULAB_MAX_DIMS > 3 + array -= ndarray->strides[ULAB_MAX_DIMS - 3] * ndarray->shape[ULAB_MAX_DIMS-3]; + array += ndarray->strides[ULAB_MAX_DIMS - 4]; + i++; + } while(i < ndarray->shape[ULAB_MAX_DIMS - 4]); + #endif + } else if(mp_obj_is_int(args[2].u_obj)){ + int8_t ax = mp_obj_get_int(args[2].u_obj); + if(ax < 0) ax += ndarray->ndim; + if((ax < 0) || (ax > ndarray->ndim - 1)) { + mp_raise_ValueError(translate("index out of range")); + } + size_t *shape = m_new(size_t, ULAB_MAX_DIMS); + memset(shape, 0, sizeof(size_t)*ULAB_MAX_DIMS); + int32_t *strides = m_new(int32_t, ULAB_MAX_DIMS); + memset(strides, 0, sizeof(int32_t)*ULAB_MAX_DIMS); + numerical_reduce_axes(ndarray, ax, shape, strides); + + size_t *rshape = m_new(size_t, ULAB_MAX_DIMS); + memset(rshape, 0, sizeof(size_t)*ULAB_MAX_DIMS); + int32_t *rstrides = m_new(int32_t, ULAB_MAX_DIMS); + memset(rstrides, 0, sizeof(int32_t)*ULAB_MAX_DIMS); + numerical_reduce_axes(results, ax, rshape, rstrides); + + ax = ULAB_MAX_DIMS - ndarray->ndim + ax; + uint8_t *_rarray; + _shift = _shift % results->shape[ax]; + + #if ULAB_MAX_DIMS > 3 + size_t i = 0; + do { + #endif + #if ULAB_MAX_DIMS > 2 + size_t j = 0; + do { + #endif + #if ULAB_MAX_DIMS > 1 + size_t k = 0; + do { + #endif + size_t l = 0; + _rarray = rarray; + if(shift < 0) { + rarray += (results->shape[ax] - _shift) * results->strides[ax]; + counter = _shift; + } else { + rarray += _shift * results->strides[ax]; + counter = results->shape[ax] - _shift; + } + do { + memcpy(rarray, array, ndarray->itemsize); + array += ndarray->strides[ax]; + rarray += results->strides[ax]; + if(--counter == 0) { + rarray = _rarray; + } + l++; + } while(l < ndarray->shape[ax]); + #if ULAB_MAX_DIMS > 1 + rarray = _rarray; + rarray += rstrides[ULAB_MAX_DIMS - 1]; + array -= ndarray->strides[ax] * ndarray->shape[ax]; + array += strides[ULAB_MAX_DIMS - 1]; + k++; + } while(k < shape[ULAB_MAX_DIMS - 1]); + #endif + #if ULAB_MAX_DIMS > 2 + rarray -= rstrides[ULAB_MAX_DIMS - 1] * rshape[ULAB_MAX_DIMS-1]; + rarray += rstrides[ULAB_MAX_DIMS - 2]; + array -= strides[ULAB_MAX_DIMS - 1] * shape[ULAB_MAX_DIMS-1]; + array += strides[ULAB_MAX_DIMS - 2]; + j++; + } while(j < shape[ULAB_MAX_DIMS - 2]); + #endif + #if ULAB_MAX_DIMS > 3 + rarray -= rstrides[ULAB_MAX_DIMS - 2] * rshape[ULAB_MAX_DIMS-2]; + rarray += rstrides[ULAB_MAX_DIMS - 3]; + array -= strides[ULAB_MAX_DIMS - 2] * shape[ULAB_MAX_DIMS-2]; + array += strides[ULAB_MAX_DIMS - 3]; + i++; + } while(i < shape[ULAB_MAX_DIMS - 3]); + #endif + } else { + mp_raise_TypeError(translate("wrong axis index")); + } + return results; +} + +MP_DEFINE_CONST_FUN_OBJ_KW(numerical_roll_obj, 2, numerical_roll); +#endif + +#if ULAB_NUMPY_HAS_SORT +//| def sort(array: ulab.ndarray, *, axis: int = -1) -> ulab.ndarray: +//| """Sort the array along the given axis, or along all axes if axis is None. +//| The array is modified in place.""" +//| ... +//| + +mp_obj_t numerical_sort(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = mp_const_none } }, + { MP_QSTR_axis, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = mp_const_none } }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + return numerical_sort_helper(args[0].u_obj, args[1].u_obj, 0); +} + +MP_DEFINE_CONST_FUN_OBJ_KW(numerical_sort_obj, 1, numerical_sort); +#endif + +#if NDARRAY_HAS_SORT +// method of an ndarray +static mp_obj_t numerical_sort_inplace(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } }, + { MP_QSTR_axis, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_int = -1 } }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + return numerical_sort_helper(args[0].u_obj, args[1].u_obj, 1); +} + +MP_DEFINE_CONST_FUN_OBJ_KW(numerical_sort_inplace_obj, 1, numerical_sort_inplace); +#endif /* NDARRAY_HAS_SORT */ + +#if ULAB_NUMPY_HAS_STD +//| def std(array: _ArrayLike, *, axis: Optional[int] = None, ddof: int = 0) -> float: +//| """Return the standard deviation of the array, as a number if axis is None, otherwise as an array.""" +//| ... +//| + +mp_obj_t numerical_std(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } } , + { MP_QSTR_axis, MP_ARG_OBJ, {.u_rom_obj = mp_const_none } }, + { MP_QSTR_ddof, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + mp_obj_t oin = args[0].u_obj; + mp_obj_t axis = args[1].u_obj; + size_t ddof = args[2].u_int; + if((axis != mp_const_none) && (mp_obj_get_int(axis) != 0) && (mp_obj_get_int(axis) != 1)) { + // this seems to pass with False, and True... + mp_raise_ValueError(translate("axis must be None, or an integer")); + } + if(mp_obj_is_type(oin, &mp_type_tuple) || mp_obj_is_type(oin, &mp_type_list) || mp_obj_is_type(oin, &mp_type_range)) { + return numerical_sum_mean_std_iterable(oin, NUMERICAL_STD, ddof); + } else if(mp_obj_is_type(oin, &ulab_ndarray_type)) { + ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(oin); + return numerical_sum_mean_std_ndarray(ndarray, axis, NUMERICAL_STD, ddof); + } else { + mp_raise_TypeError(translate("input must be tuple, list, range, or ndarray")); + } + return mp_const_none; +} + +MP_DEFINE_CONST_FUN_OBJ_KW(numerical_std_obj, 1, numerical_std); +#endif + +#if ULAB_NUMPY_HAS_SUM +//| def sum(array: _ArrayLike, *, axis: Optional[int] = None) -> Union[float, int, ulab.ndarray]: +//| """Return the sum of the array, as a number if axis is None, otherwise as an array.""" +//| ... +//| + +mp_obj_t numerical_sum(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + return numerical_function(n_args, pos_args, kw_args, NUMERICAL_SUM); +} + +MP_DEFINE_CONST_FUN_OBJ_KW(numerical_sum_obj, 1, numerical_sum); +#endif diff --git a/python/port/mod/ulab/numpy/numerical/numerical.h b/python/port/mod/ulab/numpy/numerical/numerical.h new file mode 100644 index 000000000..ef7b95d74 --- /dev/null +++ b/python/port/mod/ulab/numpy/numerical/numerical.h @@ -0,0 +1,652 @@ + +/* + * This file is part of the micropython-ulab project, + * + * https://github.com/v923z/micropython-ulab + * + * The MIT License (MIT) + * + * Copyright (c) 2019-2021 Zoltán Vörös +*/ + +#ifndef _NUMERICAL_ +#define _NUMERICAL_ + +#include "../../ulab.h" +#include "../../ndarray.h" + +// TODO: implement cumsum + +#define RUN_ARGMIN1(ndarray, type, array, results, rarray, index, op)\ +({\ + uint16_t best_index = 0;\ + type best_value = *((type *)(array));\ + if(((op) == NUMERICAL_MAX) || ((op) == NUMERICAL_ARGMAX)) {\ + for(uint16_t i=0; i < (ndarray)->shape[(index)]; i++) {\ + if(*((type *)(array)) > best_value) {\ + best_index = i;\ + best_value = *((type *)(array));\ + }\ + (array) += (ndarray)->strides[(index)];\ + }\ + } else {\ + for(uint16_t i=0; i < (ndarray)->shape[(index)]; i++) {\ + if(*((type *)(array)) < best_value) {\ + best_index = i;\ + best_value = *((type *)(array));\ + }\ + (array) += (ndarray)->strides[(index)];\ + }\ + }\ + if(((op) == NUMERICAL_ARGMAX) || ((op) == NUMERICAL_ARGMIN)) {\ + memcpy((rarray), &best_index, (results)->itemsize);\ + } else {\ + memcpy((rarray), &best_value, (results)->itemsize);\ + }\ + (rarray) += (results)->itemsize;\ +}) + +#define RUN_SUM1(type, array, results, rarray, ss)\ +({\ + type sum = 0;\ + for(size_t i=0; i < (ss).shape[0]; i++) {\ + sum += *((type *)(array));\ + (array) += (ss).strides[0];\ + }\ + memcpy((rarray), &sum, (results)->itemsize);\ + (rarray) += (results)->itemsize;\ +}) + +// The mean could be calculated by simply dividing the sum by +// the number of elements, but that method is numerically unstable +#define RUN_MEAN1(type, array, rarray, ss)\ +({\ + mp_float_t M = 0.0;\ + for(size_t i=0; i < (ss).shape[0]; i++) {\ + mp_float_t value = (mp_float_t)(*(type *)(array));\ + M = M + (value - M) / (mp_float_t)(i+1);\ + (array) += (ss).strides[0];\ + }\ + *(rarray)++ = M;\ +}) + +// Instead of the straightforward implementation of the definition, +// we take the numerically stable Welford algorithm here +// https://www.johndcook.com/blog/2008/09/26/comparing-three-methods-of-computing-standard-deviation/ +#define RUN_STD1(type, array, rarray, ss, div)\ +({\ + mp_float_t M = 0.0, m = 0.0, S = 0.0;\ + for(size_t i=0; i < (ss).shape[0]; i++) {\ + mp_float_t value = (mp_float_t)(*(type *)(array));\ + m = M + (value - M) / (mp_float_t)(i+1);\ + S = S + (value - M) * (value - m);\ + M = m;\ + (array) += (ss).strides[0];\ + }\ + *(rarray)++ = MICROPY_FLOAT_C_FUN(sqrt)(S / (div));\ +}) + +#define RUN_MEAN_STD1(type, array, rarray, ss, div, isStd)\ +({\ + mp_float_t M = 0.0, m = 0.0, S = 0.0;\ + for(size_t i=0; i < (ss).shape[0]; i++) {\ + mp_float_t value = (mp_float_t)(*(type *)(array));\ + m = M + (value - M) / (mp_float_t)(i+1);\ + if(isStd) {\ + S += (value - M) * (value - m);\ + }\ + M = m;\ + (array) += (ss).strides[0];\ + }\ + *(rarray)++ = isStd ? MICROPY_FLOAT_C_FUN(sqrt)(S / (div)) : M;\ +}) + +#define RUN_DIFF1(ndarray, type, array, results, rarray, index, stencil, N)\ +({\ + for(size_t i=0; i < (results)->shape[ULAB_MAX_DIMS - 1]; i++) {\ + type sum = 0;\ + uint8_t *source = (array);\ + for(uint8_t d=0; d < (N)+1; d++) {\ + sum -= (stencil)[d] * *((type *)source);\ + source += (ndarray)->strides[(index)];\ + }\ + (array) += (ndarray)->strides[ULAB_MAX_DIMS - 1];\ + *(type *)(rarray) = sum;\ + (rarray) += (results)->itemsize;\ + }\ +}) + +#define HEAPSORT1(type, array, increment, N)\ +({\ + type *_array = (type *)array;\ + type tmp;\ + size_t c, q = (N), p, r = (N) >> 1;\ + for (;;) {\ + if (r > 0) {\ + tmp = _array[(--r)*(increment)];\ + } else {\ + q--;\ + if(q == 0) {\ + break;\ + }\ + tmp = _array[q*(increment)];\ + _array[q*(increment)] = _array[0];\ + }\ + p = r;\ + c = r + r + 1;\ + while (c < q) {\ + if((c + 1 < q) && (_array[(c+1)*(increment)] > _array[c*(increment)])) {\ + c++;\ + }\ + if(_array[c*(increment)] > tmp) {\ + _array[p*(increment)] = _array[c*(increment)];\ + p = c;\ + c = p + p + 1;\ + } else {\ + break;\ + }\ + }\ + _array[p*(increment)] = tmp;\ + }\ +}) + +#define HEAP_ARGSORT1(type, array, increment, N, iarray, iincrement)\ +({\ + type *_array = (type *)array;\ + type tmp;\ + uint16_t itmp, c, q = (N), p, r = (N) >> 1;\ + for (;;) {\ + if (r > 0) {\ + r--;\ + itmp = (iarray)[r*(iincrement)];\ + tmp = _array[itmp*(increment)];\ + } else {\ + q--;\ + if(q == 0) {\ + break;\ + }\ + itmp = (iarray)[q*(iincrement)];\ + tmp = _array[itmp*(increment)];\ + (iarray)[q*(iincrement)] = (iarray)[0];\ + }\ + p = r;\ + c = r + r + 1;\ + while (c < q) {\ + if((c + 1 < q) && (_array[(iarray)[(c+1)*(iincrement)]*(increment)] > _array[(iarray)[c*(iincrement)]*(increment)])) {\ + c++;\ + }\ + if(_array[(iarray)[c*(iincrement)]*(increment)] > tmp) {\ + (iarray)[p*(iincrement)] = (iarray)[c*(iincrement)];\ + p = c;\ + c = p + p + 1;\ + } else {\ + break;\ + }\ + }\ + (iarray)[p*(iincrement)] = itmp;\ + }\ +}) + +#if ULAB_MAX_DIMS == 1 +#define RUN_SUM(type, array, results, rarray, ss) do {\ + RUN_SUM1(type, (array), (results), (rarray), (ss));\ +} while(0) + +#define RUN_MEAN(type, array, rarray, ss) do {\ + RUN_MEAN1(type, (array), (rarray), (ss));\ +} while(0) + +#define RUN_STD(type, array, rarray, ss, div) do {\ + RUN_STD1(type, (array), (results), (rarray), (ss), (div));\ +} while(0) + +#define RUN_MEAN_STD(type, array, rarray, ss, div, isStd) do {\ + RUN_MEAN_STD1(type, (array), (results), (rarray), (ss), (div), (isStd));\ +} while(0) + +#define RUN_ARGMIN(ndarray, type, array, results, rarray, shape, strides, index, op) do {\ + RUN_ARGMIN1((ndarray), type, (array), (results), (rarray), (index), (op));\ +} while(0) + +#define RUN_DIFF(ndarray, type, array, results, rarray, shape, strides, index, stencil, N) do {\ + RUN_DIFF1((ndarray), type, (array), (results), (rarray), (index), (stencil), (N));\ +} while(0) + +#define HEAPSORT(ndarray, type, array, shape, strides, index, increment, N) do {\ + HEAPSORT1(type, (array), (increment), (N));\ +} while(0) + +#define HEAP_ARGSORT(ndarray, type, array, shape, strides, index, increment, N, iarray, istrides, iincrement) do {\ + HEAP_ARGSORT1(type, (array), (increment), (N), (iarray), (iincrement));\ +} while(0) + +#endif + +#if ULAB_MAX_DIMS == 2 +#define RUN_SUM(type, array, results, rarray, ss) do {\ + size_t l = 0;\ + do {\ + RUN_SUM1(type, (array), (results), (rarray), (ss));\ + (array) -= (ss).strides[0] * (ss).shape[0];\ + (array) += (ss).strides[ULAB_MAX_DIMS - 1];\ + l++;\ + } while(l < (ss).shape[ULAB_MAX_DIMS - 1]);\ +} while(0) + +#define RUN_MEAN(type, array, rarray, ss) do {\ + size_t l = 0;\ + do {\ + RUN_MEAN1(type, (array), (rarray), (ss));\ + (array) -= (ss).strides[0] * (ss).shape[0];\ + (array) += (ss).strides[ULAB_MAX_DIMS - 1];\ + l++;\ + } while(l < (ss).shape[ULAB_MAX_DIMS - 1]);\ +} while(0) + +#define RUN_STD(type, array, rarray, ss, div) do {\ + size_t l = 0;\ + do {\ + RUN_STD1(type, (array), (rarray), (ss), (div));\ + (array) -= (ss).strides[0] * (ss).shape[0];\ + (array) += (ss).strides[ULAB_MAX_DIMS - 1];\ + l++;\ + } while(l < (ss).shape[ULAB_MAX_DIMS - 1]);\ +} while(0) + +#define RUN_MEAN_STD(type, array, rarray, ss, div, isStd) do {\ + size_t l = 0;\ + do {\ + RUN_MEAN_STD1(type, (array), (rarray), (ss), (div), (isStd));\ + (array) -= (ss).strides[0] * (ss).shape[0];\ + (array) += (ss).strides[ULAB_MAX_DIMS - 1];\ + l++;\ + } while(l < (ss).shape[ULAB_MAX_DIMS - 1]);\ +} while(0) + + +#define RUN_ARGMIN(ndarray, type, array, results, rarray, shape, strides, index, op) do {\ + size_t l = 0;\ + do {\ + RUN_ARGMIN1((ndarray), type, (array), (results), (rarray), (index), (op));\ + (array) -= (ndarray)->strides[(index)] * (ndarray)->shape[(index)];\ + (array) += (strides)[ULAB_MAX_DIMS - 1];\ + l++;\ + } while(l < (shape)[ULAB_MAX_DIMS - 1]);\ +} while(0) + +#define RUN_DIFF(ndarray, type, array, results, rarray, shape, strides, index, stencil, N) do {\ + size_t l = 0;\ + do {\ + RUN_DIFF1((ndarray), type, (array), (results), (rarray), (index), (stencil), (N));\ + (array) -= (ndarray)->strides[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\ + (array) += (ndarray)->strides[ULAB_MAX_DIMS - 2];\ + (rarray) -= (results)->strides[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\ + (rarray) += (results)->strides[ULAB_MAX_DIMS - 2];\ + l++;\ + } while(l < (results)->shape[ULAB_MAX_DIMS - 2]);\ +} while(0) + +#define HEAPSORT(ndarray, type, array, shape, strides, index, increment, N) do {\ + size_t l = 0;\ + do {\ + HEAPSORT1(type, (array), (increment), (N));\ + (array) += (strides)[ULAB_MAX_DIMS - 1];\ + l++;\ + } while(l < (shape)[ULAB_MAX_DIMS - 1]);\ +} while(0) + +#define HEAP_ARGSORT(ndarray, type, array, shape, strides, index, increment, N, iarray, istrides, iincrement) do {\ + size_t l = 0;\ + do {\ + HEAP_ARGSORT1(type, (array), (increment), (N), (iarray), (iincrement));\ + (array) += (strides)[ULAB_MAX_DIMS - 1];\ + (iarray) += (istrides)[ULAB_MAX_DIMS - 1];\ + l++;\ + } while(l < (shape)[ULAB_MAX_DIMS - 1]);\ +} while(0) + +#endif + +#if ULAB_MAX_DIMS == 3 +#define RUN_SUM(type, array, results, rarray, ss) do {\ + size_t k = 0;\ + do {\ + size_t l = 0;\ + do {\ + RUN_SUM1(type, (array), (results), (rarray), (ss));\ + (array) -= (ss).strides[0] * (ss).shape[0];\ + (array) += (ss).strides[ULAB_MAX_DIMS - 1];\ + l++;\ + } while(l < (ss).shape[ULAB_MAX_DIMS - 1]);\ + (array) -= (ss).strides[ULAB_MAX_DIMS - 1] * (ss).shape[ULAB_MAX_DIMS - 1];\ + (array) += (ss).strides[ULAB_MAX_DIMS - 2];\ + k++;\ + } while(k < (ss).shape[ULAB_MAX_DIMS - 2]);\ +} while(0) + +#define RUN_MEAN(type, array, rarray, ss) do {\ + size_t k = 0;\ + do {\ + size_t l = 0;\ + do {\ + RUN_MEAN1(type, (array), (rarray), (ss));\ + (array) -= (ss).strides[0] * (ss).shape[0];\ + (array) += (ss).strides[ULAB_MAX_DIMS - 1];\ + l++;\ + } while(l < (ss).shape[ULAB_MAX_DIMS - 1]);\ + (array) -= (ss).strides[ULAB_MAX_DIMS - 1] * (ss).shape[ULAB_MAX_DIMS - 1];\ + (array) += (ss).strides[ULAB_MAX_DIMS - 2];\ + k++;\ + } while(k < (ss).shape[ULAB_MAX_DIMS - 2]);\ +} while(0) + +#define RUN_STD(type, array, rarray, ss, div) do {\ + size_t k = 0;\ + do {\ + size_t l = 0;\ + do {\ + RUN_STD1(type, (array), (rarray), (ss), (div));\ + (array) -= (ss).strides[0] * (ss).shape[0];\ + (array) += (ss).strides[ULAB_MAX_DIMS - 1];\ + l++;\ + } while(l < (ss).shape[ULAB_MAX_DIMS - 1]);\ + (array) -= (ss).strides[ULAB_MAX_DIMS - 1] * (ss).shape[ULAB_MAX_DIMS - 1];\ + (array) += (ss).strides[ULAB_MAX_DIMS - 2];\ + k++;\ + } while(k < (ss).shape[ULAB_MAX_DIMS - 2]);\ +} while(0) + +#define RUN_MEAN_STD(type, array, rarray, ss, div, isStd) do {\ + size_t k = 0;\ + do {\ + size_t l = 0;\ + do {\ + RUN_MEAN_STD1(type, (array), (rarray), (ss), (div), (isStd));\ + (array) -= (ss).strides[0] * (ss).shape[0];\ + (array) += (ss).strides[ULAB_MAX_DIMS - 1];\ + l++;\ + } while(l < (ss).shape[ULAB_MAX_DIMS - 1]);\ + (array) -= (ss).strides[ULAB_MAX_DIMS - 1] * (ss).shape[ULAB_MAX_DIMS - 1];\ + (array) += (ss).strides[ULAB_MAX_DIMS - 2];\ + k++;\ + } while(k < (ss).shape[ULAB_MAX_DIMS - 2]);\ +} while(0) + +#define RUN_ARGMIN(ndarray, type, array, results, rarray, shape, strides, index, op) do {\ + size_t k = 0;\ + do {\ + size_t l = 0;\ + do {\ + RUN_ARGMIN1((ndarray), type, (array), (results), (rarray), (index), (op));\ + (array) -= (ndarray)->strides[(index)] * (ndarray)->shape[(index)];\ + (array) += (strides)[ULAB_MAX_DIMS - 1];\ + l++;\ + } while(l < (shape)[ULAB_MAX_DIMS - 1]);\ + (array) -= (strides)[ULAB_MAX_DIMS - 1] * (shape)[ULAB_MAX_DIMS-1];\ + (array) += (strides)[ULAB_MAX_DIMS - 2];\ + k++;\ + } while(k < (shape)[ULAB_MAX_DIMS - 2]);\ +} while(0) + +#define RUN_DIFF(ndarray, type, array, results, rarray, shape, strides, index, stencil, N) do {\ + size_t k = 0;\ + do {\ + size_t l = 0;\ + do {\ + RUN_DIFF1((ndarray), type, (array), (results), (rarray), (index), (stencil), (N));\ + (array) -= (ndarray)->strides[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\ + (array) += (ndarray)->strides[ULAB_MAX_DIMS - 2];\ + (rarray) -= (results)->strides[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\ + (rarray) += (results)->strides[ULAB_MAX_DIMS - 2];\ + l++;\ + } while(l < (shape)[ULAB_MAX_DIMS - 2]);\ + (array) -= (ndarray)->strides[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS-2];\ + (array) += (ndarray)->strides[ULAB_MAX_DIMS - 3];\ + (rarray) -= (results)->strides[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\ + (rarray) += (results)->strides[ULAB_MAX_DIMS - 3];\ + k++;\ + } while(k < (shape)[ULAB_MAX_DIMS - 3]);\ +} while(0) + +#define HEAPSORT(ndarray, type, array, shape, strides, index, increment, N) do {\ + size_t k = 0;\ + do {\ + size_t l = 0;\ + do {\ + HEAPSORT1(type, (array), (increment), (N));\ + (array) += (strides)[ULAB_MAX_DIMS - 1];\ + l++;\ + } while(l < (shape)[ULAB_MAX_DIMS - 1]);\ + (array) -= (strides)[ULAB_MAX_DIMS - 1] * (shape)[ULAB_MAX_DIMS-1];\ + (array) += (strides)[ULAB_MAX_DIMS - 2];\ + k++;\ + } while(k < (shape)[ULAB_MAX_DIMS - 2]);\ +} while(0) + +#define HEAP_ARGSORT(ndarray, type, array, shape, strides, index, increment, N, iarray, istrides, iincrement) do {\ + size_t k = 0;\ + do {\ + size_t l = 0;\ + do {\ + HEAP_ARGSORT1(type, (array), (increment), (N), (iarray), (iincrement));\ + (array) += (strides)[ULAB_MAX_DIMS - 1];\ + (iarray) += (istrides)[ULAB_MAX_DIMS - 1];\ + l++;\ + } while(l < (shape)[ULAB_MAX_DIMS - 1]);\ + (iarray) -= (istrides)[ULAB_MAX_DIMS - 1] * (shape)[ULAB_MAX_DIMS-1];\ + (iarray) += (istrides)[ULAB_MAX_DIMS - 2];\ + (array) -= (strides)[ULAB_MAX_DIMS - 1] * (shape)[ULAB_MAX_DIMS-1];\ + (array) += (strides)[ULAB_MAX_DIMS - 2];\ + k++;\ + } while(k < (shape)[ULAB_MAX_DIMS - 2]);\ +} while(0) + +#endif + +#if ULAB_MAX_DIMS == 4 +#define RUN_SUM(type, array, results, rarray, shape, strides, index) do {\ + size_t j = 0;\ + do {\ + size_t k = 0;\ + do {\ + size_t l = 0;\ + do {\ + RUN_SUM1(type, (array), (results), (rarray), (ss));\ + (array) -= (ss).strides[0] * (ss).shape[0];\ + (array) += (ss).strides[ULAB_MAX_DIMS - 1];\ + l++;\ + } while(l < (ss).shape[ULAB_MAX_DIMS - 1]);\ + (array) -= (ss).strides[ULAB_MAX_DIMS - 1] * (ss).shape[ULAB_MAX_DIMS - 1];\ + (array) += (ss).strides[ULAB_MAX_DIMS - 2];\ + k++;\ + } while(k < (ss).shape[ULAB_MAX_DIMS - 2]);\ + (array) -= (ss).strides[ULAB_MAX_DIMS - 2] * (ss).shape[ULAB_MAX_DIMS - 2];\ + (array) += (ss).strides[ULAB_MAX_DIMS - 3];\ + j++;\ + } while(j < (ss).shape[ULAB_MAX_DIMS - 3]);\ +} while(0) + +#define RUN_MEAN(type, array, rarray, ss) do {\ + size_t j = 0;\ + do {\ + size_t k = 0;\ + do {\ + size_t l = 0;\ + do {\ + RUN_MEAN1(type, (array), (rarray), (ss));\ + (array) -= (ss).strides[0] * (ss).shape[0];\ + (array) += (ss).strides[ULAB_MAX_DIMS - 1];\ + l++;\ + } while(l < (ss).shape[ULAB_MAX_DIMS - 1]);\ + (array) -= (ss).strides[ULAB_MAX_DIMS - 1] * (ss).shape[ULAB_MAX_DIMS - 1];\ + (array) += (ss).strides[ULAB_MAX_DIMS - 2];\ + k++;\ + } while(k < (ss).shape[ULAB_MAX_DIMS - 2]);\ + (array) -= (ss).strides[ULAB_MAX_DIMS - 2] * (ss).shape[ULAB_MAX_DIMS - 2];\ + (array) += (ss).strides[ULAB_MAX_DIMS - 3];\ + j++;\ + } while(j < (ss).shape[ULAB_MAX_DIMS - 3]);\ +} while(0) + +#define RUN_STD(type, array, rarray, ss, div) do {\ + size_t j = 0;\ + do {\ + size_t k = 0;\ + do {\ + size_t l = 0;\ + do {\ + RUN_STD1(type, (array), (rarray), (ss), (div));\ + (array) -= (ss).strides[0] * (ss).shape[0];\ + (array) += (ss).strides[ULAB_MAX_DIMS - 1];\ + l++;\ + } while(l < (ss).shape[ULAB_MAX_DIMS - 1]);\ + (array) -= (ss).strides[ULAB_MAX_DIMS - 1] * (ss).shape[ULAB_MAX_DIMS - 1];\ + (array) += (ss).strides[ULAB_MAX_DIMS - 2];\ + k++;\ + } while(k < (ss).shape[ULAB_MAX_DIMS - 2]);\ + (array) -= (ss).strides[ULAB_MAX_DIMS - 2] * (ss).shape[ULAB_MAX_DIMS - 2];\ + (array) += (ss).strides[ULAB_MAX_DIMS - 3];\ + j++;\ + } while(j < (ss).shape[ULAB_MAX_DIMS - 3]);\ +} while(0) + +#define RUN_MEAN_STD(type, array, rarray, ss, div, isStd) do {\ + size_t j = 0;\ + do {\ + size_t k = 0;\ + do {\ + size_t l = 0;\ + do {\ + RUN_MEAN_STD1(type, (array), (rarray), (ss), (div), (isStd));\ + (array) -= (ss).strides[0] * (ss).shape[0];\ + (array) += (ss).strides[ULAB_MAX_DIMS - 1];\ + l++;\ + } while(l < (ss).shape[ULAB_MAX_DIMS - 1]);\ + (array) -= (ss).strides[ULAB_MAX_DIMS - 1] * (ss).shape[ULAB_MAX_DIMS - 1];\ + (array) += (ss).strides[ULAB_MAX_DIMS - 2];\ + k++;\ + } while(k < (ss).shape[ULAB_MAX_DIMS - 2]);\ + (array) -= (ss).strides[ULAB_MAX_DIMS - 2] * (ss).shape[ULAB_MAX_DIMS - 2];\ + (array) += (ss).strides[ULAB_MAX_DIMS - 3];\ + j++;\ + } while(j < (ss).shape[ULAB_MAX_DIMS - 3]);\ +} while(0) + +#define RUN_ARGMIN(ndarray, type, array, results, rarray, shape, strides, index, op) do {\ + size_t j = 0;\ + do {\ + size_t k = 0;\ + do {\ + size_t l = 0;\ + do {\ + RUN_ARGMIN1((ndarray), type, (array), (results), (rarray), (index), (op));\ + (array) -= (ndarray)->strides[(index)] * (ndarray)->shape[(index)];\ + (array) += (strides)[ULAB_MAX_DIMS - 1];\ + l++;\ + } while(l < (shape)[ULAB_MAX_DIMS - 1]);\ + (array) -= (strides)[ULAB_MAX_DIMS - 1] * (shape)[ULAB_MAX_DIMS-1];\ + (array) += (strides)[ULAB_MAX_DIMS - 2];\ + k++;\ + } while(k < (shape)[ULAB_MAX_DIMS - 2]);\ + (array) -= (strides)[ULAB_MAX_DIMS - 2] * (shape)[ULAB_MAX_DIMS-2];\ + (array) += (strides)[ULAB_MAX_DIMS - 3];\ + j++;\ + } while(j < (shape)[ULAB_MAX_DIMS - 3]);\ +} while(0) + +#define RUN_DIFF(ndarray, type, array, results, rarray, shape, strides, index, stencil, N) do {\ + size_t j = 0;\ + do {\ + size_t k = 0;\ + do {\ + size_t l = 0;\ + do {\ + RUN_DIFF1((ndarray), type, (array), (results), (rarray), (index), (stencil), (N));\ + (array) -= (ndarray)->strides[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\ + (array) += (ndarray)->strides[ULAB_MAX_DIMS - 2];\ + (rarray) -= (results)->strides[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\ + (rarray) += (results)->strides[ULAB_MAX_DIMS - 2];\ + l++;\ + } while(l < (shape)[ULAB_MAX_DIMS - 2]);\ + (array) -= (strides)[ULAB_MAX_DIMS - 2] * (shape)[ULAB_MAX_DIMS-2];\ + (array) += (strides)[ULAB_MAX_DIMS - 3];\ + (rarray) -= (results)->strides[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\ + (rarray) += (results)->strides[ULAB_MAX_DIMS - 3];\ + k++;\ + } while(k < (shape)[ULAB_MAX_DIMS - 3]);\ + (array) -= (strides)[ULAB_MAX_DIMS - 3] * (shape)[ULAB_MAX_DIMS-3];\ + (array) += (strides)[ULAB_MAX_DIMS - 4];\ + (rarray) -= (results)->strides[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS - 3];\ + (rarray) += (results)->strides[ULAB_MAX_DIMS - 4];\ + j++;\ + } while(j < (shape)[ULAB_MAX_DIMS - 4]);\ +} while(0) + +#define HEAPSORT(ndarray, type, array, shape, strides, index, increment, N) do {\ + size_t j = 0;\ + do {\ + size_t k = 0;\ + do {\ + size_t l = 0;\ + do {\ + HEAPSORT1(type, (array), (increment), (N));\ + (array) += (strides)[ULAB_MAX_DIMS - 1];\ + l++;\ + } while(l < (shape)[ULAB_MAX_DIMS - 1]);\ + (array) -= (strides)[ULAB_MAX_DIMS - 1] * (shape)[ULAB_MAX_DIMS-1];\ + (array) += (strides)[ULAB_MAX_DIMS - 2];\ + k++;\ + } while(k < (shape)[ULAB_MAX_DIMS - 2]);\ + (array) -= (strides)[ULAB_MAX_DIMS - 2] * (shape)[ULAB_MAX_DIMS-2];\ + (array) += (strides)[ULAB_MAX_DIMS - 3];\ + j++;\ + } while(j < (shape)[ULAB_MAX_DIMS - 3]);\ +} while(0) + +#define HEAP_ARGSORT(ndarray, type, array, shape, strides, index, increment, N, iarray, istrides, iincrement) do {\ + size_t j = 0;\ + do {\ + size_t k = 0;\ + do {\ + size_t l = 0;\ + do {\ + HEAP_ARGSORT1(type, (array), (increment), (N), (iarray), (iincrement));\ + (array) += (strides)[ULAB_MAX_DIMS - 1];\ + (iarray) += (istrides)[ULAB_MAX_DIMS - 1];\ + l++;\ + } while(l < (shape)[ULAB_MAX_DIMS - 1]);\ + (iarray) -= (istrides)[ULAB_MAX_DIMS - 1] * (shape)[ULAB_MAX_DIMS-1];\ + (iarray) += (istrides)[ULAB_MAX_DIMS - 2];\ + (array) -= (strides)[ULAB_MAX_DIMS - 1] * (shape)[ULAB_MAX_DIMS-1];\ + (array) += (strides)[ULAB_MAX_DIMS - 2];\ + k++;\ + } while(k < (shape)[ULAB_MAX_DIMS - 2]);\ + (iarray) -= (istrides)[ULAB_MAX_DIMS - 2] * (shape)[ULAB_MAX_DIMS-2];\ + (iarray) += (istrides)[ULAB_MAX_DIMS - 3];\ + (array) -= (strides)[ULAB_MAX_DIMS - 2] * (shape)[ULAB_MAX_DIMS-2];\ + (array) += (strides)[ULAB_MAX_DIMS - 3];\ + j++;\ + } while(j < (shape)[ULAB_MAX_DIMS - 3]);\ +} while(0) + +#endif + +MP_DECLARE_CONST_FUN_OBJ_KW(numerical_all_obj); +MP_DECLARE_CONST_FUN_OBJ_KW(numerical_any_obj); +MP_DECLARE_CONST_FUN_OBJ_KW(numerical_argmax_obj); +MP_DECLARE_CONST_FUN_OBJ_KW(numerical_argmin_obj); +MP_DECLARE_CONST_FUN_OBJ_KW(numerical_argsort_obj); +MP_DECLARE_CONST_FUN_OBJ_2(numerical_cross_obj); +MP_DECLARE_CONST_FUN_OBJ_KW(numerical_diff_obj); +MP_DECLARE_CONST_FUN_OBJ_KW(numerical_flip_obj); +MP_DECLARE_CONST_FUN_OBJ_KW(numerical_max_obj); +MP_DECLARE_CONST_FUN_OBJ_KW(numerical_mean_obj); +MP_DECLARE_CONST_FUN_OBJ_KW(numerical_median_obj); +MP_DECLARE_CONST_FUN_OBJ_KW(numerical_min_obj); +MP_DECLARE_CONST_FUN_OBJ_KW(numerical_roll_obj); +MP_DECLARE_CONST_FUN_OBJ_KW(numerical_std_obj); +MP_DECLARE_CONST_FUN_OBJ_KW(numerical_sum_obj); +MP_DECLARE_CONST_FUN_OBJ_KW(numerical_sort_obj); +MP_DECLARE_CONST_FUN_OBJ_KW(numerical_sort_inplace_obj); + +#endif diff --git a/python/port/mod/ulab/numpy/numpy.c b/python/port/mod/ulab/numpy/numpy.c new file mode 100644 index 000000000..8d8f3ab6b --- /dev/null +++ b/python/port/mod/ulab/numpy/numpy.c @@ -0,0 +1,336 @@ + +/* + * This file is part of the micropython-ulab project, + * + * https://github.com/v923z/micropython-ulab + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jeff Epler for Adafruit Industries + * 2020 Scott Shawcroft for Adafruit Industries + * 2020-2021 Zoltán Vörös + * 2020 Taku Fukada +*/ + +#include +#include +#include + +#include "numpy.h" +#include "../ulab_create.h" +#include "approx/approx.h" +#include "compare/compare.h" +#include "fft/fft.h" +#include "filter/filter.h" +#include "linalg/linalg.h" +#include "numerical/numerical.h" +#include "stats/stats.h" +#include "transform/transform.h" +#include "poly/poly.h" +#include "vector/vector.h" + +//| """Compatibility layer for numpy""" +//| + +//| class ndarray: ... + +// math constants +#if ULAB_NUMPY_HAS_E +#if MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_C +#define ulab_const_float_e MP_ROM_PTR((mp_obj_t)(((0x402df854 & ~3) | 2) + 0x80800000)) +#elif MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_D +#define ulab_const_float_e {((mp_obj_t)((uint64_t)0x4005bf0a8b145769 + 0x8004000000000000))} +#else +mp_obj_float_t ulab_const_float_e_obj = {{&mp_type_float}, MP_E}; +#define ulab_const_float_e MP_ROM_PTR(&ulab_const_float_e_obj) +#endif +#endif + +#if ULAB_NUMPY_HAS_INF +#if MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_C +#define numpy_const_float_inf MP_ROM_PTR((mp_obj_t)(0x7f800002 + 0x80800000)) +#elif MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_D +#define numpy_const_float_inf {((mp_obj_t)((uint64_t)0x7ff0000000000000 + 0x8004000000000000))} +#else +mp_obj_float_t numpy_const_float_inf_obj = {{&mp_type_float}, (mp_float_t)INFINITY}; +#define numpy_const_float_inf MP_ROM_PTR(&numpy_const_float_inf_obj) +#endif +#endif + +#if ULAB_NUMPY_HAS_NAN +#if MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_C +#define numpy_const_float_nan MP_ROM_PTR((mp_obj_t)(0x7fc00002 + 0x80800000)) +#elif MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_D +#define numpy_const_float_nan {((mp_obj_t)((uint64_t)0x7ff8000000000000 + 0x8004000000000000))} +#else +mp_obj_float_t numpy_const_float_nan_obj = {{&mp_type_float}, (mp_float_t)NAN}; +#define numpy_const_float_nan MP_ROM_PTR(&numpy_const_float_nan_obj) +#endif +#endif + +#if ULAB_NUMPY_HAS_PI +#if MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_C +#define ulab_const_float_pi MP_ROM_PTR((mp_obj_t)(((0x40490fdb & ~3) | 2) + 0x80800000)) +#elif MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_D +#define ulab_const_float_pi {((mp_obj_t)((uint64_t)0x400921fb54442d18 + 0x8004000000000000))} +#else +mp_obj_float_t ulab_const_float_pi_obj = {{&mp_type_float}, MP_PI}; +#define ulab_const_float_pi MP_ROM_PTR(&ulab_const_float_pi_obj) +#endif +#endif + +static const mp_rom_map_elem_t ulab_numpy_globals_table[] = { + { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_numpy) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_ndarray), (mp_obj_t)&ulab_ndarray_type }, + { MP_OBJ_NEW_QSTR(MP_QSTR_array), MP_ROM_PTR(&ndarray_array_constructor_obj) }, + #if ULAB_NUMPY_HAS_FROMBUFFER + { MP_ROM_QSTR(MP_QSTR_frombuffer), MP_ROM_PTR(&create_frombuffer_obj) }, + #endif + // math constants + #if ULAB_NUMPY_HAS_E + { MP_ROM_QSTR(MP_QSTR_e), ulab_const_float_e }, + #endif + #if ULAB_NUMPY_HAS_INF + { MP_ROM_QSTR(MP_QSTR_inf), numpy_const_float_inf }, + #endif + #if ULAB_NUMPY_HAS_NAN + { MP_ROM_QSTR(MP_QSTR_nan), numpy_const_float_nan }, + #endif + #if ULAB_NUMPY_HAS_PI + { MP_ROM_QSTR(MP_QSTR_pi), ulab_const_float_pi }, + #endif + // class constants, always included + { MP_ROM_QSTR(MP_QSTR_bool), MP_ROM_INT(NDARRAY_BOOL) }, + { MP_ROM_QSTR(MP_QSTR_uint8), MP_ROM_INT(NDARRAY_UINT8) }, + { MP_ROM_QSTR(MP_QSTR_int8), MP_ROM_INT(NDARRAY_INT8) }, + { MP_ROM_QSTR(MP_QSTR_uint16), MP_ROM_INT(NDARRAY_UINT16) }, + { MP_ROM_QSTR(MP_QSTR_int16), MP_ROM_INT(NDARRAY_INT16) }, + { MP_ROM_QSTR(MP_QSTR_float), MP_ROM_INT(NDARRAY_FLOAT) }, + // modules of numpy + #if ULAB_NUMPY_HAS_FFT_MODULE + { MP_ROM_QSTR(MP_QSTR_fft), MP_ROM_PTR(&ulab_fft_module) }, + #endif + #if ULAB_NUMPY_HAS_LINALG_MODULE + { MP_ROM_QSTR(MP_QSTR_linalg), MP_ROM_PTR(&ulab_linalg_module) }, + #endif + #if ULAB_HAS_PRINTOPTIONS + { MP_ROM_QSTR(MP_QSTR_set_printoptions), (mp_obj_t)&ndarray_set_printoptions_obj }, + { MP_ROM_QSTR(MP_QSTR_get_printoptions), (mp_obj_t)&ndarray_get_printoptions_obj }, + #endif + #if ULAB_NUMPY_HAS_NDINFO + { MP_ROM_QSTR(MP_QSTR_ndinfo), (mp_obj_t)&ndarray_info_obj }, + #endif + #if ULAB_NUMPY_HAS_ARANGE + { MP_ROM_QSTR(MP_QSTR_arange), (mp_obj_t)&create_arange_obj }, + #endif + #if ULAB_NUMPY_HAS_CONCATENATE + { MP_ROM_QSTR(MP_QSTR_concatenate), (mp_obj_t)&create_concatenate_obj }, + #endif + #if ULAB_NUMPY_HAS_DIAG + { MP_ROM_QSTR(MP_QSTR_diag), (mp_obj_t)&create_diag_obj }, + #endif + #if ULAB_NUMPY_HAS_EMPTY + { MP_ROM_QSTR(MP_QSTR_empty), (mp_obj_t)&create_zeros_obj }, + #endif + #if ULAB_MAX_DIMS > 1 + #if ULAB_NUMPY_HAS_EYE + { MP_ROM_QSTR(MP_QSTR_eye), (mp_obj_t)&create_eye_obj }, + #endif + #endif /* ULAB_MAX_DIMS */ + // functions of the approx sub-module + #if ULAB_NUMPY_HAS_INTERP + { MP_OBJ_NEW_QSTR(MP_QSTR_interp), (mp_obj_t)&approx_interp_obj }, + #endif + #if ULAB_NUMPY_HAS_TRAPZ + { MP_OBJ_NEW_QSTR(MP_QSTR_trapz), (mp_obj_t)&approx_trapz_obj }, + #endif + // functions of the create sub-module + #if ULAB_NUMPY_HAS_FULL + { MP_ROM_QSTR(MP_QSTR_full), (mp_obj_t)&create_full_obj }, + #endif + #if ULAB_NUMPY_HAS_LINSPACE + { MP_ROM_QSTR(MP_QSTR_linspace), (mp_obj_t)&create_linspace_obj }, + #endif + #if ULAB_NUMPY_HAS_LOGSPACE + { MP_ROM_QSTR(MP_QSTR_logspace), (mp_obj_t)&create_logspace_obj }, + #endif + #if ULAB_NUMPY_HAS_ONES + { MP_ROM_QSTR(MP_QSTR_ones), (mp_obj_t)&create_ones_obj }, + #endif + #if ULAB_NUMPY_HAS_ZEROS + { MP_ROM_QSTR(MP_QSTR_zeros), (mp_obj_t)&create_zeros_obj }, + #endif + // functions of the compare sub-module + #if ULAB_NUMPY_HAS_CLIP + { MP_OBJ_NEW_QSTR(MP_QSTR_clip), (mp_obj_t)&compare_clip_obj }, + #endif + #if ULAB_NUMPY_HAS_EQUAL + { MP_OBJ_NEW_QSTR(MP_QSTR_equal), (mp_obj_t)&compare_equal_obj }, + #endif + #if ULAB_NUMPY_HAS_NOTEQUAL + { MP_OBJ_NEW_QSTR(MP_QSTR_not_equal), (mp_obj_t)&compare_not_equal_obj }, + #endif + #if ULAB_NUMPY_HAS_ISFINITE + { MP_OBJ_NEW_QSTR(MP_QSTR_isfinite), (mp_obj_t)&compare_isfinite_obj }, + #endif + #if ULAB_NUMPY_HAS_ISINF + { MP_OBJ_NEW_QSTR(MP_QSTR_isinf), (mp_obj_t)&compare_isinf_obj }, + #endif + #if ULAB_NUMPY_HAS_MAXIMUM + { MP_OBJ_NEW_QSTR(MP_QSTR_maximum), (mp_obj_t)&compare_maximum_obj }, + #endif + #if ULAB_NUMPY_HAS_MINIMUM + { MP_OBJ_NEW_QSTR(MP_QSTR_minimum), (mp_obj_t)&compare_minimum_obj }, + #endif + #if ULAB_NUMPY_HAS_WHERE + { MP_OBJ_NEW_QSTR(MP_QSTR_where), (mp_obj_t)&compare_where_obj }, + #endif + // functions of the filter sub-module + #if ULAB_NUMPY_HAS_CONVOLVE + { MP_OBJ_NEW_QSTR(MP_QSTR_convolve), (mp_obj_t)&filter_convolve_obj }, + #endif + // functions of the numerical sub-module + #if ULAB_NUMPY_HAS_ALL + { MP_OBJ_NEW_QSTR(MP_QSTR_all), (mp_obj_t)&numerical_all_obj }, + #endif + #if ULAB_NUMPY_HAS_ANY + { MP_OBJ_NEW_QSTR(MP_QSTR_any), (mp_obj_t)&numerical_any_obj }, + #endif + #if ULAB_NUMPY_HAS_ARGMINMAX + { MP_OBJ_NEW_QSTR(MP_QSTR_argmax), (mp_obj_t)&numerical_argmax_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_argmin), (mp_obj_t)&numerical_argmin_obj }, + #endif + #if ULAB_NUMPY_HAS_ARGSORT + { MP_OBJ_NEW_QSTR(MP_QSTR_argsort), (mp_obj_t)&numerical_argsort_obj }, + #endif + #if ULAB_NUMPY_HAS_CROSS + { MP_OBJ_NEW_QSTR(MP_QSTR_cross), (mp_obj_t)&numerical_cross_obj }, + #endif + #if ULAB_NUMPY_HAS_DIFF + { MP_OBJ_NEW_QSTR(MP_QSTR_diff), (mp_obj_t)&numerical_diff_obj }, + #endif + #if ULAB_NUMPY_HAS_DOT + { MP_OBJ_NEW_QSTR(MP_QSTR_dot), (mp_obj_t)&transform_dot_obj }, + #endif + #if ULAB_NUMPY_HAS_TRACE + { MP_ROM_QSTR(MP_QSTR_trace), (mp_obj_t)&stats_trace_obj }, + #endif + #if ULAB_NUMPY_HAS_FLIP + { MP_OBJ_NEW_QSTR(MP_QSTR_flip), (mp_obj_t)&numerical_flip_obj }, + #endif + #if ULAB_NUMPY_HAS_MINMAX + { MP_OBJ_NEW_QSTR(MP_QSTR_max), (mp_obj_t)&numerical_max_obj }, + #endif + #if ULAB_NUMPY_HAS_MEAN + { MP_OBJ_NEW_QSTR(MP_QSTR_mean), (mp_obj_t)&numerical_mean_obj }, + #endif + #if ULAB_NUMPY_HAS_MEDIAN + { MP_OBJ_NEW_QSTR(MP_QSTR_median), (mp_obj_t)&numerical_median_obj }, + #endif + #if ULAB_NUMPY_HAS_MINMAX + { MP_OBJ_NEW_QSTR(MP_QSTR_min), (mp_obj_t)&numerical_min_obj }, + #endif + #if ULAB_NUMPY_HAS_ROLL + { MP_OBJ_NEW_QSTR(MP_QSTR_roll), (mp_obj_t)&numerical_roll_obj }, + #endif + #if ULAB_NUMPY_HAS_SORT + { MP_OBJ_NEW_QSTR(MP_QSTR_sort), (mp_obj_t)&numerical_sort_obj }, + #endif + #if ULAB_NUMPY_HAS_STD + { MP_OBJ_NEW_QSTR(MP_QSTR_std), (mp_obj_t)&numerical_std_obj }, + #endif + #if ULAB_NUMPY_HAS_SUM + { MP_OBJ_NEW_QSTR(MP_QSTR_sum), (mp_obj_t)&numerical_sum_obj }, + #endif + // functions of the poly sub-module + #if ULAB_NUMPY_HAS_POLYFIT + { MP_OBJ_NEW_QSTR(MP_QSTR_polyfit), (mp_obj_t)&poly_polyfit_obj }, + #endif + #if ULAB_NUMPY_HAS_POLYVAL + { MP_OBJ_NEW_QSTR(MP_QSTR_polyval), (mp_obj_t)&poly_polyval_obj }, + #endif + // functions of the vector sub-module + #if ULAB_NUMPY_HAS_ACOS + { MP_OBJ_NEW_QSTR(MP_QSTR_acos), (mp_obj_t)&vectorise_acos_obj }, + #endif + #if ULAB_NUMPY_HAS_ACOSH + { MP_OBJ_NEW_QSTR(MP_QSTR_acosh), (mp_obj_t)&vectorise_acosh_obj }, + #endif + #if ULAB_NUMPY_HAS_ARCTAN2 + { MP_OBJ_NEW_QSTR(MP_QSTR_arctan2), (mp_obj_t)&vectorise_arctan2_obj }, + #endif + #if ULAB_NUMPY_HAS_AROUND + { MP_OBJ_NEW_QSTR(MP_QSTR_around), (mp_obj_t)&vectorise_around_obj }, + #endif + #if ULAB_NUMPY_HAS_ASIN + { MP_OBJ_NEW_QSTR(MP_QSTR_asin), (mp_obj_t)&vectorise_asin_obj }, + #endif + #if ULAB_NUMPY_HAS_ASINH + { MP_OBJ_NEW_QSTR(MP_QSTR_asinh), (mp_obj_t)&vectorise_asinh_obj }, + #endif + #if ULAB_NUMPY_HAS_ATAN + { MP_OBJ_NEW_QSTR(MP_QSTR_atan), (mp_obj_t)&vectorise_atan_obj }, + #endif + #if ULAB_NUMPY_HAS_ATANH + { MP_OBJ_NEW_QSTR(MP_QSTR_atanh), (mp_obj_t)&vectorise_atanh_obj }, + #endif + #if ULAB_NUMPY_HAS_CEIL + { MP_OBJ_NEW_QSTR(MP_QSTR_ceil), (mp_obj_t)&vectorise_ceil_obj }, + #endif + #if ULAB_NUMPY_HAS_COS + { MP_OBJ_NEW_QSTR(MP_QSTR_cos), (mp_obj_t)&vectorise_cos_obj }, + #endif + #if ULAB_NUMPY_HAS_COSH + { MP_OBJ_NEW_QSTR(MP_QSTR_cosh), (mp_obj_t)&vectorise_cosh_obj }, + #endif + #if ULAB_NUMPY_HAS_DEGREES + { MP_OBJ_NEW_QSTR(MP_QSTR_degrees), (mp_obj_t)&vectorise_degrees_obj }, + #endif + #if ULAB_NUMPY_HAS_EXP + { MP_OBJ_NEW_QSTR(MP_QSTR_exp), (mp_obj_t)&vectorise_exp_obj }, + #endif + #if ULAB_NUMPY_HAS_EXPM1 + { MP_OBJ_NEW_QSTR(MP_QSTR_expm1), (mp_obj_t)&vectorise_expm1_obj }, + #endif + #if ULAB_NUMPY_HAS_FLOOR + { MP_OBJ_NEW_QSTR(MP_QSTR_floor), (mp_obj_t)&vectorise_floor_obj }, + #endif + #if ULAB_NUMPY_HAS_LOG + { MP_OBJ_NEW_QSTR(MP_QSTR_log), (mp_obj_t)&vectorise_log_obj }, + #endif + #if ULAB_NUMPY_HAS_LOG10 + { MP_OBJ_NEW_QSTR(MP_QSTR_log10), (mp_obj_t)&vectorise_log10_obj }, + #endif + #if ULAB_NUMPY_HAS_LOG2 + { MP_OBJ_NEW_QSTR(MP_QSTR_log2), (mp_obj_t)&vectorise_log2_obj }, + #endif + #if ULAB_NUMPY_HAS_RADIANS + { MP_OBJ_NEW_QSTR(MP_QSTR_radians), (mp_obj_t)&vectorise_radians_obj }, + #endif + #if ULAB_NUMPY_HAS_SIN + { MP_OBJ_NEW_QSTR(MP_QSTR_sin), (mp_obj_t)&vectorise_sin_obj }, + #endif + #if ULAB_NUMPY_HAS_SINH + { MP_OBJ_NEW_QSTR(MP_QSTR_sinh), (mp_obj_t)&vectorise_sinh_obj }, + #endif + #if ULAB_NUMPY_HAS_SQRT + { MP_OBJ_NEW_QSTR(MP_QSTR_sqrt), (mp_obj_t)&vectorise_sqrt_obj }, + #endif + #if ULAB_NUMPY_HAS_TAN + { MP_OBJ_NEW_QSTR(MP_QSTR_tan), (mp_obj_t)&vectorise_tan_obj }, + #endif + #if ULAB_NUMPY_HAS_TANH + { MP_OBJ_NEW_QSTR(MP_QSTR_tanh), (mp_obj_t)&vectorise_tanh_obj }, + #endif + #if ULAB_NUMPY_HAS_VECTORIZE + { MP_OBJ_NEW_QSTR(MP_QSTR_vectorize), (mp_obj_t)&vectorise_vectorize_obj }, + #endif + +}; + +static MP_DEFINE_CONST_DICT(mp_module_ulab_numpy_globals, ulab_numpy_globals_table); + +mp_obj_module_t ulab_numpy_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t*)&mp_module_ulab_numpy_globals, +}; diff --git a/python/port/mod/ulab/numpy/numpy.h b/python/port/mod/ulab/numpy/numpy.h new file mode 100644 index 000000000..cccebde18 --- /dev/null +++ b/python/port/mod/ulab/numpy/numpy.h @@ -0,0 +1,21 @@ + +/* + * This file is part of the micropython-ulab project, + * + * https://github.com/v923z/micropython-ulab + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Zoltán Vörös + * +*/ + +#ifndef _NUMPY_ +#define _NUMPY_ + +#include "../ulab.h" +#include "../ndarray.h" + +extern mp_obj_module_t ulab_numpy_module; + +#endif /* _NUMPY_ */ diff --git a/python/port/mod/ulab/numpy/poly/poly.c b/python/port/mod/ulab/numpy/poly/poly.c new file mode 100644 index 000000000..c9a07655f --- /dev/null +++ b/python/port/mod/ulab/numpy/poly/poly.c @@ -0,0 +1,232 @@ + +/* + * This file is part of the micropython-ulab project, + * + * https://github.com/v923z/micropython-ulab + * + * The MIT License (MIT) + * + * Copyright (c) 2019-2021 Zoltán Vörös + * 2020 Jeff Epler for Adafruit Industries + * 2020 Scott Shawcroft for Adafruit Industries + * 2020 Taku Fukada +*/ + +#include +#include +#include + +#include "../../ulab.h" +#include "../linalg/linalg_tools.h" +#include "../../ulab_tools.h" +#include "poly.h" + +#if ULAB_NUMPY_HAS_POLYFIT + +mp_obj_t poly_polyfit(size_t n_args, const mp_obj_t *args) { + if(!ndarray_object_is_array_like(args[0])) { + mp_raise_ValueError(translate("input data must be an iterable")); + } + size_t lenx = 0, leny = 0; + uint8_t deg = 0; + mp_float_t *x, *XT, *y, *prod; + + if(n_args == 2) { // only the y values are supplied + // TODO: this is actually not enough: the first argument can very well be a matrix, + // in which case we are between the rock and a hard place + leny = (size_t)mp_obj_get_int(mp_obj_len_maybe(args[0])); + deg = (uint8_t)mp_obj_get_int(args[1]); + if(leny < deg) { + mp_raise_ValueError(translate("more degrees of freedom than data points")); + } + lenx = leny; + x = m_new(mp_float_t, lenx); // assume uniformly spaced data points + for(size_t i=0; i < lenx; i++) { + x[i] = i; + } + y = m_new(mp_float_t, leny); + fill_array_iterable(y, args[0]); + } else /* n_args == 3 */ { + if(!ndarray_object_is_array_like(args[1])) { + mp_raise_ValueError(translate("input data must be an iterable")); + } + lenx = (size_t)mp_obj_get_int(mp_obj_len_maybe(args[0])); + leny = (size_t)mp_obj_get_int(mp_obj_len_maybe(args[1])); + if(lenx != leny) { + mp_raise_ValueError(translate("input vectors must be of equal length")); + } + deg = (uint8_t)mp_obj_get_int(args[2]); + if(leny < deg) { + mp_raise_ValueError(translate("more degrees of freedom than data points")); + } + x = m_new(mp_float_t, lenx); + fill_array_iterable(x, args[0]); + y = m_new(mp_float_t, leny); + fill_array_iterable(y, args[1]); + } + + // one could probably express X as a function of XT, + // and thereby save RAM, because X is used only in the product + XT = m_new(mp_float_t, (deg+1)*leny); // XT is a matrix of shape (deg+1, len) (rows, columns) + for(size_t i=0; i < leny; i++) { // column index + XT[i+0*lenx] = 1.0; // top row + for(uint8_t j=1; j < deg+1; j++) { // row index + XT[i+j*leny] = XT[i+(j-1)*leny]*x[i]; + } + } + + prod = m_new(mp_float_t, (deg+1)*(deg+1)); // the product matrix is of shape (deg+1, deg+1) + mp_float_t sum; + for(uint8_t i=0; i < deg+1; i++) { // column index + for(uint8_t j=0; j < deg+1; j++) { // row index + sum = 0.0; + for(size_t k=0; k < lenx; k++) { + // (j, k) * (k, i) + // Note that the second matrix is simply the transpose of the first: + // X(k, i) = XT(i, k) = XT[k*lenx+i] + sum += XT[j*lenx+k]*XT[i*lenx+k]; // X[k*(deg+1)+i]; + } + prod[j*(deg+1)+i] = sum; + } + } + if(!linalg_invert_matrix(prod, deg+1)) { + // Although X was a Vandermonde matrix, whose inverse is guaranteed to exist, + // we bail out here, if prod couldn't be inverted: if the values in x are not all + // distinct, prod is singular + m_del(mp_float_t, XT, (deg+1)*lenx); + m_del(mp_float_t, x, lenx); + m_del(mp_float_t, y, lenx); + m_del(mp_float_t, prod, (deg+1)*(deg+1)); + mp_raise_ValueError(translate("could not invert Vandermonde matrix")); + } + // at this point, we have the inverse of X^T * X + // y is a column vector; x is free now, we can use it for storing intermediate values + for(uint8_t i=0; i < deg+1; i++) { // row index + sum = 0.0; + for(size_t j=0; j < lenx; j++) { // column index + sum += XT[i*lenx+j]*y[j]; + } + x[i] = sum; + } + // XT is no longer needed + m_del(mp_float_t, XT, (deg+1)*leny); + + ndarray_obj_t *beta = ndarray_new_linear_array(deg+1, NDARRAY_FLOAT); + mp_float_t *betav = (mp_float_t *)beta->array; + // x[0..(deg+1)] contains now the product X^T * y; we can get rid of y + m_del(float, y, leny); + + // now, we calculate beta, i.e., we apply prod = (X^T * X)^(-1) on x = X^T * y; x is a column vector now + for(uint8_t i=0; i < deg+1; i++) { + sum = 0.0; + for(uint8_t j=0; j < deg+1; j++) { + sum += prod[i*(deg+1)+j]*x[j]; + } + betav[i] = sum; + } + m_del(mp_float_t, x, lenx); + m_del(mp_float_t, prod, (deg+1)*(deg+1)); + for(uint8_t i=0; i < (deg+1)/2; i++) { + // We have to reverse the array, for the leading coefficient comes first. + SWAP(mp_float_t, betav[i], betav[deg-i]); + } + return MP_OBJ_FROM_PTR(beta); +} + +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(poly_polyfit_obj, 2, 3, poly_polyfit); +#endif + +#if ULAB_NUMPY_HAS_POLYVAL + +mp_obj_t poly_polyval(mp_obj_t o_p, mp_obj_t o_x) { + if(!ndarray_object_is_array_like(o_p) || !ndarray_object_is_array_like(o_x)) { + mp_raise_TypeError(translate("inputs are not iterable")); + } + // p had better be a one-dimensional standard iterable + uint8_t plen = mp_obj_get_int(mp_obj_len_maybe(o_p)); + mp_float_t *p = m_new(mp_float_t, plen); + mp_obj_iter_buf_t p_buf; + mp_obj_t p_item, p_iterable = mp_getiter(o_p, &p_buf); + uint8_t i = 0; + while((p_item = mp_iternext(p_iterable)) != MP_OBJ_STOP_ITERATION) { + p[i] = mp_obj_get_float(p_item); + i++; + } + + // polynomials are going to be of type float, except, when both + // the coefficients and the independent variable are integers + ndarray_obj_t *ndarray; + if(mp_obj_is_type(o_x, &ulab_ndarray_type)) { + ndarray_obj_t *source = MP_OBJ_TO_PTR(o_x); + uint8_t *sarray = (uint8_t *)source->array; + ndarray = ndarray_new_dense_ndarray(source->ndim, source->shape, NDARRAY_FLOAT); + mp_float_t *array = (mp_float_t *)ndarray->array; + + mp_float_t (*func)(void *) = ndarray_get_float_function(source->dtype); + + // TODO: these loops are really nothing, but the re-impplementation of + // ITERATE_VECTOR from vectorise.c. We could pass a function pointer here + #if ULAB_MAX_DIMS > 3 + size_t i = 0; + do { + #endif + #if ULAB_MAX_DIMS > 2 + size_t j = 0; + do { + #endif + #if ULAB_MAX_DIMS > 1 + size_t k = 0; + do { + #endif + size_t l = 0; + do { + mp_float_t y = p[0]; + mp_float_t _x = func(sarray); + for(uint8_t m=0; m < plen-1; m++) { + y *= _x; + y += p[m+1]; + } + *array++ = y; + sarray += source->strides[ULAB_MAX_DIMS - 1]; + l++; + } while(l < source->shape[ULAB_MAX_DIMS - 1]); + #if ULAB_MAX_DIMS > 1 + sarray -= source->strides[ULAB_MAX_DIMS - 1] * source->shape[ULAB_MAX_DIMS-1]; + sarray += source->strides[ULAB_MAX_DIMS - 2]; + k++; + } while(k < source->shape[ULAB_MAX_DIMS - 2]); + #endif + #if ULAB_MAX_DIMS > 2 + sarray -= source->strides[ULAB_MAX_DIMS - 2] * source->shape[ULAB_MAX_DIMS-2]; + sarray += source->strides[ULAB_MAX_DIMS - 3]; + j++; + } while(j < source->shape[ULAB_MAX_DIMS - 3]); + #endif + #if ULAB_MAX_DIMS > 3 + sarray -= source->strides[ULAB_MAX_DIMS - 3] * source->shape[ULAB_MAX_DIMS-3]; + sarray += source->strides[ULAB_MAX_DIMS - 4]; + i++; + } while(i < source->shape[ULAB_MAX_DIMS - 4]); + #endif + } else { + // o_x had better be a one-dimensional standard iterable + ndarray = ndarray_new_linear_array(mp_obj_get_int(mp_obj_len_maybe(o_x)), NDARRAY_FLOAT); + mp_float_t *array = (mp_float_t *)ndarray->array; + mp_obj_iter_buf_t x_buf; + mp_obj_t x_item, x_iterable = mp_getiter(o_x, &x_buf); + while ((x_item = mp_iternext(x_iterable)) != MP_OBJ_STOP_ITERATION) { + mp_float_t _x = mp_obj_get_float(x_item); + mp_float_t y = p[0]; + for(uint8_t j=0; j < plen-1; j++) { + y *= _x; + y += p[j+1]; + } + *array++ = y; + } + } + m_del(mp_float_t, p, plen); + return MP_OBJ_FROM_PTR(ndarray); +} + +MP_DEFINE_CONST_FUN_OBJ_2(poly_polyval_obj, poly_polyval); +#endif diff --git a/python/port/mod/ulab/numpy/poly/poly.h b/python/port/mod/ulab/numpy/poly/poly.h new file mode 100644 index 000000000..cf66ab193 --- /dev/null +++ b/python/port/mod/ulab/numpy/poly/poly.h @@ -0,0 +1,21 @@ + +/* + * This file is part of the micropython-ulab project, + * + * https://github.com/v923z/micropython-ulab + * + * The MIT License (MIT) + * + * Copyright (c) 2019-2021 Zoltán Vörös +*/ + +#ifndef _POLY_ +#define _POLY_ + +#include "../../ulab.h" +#include "../../ndarray.h" + +MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(poly_polyfit_obj); +MP_DECLARE_CONST_FUN_OBJ_2(poly_polyval_obj); + +#endif diff --git a/python/port/mod/ulab/numpy/stats/stats.c b/python/port/mod/ulab/numpy/stats/stats.c new file mode 100644 index 000000000..9172b2340 --- /dev/null +++ b/python/port/mod/ulab/numpy/stats/stats.c @@ -0,0 +1,52 @@ +/* + * This file is part of the micropython-ulab project, + * + * https://github.com/v923z/micropython-ulab + * + * The MIT License (MIT) + * + * Copyright (c) 2019-2021 Zoltán Vörös + * 2020 Scott Shawcroft for Adafruit Industries + * 2020 Roberto Colistete Jr. + * 2020 Taku Fukada + * +*/ + +#include +#include +#include +#include +#include +#include + +#include "../../ulab.h" +#include "../../ulab_tools.h" +#include "stats.h" + +#if ULAB_MAX_DIMS > 1 +#if ULAB_NUMPY_HAS_TRACE + +//| def trace(m: ulab.numpy.ndarray) -> float: +//| """ +//| :param m: a square matrix +//| +//| Compute the trace of the matrix, the sum of its diagonal elements.""" +//| ... +//| + +static mp_obj_t stats_trace(mp_obj_t oin) { + ndarray_obj_t *ndarray = tools_object_is_square(oin); + mp_float_t trace = 0.0; + for(size_t i=0; i < ndarray->shape[ULAB_MAX_DIMS - 1]; i++) { + int32_t pos = i * (ndarray->strides[ULAB_MAX_DIMS - 1] + ndarray->strides[ULAB_MAX_DIMS - 2]); + trace += ndarray_get_float_index(ndarray->array, ndarray->dtype, pos/ndarray->itemsize); + } + if(ndarray->dtype == NDARRAY_FLOAT) { + return mp_obj_new_float(trace); + } + return mp_obj_new_int_from_float(trace); +} + +MP_DEFINE_CONST_FUN_OBJ_1(stats_trace_obj, stats_trace); +#endif +#endif diff --git a/python/port/mod/ulab/numpy/stats/stats.h b/python/port/mod/ulab/numpy/stats/stats.h new file mode 100644 index 000000000..e5fab5f9f --- /dev/null +++ b/python/port/mod/ulab/numpy/stats/stats.h @@ -0,0 +1,20 @@ + +/* + * This file is part of the micropython-ulab project, + * + * https://github.com/v923z/micropython-ulab + * + * The MIT License (MIT) + * + * Copyright (c) 2019-2021 Zoltán Vörös +*/ + +#ifndef _STATS_ +#define _STATS_ + +#include "../../ulab.h" +#include "../../ndarray.h" + +MP_DECLARE_CONST_FUN_OBJ_1(stats_trace_obj); + +#endif diff --git a/python/port/mod/ulab/numpy/transform/transform.c b/python/port/mod/ulab/numpy/transform/transform.c new file mode 100644 index 000000000..71473f5c6 --- /dev/null +++ b/python/port/mod/ulab/numpy/transform/transform.c @@ -0,0 +1,89 @@ +/* + * This file is part of the micropython-ulab project, + * + * https://github.com/v923z/micropython-ulab + * + * The MIT License (MIT) + * + * Copyright (c) 2019-2021 Zoltán Vörös + * +*/ + +#include +#include +#include +#include +#include +#include + +#include "../../ulab.h" +#include "../../ulab_tools.h" +#include "transform.h" + + +#if ULAB_NUMPY_HAS_DOT +//| def dot(m1: ulab.numpy.ndarray, m2: ulab.numpy.ndarray) -> Union[ulab.numpy.ndarray, float]: +//| """ +//| :param ~ulab.numpy.ndarray m1: a matrix, or a vector +//| :param ~ulab.numpy.ndarray m2: a matrix, or a vector +//| +//| Computes the product of two matrices, or two vectors. In the letter case, the inner product is returned.""" +//| ... +//| + +mp_obj_t transform_dot(mp_obj_t _m1, mp_obj_t _m2) { + // TODO: should the results be upcast? + // This implements 2D operations only! + if(!mp_obj_is_type(_m1, &ulab_ndarray_type) || !mp_obj_is_type(_m2, &ulab_ndarray_type)) { + mp_raise_TypeError(translate("arguments must be ndarrays")); + } + ndarray_obj_t *m1 = MP_OBJ_TO_PTR(_m1); + ndarray_obj_t *m2 = MP_OBJ_TO_PTR(_m2); + uint8_t *array1 = (uint8_t *)m1->array; + uint8_t *array2 = (uint8_t *)m2->array; + + mp_float_t (*func1)(void *) = ndarray_get_float_function(m1->dtype); + mp_float_t (*func2)(void *) = ndarray_get_float_function(m2->dtype); + + if(m1->shape[ULAB_MAX_DIMS - 1] != m2->shape[ULAB_MAX_DIMS - m2->ndim]) { + mp_raise_ValueError(translate("dimensions do not match")); + } + uint8_t ndim = MIN(m1->ndim, m2->ndim); + size_t shape1 = m1->ndim == 2 ? m1->shape[ULAB_MAX_DIMS - m1->ndim] : 1; + size_t shape2 = m2->ndim == 2 ? m2->shape[ULAB_MAX_DIMS - 1] : 1; + + size_t *shape = NULL; + if(ndim == 2) { // matrix times matrix -> matrix + shape = ndarray_shape_vector(0, 0, shape1, shape2); + } else { // matrix times vector -> vector, vector times vector -> vector (size 1) + shape = ndarray_shape_vector(0, 0, 0, shape1); + } + ndarray_obj_t *results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT); + mp_float_t *rarray = (mp_float_t *)results->array; + + for(size_t i=0; i < shape1; i++) { // rows of m1 + for(size_t j=0; j < shape2; j++) { // columns of m2 + mp_float_t dot = 0.0; + for(size_t k=0; k < m1->shape[ULAB_MAX_DIMS - 1]; k++) { + // (i, k) * (k, j) + dot += func1(array1) * func2(array2); + array1 += m1->strides[ULAB_MAX_DIMS - 1]; + array2 += m2->strides[ULAB_MAX_DIMS - m2->ndim]; + } + *rarray++ = dot; + array1 -= m1->strides[ULAB_MAX_DIMS - 1] * m1->shape[ULAB_MAX_DIMS - 1]; + array2 -= m2->strides[ULAB_MAX_DIMS - m2->ndim] * m2->shape[ULAB_MAX_DIMS - m2->ndim]; + array2 += m2->strides[ULAB_MAX_DIMS - 1]; + } + array1 += m1->strides[ULAB_MAX_DIMS - m1->ndim]; + array2 = m2->array; + } + if((m1->ndim * m2->ndim) == 1) { // return a scalar, if product of two vectors + return mp_obj_new_float(*(--rarray)); + } else { + return MP_OBJ_FROM_PTR(results); + } +} + +MP_DEFINE_CONST_FUN_OBJ_2(transform_dot_obj, transform_dot); +#endif diff --git a/python/port/mod/ulab/numpy/transform/transform.h b/python/port/mod/ulab/numpy/transform/transform.h new file mode 100644 index 000000000..ba4194e3b --- /dev/null +++ b/python/port/mod/ulab/numpy/transform/transform.h @@ -0,0 +1,28 @@ +/* + * This file is part of the micropython-ulab project, + * + * https://github.com/v923z/micropython-ulab + * + * The MIT License (MIT) + * + * Copyright (c) 2019-2021 Zoltán Vörös + * +*/ + +#ifndef _TRANSFORM_ +#define _TRANSFORM_ + +#include +#include +#include +#include +#include +#include + +#include "../../ulab.h" +#include "../../ulab_tools.h" +#include "transform.h" + +MP_DECLARE_CONST_FUN_OBJ_2(transform_dot_obj); + +#endif diff --git a/python/port/mod/ulab/numpy/vector/vector.c b/python/port/mod/ulab/numpy/vector/vector.c new file mode 100644 index 000000000..bc8326c6d --- /dev/null +++ b/python/port/mod/ulab/numpy/vector/vector.c @@ -0,0 +1,635 @@ + +/* + * This file is part of the micropython-ulab project, + * + * https://github.com/v923z/micropython-ulab + * + * The MIT License (MIT) + * + * Copyright (c) 2019-2021 Zoltán Vörös + * 2020 Jeff Epler for Adafruit Industries + * 2020 Scott Shawcroft for Adafruit Industries + * 2020 Taku Fukada +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include "../../ulab.h" +#include "../../ulab_tools.h" +#include "vector.h" + +//| """Element-by-element functions +//| +//| These functions can operate on numbers, 1-D iterables, and arrays of 1 to 4 dimensions by +//| applying the function to every element in the array. This is typically +//| much more efficient than expressing the same operation as a Python loop.""" +//| +//| from ulab import _DType, _ArrayLike +//| + +static mp_obj_t vectorise_generic_vector(mp_obj_t o_in, mp_float_t (*f)(mp_float_t)) { + // Return a single value, if o_in is not iterable + if(mp_obj_is_float(o_in) || mp_obj_is_int(o_in)) { + return mp_obj_new_float(f(mp_obj_get_float(o_in))); + } + ndarray_obj_t *ndarray = NULL; + if(mp_obj_is_type(o_in, &ulab_ndarray_type)) { + ndarray_obj_t *source = MP_OBJ_TO_PTR(o_in); + uint8_t *sarray = (uint8_t *)source->array; + ndarray = ndarray_new_dense_ndarray(source->ndim, source->shape, NDARRAY_FLOAT); + mp_float_t *array = (mp_float_t *)ndarray->array; + + #if ULAB_VECTORISE_USES_FUN_POINTER + + mp_float_t (*func)(void *) = ndarray_get_float_function(source->dtype); + + #if ULAB_MAX_DIMS > 3 + size_t i = 0; + do { + #endif + #if ULAB_MAX_DIMS > 2 + size_t j = 0; + do { + #endif + #if ULAB_MAX_DIMS > 1 + size_t k = 0; + do { + #endif + size_t l = 0; + do { + mp_float_t value = func(sarray); + *array++ = f(value); + sarray += source->strides[ULAB_MAX_DIMS - 1]; + l++; + } while(l < source->shape[ULAB_MAX_DIMS - 1]); + #if ULAB_MAX_DIMS > 1 + sarray -= source->strides[ULAB_MAX_DIMS - 1] * source->shape[ULAB_MAX_DIMS-1]; + sarray += source->strides[ULAB_MAX_DIMS - 2]; + k++; + } while(k < source->shape[ULAB_MAX_DIMS - 2]); + #endif /* ULAB_MAX_DIMS > 1 */ + #if ULAB_MAX_DIMS > 2 + sarray -= source->strides[ULAB_MAX_DIMS - 2] * source->shape[ULAB_MAX_DIMS-2]; + sarray += source->strides[ULAB_MAX_DIMS - 3]; + j++; + } while(j < source->shape[ULAB_MAX_DIMS - 3]); + #endif /* ULAB_MAX_DIMS > 2 */ + #if ULAB_MAX_DIMS > 3 + sarray -= source->strides[ULAB_MAX_DIMS - 3] * source->shape[ULAB_MAX_DIMS-3]; + sarray += source->strides[ULAB_MAX_DIMS - 4]; + i++; + } while(i < source->shape[ULAB_MAX_DIMS - 4]); + #endif /* ULAB_MAX_DIMS > 3 */ + #else + if(source->dtype == NDARRAY_UINT8) { + ITERATE_VECTOR(uint8_t, array, source, sarray); + } else if(source->dtype == NDARRAY_INT8) { + ITERATE_VECTOR(int8_t, array, source, sarray); + } else if(source->dtype == NDARRAY_UINT16) { + ITERATE_VECTOR(uint16_t, array, source, sarray); + } else if(source->dtype == NDARRAY_INT16) { + ITERATE_VECTOR(int16_t, array, source, sarray); + } else { + ITERATE_VECTOR(mp_float_t, array, source, sarray); + } + #endif /* ULAB_VECTORISE_USES_FUN_POINTER */ + } else { + ndarray = ndarray_from_mp_obj(o_in, 0); + mp_float_t *array = (mp_float_t *)ndarray->array; + for(size_t i = 0; i < ndarray->len; i++) { + *array = f(*array); + array++; + } + } + return MP_OBJ_FROM_PTR(ndarray); +} + +#if ULAB_NUMPY_HAS_ACOS +//| def acos(a: _ArrayLike) -> ulab.ndarray: +//| """Computes the inverse cosine function""" +//| ... +//| + +MATH_FUN_1(acos, acos); +MP_DEFINE_CONST_FUN_OBJ_1(vectorise_acos_obj, vectorise_acos); +#endif + +#if ULAB_NUMPY_HAS_ACOSH +//| def acosh(a: _ArrayLike) -> ulab.ndarray: +//| """Computes the inverse hyperbolic cosine function""" +//| ... +//| + +MATH_FUN_1(acosh, acosh); +MP_DEFINE_CONST_FUN_OBJ_1(vectorise_acosh_obj, vectorise_acosh); +#endif + +#if ULAB_NUMPY_HAS_ASIN +//| def asin(a: _ArrayLike) -> ulab.ndarray: +//| """Computes the inverse sine function""" +//| ... +//| + +MATH_FUN_1(asin, asin); +MP_DEFINE_CONST_FUN_OBJ_1(vectorise_asin_obj, vectorise_asin); +#endif + +#if ULAB_NUMPY_HAS_ASINH +//| def asinh(a: _ArrayLike) -> ulab.ndarray: +//| """Computes the inverse hyperbolic sine function""" +//| ... +//| + +MATH_FUN_1(asinh, asinh); +MP_DEFINE_CONST_FUN_OBJ_1(vectorise_asinh_obj, vectorise_asinh); +#endif + +#if ULAB_NUMPY_HAS_AROUND +//| def around(a: _ArrayLike, *, decimals: int = 0) -> ulab.ndarray: +//| """Returns a new float array in which each element is rounded to +//| ``decimals`` places.""" +//| ... +//| + +mp_obj_t vectorise_around(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none} }, + { MP_QSTR_decimals, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0 } } + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + if(!mp_obj_is_type(args[0].u_obj, &ulab_ndarray_type)) { + mp_raise_TypeError(translate("first argument must be an ndarray")); + } + int8_t n = args[1].u_int; + mp_float_t mul = MICROPY_FLOAT_C_FUN(pow)(10.0, n); + ndarray_obj_t *source = MP_OBJ_TO_PTR(args[0].u_obj); + ndarray_obj_t *ndarray = ndarray_new_dense_ndarray(source->ndim, source->shape, NDARRAY_FLOAT); + mp_float_t *narray = (mp_float_t *)ndarray->array; + uint8_t *sarray = (uint8_t *)source->array; + + mp_float_t (*func)(void *) = ndarray_get_float_function(source->dtype); + + #if ULAB_MAX_DIMS > 3 + size_t i = 0; + do { + #endif + #if ULAB_MAX_DIMS > 2 + size_t j = 0; + do { + #endif + #if ULAB_MAX_DIMS > 1 + size_t k = 0; + do { + #endif + size_t l = 0; + do { + mp_float_t f = func(sarray); + *narray++ = MICROPY_FLOAT_C_FUN(round)(f * mul) / mul; + sarray += source->strides[ULAB_MAX_DIMS - 1]; + l++; + } while(l < source->shape[ULAB_MAX_DIMS - 1]); + #if ULAB_MAX_DIMS > 1 + sarray -= source->strides[ULAB_MAX_DIMS - 1] * source->shape[ULAB_MAX_DIMS-1]; + sarray += source->strides[ULAB_MAX_DIMS - 2]; + k++; + } while(k < source->shape[ULAB_MAX_DIMS - 2]); + #endif + #if ULAB_MAX_DIMS > 2 + sarray -= source->strides[ULAB_MAX_DIMS - 2] * source->shape[ULAB_MAX_DIMS-2]; + sarray += source->strides[ULAB_MAX_DIMS - 3]; + j++; + } while(j < source->shape[ULAB_MAX_DIMS - 3]); + #endif + #if ULAB_MAX_DIMS > 3 + sarray -= source->strides[ULAB_MAX_DIMS - 3] * source->shape[ULAB_MAX_DIMS-3]; + sarray += source->strides[ULAB_MAX_DIMS - 4]; + i++; + } while(i < source->shape[ULAB_MAX_DIMS - 4]); + #endif + return MP_OBJ_FROM_PTR(ndarray); +} + +MP_DEFINE_CONST_FUN_OBJ_KW(vectorise_around_obj, 1, vectorise_around); +#endif + +#if ULAB_NUMPY_HAS_ATAN +//| def atan(a: _ArrayLike) -> ulab.ndarray: +//| """Computes the inverse tangent function; the return values are in the +//| range [-pi/2,pi/2].""" +//| ... +//| + +MATH_FUN_1(atan, atan); +MP_DEFINE_CONST_FUN_OBJ_1(vectorise_atan_obj, vectorise_atan); +#endif + +#if ULAB_NUMPY_HAS_ARCTAN2 +//| def arctan2(ya: _ArrayLike, xa: _ArrayLike) -> ulab.ndarray: +//| """Computes the inverse tangent function of y/x; the return values are in +//| the range [-pi, pi].""" +//| ... +//| + +mp_obj_t vectorise_arctan2(mp_obj_t y, mp_obj_t x) { + ndarray_obj_t *ndarray_x = ndarray_from_mp_obj(x, 0); + ndarray_obj_t *ndarray_y = ndarray_from_mp_obj(y, 0); + + uint8_t ndim = 0; + size_t *shape = m_new(size_t, ULAB_MAX_DIMS); + int32_t *xstrides = m_new(int32_t, ULAB_MAX_DIMS); + int32_t *ystrides = m_new(int32_t, ULAB_MAX_DIMS); + if(!ndarray_can_broadcast(ndarray_x, ndarray_y, &ndim, shape, xstrides, ystrides)) { + mp_raise_ValueError(translate("operands could not be broadcast together")); + m_del(size_t, shape, ULAB_MAX_DIMS); + m_del(int32_t, xstrides, ULAB_MAX_DIMS); + m_del(int32_t, ystrides, ULAB_MAX_DIMS); + } + + uint8_t *xarray = (uint8_t *)ndarray_x->array; + uint8_t *yarray = (uint8_t *)ndarray_y->array; + + ndarray_obj_t *results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT); + mp_float_t *rarray = (mp_float_t *)results->array; + + mp_float_t (*funcx)(void *) = ndarray_get_float_function(ndarray_x->dtype); + mp_float_t (*funcy)(void *) = ndarray_get_float_function(ndarray_y->dtype); + + #if ULAB_MAX_DIMS > 3 + size_t i = 0; + do { + #endif + #if ULAB_MAX_DIMS > 2 + size_t j = 0; + do { + #endif + #if ULAB_MAX_DIMS > 1 + size_t k = 0; + do { + #endif + size_t l = 0; + do { + mp_float_t _x = funcx(xarray); + mp_float_t _y = funcy(yarray); + *rarray++ = MICROPY_FLOAT_C_FUN(atan2)(_y, _x); + xarray += xstrides[ULAB_MAX_DIMS - 1]; + yarray += ystrides[ULAB_MAX_DIMS - 1]; + l++; + } while(l < results->shape[ULAB_MAX_DIMS - 1]); + #if ULAB_MAX_DIMS > 1 + xarray -= xstrides[ULAB_MAX_DIMS - 1] * results->shape[ULAB_MAX_DIMS-1]; + xarray += xstrides[ULAB_MAX_DIMS - 2]; + yarray -= ystrides[ULAB_MAX_DIMS - 1] * results->shape[ULAB_MAX_DIMS-1]; + yarray += ystrides[ULAB_MAX_DIMS - 2]; + k++; + } while(k < results->shape[ULAB_MAX_DIMS - 2]); + #endif + #if ULAB_MAX_DIMS > 2 + xarray -= xstrides[ULAB_MAX_DIMS - 2] * results->shape[ULAB_MAX_DIMS-2]; + xarray += xstrides[ULAB_MAX_DIMS - 3]; + yarray -= ystrides[ULAB_MAX_DIMS - 2] * results->shape[ULAB_MAX_DIMS-2]; + yarray += ystrides[ULAB_MAX_DIMS - 3]; + j++; + } while(j < results->shape[ULAB_MAX_DIMS - 3]); + #endif + #if ULAB_MAX_DIMS > 3 + xarray -= xstrides[ULAB_MAX_DIMS - 3] * results->shape[ULAB_MAX_DIMS-3]; + xarray += xstrides[ULAB_MAX_DIMS - 4]; + yarray -= ystrides[ULAB_MAX_DIMS - 3] * results->shape[ULAB_MAX_DIMS-3]; + yarray += ystrides[ULAB_MAX_DIMS - 4]; + i++; + } while(i < results->shape[ULAB_MAX_DIMS - 4]); + #endif + + return MP_OBJ_FROM_PTR(results); +} + +MP_DEFINE_CONST_FUN_OBJ_2(vectorise_arctan2_obj, vectorise_arctan2); +#endif /* ULAB_VECTORISE_HAS_ARCTAN2 */ + +#if ULAB_NUMPY_HAS_ATANH +//| def atanh(a: _ArrayLike) -> ulab.ndarray: +//| """Computes the inverse hyperbolic tangent function""" +//| ... +//| + +MATH_FUN_1(atanh, atanh); +MP_DEFINE_CONST_FUN_OBJ_1(vectorise_atanh_obj, vectorise_atanh); +#endif + +#if ULAB_NUMPY_HAS_CEIL +//| def ceil(a: _ArrayLike) -> ulab.ndarray: +//| """Rounds numbers up to the next whole number""" +//| ... +//| + +MATH_FUN_1(ceil, ceil); +MP_DEFINE_CONST_FUN_OBJ_1(vectorise_ceil_obj, vectorise_ceil); +#endif + +#if ULAB_NUMPY_HAS_COS +//| def cos(a: _ArrayLike) -> ulab.ndarray: +//| """Computes the cosine function""" +//| ... +//| + +MATH_FUN_1(cos, cos); +MP_DEFINE_CONST_FUN_OBJ_1(vectorise_cos_obj, vectorise_cos); +#endif + +#if ULAB_NUMPY_HAS_COSH +//| def cosh(a: _ArrayLike) -> ulab.ndarray: +//| """Computes the hyperbolic cosine function""" +//| ... +//| + +MATH_FUN_1(cosh, cosh); +MP_DEFINE_CONST_FUN_OBJ_1(vectorise_cosh_obj, vectorise_cosh); +#endif + +#if ULAB_NUMPY_HAS_DEGREES +//| def degrees(a: _ArrayLike) -> ulab.ndarray: +//| """Converts angles from radians to degrees""" +//| ... +//| + +static mp_float_t vectorise_degrees_(mp_float_t value) { + return value * MICROPY_FLOAT_CONST(180.0) / MP_PI; +} + +static mp_obj_t vectorise_degrees(mp_obj_t x_obj) { + return vectorise_generic_vector(x_obj, vectorise_degrees_); +} + +MP_DEFINE_CONST_FUN_OBJ_1(vectorise_degrees_obj, vectorise_degrees); +#endif + +#if ULAB_SCIPY_SPECIAL_HAS_ERF +//| def erf(a: _ArrayLike) -> ulab.ndarray: +//| """Computes the error function, which has applications in statistics""" +//| ... +//| + +MATH_FUN_1(erf, erf); +MP_DEFINE_CONST_FUN_OBJ_1(vectorise_erf_obj, vectorise_erf); +#endif + +#if ULAB_SCIPY_SPECIAL_HAS_ERFC +//| def erfc(a: _ArrayLike) -> ulab.ndarray: +//| """Computes the complementary error function, which has applications in statistics""" +//| ... +//| + +MATH_FUN_1(erfc, erfc); +MP_DEFINE_CONST_FUN_OBJ_1(vectorise_erfc_obj, vectorise_erfc); +#endif + +#if ULAB_NUMPY_HAS_EXP +//| def exp(a: _ArrayLike) -> ulab.ndarray: +//| """Computes the exponent function.""" +//| ... +//| + +MATH_FUN_1(exp, exp); +MP_DEFINE_CONST_FUN_OBJ_1(vectorise_exp_obj, vectorise_exp); +#endif + +#if ULAB_NUMPY_HAS_EXPM1 +//| def expm1(a: _ArrayLike) -> ulab.ndarray: +//| """Computes $e^x-1$. In certain applications, using this function preserves numeric accuracy better than the `exp` function.""" +//| ... +//| + +MATH_FUN_1(expm1, expm1); +MP_DEFINE_CONST_FUN_OBJ_1(vectorise_expm1_obj, vectorise_expm1); +#endif + +#if ULAB_NUMPY_HAS_FLOOR +//| def floor(a: _ArrayLike) -> ulab.ndarray: +//| """Rounds numbers up to the next whole number""" +//| ... +//| + +MATH_FUN_1(floor, floor); +MP_DEFINE_CONST_FUN_OBJ_1(vectorise_floor_obj, vectorise_floor); +#endif + +#if ULAB_SCIPY_SPECIAL_HAS_GAMMA +//| def gamma(a: _ArrayLike) -> ulab.ndarray: +//| """Computes the gamma function""" +//| ... +//| + +MATH_FUN_1(gamma, tgamma); +MP_DEFINE_CONST_FUN_OBJ_1(vectorise_gamma_obj, vectorise_gamma); +#endif + +#if ULAB_SCIPY_SPECIAL_HAS_GAMMALN +//| def lgamma(a: _ArrayLike) -> ulab.ndarray: +//| """Computes the natural log of the gamma function""" +//| ... +//| + +MATH_FUN_1(lgamma, lgamma); +MP_DEFINE_CONST_FUN_OBJ_1(vectorise_lgamma_obj, vectorise_lgamma); +#endif + +#if ULAB_NUMPY_HAS_LOG +//| def log(a: _ArrayLike) -> ulab.ndarray: +//| """Computes the natural log""" +//| ... +//| + +MATH_FUN_1(log, log); +MP_DEFINE_CONST_FUN_OBJ_1(vectorise_log_obj, vectorise_log); +#endif + +#if ULAB_NUMPY_HAS_LOG10 +//| def log10(a: _ArrayLike) -> ulab.ndarray: +//| """Computes the log base 10""" +//| ... +//| + +MATH_FUN_1(log10, log10); +MP_DEFINE_CONST_FUN_OBJ_1(vectorise_log10_obj, vectorise_log10); +#endif + +#if ULAB_NUMPY_HAS_LOG2 +//| def log2(a: _ArrayLike) -> ulab.ndarray: +//| """Computes the log base 2""" +//| ... +//| + +MATH_FUN_1(log2, log2); +MP_DEFINE_CONST_FUN_OBJ_1(vectorise_log2_obj, vectorise_log2); +#endif + +#if ULAB_NUMPY_HAS_RADIANS +//| def radians(a: _ArrayLike) -> ulab.ndarray: +//| """Converts angles from degrees to radians""" +//| ... +//| + +static mp_float_t vectorise_radians_(mp_float_t value) { + return value * MP_PI / MICROPY_FLOAT_CONST(180.0); +} + +static mp_obj_t vectorise_radians(mp_obj_t x_obj) { + return vectorise_generic_vector(x_obj, vectorise_radians_); +} + +MP_DEFINE_CONST_FUN_OBJ_1(vectorise_radians_obj, vectorise_radians); +#endif + +#if ULAB_NUMPY_HAS_SIN +//| def sin(a: _ArrayLike) -> ulab.ndarray: +//| """Computes the sine function""" +//| ... +//| + +MATH_FUN_1(sin, sin); +MP_DEFINE_CONST_FUN_OBJ_1(vectorise_sin_obj, vectorise_sin); +#endif + +#if ULAB_NUMPY_HAS_SINH +//| def sinh(a: _ArrayLike) -> ulab.ndarray: +//| """Computes the hyperbolic sine""" +//| ... +//| + +MATH_FUN_1(sinh, sinh); +MP_DEFINE_CONST_FUN_OBJ_1(vectorise_sinh_obj, vectorise_sinh); +#endif + +#if ULAB_NUMPY_HAS_SQRT +//| def sqrt(a: _ArrayLike) -> ulab.ndarray: +//| """Computes the square root""" +//| ... +//| + +MATH_FUN_1(sqrt, sqrt); +MP_DEFINE_CONST_FUN_OBJ_1(vectorise_sqrt_obj, vectorise_sqrt); +#endif + +#if ULAB_NUMPY_HAS_TAN +//| def tan(a: _ArrayLike) -> ulab.ndarray: +//| """Computes the tangent""" +//| ... +//| + +MATH_FUN_1(tan, tan); +MP_DEFINE_CONST_FUN_OBJ_1(vectorise_tan_obj, vectorise_tan); +#endif + +#if ULAB_NUMPY_HAS_TANH +//| def tanh(a: _ArrayLike) -> ulab.ndarray: +//| """Computes the hyperbolic tangent""" +//| ... + +MATH_FUN_1(tanh, tanh); +MP_DEFINE_CONST_FUN_OBJ_1(vectorise_tanh_obj, vectorise_tanh); +#endif + +#if ULAB_NUMPY_HAS_VECTORIZE +static mp_obj_t vectorise_vectorized_function_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { + (void) n_args; + (void) n_kw; + vectorized_function_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_obj_t avalue[1]; + mp_obj_t fvalue; + if(mp_obj_is_type(args[0], &ulab_ndarray_type)) { + ndarray_obj_t *source = MP_OBJ_TO_PTR(args[0]); + ndarray_obj_t *ndarray = ndarray_new_dense_ndarray(source->ndim, source->shape, self->otypes); + for(size_t i=0; i < source->len; i++) { + avalue[0] = mp_binary_get_val_array(source->dtype, source->array, i); + fvalue = self->type->call(self->fun, 1, 0, avalue); + ndarray_set_value(self->otypes, ndarray->array, i, fvalue); + } + return MP_OBJ_FROM_PTR(ndarray); + } else if(mp_obj_is_type(args[0], &mp_type_tuple) || mp_obj_is_type(args[0], &mp_type_list) || + mp_obj_is_type(args[0], &mp_type_range)) { // i.e., the input is a generic iterable + size_t len = (size_t)mp_obj_get_int(mp_obj_len_maybe(args[0])); + ndarray_obj_t *ndarray = ndarray_new_linear_array(len, self->otypes); + mp_obj_iter_buf_t iter_buf; + mp_obj_t iterable = mp_getiter(args[0], &iter_buf); + size_t i=0; + while ((avalue[0] = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) { + fvalue = self->type->call(self->fun, 1, 0, avalue); + ndarray_set_value(self->otypes, ndarray->array, i, fvalue); + i++; + } + return MP_OBJ_FROM_PTR(ndarray); + } else if(mp_obj_is_int(args[0]) || mp_obj_is_float(args[0])) { + ndarray_obj_t *ndarray = ndarray_new_linear_array(1, self->otypes); + fvalue = self->type->call(self->fun, 1, 0, args); + ndarray_set_value(self->otypes, ndarray->array, 0, fvalue); + return MP_OBJ_FROM_PTR(ndarray); + } else { + mp_raise_ValueError(translate("wrong input type")); + } + return mp_const_none; +} + +const mp_obj_type_t vectorise_function_type = { + { &mp_type_type }, + .name = MP_QSTR_, + .call = vectorise_vectorized_function_call, +}; + +//| def vectorize( +//| f: Union[Callable[[int], float], Callable[[float], float]], +//| *, +//| otypes: Optional[_DType] = None +//| ) -> Callable[[_ArrayLike], ulab.ndarray]: +//| """ +//| :param callable f: The function to wrap +//| :param otypes: List of array types that may be returned by the function. None is interpreted to mean the return value is float. +//| +//| Wrap a Python function ``f`` so that it can be applied to arrays. +//| The callable must return only values of the types specified by ``otypes``, or the result is undefined.""" +//| ... +//| + +static mp_obj_t vectorise_vectorize(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none} }, + { MP_QSTR_otypes, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = mp_const_none} } + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + const mp_obj_type_t *type = mp_obj_get_type(args[0].u_obj); + if(type->call == NULL) { + mp_raise_TypeError(translate("first argument must be a callable")); + } + mp_obj_t _otypes = args[1].u_obj; + uint8_t otypes = NDARRAY_FLOAT; + if(_otypes == mp_const_none) { + // TODO: is this what numpy does? + otypes = NDARRAY_FLOAT; + } else if(mp_obj_is_int(_otypes)) { + otypes = mp_obj_get_int(_otypes); + if(otypes != NDARRAY_FLOAT && otypes != NDARRAY_UINT8 && otypes != NDARRAY_INT8 && + otypes != NDARRAY_UINT16 && otypes != NDARRAY_INT16) { + mp_raise_ValueError(translate("wrong output type")); + } + } + else { + mp_raise_ValueError(translate("wrong output type")); + } + vectorized_function_obj_t *function = m_new_obj(vectorized_function_obj_t); + function->base.type = &vectorise_function_type; + function->otypes = otypes; + function->fun = args[0].u_obj; + function->type = type; + return MP_OBJ_FROM_PTR(function); +} + +MP_DEFINE_CONST_FUN_OBJ_KW(vectorise_vectorize_obj, 1, vectorise_vectorize); +#endif diff --git a/python/port/mod/ulab/numpy/vector/vector.h b/python/port/mod/ulab/numpy/vector/vector.h new file mode 100644 index 000000000..6b00a221b --- /dev/null +++ b/python/port/mod/ulab/numpy/vector/vector.h @@ -0,0 +1,156 @@ + +/* + * This file is part of the micropython-ulab project, + * + * https://github.com/v923z/micropython-ulab + * + * The MIT License (MIT) + * + * Copyright (c) 2019-2021 Zoltán Vörös +*/ + +#ifndef _VECTOR_ +#define _VECTOR_ + +#include "../../ulab.h" +#include "../../ndarray.h" + +MP_DECLARE_CONST_FUN_OBJ_1(vectorise_acos_obj); +MP_DECLARE_CONST_FUN_OBJ_1(vectorise_acosh_obj); +MP_DECLARE_CONST_FUN_OBJ_2(vectorise_arctan2_obj); +MP_DECLARE_CONST_FUN_OBJ_KW(vectorise_around_obj); +MP_DECLARE_CONST_FUN_OBJ_1(vectorise_asin_obj); +MP_DECLARE_CONST_FUN_OBJ_1(vectorise_asinh_obj); +MP_DECLARE_CONST_FUN_OBJ_1(vectorise_atan_obj); +MP_DECLARE_CONST_FUN_OBJ_1(vectorise_atanh_obj); +MP_DECLARE_CONST_FUN_OBJ_1(vectorise_ceil_obj); +MP_DECLARE_CONST_FUN_OBJ_1(vectorise_cos_obj); +MP_DECLARE_CONST_FUN_OBJ_1(vectorise_cosh_obj); +MP_DECLARE_CONST_FUN_OBJ_1(vectorise_degrees_obj); +MP_DECLARE_CONST_FUN_OBJ_1(vectorise_erf_obj); +MP_DECLARE_CONST_FUN_OBJ_1(vectorise_erfc_obj); +MP_DECLARE_CONST_FUN_OBJ_1(vectorise_exp_obj); +MP_DECLARE_CONST_FUN_OBJ_1(vectorise_expm1_obj); +MP_DECLARE_CONST_FUN_OBJ_1(vectorise_floor_obj); +MP_DECLARE_CONST_FUN_OBJ_1(vectorise_gamma_obj); +MP_DECLARE_CONST_FUN_OBJ_1(vectorise_lgamma_obj); +MP_DECLARE_CONST_FUN_OBJ_1(vectorise_log_obj); +MP_DECLARE_CONST_FUN_OBJ_1(vectorise_log10_obj); +MP_DECLARE_CONST_FUN_OBJ_1(vectorise_log2_obj); +MP_DECLARE_CONST_FUN_OBJ_1(vectorise_radians_obj); +MP_DECLARE_CONST_FUN_OBJ_1(vectorise_sin_obj); +MP_DECLARE_CONST_FUN_OBJ_1(vectorise_sinh_obj); +MP_DECLARE_CONST_FUN_OBJ_1(vectorise_sqrt_obj); +MP_DECLARE_CONST_FUN_OBJ_1(vectorise_tan_obj); +MP_DECLARE_CONST_FUN_OBJ_1(vectorise_tanh_obj); +MP_DECLARE_CONST_FUN_OBJ_KW(vectorise_vectorize_obj); + +typedef struct _vectorized_function_obj_t { + mp_obj_base_t base; + uint8_t otypes; + mp_obj_t fun; + const mp_obj_type_t *type; +} vectorized_function_obj_t; + +#if ULAB_HAS_FUNCTION_ITERATOR +#define ITERATE_VECTOR(type, array, source, sarray)\ +({\ + size_t *scoords = ndarray_new_coords((source)->ndim);\ + for(size_t i=0; i < (source)->len/(source)->shape[ULAB_MAX_DIMS -1]; i++) {\ + for(size_t l=0; l < (source)->shape[ULAB_MAX_DIMS - 1]; l++) {\ + *(array)++ = f(*((type *)(sarray)));\ + (sarray) += (source)->strides[ULAB_MAX_DIMS - 1];\ + }\ + ndarray_rewind_array((source)->ndim, sarray, (source)->shape, (source)->strides, scoords);\ + }\ +}) + +#else + +#if ULAB_MAX_DIMS == 4 +#define ITERATE_VECTOR(type, array, source, sarray) do {\ + size_t i=0;\ + do {\ + size_t j = 0;\ + do {\ + size_t k = 0;\ + do {\ + size_t l = 0;\ + do {\ + *(array)++ = f(*((type *)(sarray)));\ + (sarray) += (source)->strides[ULAB_MAX_DIMS - 1];\ + l++;\ + } while(l < (source)->shape[ULAB_MAX_DIMS-1]);\ + (sarray) -= (source)->strides[ULAB_MAX_DIMS - 1] * (source)->shape[ULAB_MAX_DIMS-1];\ + (sarray) += (source)->strides[ULAB_MAX_DIMS - 2];\ + k++;\ + } while(k < (source)->shape[ULAB_MAX_DIMS-2]);\ + (sarray) -= (source)->strides[ULAB_MAX_DIMS - 2] * (source)->shape[ULAB_MAX_DIMS-2];\ + (sarray) += (source)->strides[ULAB_MAX_DIMS - 3];\ + j++;\ + } while(j < (source)->shape[ULAB_MAX_DIMS-3]);\ + (sarray) -= (source)->strides[ULAB_MAX_DIMS - 3] * (source)->shape[ULAB_MAX_DIMS-3];\ + (sarray) += (source)->strides[ULAB_MAX_DIMS - 4];\ + i++;\ + } while(i < (source)->shape[ULAB_MAX_DIMS-4]);\ +} while(0) +#endif /* ULAB_MAX_DIMS == 4 */ + +#if ULAB_MAX_DIMS == 3 +#define ITERATE_VECTOR(type, array, source, sarray) do {\ + size_t j = 0;\ + do {\ + size_t k = 0;\ + do {\ + size_t l = 0;\ + do {\ + *(array)++ = f(*((type *)(sarray)));\ + (sarray) += (source)->strides[ULAB_MAX_DIMS - 1];\ + l++;\ + } while(l < (source)->shape[ULAB_MAX_DIMS-1]);\ + (sarray) -= (source)->strides[ULAB_MAX_DIMS - 1] * (source)->shape[ULAB_MAX_DIMS-1];\ + (sarray) += (source)->strides[ULAB_MAX_DIMS - 2];\ + k++;\ + } while(k < (source)->shape[ULAB_MAX_DIMS-2]);\ + (sarray) -= (source)->strides[ULAB_MAX_DIMS - 2] * (source)->shape[ULAB_MAX_DIMS-2];\ + (sarray) += (source)->strides[ULAB_MAX_DIMS - 3];\ + j++;\ + } while(j < (source)->shape[ULAB_MAX_DIMS-3]);\ +} while(0) +#endif /* ULAB_MAX_DIMS == 3 */ + +#if ULAB_MAX_DIMS == 2 +#define ITERATE_VECTOR(type, array, source, sarray) do {\ + size_t k = 0;\ + do {\ + size_t l = 0;\ + do {\ + *(array)++ = f(*((type *)(sarray)));\ + (sarray) += (source)->strides[ULAB_MAX_DIMS - 1];\ + l++;\ + } while(l < (source)->shape[ULAB_MAX_DIMS-1]);\ + (sarray) -= (source)->strides[ULAB_MAX_DIMS - 1] * (source)->shape[ULAB_MAX_DIMS-1];\ + (sarray) += (source)->strides[ULAB_MAX_DIMS - 2];\ + k++;\ + } while(k < (source)->shape[ULAB_MAX_DIMS-2]);\ +} while(0) +#endif /* ULAB_MAX_DIMS == 2 */ + +#if ULAB_MAX_DIMS == 1 +#define ITERATE_VECTOR(type, array, source, sarray) do {\ + size_t l = 0;\ + do {\ + *(array)++ = f(*((type *)(sarray)));\ + (sarray) += (source)->strides[ULAB_MAX_DIMS - 1];\ + l++;\ + } while(l < (source)->shape[ULAB_MAX_DIMS-1]);\ +} while(0) +#endif /* ULAB_MAX_DIMS == 1 */ +#endif /* ULAB_HAS_FUNCTION_ITERATOR */ + +#define MATH_FUN_1(py_name, c_name) \ + static mp_obj_t vectorise_ ## py_name(mp_obj_t x_obj) { \ + return vectorise_generic_vector(x_obj, MICROPY_FLOAT_C_FUN(c_name)); \ +} + +#endif /* _VECTOR_ */ diff --git a/python/port/mod/ulab/scipy/linalg/linalg.c b/python/port/mod/ulab/scipy/linalg/linalg.c new file mode 100644 index 000000000..0f1b933af --- /dev/null +++ b/python/port/mod/ulab/scipy/linalg/linalg.c @@ -0,0 +1,279 @@ + +/* + * This file is part of the micropython-ulab project, + * + * https://github.com/v923z/micropython-ulab + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Vikas Udupa + * +*/ + +#include +#include +#include +#include +#include +#include + +#include "../../ulab.h" +#include "../../ulab_tools.h" +#include "linalg.h" + +#if ULAB_SCIPY_HAS_LINALG_MODULE +//| +//| import ulab.scipy +//| +//| """Linear algebra functions""" +//| + +#if ULAB_MAX_DIMS > 1 + +#define TOLERANCE 0.0000001 + +//| def solve_triangular(A: ulab.numpy.ndarray, b: ulab.numpy.ndarray, lower: bool) -> ulab.numpy.ndarray: +//| """ +//| :param ~ulab.numpy.ndarray A: a matrix +//| :param ~ulab.numpy.ndarray b: a vector +//| :param ~bool lower: if true, use only data contained in lower triangle of A, else use upper triangle of A +//| :return: solution to the system A x = b. Shape of return matches b +//| :raises TypeError: if A and b are not of type ndarray and are not dense +//| :raises ValueError: if A is a singular matrix +//| +//| Solve the equation A x = b for x, assuming A is a triangular matrix""" +//| ... +//| + +static mp_obj_t solve_triangular(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + + size_t i, j; + + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = mp_const_none} } , + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = mp_const_none} } , + { MP_QSTR_lower, MP_ARG_OBJ, { .u_rom_obj = mp_const_false } }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + if(!mp_obj_is_type(args[0].u_obj, &ulab_ndarray_type) || !mp_obj_is_type(args[1].u_obj, &ulab_ndarray_type)) { + mp_raise_TypeError(translate("first two arguments must be ndarrays")); + } + + ndarray_obj_t *A = MP_OBJ_TO_PTR(args[0].u_obj); + ndarray_obj_t *b = MP_OBJ_TO_PTR(args[1].u_obj); + + if(!ndarray_is_dense(A) || !ndarray_is_dense(b)) { + mp_raise_TypeError(translate("input must be a dense ndarray")); + } + + size_t A_rows = A->shape[ULAB_MAX_DIMS - 2]; + size_t A_cols = A->shape[ULAB_MAX_DIMS - 1]; + + uint8_t *A_arr = (uint8_t *)A->array; + uint8_t *b_arr = (uint8_t *)b->array; + + mp_float_t (*get_A_ele)(void *) = ndarray_get_float_function(A->dtype); + mp_float_t (*get_b_ele)(void *) = ndarray_get_float_function(b->dtype); + + uint8_t *temp_A = A_arr; + + // check if input matrix A is singular + for (i = 0; i < A_rows; i++) { + if (MICROPY_FLOAT_C_FUN(fabs)(get_A_ele(A_arr)) < TOLERANCE) + mp_raise_ValueError(translate("input matrix is singular")); + A_arr += A->strides[ULAB_MAX_DIMS - 2]; + A_arr += A->strides[ULAB_MAX_DIMS - 1]; + } + + A_arr = temp_A; + + ndarray_obj_t *x = ndarray_new_dense_ndarray(b->ndim, b->shape, NDARRAY_FLOAT); + mp_float_t *x_arr = (mp_float_t *)x->array; + + if (mp_obj_is_true(args[2].u_obj)) { + // Solve the lower triangular matrix by iterating each row of A. + // Start by finding the first unknown using the first row. + // On finding this unknown, find the second unknown using the second row. + // Continue the same till the last unknown is found using the last row. + + for (i = 0; i < A_rows; i++) { + mp_float_t sum = 0.0; + for (j = 0; j < i; j++) { + sum += (get_A_ele(A_arr) * (*x_arr++)); + A_arr += A->strides[ULAB_MAX_DIMS - 1]; + } + + sum = (get_b_ele(b_arr) - sum) / (get_A_ele(A_arr)); + *x_arr = sum; + + x_arr -= j; + A_arr -= A->strides[ULAB_MAX_DIMS - 1] * j; + A_arr += A->strides[ULAB_MAX_DIMS - 2]; + b_arr += b->strides[ULAB_MAX_DIMS - 1]; + } + } else { + // Solve the upper triangular matrix by iterating each row of A. + // Start by finding the last unknown using the last row. + // On finding this unknown, find the last-but-one unknown using the last-but-one row. + // Continue the same till the first unknown is found using the first row. + + A_arr += (A->strides[ULAB_MAX_DIMS - 2] * A_rows); + b_arr += (b->strides[ULAB_MAX_DIMS - 1] * A_cols); + x_arr += A_cols; + + for (i = A_rows - 1; i < A_rows; i--) { + mp_float_t sum = 0.0; + for (j = i + 1; j < A_cols; j++) { + sum += (get_A_ele(A_arr) * (*x_arr++)); + A_arr += A->strides[ULAB_MAX_DIMS - 1]; + } + + x_arr -= (j - i); + A_arr -= (A->strides[ULAB_MAX_DIMS - 1] * (j - i)); + b_arr -= b->strides[ULAB_MAX_DIMS - 1]; + + sum = (get_b_ele(b_arr) - sum) / get_A_ele(A_arr); + *x_arr = sum; + + A_arr -= A->strides[ULAB_MAX_DIMS - 2]; + } + } + + return MP_OBJ_FROM_PTR(x); +} + +MP_DEFINE_CONST_FUN_OBJ_KW(linalg_solve_triangular_obj, 2, solve_triangular); + +//| def cho_solve(L: ulab.numpy.ndarray, b: ulab.numpy.ndarray) -> ulab.numpy.ndarray: +//| """ +//| :param ~ulab.numpy.ndarray L: the lower triangular, Cholesky factorization of A +//| :param ~ulab.numpy.ndarray b: right-hand-side vector b +//| :return: solution to the system A x = b. Shape of return matches b +//| :raises TypeError: if L and b are not of type ndarray and are not dense +//| +//| Solve the linear equations A x = b, given the Cholesky factorization of A as input""" +//| ... +//| + +static mp_obj_t cho_solve(mp_obj_t _L, mp_obj_t _b) { + + if(!mp_obj_is_type(_L, &ulab_ndarray_type) || !mp_obj_is_type(_b, &ulab_ndarray_type)) { + mp_raise_TypeError(translate("first two arguments must be ndarrays")); + } + + ndarray_obj_t *L = MP_OBJ_TO_PTR(_L); + ndarray_obj_t *b = MP_OBJ_TO_PTR(_b); + + if(!ndarray_is_dense(L) || !ndarray_is_dense(b)) { + mp_raise_TypeError(translate("input must be a dense ndarray")); + } + + mp_float_t (*get_L_ele)(void *) = ndarray_get_float_function(L->dtype); + mp_float_t (*get_b_ele)(void *) = ndarray_get_float_function(b->dtype); + void (*set_L_ele)(void *, mp_float_t) = ndarray_set_float_function(L->dtype); + + size_t L_rows = L->shape[ULAB_MAX_DIMS - 2]; + size_t L_cols = L->shape[ULAB_MAX_DIMS - 1]; + + // Obtain transpose of the input matrix L in L_t + size_t L_t_shape[ULAB_MAX_DIMS]; + size_t L_t_rows = L_t_shape[ULAB_MAX_DIMS - 2] = L_cols; + size_t L_t_cols = L_t_shape[ULAB_MAX_DIMS - 1] = L_rows; + ndarray_obj_t *L_t = ndarray_new_dense_ndarray(L->ndim, L_t_shape, L->dtype); + + uint8_t *L_arr = (uint8_t *)L->array; + uint8_t *L_t_arr = (uint8_t *)L_t->array; + uint8_t *b_arr = (uint8_t *)b->array; + + size_t i, j; + + uint8_t *L_ptr = L_arr; + uint8_t *L_t_ptr = L_t_arr; + for (i = 0; i < L_rows; i++) { + for (j = 0; j < L_cols; j++) { + set_L_ele(L_t_ptr, get_L_ele(L_ptr)); + L_t_ptr += L_t->strides[ULAB_MAX_DIMS - 2]; + L_ptr += L->strides[ULAB_MAX_DIMS - 1]; + } + + L_t_ptr -= j * L_t->strides[ULAB_MAX_DIMS - 2]; + L_t_ptr += L_t->strides[ULAB_MAX_DIMS - 1]; + L_ptr -= j * L->strides[ULAB_MAX_DIMS - 1]; + L_ptr += L->strides[ULAB_MAX_DIMS - 2]; + } + + ndarray_obj_t *x = ndarray_new_dense_ndarray(b->ndim, b->shape, NDARRAY_FLOAT); + mp_float_t *x_arr = (mp_float_t *)x->array; + + ndarray_obj_t *y = ndarray_new_dense_ndarray(b->ndim, b->shape, NDARRAY_FLOAT); + mp_float_t *y_arr = (mp_float_t *)y->array; + + // solve L y = b to obtain y, where L_t x = y + for (i = 0; i < L_rows; i++) { + mp_float_t sum = 0.0; + for (j = 0; j < i; j++) { + sum += (get_L_ele(L_arr) * (*y_arr++)); + L_arr += L->strides[ULAB_MAX_DIMS - 1]; + } + + sum = (get_b_ele(b_arr) - sum) / (get_L_ele(L_arr)); + *y_arr = sum; + + y_arr -= j; + L_arr -= L->strides[ULAB_MAX_DIMS - 1] * j; + L_arr += L->strides[ULAB_MAX_DIMS - 2]; + b_arr += b->strides[ULAB_MAX_DIMS - 1]; + } + + // using y, solve L_t x = y to obtain x + L_t_arr += (L_t->strides[ULAB_MAX_DIMS - 2] * L_t_rows); + y_arr += L_t_cols; + x_arr += L_t_cols; + + for (i = L_t_rows - 1; i < L_t_rows; i--) { + mp_float_t sum = 0.0; + for (j = i + 1; j < L_t_cols; j++) { + sum += (get_L_ele(L_t_arr) * (*x_arr++)); + L_t_arr += L_t->strides[ULAB_MAX_DIMS - 1]; + } + + x_arr -= (j - i); + L_t_arr -= (L_t->strides[ULAB_MAX_DIMS - 1] * (j - i)); + y_arr--; + + sum = ((*y_arr) - sum) / get_L_ele(L_t_arr); + *x_arr = sum; + + L_t_arr -= L_t->strides[ULAB_MAX_DIMS - 2]; + } + + return MP_OBJ_FROM_PTR(x); +} + +MP_DEFINE_CONST_FUN_OBJ_2(linalg_cho_solve_obj, cho_solve); + +#endif + +static const mp_rom_map_elem_t ulab_scipy_linalg_globals_table[] = { + { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_linalg) }, + #if ULAB_MAX_DIMS > 1 + #if ULAB_SCIPY_LINALG_HAS_SOLVE_TRIANGULAR + { MP_ROM_QSTR(MP_QSTR_solve_triangular), (mp_obj_t)&linalg_solve_triangular_obj }, + #endif + #if ULAB_SCIPY_LINALG_HAS_CHO_SOLVE + { MP_ROM_QSTR(MP_QSTR_cho_solve), (mp_obj_t)&linalg_cho_solve_obj }, + #endif + #endif +}; + +static MP_DEFINE_CONST_DICT(mp_module_ulab_scipy_linalg_globals, ulab_scipy_linalg_globals_table); + +mp_obj_module_t ulab_scipy_linalg_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t*)&mp_module_ulab_scipy_linalg_globals, +}; + +#endif diff --git a/python/port/mod/ulab/scipy/linalg/linalg.h b/python/port/mod/ulab/scipy/linalg/linalg.h new file mode 100644 index 000000000..7396affd6 --- /dev/null +++ b/python/port/mod/ulab/scipy/linalg/linalg.h @@ -0,0 +1,21 @@ + +/* + * This file is part of the micropython-ulab project, + * + * https://github.com/v923z/micropython-ulab + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Vikas Udupa + * +*/ + +#ifndef _SCIPY_LINALG_ +#define _SCIPY_LINALG_ + +extern mp_obj_module_t ulab_scipy_linalg_module; + +MP_DECLARE_CONST_FUN_OBJ_KW(linalg_solve_triangular_obj); +MP_DECLARE_CONST_FUN_OBJ_2(linalg_cho_solve_obj); + +#endif /* _SCIPY_LINALG_ */ diff --git a/python/port/mod/ulab/scipy/optimize/optimize.c b/python/port/mod/ulab/scipy/optimize/optimize.c new file mode 100644 index 000000000..9218e6936 --- /dev/null +++ b/python/port/mod/ulab/scipy/optimize/optimize.c @@ -0,0 +1,414 @@ + +/* + * This file is part of the micropython-ulab project, + * + * https://github.com/v923z/micropython-ulab + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jeff Epler for Adafruit Industries + * 2020 Scott Shawcroft for Adafruit Industries + * 2020-2021 Zoltán Vörös + * 2020 Taku Fukada +*/ + +#include +#include +#include +#include + +#include "../../ndarray.h" +#include "../../ulab.h" +#include "../../ulab_tools.h" +#include "optimize.h" + +const mp_obj_float_t xtolerance = {{&mp_type_float}, MICROPY_FLOAT_CONST(2.4e-7)}; +const mp_obj_float_t rtolerance = {{&mp_type_float}, MICROPY_FLOAT_CONST(0.0)}; + +static mp_float_t optimize_python_call(const mp_obj_type_t *type, mp_obj_t fun, mp_float_t x, mp_obj_t *fargs, uint8_t nparams) { + // Helper function for calculating the value of f(x, a, b, c, ...), + // where f is defined in python. Takes a float, returns a float. + // The array of mp_obj_t type must be supplied, as must the number of parameters (a, b, c...) in nparams + fargs[0] = mp_obj_new_float(x); + return mp_obj_get_float(type->call(fun, nparams+1, 0, fargs)); +} + +#if ULAB_SCIPY_OPTIMIZE_HAS_BISECT +//| def bisect( +//| fun: Callable[[float], float], +//| a: float, +//| b: float, +//| *, +//| xtol: float = 2.4e-7, +//| maxiter: int = 100 +//| ) -> float: +//| """ +//| :param callable f: The function to bisect +//| :param float a: The left side of the interval +//| :param float b: The right side of the interval +//| :param float xtol: The tolerance value +//| :param float maxiter: The maximum number of iterations to perform +//| +//| Find a solution (zero) of the function ``f(x)`` on the interval +//| (``a``..``b``) using the bisection method. The result is accurate to within +//| ``xtol`` unless more than ``maxiter`` steps are required.""" +//| ... +//| + +STATIC mp_obj_t optimize_bisect(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + // Simple bisection routine + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } }, + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } }, + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } }, + { MP_QSTR_xtol, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&xtolerance)} }, + { MP_QSTR_maxiter, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 100} }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + mp_obj_t fun = args[0].u_obj; + const mp_obj_type_t *type = mp_obj_get_type(fun); + if(type->call == NULL) { + mp_raise_TypeError(translate("first argument must be a function")); + } + mp_float_t xtol = mp_obj_get_float(args[3].u_obj); + mp_obj_t *fargs = m_new(mp_obj_t, 1); + mp_float_t left, right; + mp_float_t x_mid; + mp_float_t a = mp_obj_get_float(args[1].u_obj); + mp_float_t b = mp_obj_get_float(args[2].u_obj); + left = optimize_python_call(type, fun, a, fargs, 0); + right = optimize_python_call(type, fun, b, fargs, 0); + if(left * right > 0) { + mp_raise_ValueError(translate("function has the same sign at the ends of interval")); + } + mp_float_t rtb = left < MICROPY_FLOAT_CONST(0.0) ? a : b; + mp_float_t dx = left < MICROPY_FLOAT_CONST(0.0) ? b - a : a - b; + if(args[4].u_int < 0) { + mp_raise_ValueError(translate("maxiter should be > 0")); + } + for(uint16_t i=0; i < args[4].u_int; i++) { + dx *= MICROPY_FLOAT_CONST(0.5); + x_mid = rtb + dx; + if(optimize_python_call(type, fun, x_mid, fargs, 0) < MICROPY_FLOAT_CONST(0.0)) { + rtb = x_mid; + } + if(MICROPY_FLOAT_C_FUN(fabs)(dx) < xtol) break; + } + return mp_obj_new_float(rtb); +} + +MP_DEFINE_CONST_FUN_OBJ_KW(optimize_bisect_obj, 3, optimize_bisect); +#endif + +#if ULAB_SCIPY_OPTIMIZE_HAS_FMIN +//| def fmin( +//| fun: Callable[[float], float], +//| x0: float, +//| *, +//| xatol: float = 2.4e-7, +//| fatol: float = 2.4e-7, +//| maxiter: int = 200 +//| ) -> float: +//| """ +//| :param callable f: The function to bisect +//| :param float x0: The initial x value +//| :param float xatol: The absolute tolerance value +//| :param float fatol: The relative tolerance value +//| +//| Find a minimum of the function ``f(x)`` using the downhill simplex method. +//| The located ``x`` is within ``fxtol`` of the actual minimum, and ``f(x)`` +//| is within ``fatol`` of the actual minimum unless more than ``maxiter`` +//| steps are requried.""" +//| ... +//| + +STATIC mp_obj_t optimize_fmin(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + // downhill simplex method in 1D + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } }, + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } }, + { MP_QSTR_xatol, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&xtolerance)} }, + { MP_QSTR_fatol, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&xtolerance)} }, + { MP_QSTR_maxiter, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 200} }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + mp_obj_t fun = args[0].u_obj; + const mp_obj_type_t *type = mp_obj_get_type(fun); + if(type->call == NULL) { + mp_raise_TypeError(translate("first argument must be a function")); + } + + // parameters controlling convergence conditions + mp_float_t xatol = mp_obj_get_float(args[2].u_obj); + mp_float_t fatol = mp_obj_get_float(args[3].u_obj); + if(args[4].u_int <= 0) { + mp_raise_ValueError(translate("maxiter must be > 0")); + } + uint16_t maxiter = (uint16_t)args[4].u_int; + + mp_float_t x0 = mp_obj_get_float(args[1].u_obj); + mp_float_t x1 = MICROPY_FLOAT_C_FUN(fabs)(x0) > OPTIMIZE_EPSILON ? (MICROPY_FLOAT_CONST(1.0) + OPTIMIZE_NONZDELTA) * x0 : OPTIMIZE_ZDELTA; + mp_obj_t *fargs = m_new(mp_obj_t, 1); + mp_float_t f0 = optimize_python_call(type, fun, x0, fargs, 0); + mp_float_t f1 = optimize_python_call(type, fun, x1, fargs, 0); + if(f1 < f0) { + SWAP(mp_float_t, x0, x1); + SWAP(mp_float_t, f0, f1); + } + for(uint16_t i=0; i < maxiter; i++) { + uint8_t shrink = 0; + f0 = optimize_python_call(type, fun, x0, fargs, 0); + f1 = optimize_python_call(type, fun, x1, fargs, 0); + + // reflection + mp_float_t xr = (MICROPY_FLOAT_CONST(1.0) + OPTIMIZE_ALPHA) * x0 - OPTIMIZE_ALPHA * x1; + mp_float_t fr = optimize_python_call(type, fun, xr, fargs, 0); + if(fr < f0) { // expansion + mp_float_t xe = (1 + OPTIMIZE_ALPHA * OPTIMIZE_BETA) * x0 - OPTIMIZE_ALPHA * OPTIMIZE_BETA * x1; + mp_float_t fe = optimize_python_call(type, fun, xe, fargs, 0); + if(fe < fr) { + x1 = xe; + f1 = fe; + } else { + x1 = xr; + f1 = fr; + } + } else { + if(fr < f1) { // contraction + mp_float_t xc = (1 + OPTIMIZE_GAMMA * OPTIMIZE_ALPHA) * x0 - OPTIMIZE_GAMMA * OPTIMIZE_ALPHA * x1; + mp_float_t fc = optimize_python_call(type, fun, xc, fargs, 0); + if(fc < fr) { + x1 = xc; + f1 = fc; + } else { + shrink = 1; + } + } else { // inside contraction + mp_float_t xc = (MICROPY_FLOAT_CONST(1.0) - OPTIMIZE_GAMMA) * x0 + OPTIMIZE_GAMMA * x1; + mp_float_t fc = optimize_python_call(type, fun, xc, fargs, 0); + if(fc < f1) { + x1 = xc; + f1 = fc; + } else { + shrink = 1; + } + } + if(shrink == 1) { + x1 = x0 + OPTIMIZE_DELTA * (x1 - x0); + f1 = optimize_python_call(type, fun, x1, fargs, 0); + } + if((MICROPY_FLOAT_C_FUN(fabs)(f1 - f0) < fatol) || + (MICROPY_FLOAT_C_FUN(fabs)(x1 - x0) < xatol)) { + break; + } + if(f1 < f0) { + SWAP(mp_float_t, x0, x1); + SWAP(mp_float_t, f0, f1); + } + } + } + return mp_obj_new_float(x0); +} + +MP_DEFINE_CONST_FUN_OBJ_KW(optimize_fmin_obj, 2, optimize_fmin); +#endif + +#if ULAB_SCIPY_OPTIMIZE_HAS_CURVE_FIT +static void optimize_jacobi(const mp_obj_type_t *type, mp_obj_t fun, mp_float_t *x, mp_float_t *y, uint16_t len, mp_float_t *params, uint8_t nparams, mp_float_t *jacobi, mp_float_t *grad) { + /* Calculates the Jacobian and the gradient of the cost function + * + * The entries in the Jacobian are + * J(m, n) = de_m/da_n, + * + * where + * + * e_m = (f(x_m, a1, a2, ...) - y_m)/sigma_m is the error at x_m, + * + * and + * + * a1, a2, ..., a_n are the free parameters + */ + mp_obj_t *fargs0 = m_new(mp_obj_t, lenp+1); + mp_obj_t *fargs1 = m_new(mp_obj_t, lenp+1); + for(uint8_t p=0; p < nparams; p++) { + fargs0[p+1] = mp_obj_new_float(params[p]); + fargs1[p+1] = mp_obj_new_float(params[p]); + } + for(uint8_t p=0; p < nparams; p++) { + mp_float_t da = params[p] != MICROPY_FLOAT_CONST(0.0) ? (MICROPY_FLOAT_CONST(1.0) + APPROX_NONZDELTA) * params[p] : APPROX_ZDELTA; + fargs1[p+1] = mp_obj_new_float(params[p] + da); + grad[p] = MICROPY_FLOAT_CONST(0.0); + for(uint16_t i=0; i < len; i++) { + mp_float_t f0 = optimize_python_call(type, fun, x[i], fargs0, nparams); + mp_float_t f1 = optimize_python_call(type, fun, x[i], fargs1, nparams); + jacobi[i*nparamp+p] = (f1 - f0) / da; + grad[p] += (f0 - y[i]) * jacobi[i*nparamp+p]; + } + fargs1[p+1] = fargs0[p+1]; // set back to the original value + } +} + +static void optimize_delta(mp_float_t *jacobi, mp_float_t *grad, uint16_t len, uint8_t nparams, mp_float_t lambda) { + // +} + +mp_obj_t optimize_curve_fit(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + // Levenberg-Marquardt non-linear fit + // The implementation follows the introductory discussion in Mark Tanstrum's paper, https://arxiv.org/abs/1201.5885 + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } }, + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } }, + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } }, + { MP_QSTR_p0, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } }, + { MP_QSTR_xatol, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&xtolerance)} }, + { MP_QSTR_fatol, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&xtolerance)} }, + { MP_QSTR_maxiter, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = mp_const_none} }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + mp_obj_t fun = args[0].u_obj; + const mp_obj_type_t *type = mp_obj_get_type(fun); + if(type->call == NULL) { + mp_raise_TypeError(translate("first argument must be a function")); + } + + mp_obj_t x_obj = args[1].u_obj; + mp_obj_t y_obj = args[2].u_obj; + mp_obj_t p0_obj = args[3].u_obj; + if(!ndarray_object_is_array_like(x_obj) || !ndarray_object_is_array_like(y_obj)) { + mp_raise_TypeError(translate("data must be iterable")); + } + if(!ndarray_object_is_nditerable(p0_obj)) { + mp_raise_TypeError(translate("initial values must be iterable")); + } + size_t len = (size_t)mp_obj_get_int(mp_obj_len_maybe(x_obj)); + uint8_t lenp = (uint8_t)mp_obj_get_int(mp_obj_len_maybe(p0_obj)); + if(len != (uint16_t)mp_obj_get_int(mp_obj_len_maybe(y_obj))) { + mp_raise_ValueError(translate("data must be of equal length")); + } + + mp_float_t *x = m_new(mp_float_t, len); + fill_array_iterable(x, x_obj); + mp_float_t *y = m_new(mp_float_t, len); + fill_array_iterable(y, y_obj); + mp_float_t *p0 = m_new(mp_float_t, lenp); + fill_array_iterable(p0, p0_obj); + mp_float_t *grad = m_new(mp_float_t, len); + mp_float_t *jacobi = m_new(mp_float_t, len*len); + mp_obj_t *fargs = m_new(mp_obj_t, lenp+1); + + m_del(mp_float_t, p0, lenp); + // parameters controlling convergence conditions + //mp_float_t xatol = mp_obj_get_float(args[2].u_obj); + //mp_float_t fatol = mp_obj_get_float(args[3].u_obj); + + // this has finite binary representation; we will multiply/divide by 4 + //mp_float_t lambda = 0.0078125; + + //linalg_invert_matrix(mp_float_t *data, size_t N) + + m_del(mp_float_t, x, len); + m_del(mp_float_t, y, len); + m_del(mp_float_t, grad, len); + m_del(mp_float_t, jacobi, len*len); + m_del(mp_obj_t, fargs, lenp+1); + return mp_const_none; +} + +MP_DEFINE_CONST_FUN_OBJ_KW(optimize_curve_fit_obj, 2, optimize_curve_fit); +#endif + +#if ULAB_SCIPY_OPTIMIZE_HAS_NEWTON +//| def newton( +//| fun: Callable[[float], float], +//| x0: float, +//| *, +//| xtol: float = 2.4e-7, +//| rtol: float = 0.0, +//| maxiter: int = 50 +//| ) -> float: +//| """ +//| :param callable f: The function to bisect +//| :param float x0: The initial x value +//| :param float xtol: The absolute tolerance value +//| :param float rtol: The relative tolerance value +//| :param float maxiter: The maximum number of iterations to perform +//| +//| Find a solution (zero) of the function ``f(x)`` using Newton's Method. +//| The result is accurate to within ``xtol * rtol * |f(x)|`` unless more than +//| ``maxiter`` steps are requried.""" +//| ... +//| + +static mp_obj_t optimize_newton(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + // this is actually the secant method, as the first derivative of the function + // is not accepted as an argument. The function whose root we want to solve for + // must depend on a single variable without parameters, i.e., f(x) + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = mp_const_none } }, + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = mp_const_none } }, + { MP_QSTR_tol, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_PTR(&xtolerance) } }, + { MP_QSTR_rtol, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_PTR(&rtolerance) } }, + { MP_QSTR_maxiter, MP_ARG_KW_ONLY | MP_ARG_INT, { .u_int = 50 } }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + mp_obj_t fun = args[0].u_obj; + const mp_obj_type_t *type = mp_obj_get_type(fun); + if(type->call == NULL) { + mp_raise_TypeError(translate("first argument must be a function")); + } + mp_float_t x = mp_obj_get_float(args[1].u_obj); + mp_float_t tol = mp_obj_get_float(args[2].u_obj); + mp_float_t rtol = mp_obj_get_float(args[3].u_obj); + mp_float_t dx, df, fx; + dx = x > MICROPY_FLOAT_CONST(0.0) ? OPTIMIZE_EPS * x : -OPTIMIZE_EPS * x; + mp_obj_t *fargs = m_new(mp_obj_t, 1); + if(args[4].u_int <= 0) { + mp_raise_ValueError(translate("maxiter must be > 0")); + } + for(uint16_t i=0; i < args[4].u_int; i++) { + fx = optimize_python_call(type, fun, x, fargs, 0); + df = (optimize_python_call(type, fun, x + dx, fargs, 0) - fx) / dx; + dx = fx / df; + x -= dx; + if(MICROPY_FLOAT_C_FUN(fabs)(dx) < (tol + rtol * MICROPY_FLOAT_C_FUN(fabs)(x))) break; + } + return mp_obj_new_float(x); +} + +MP_DEFINE_CONST_FUN_OBJ_KW(optimize_newton_obj, 2, optimize_newton); +#endif + +static const mp_rom_map_elem_t ulab_scipy_optimize_globals_table[] = { + { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_optimize) }, + #if ULAB_SCIPY_OPTIMIZE_HAS_BISECT + { MP_OBJ_NEW_QSTR(MP_QSTR_bisect), (mp_obj_t)&optimize_bisect_obj }, + #endif + #if ULAB_SCIPY_OPTIMIZE_HAS_CURVE_FIT + { MP_OBJ_NEW_QSTR(MP_QSTR_curve_fit), (mp_obj_t)&optimize_curve_fit_obj }, + #endif + #if ULAB_SCIPY_OPTIMIZE_HAS_FMIN + { MP_OBJ_NEW_QSTR(MP_QSTR_fmin), (mp_obj_t)&optimize_fmin_obj }, + #endif + #if ULAB_SCIPY_OPTIMIZE_HAS_NEWTON + { MP_OBJ_NEW_QSTR(MP_QSTR_newton), (mp_obj_t)&optimize_newton_obj }, + #endif +}; + +static MP_DEFINE_CONST_DICT(mp_module_ulab_scipy_optimize_globals, ulab_scipy_optimize_globals_table); + +mp_obj_module_t ulab_scipy_optimize_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t*)&mp_module_ulab_scipy_optimize_globals, +}; diff --git a/python/port/mod/ulab/scipy/optimize/optimize.h b/python/port/mod/ulab/scipy/optimize/optimize.h new file mode 100644 index 000000000..5d956df49 --- /dev/null +++ b/python/port/mod/ulab/scipy/optimize/optimize.h @@ -0,0 +1,41 @@ + +/* + * This file is part of the micropython-ulab project, + * + * https://github.com/v923z/micropython-ulab + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Zoltán Vörös + * +*/ + +#ifndef _SCIPY_OPTIMIZE_ +#define _SCIPY_OPTIMIZE_ + +#include "../../ulab_tools.h" + +#ifndef OPTIMIZE_EPSILON +#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT +#define OPTIMIZE_EPSILON MICROPY_FLOAT_CONST(1.2e-7) +#elif MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE +#define OPTIMIZE_EPSILON MICROPY_FLOAT_CONST(2.3e-16) +#endif +#endif + +#define OPTIMIZE_EPS MICROPY_FLOAT_CONST(1.0e-4) +#define OPTIMIZE_NONZDELTA MICROPY_FLOAT_CONST(0.05) +#define OPTIMIZE_ZDELTA MICROPY_FLOAT_CONST(0.00025) +#define OPTIMIZE_ALPHA MICROPY_FLOAT_CONST(1.0) +#define OPTIMIZE_BETA MICROPY_FLOAT_CONST(2.0) +#define OPTIMIZE_GAMMA MICROPY_FLOAT_CONST(0.5) +#define OPTIMIZE_DELTA MICROPY_FLOAT_CONST(0.5) + +extern mp_obj_module_t ulab_scipy_optimize_module; + +MP_DECLARE_CONST_FUN_OBJ_KW(optimize_bisect_obj); +MP_DECLARE_CONST_FUN_OBJ_KW(optimize_curve_fit_obj); +MP_DECLARE_CONST_FUN_OBJ_KW(optimize_fmin_obj); +MP_DECLARE_CONST_FUN_OBJ_KW(optimize_newton_obj); + +#endif /* _SCIPY_OPTIMIZE_ */ diff --git a/python/port/mod/ulab/scipy/scipy.c b/python/port/mod/ulab/scipy/scipy.c new file mode 100644 index 000000000..3e3a8280f --- /dev/null +++ b/python/port/mod/ulab/scipy/scipy.c @@ -0,0 +1,51 @@ + +/* + * This file is part of the micropython-ulab project, + * + * https://github.com/v923z/micropython-ulab + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jeff Epler for Adafruit Industries + * 2020 Scott Shawcroft for Adafruit Industries + * 2020-2021 Zoltán Vörös + * 2020 Taku Fukada +*/ + +#include +#include + +#include "../ulab.h" +#include "optimize/optimize.h" +#include "signal/signal.h" +#include "special/special.h" +#include "linalg/linalg.h" + +#if ULAB_HAS_SCIPY + +//| """Compatibility layer for scipy""" +//| + +static const mp_rom_map_elem_t ulab_scipy_globals_table[] = { + { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_scipy) }, + #if ULAB_SCIPY_HAS_LINALG_MODULE + { MP_ROM_QSTR(MP_QSTR_linalg), MP_ROM_PTR(&ulab_scipy_linalg_module) }, + #endif + #if ULAB_SCIPY_HAS_OPTIMIZE_MODULE + { MP_ROM_QSTR(MP_QSTR_optimize), MP_ROM_PTR(&ulab_scipy_optimize_module) }, + #endif + #if ULAB_SCIPY_HAS_SIGNAL_MODULE + { MP_ROM_QSTR(MP_QSTR_signal), MP_ROM_PTR(&ulab_scipy_signal_module) }, + #endif + #if ULAB_SCIPY_HAS_SPECIAL_MODULE + { MP_ROM_QSTR(MP_QSTR_special), MP_ROM_PTR(&ulab_scipy_special_module) }, + #endif +}; + +static MP_DEFINE_CONST_DICT(mp_module_ulab_scipy_globals, ulab_scipy_globals_table); + +mp_obj_module_t ulab_scipy_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t*)&mp_module_ulab_scipy_globals, +}; +#endif diff --git a/python/port/mod/ulab/scipy/scipy.h b/python/port/mod/ulab/scipy/scipy.h new file mode 100644 index 000000000..ff287a5a1 --- /dev/null +++ b/python/port/mod/ulab/scipy/scipy.h @@ -0,0 +1,21 @@ + +/* + * This file is part of the micropython-ulab project, + * + * https://github.com/v923z/micropython-ulab + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Zoltán Vörös + * +*/ + +#ifndef _SCIPY_ +#define _SCIPY_ + +#include "../ulab.h" +#include "../ndarray.h" + +extern mp_obj_module_t ulab_scipy_module; + +#endif /* _SCIPY_ */ diff --git a/python/port/mod/ulab/scipy/signal/signal.c b/python/port/mod/ulab/scipy/signal/signal.c new file mode 100644 index 000000000..0dbafd2c1 --- /dev/null +++ b/python/port/mod/ulab/scipy/signal/signal.c @@ -0,0 +1,153 @@ + +/* + * This file is part of the micropython-ulab project, + * + * https://github.com/v923z/micropython-ulab + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jeff Epler for Adafruit Industries + * 2020 Scott Shawcroft for Adafruit Industries + * 2020-2021 Zoltán Vörös + * 2020 Taku Fukada +*/ + +#include +#include +#include + +#include "../../ulab.h" +#include "../../ndarray.h" +#include "../../numpy/fft/fft_tools.h" + +#if ULAB_SCIPY_SIGNAL_HAS_SPECTROGRAM +//| def spectrogram(r: ulab.ndarray) -> ulab.ndarray: +//| """ +//| :param ulab.ndarray r: A 1-dimension array of values whose size is a power of 2 +//| +//| Computes the spectrum of the input signal. This is the absolute value of the (complex-valued) fft of the signal. +//| This function is similar to scipy's ``scipy.signal.spectrogram``.""" +//| ... +//| + +mp_obj_t signal_spectrogram(size_t n_args, const mp_obj_t *args) { + if(n_args == 2) { + return fft_fft_ifft_spectrogram(n_args, args[0], args[1], FFT_SPECTROGRAM); + } else { + return fft_fft_ifft_spectrogram(n_args, args[0], mp_const_none, FFT_SPECTROGRAM); + } +} + +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(signal_spectrogram_obj, 1, 2, signal_spectrogram); +#endif /* ULAB_SCIPY_SIGNAL_HAS_SPECTROGRAM */ + +#if ULAB_SCIPY_SIGNAL_HAS_SOSFILT +static void signal_sosfilt_array(mp_float_t *x, const mp_float_t *coeffs, mp_float_t *zf, const size_t len) { + for(size_t i=0; i < len; i++) { + mp_float_t xn = *x; + *x = coeffs[0] * xn + zf[0]; + zf[0] = zf[1] + coeffs[1] * xn - coeffs[4] * *x; + zf[1] = coeffs[2] * xn - coeffs[5] * *x; + x++; + } + x -= len; +} + +mp_obj_t signal_sosfilt(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + static const mp_arg_t allowed_args[] = { + { MP_QSTR_sos, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } }, + { MP_QSTR_x, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } }, + { MP_QSTR_zi, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + if(!ndarray_object_is_array_like(args[0].u_obj) || !ndarray_object_is_array_like(args[1].u_obj)) { + mp_raise_TypeError(translate("sosfilt requires iterable arguments")); + } + size_t lenx = (size_t)mp_obj_get_int(mp_obj_len_maybe(args[1].u_obj)); + ndarray_obj_t *y = ndarray_new_linear_array(lenx, NDARRAY_FLOAT); + mp_float_t *yarray = (mp_float_t *)y->array; + mp_float_t coeffs[6]; + if(mp_obj_is_type(args[1].u_obj, &ulab_ndarray_type)) { + ndarray_obj_t *inarray = MP_OBJ_TO_PTR(args[1].u_obj); + #if ULAB_MAX_DIMS > 1 + if(inarray->ndim > 1) { + mp_raise_ValueError(translate("input must be one-dimensional")); + } + #endif + uint8_t *iarray = (uint8_t *)inarray->array; + for(size_t i=0; i < lenx; i++) { + *yarray++ = ndarray_get_float_value(iarray, inarray->dtype); + iarray += inarray->strides[ULAB_MAX_DIMS - 1]; + } + yarray -= lenx; + } else { + fill_array_iterable(yarray, args[1].u_obj); + } + + mp_obj_iter_buf_t iter_buf; + mp_obj_t item, iterable = mp_getiter(args[0].u_obj, &iter_buf); + size_t lensos = (size_t)mp_obj_get_int(mp_obj_len_maybe(args[0].u_obj)); + + size_t *shape = ndarray_shape_vector(0, 0, lensos, 2); + ndarray_obj_t *zf = ndarray_new_dense_ndarray(2, shape, NDARRAY_FLOAT); + mp_float_t *zf_array = (mp_float_t *)zf->array; + + if(args[2].u_obj != mp_const_none) { + if(!mp_obj_is_type(args[2].u_obj, &ulab_ndarray_type)) { + mp_raise_TypeError(translate("zi must be an ndarray")); + } else { + ndarray_obj_t *zi = MP_OBJ_TO_PTR(args[2].u_obj); + if((zi->shape[ULAB_MAX_DIMS - 1] != lensos) || (zi->shape[ULAB_MAX_DIMS - 1] != 2)) { + mp_raise_ValueError(translate("zi must be of shape (n_section, 2)")); + } + if(zi->dtype != NDARRAY_FLOAT) { + mp_raise_ValueError(translate("zi must be of float type")); + } + // TODO: this won't work with sparse arrays + memcpy(zf_array, zi->array, 2*lensos*sizeof(mp_float_t)); + } + } + while((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) { + if(mp_obj_get_int(mp_obj_len_maybe(item)) != 6) { + mp_raise_ValueError(translate("sos array must be of shape (n_section, 6)")); + } else { + fill_array_iterable(coeffs, item); + if(coeffs[3] != MICROPY_FLOAT_CONST(1.0)) { + mp_raise_ValueError(translate("sos[:, 3] should be all ones")); + } + signal_sosfilt_array(yarray, coeffs, zf_array, lenx); + zf_array += 2; + } + } + if(args[2].u_obj == mp_const_none) { + return MP_OBJ_FROM_PTR(y); + } else { + mp_obj_tuple_t *tuple = MP_OBJ_TO_PTR(mp_obj_new_tuple(2, NULL)); + tuple->items[0] = MP_OBJ_FROM_PTR(y); + tuple->items[1] = MP_OBJ_FROM_PTR(zf); + return tuple; + } +} + +MP_DEFINE_CONST_FUN_OBJ_KW(signal_sosfilt_obj, 2, signal_sosfilt); +#endif /* ULAB_SCIPY_SIGNAL_HAS_SOSFILT */ + +static const mp_rom_map_elem_t ulab_scipy_signal_globals_table[] = { + { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_signal) }, + #if ULAB_SCIPY_SIGNAL_HAS_SPECTROGRAM + { MP_OBJ_NEW_QSTR(MP_QSTR_spectrogram), (mp_obj_t)&signal_spectrogram_obj }, + #endif + #if ULAB_SCIPY_SIGNAL_HAS_SOSFILT + { MP_OBJ_NEW_QSTR(MP_QSTR_sosfilt), (mp_obj_t)&signal_sosfilt_obj }, + #endif +}; + +static MP_DEFINE_CONST_DICT(mp_module_ulab_scipy_signal_globals, ulab_scipy_signal_globals_table); + +mp_obj_module_t ulab_scipy_signal_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t*)&mp_module_ulab_scipy_signal_globals, +}; diff --git a/python/port/mod/ulab/scipy/signal/signal.h b/python/port/mod/ulab/scipy/signal/signal.h new file mode 100644 index 000000000..d33220e62 --- /dev/null +++ b/python/port/mod/ulab/scipy/signal/signal.h @@ -0,0 +1,24 @@ + +/* + * This file is part of the micropython-ulab project, + * + * https://github.com/v923z/micropython-ulab + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Zoltán Vörös + * +*/ + +#ifndef _SCIPY_SIGNAL_ +#define _SCIPY_SIGNAL_ + +#include "../../ulab.h" +#include "../../ndarray.h" + +extern mp_obj_module_t ulab_scipy_signal_module; + +MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(signal_spectrogram_obj); +MP_DECLARE_CONST_FUN_OBJ_KW(signal_sosfilt_obj); + +#endif /* _SCIPY_SIGNAL_ */ diff --git a/python/port/mod/ulab/scipy/special/special.c b/python/port/mod/ulab/scipy/special/special.c new file mode 100644 index 000000000..bd4cf87c4 --- /dev/null +++ b/python/port/mod/ulab/scipy/special/special.c @@ -0,0 +1,42 @@ + +/* + * This file is part of the micropython-ulab project, + * + * https://github.com/v923z/micropython-ulab + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jeff Epler for Adafruit Industries + * 2020 Scott Shawcroft for Adafruit Industries + * 2020-2021 Zoltán Vörös + * 2020 Taku Fukada +*/ + +#include +#include + +#include "../../ulab.h" +#include "../../numpy/vector/vector.h" + +static const mp_rom_map_elem_t ulab_scipy_special_globals_table[] = { + { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_special) }, + #if ULAB_SCIPY_SPECIAL_HAS_ERF + { MP_OBJ_NEW_QSTR(MP_QSTR_erf), (mp_obj_t)&vectorise_erf_obj }, + #endif + #if ULAB_SCIPY_SPECIAL_HAS_ERFC + { MP_OBJ_NEW_QSTR(MP_QSTR_erfc), (mp_obj_t)&vectorise_erfc_obj }, + #endif + #if ULAB_SCIPY_SPECIAL_HAS_GAMMA + { MP_OBJ_NEW_QSTR(MP_QSTR_gamma), (mp_obj_t)&vectorise_gamma_obj }, + #endif + #if ULAB_SCIPY_SPECIAL_HAS_GAMMALN + { MP_OBJ_NEW_QSTR(MP_QSTR_gammaln), (mp_obj_t)&vectorise_lgamma_obj }, + #endif +}; + +static MP_DEFINE_CONST_DICT(mp_module_ulab_scipy_special_globals, ulab_scipy_special_globals_table); + +mp_obj_module_t ulab_scipy_special_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t*)&mp_module_ulab_scipy_special_globals, +}; diff --git a/python/port/mod/ulab/scipy/special/special.h b/python/port/mod/ulab/scipy/special/special.h new file mode 100644 index 000000000..ca0bac58d --- /dev/null +++ b/python/port/mod/ulab/scipy/special/special.h @@ -0,0 +1,21 @@ + +/* + * This file is part of the micropython-ulab project, + * + * https://github.com/v923z/micropython-ulab + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Zoltán Vörös + * +*/ + +#ifndef _SCIPY_SPECIAL_ +#define _SCIPY_SPECIAL_ + +#include "../../ulab.h" +#include "../../ndarray.h" + +extern mp_obj_module_t ulab_scipy_special_module; + +#endif /* _SCIPY_SPECIAL_ */ diff --git a/python/port/mod/ulab/ulab.c b/python/port/mod/ulab/ulab.c new file mode 100644 index 000000000..d7a563c85 --- /dev/null +++ b/python/port/mod/ulab/ulab.c @@ -0,0 +1,162 @@ + +/* + * This file is part of the micropython-ulab project, + * + * https://github.com/v923z/micropython-ulab + * + * The MIT License (MIT) + * + * Copyright (c) 2019-2021 Zoltán Vörös + * 2020 Jeff Epler for Adafruit Industries +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ulab.h" +#include "ulab_create.h" +#include "ndarray.h" +#include "ndarray_properties.h" + +#include "numpy/numpy.h" +#include "scipy/scipy.h" +#include "numpy/fft/fft.h" +#include "numpy/linalg/linalg.h" +// TODO: we should get rid of this; array.sort depends on it +#include "numpy/numerical/numerical.h" + +#include "user/user.h" +#include "utils/utils.h" + +#define ULAB_VERSION 3.1.0 +#define xstr(s) str(s) +#define str(s) #s +#define ULAB_VERSION_STRING xstr(ULAB_VERSION) xstr(-) xstr(ULAB_MAX_DIMS) xstr(D) + +STATIC MP_DEFINE_STR_OBJ(ulab_version_obj, ULAB_VERSION_STRING); + + +STATIC const mp_rom_map_elem_t ulab_ndarray_locals_dict_table[] = { + #if ULAB_MAX_DIMS > 1 + #if NDARRAY_HAS_RESHAPE + { MP_ROM_QSTR(MP_QSTR_reshape), MP_ROM_PTR(&ndarray_reshape_obj) }, + #endif + #if NDARRAY_HAS_TRANSPOSE + { MP_ROM_QSTR(MP_QSTR_transpose), MP_ROM_PTR(&ndarray_transpose_obj) }, + #endif + #endif + #if NDARRAY_HAS_BYTESWAP + { MP_ROM_QSTR(MP_QSTR_byteswap), MP_ROM_PTR(&ndarray_byteswap_obj) }, + #endif + #if NDARRAY_HAS_COPY + { MP_ROM_QSTR(MP_QSTR_copy), MP_ROM_PTR(&ndarray_copy_obj) }, + #endif + #if NDARRAY_HAS_FLATTEN + { MP_ROM_QSTR(MP_QSTR_flatten), MP_ROM_PTR(&ndarray_flatten_obj) }, + #endif + #if NDARRAY_HAS_TOBYTES + { MP_ROM_QSTR(MP_QSTR_tobytes), MP_ROM_PTR(&ndarray_tobytes_obj) }, + #endif + #if NDARRAY_HAS_SORT + { MP_ROM_QSTR(MP_QSTR_sort), MP_ROM_PTR(&numerical_sort_inplace_obj) }, + #endif + #ifdef CIRCUITPY + #if NDARRAY_HAS_DTYPE + { MP_ROM_QSTR(MP_QSTR_dtype), MP_ROM_PTR(&ndarray_dtype_obj) }, + #endif + #if NDARRAY_HAS_ITEMSIZE + { MP_ROM_QSTR(MP_QSTR_itemsize), MP_ROM_PTR(&ndarray_itemsize_obj) }, + #endif + #if NDARRAY_HAS_SHAPE + { MP_ROM_QSTR(MP_QSTR_shape), MP_ROM_PTR(&ndarray_shape_obj) }, + #endif + #if NDARRAY_HAS_SIZE + { MP_ROM_QSTR(MP_QSTR_size), MP_ROM_PTR(&ndarray_size_obj) }, + #endif + #if NDARRAY_HAS_STRIDES + { MP_ROM_QSTR(MP_QSTR_strides), MP_ROM_PTR(&ndarray_strides_obj) }, + #endif + #endif /* CIRCUITPY */ +}; + +STATIC MP_DEFINE_CONST_DICT(ulab_ndarray_locals_dict, ulab_ndarray_locals_dict_table); + +const mp_obj_type_t ulab_ndarray_type = { + { &mp_type_type }, + #if defined(MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE) && defined(MP_TYPE_FLAG_EQ_HAS_NEQ_TEST) + .flags = MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE | MP_TYPE_FLAG_EQ_HAS_NEQ_TEST, + #endif + .name = MP_QSTR_ndarray, + .print = ndarray_print, + .make_new = ndarray_make_new, + #if NDARRAY_IS_SLICEABLE + .subscr = ndarray_subscr, + #endif + #if NDARRAY_IS_ITERABLE + .getiter = ndarray_getiter, + #endif + #if NDARRAY_HAS_UNARY_OPS + .unary_op = ndarray_unary_op, + #endif + #if NDARRAY_HAS_BINARY_OPS + .binary_op = ndarray_binary_op, + #endif + #ifndef CIRCUITPY + .attr = ndarray_properties_attr, + #endif + .buffer_p = { .get_buffer = ndarray_get_buffer, }, + .locals_dict = (mp_obj_dict_t*)&ulab_ndarray_locals_dict, +}; + +#if ULAB_HAS_DTYPE_OBJECT +const mp_obj_type_t ulab_dtype_type = { + { &mp_type_type }, + .name = MP_QSTR_dtype, + .print = ndarray_dtype_print, + .make_new = ndarray_dtype_make_new, +}; +#endif + +STATIC const mp_map_elem_t ulab_globals_table[] = { + { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_ulab) }, + { MP_ROM_QSTR(MP_QSTR___version__), MP_ROM_PTR(&ulab_version_obj) }, + #if ULAB_HAS_DTYPE_OBJECT + { MP_OBJ_NEW_QSTR(MP_QSTR_dtype), (mp_obj_t)&ulab_dtype_type }, + #else + #if NDARRAY_HAS_DTYPE + { MP_OBJ_NEW_QSTR(MP_QSTR_dtype), (mp_obj_t)&ndarray_dtype_obj }, + #endif /* NDARRAY_HAS_DTYPE */ + #endif /* ULAB_HAS_DTYPE_OBJECT */ + { MP_ROM_QSTR(MP_QSTR_numpy), MP_ROM_PTR(&ulab_numpy_module) }, + #if ULAB_HAS_SCIPY + { MP_ROM_QSTR(MP_QSTR_scipy), MP_ROM_PTR(&ulab_scipy_module) }, + #endif + #if ULAB_HAS_USER_MODULE + { MP_ROM_QSTR(MP_QSTR_user), MP_ROM_PTR(&ulab_user_module) }, + #endif + #if ULAB_HAS_UTILS_MODULE + { MP_ROM_QSTR(MP_QSTR_utils), MP_ROM_PTR(&ulab_utils_module) }, + #endif +}; + +STATIC MP_DEFINE_CONST_DICT ( + mp_module_ulab_globals, + ulab_globals_table +); + +#ifdef OPENMV +const struct _mp_obj_module_t ulab_user_cmodule = { +#else +const mp_obj_module_t ulab_user_cmodule = { +#endif + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t*)&mp_module_ulab_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_ulab, ulab_user_cmodule, MODULE_ULAB_ENABLED); diff --git a/python/port/mod/ulab/ulab.h b/python/port/mod/ulab/ulab.h new file mode 100644 index 000000000..e38310020 --- /dev/null +++ b/python/port/mod/ulab/ulab.h @@ -0,0 +1,658 @@ + +/* + * This file is part of the micropython-ulab project, + * + * https://github.com/v923z/micropython-ulab + * + * The MIT License (MIT) + * + * Copyright (c) 2019-2021 Zoltán Vörös +*/ + +#ifndef __ULAB__ +#define __ULAB__ + + + +// The pre-processor constants in this file determine how ulab behaves: +// +// - how many dimensions ulab can handle +// - which functions are included in the compiled firmware +// - whether the python syntax is numpy-like, or modular +// - whether arrays can be sliced and iterated over +// - which binary/unary operators are supported +// +// A considerable amount of flash space can be saved by removing (setting +// the corresponding constants to 0) the unnecessary functions and features. + +// Values defined here can be overridden by your own config file as +// make -DULAB_CONFIG_FILE="my_ulab_config.h" +#if defined(ULAB_CONFIG_FILE) +#include ULAB_CONFIG_FILE +#endif + + +// Determines, whether scipy is defined in ulab. The sub-modules and functions +// of scipy have to be defined separately +#ifndef ULAB_HAS_SCIPY +#define ULAB_HAS_SCIPY (0) +#endif + +// The maximum number of dimensions the firmware should be able to support +// Possible values lie between 1, and 4, inclusive +#define ULAB_MAX_DIMS 2 + +// By setting this constant to 1, iteration over array dimensions will be implemented +// as a function (ndarray_rewind_array), instead of writing out the loops in macros +// This reduces firmware size at the expense of speed +#define ULAB_HAS_FUNCTION_ITERATOR (0) + +// If NDARRAY_IS_ITERABLE is 1, the ndarray object defines its own iterator function +// This option saves approx. 250 bytes of flash space +#ifndef NDARRAY_IS_ITERABLE +#define NDARRAY_IS_ITERABLE (1) +#endif + +// Slicing can be switched off by setting this variable to 0 +#ifndef NDARRAY_IS_SLICEABLE +#define NDARRAY_IS_SLICEABLE (1) +#endif + +// The default threshold for pretty printing. These variables can be overwritten +// at run-time via the set_printoptions() function +#ifndef ULAB_HAS_PRINTOPTIONS +#define ULAB_HAS_PRINTOPTIONS (1) +#endif +#define NDARRAY_PRINT_THRESHOLD 10 +#define NDARRAY_PRINT_EDGEITEMS 3 + +// determines, whether the dtype is an object, or simply a character +// the object implementation is numpythonic, but requires more space +#ifndef ULAB_HAS_DTYPE_OBJECT +#define ULAB_HAS_DTYPE_OBJECT (0) +#endif + +// the ndarray binary operators +#ifndef NDARRAY_HAS_BINARY_OPS +#define NDARRAY_HAS_BINARY_OPS (1) +#endif + +// Firmware size can be reduced at the expense of speed by using function +// pointers in iterations. For each operator, he function pointer saves around +// 2 kB in the two-dimensional case, and around 4 kB in the four-dimensional case. + +#ifndef NDARRAY_BINARY_USES_FUN_POINTER +#define NDARRAY_BINARY_USES_FUN_POINTER (0) +#endif + +#ifndef NDARRAY_HAS_BINARY_OP_ADD +#define NDARRAY_HAS_BINARY_OP_ADD (1) +#endif + +#ifndef NDARRAY_HAS_BINARY_OP_EQUAL +#define NDARRAY_HAS_BINARY_OP_EQUAL (1) +#endif + +#ifndef NDARRAY_HAS_BINARY_OP_LESS +#define NDARRAY_HAS_BINARY_OP_LESS (1) +#endif + +#ifndef NDARRAY_HAS_BINARY_OP_LESS_EQUAL +#define NDARRAY_HAS_BINARY_OP_LESS_EQUAL (1) +#endif + +#ifndef NDARRAY_HAS_BINARY_OP_MORE +#define NDARRAY_HAS_BINARY_OP_MORE (1) +#endif + +#ifndef NDARRAY_HAS_BINARY_OP_MORE_EQUAL +#define NDARRAY_HAS_BINARY_OP_MORE_EQUAL (1) +#endif + +#ifndef NDARRAY_HAS_BINARY_OP_MULTIPLY +#define NDARRAY_HAS_BINARY_OP_MULTIPLY (1) +#endif + +#ifndef NDARRAY_HAS_BINARY_OP_NOT_EQUAL +#define NDARRAY_HAS_BINARY_OP_NOT_EQUAL (1) +#endif + +#ifndef NDARRAY_HAS_BINARY_OP_POWER +#define NDARRAY_HAS_BINARY_OP_POWER (1) +#endif + +#ifndef NDARRAY_HAS_BINARY_OP_SUBTRACT +#define NDARRAY_HAS_BINARY_OP_SUBTRACT (1) +#endif + +#ifndef NDARRAY_HAS_BINARY_OP_TRUE_DIVIDE +#define NDARRAY_HAS_BINARY_OP_TRUE_DIVIDE (1) +#endif + +#ifndef NDARRAY_HAS_INPLACE_OPS +#define NDARRAY_HAS_INPLACE_OPS (1) +#endif + +#ifndef NDARRAY_HAS_INPLACE_ADD +#define NDARRAY_HAS_INPLACE_ADD (1) +#endif + +#ifndef NDARRAY_HAS_INPLACE_MULTIPLY +#define NDARRAY_HAS_INPLACE_MULTIPLY (1) +#endif + +#ifndef NDARRAY_HAS_INPLACE_POWER +#define NDARRAY_HAS_INPLACE_POWER (1) +#endif + +#ifndef NDARRAY_HAS_INPLACE_SUBTRACT +#define NDARRAY_HAS_INPLACE_SUBTRACT (1) +#endif + +#ifndef NDARRAY_HAS_INPLACE_TRUE_DIVIDE +#define NDARRAY_HAS_INPLACE_TRUE_DIVIDE (1) +#endif + +// the ndarray unary operators +#ifndef NDARRAY_HAS_UNARY_OPS +#define NDARRAY_HAS_UNARY_OPS (1) +#endif + +#ifndef NDARRAY_HAS_UNARY_OP_ABS +#define NDARRAY_HAS_UNARY_OP_ABS (1) +#endif + +#ifndef NDARRAY_HAS_UNARY_OP_INVERT +#define NDARRAY_HAS_UNARY_OP_INVERT (1) +#endif + +#ifndef NDARRAY_HAS_UNARY_OP_LEN +#define NDARRAY_HAS_UNARY_OP_LEN (1) +#endif + +#ifndef NDARRAY_HAS_UNARY_OP_NEGATIVE +#define NDARRAY_HAS_UNARY_OP_NEGATIVE (1) +#endif + +#ifndef NDARRAY_HAS_UNARY_OP_POSITIVE +#define NDARRAY_HAS_UNARY_OP_POSITIVE (1) +#endif + + +// determines, which ndarray methods are available +#ifndef NDARRAY_HAS_BYTESWAP +#define NDARRAY_HAS_BYTESWAP (1) +#endif + +#ifndef NDARRAY_HAS_COPY +#define NDARRAY_HAS_COPY (1) +#endif + +#ifndef NDARRAY_HAS_DTYPE +#define NDARRAY_HAS_DTYPE (1) +#endif + +#ifndef NDARRAY_HAS_FLATTEN +#define NDARRAY_HAS_FLATTEN (1) +#endif + +#ifndef NDARRAY_HAS_ITEMSIZE +#define NDARRAY_HAS_ITEMSIZE (1) +#endif + +#ifndef NDARRAY_HAS_RESHAPE +#define NDARRAY_HAS_RESHAPE (1) +#endif + +#ifndef NDARRAY_HAS_SHAPE +#define NDARRAY_HAS_SHAPE (1) +#endif + +#ifndef NDARRAY_HAS_SIZE +#define NDARRAY_HAS_SIZE (1) +#endif + +#ifndef NDARRAY_HAS_SORT +#define NDARRAY_HAS_SORT (1) +#endif + +#ifndef NDARRAY_HAS_STRIDES +#define NDARRAY_HAS_STRIDES (1) +#endif + +#ifndef NDARRAY_HAS_TOBYTES +#define NDARRAY_HAS_TOBYTES (0) +#endif + +#ifndef NDARRAY_HAS_TRANSPOSE +#define NDARRAY_HAS_TRANSPOSE (1) +#endif + +// Firmware size can be reduced at the expense of speed by using a function +// pointer in iterations. Setting ULAB_VECTORISE_USES_FUNCPOINTER to 1 saves +// around 800 bytes in the four-dimensional case, and around 200 in two dimensions. +#ifndef ULAB_VECTORISE_USES_FUN_POINTER +#define ULAB_VECTORISE_USES_FUN_POINTER (1) +#endif + +// determines, whether e is defined in ulab.numpy itself +#ifndef ULAB_NUMPY_HAS_E +#define ULAB_NUMPY_HAS_E (1) +#endif + +// ulab defines infinite as a class constant in ulab.numpy +#ifndef ULAB_NUMPY_HAS_INF +#define ULAB_NUMPY_HAS_INF (1) +#endif + +// ulab defines NaN as a class constant in ulab.numpy +#ifndef ULAB_NUMPY_HAS_NAN +#define ULAB_NUMPY_HAS_NAN (1) +#endif + +// determines, whether pi is defined in ulab.numpy itself +#ifndef ULAB_NUMPY_HAS_PI +#define ULAB_NUMPY_HAS_PI (1) +#endif + +// determines, whether the ndinfo function is available +#ifndef ULAB_NUMPY_HAS_NDINFO +#define ULAB_NUMPY_HAS_NDINFO (1) +#endif + +// frombuffer adds 600 bytes to the firmware +#ifndef ULAB_NUMPY_HAS_FROMBUFFER +#define ULAB_NUMPY_HAS_FROMBUFFER (1) +#endif + +// functions that create an array +#ifndef ULAB_NUMPY_HAS_ARANGE +#define ULAB_NUMPY_HAS_ARANGE (1) +#endif + +#ifndef ULAB_NUMPY_HAS_CONCATENATE +#define ULAB_NUMPY_HAS_CONCATENATE (1) +#endif + +#ifndef ULAB_NUMPY_HAS_DIAG +#define ULAB_NUMPY_HAS_DIAG (1) +#endif + +#ifndef ULAB_NUMPY_HAS_EMPTY +#define ULAB_NUMPY_HAS_EMPTY (1) +#endif + +#ifndef ULAB_NUMPY_HAS_EYE +#define ULAB_NUMPY_HAS_EYE (1) +#endif + +#ifndef ULAB_NUMPY_HAS_FULL +#define ULAB_NUMPY_HAS_FULL (1) +#endif + +#ifndef ULAB_NUMPY_HAS_LINSPACE +#define ULAB_NUMPY_HAS_LINSPACE (1) +#endif + +#ifndef ULAB_NUMPY_HAS_LOGSPACE +#define ULAB_NUMPY_HAS_LOGSPACE (1) +#endif + +#ifndef ULAB_NUMPY_HAS_ONES +#define ULAB_NUMPY_HAS_ONES (1) +#endif + +#ifndef ULAB_NUMPY_HAS_ZEROS +#define ULAB_NUMPY_HAS_ZEROS (1) +#endif + +// functions that compare arrays +#ifndef ULAB_NUMPY_HAS_CLIP +#define ULAB_NUMPY_HAS_CLIP (1) +#endif + +#ifndef ULAB_NUMPY_HAS_EQUAL +#define ULAB_NUMPY_HAS_EQUAL (1) +#endif + +#ifndef ULAB_NUMPY_HAS_ISFINITE +#define ULAB_NUMPY_HAS_ISFINITE (1) +#endif + +#ifndef ULAB_NUMPY_HAS_ISINF +#define ULAB_NUMPY_HAS_ISINF (1) +#endif + +#ifndef ULAB_NUMPY_HAS_MAXIMUM +#define ULAB_NUMPY_HAS_MAXIMUM (1) +#endif + +#ifndef ULAB_NUMPY_HAS_MINIMUM +#define ULAB_NUMPY_HAS_MINIMUM (1) +#endif + +#ifndef ULAB_NUMPY_HAS_NOTEQUAL +#define ULAB_NUMPY_HAS_NOTEQUAL (1) +#endif + +#ifndef ULAB_NUMPY_HAS_WHERE +#define ULAB_NUMPY_HAS_WHERE (1) +#endif + +// the linalg module; functions of the linalg module still have +// to be defined separately +#ifndef ULAB_NUMPY_HAS_LINALG_MODULE +#define ULAB_NUMPY_HAS_LINALG_MODULE (1) +#endif + +#ifndef ULAB_LINALG_HAS_CHOLESKY +#define ULAB_LINALG_HAS_CHOLESKY (1) +#endif + +#ifndef ULAB_LINALG_HAS_DET +#define ULAB_LINALG_HAS_DET (1) +#endif + +#ifndef ULAB_LINALG_HAS_EIG +#define ULAB_LINALG_HAS_EIG (1) +#endif + +#ifndef ULAB_LINALG_HAS_INV +#define ULAB_LINALG_HAS_INV (1) +#endif + +#ifndef ULAB_LINALG_HAS_NORM +#define ULAB_LINALG_HAS_NORM (1) +#endif + +// the FFT module; functions of the fft module still have +// to be defined separately +#ifndef ULAB_NUMPY_HAS_FFT_MODULE +#define ULAB_NUMPY_HAS_FFT_MODULE (1) +#endif + +#ifndef ULAB_FFT_HAS_FFT +#define ULAB_FFT_HAS_FFT (1) +#endif + +#ifndef ULAB_FFT_HAS_IFFT +#define ULAB_FFT_HAS_IFFT (1) +#endif + +#ifndef ULAB_NUMPY_HAS_ALL +#define ULAB_NUMPY_HAS_ALL (1) +#endif + +#ifndef ULAB_NUMPY_HAS_ANY +#define ULAB_NUMPY_HAS_ANY (1) +#endif + +#ifndef ULAB_NUMPY_HAS_ARGMINMAX +#define ULAB_NUMPY_HAS_ARGMINMAX (1) +#endif + +#ifndef ULAB_NUMPY_HAS_ARGSORT +#define ULAB_NUMPY_HAS_ARGSORT (1) +#endif + +#ifndef ULAB_NUMPY_HAS_CONVOLVE +#define ULAB_NUMPY_HAS_CONVOLVE (1) +#endif + +#ifndef ULAB_NUMPY_HAS_CROSS +#define ULAB_NUMPY_HAS_CROSS (1) +#endif + +#ifndef ULAB_NUMPY_HAS_DIFF +#define ULAB_NUMPY_HAS_DIFF (1) +#endif + +#ifndef ULAB_NUMPY_HAS_DOT +#define ULAB_NUMPY_HAS_DOT (1) +#endif + +#ifndef ULAB_NUMPY_HAS_FLIP +#define ULAB_NUMPY_HAS_FLIP (1) +#endif + +#ifndef ULAB_NUMPY_HAS_INTERP +#define ULAB_NUMPY_HAS_INTERP (1) +#endif + +#ifndef ULAB_NUMPY_HAS_MEAN +#define ULAB_NUMPY_HAS_MEAN (1) +#endif + +#ifndef ULAB_NUMPY_HAS_MEDIAN +#define ULAB_NUMPY_HAS_MEDIAN (1) +#endif + +#ifndef ULAB_NUMPY_HAS_MINMAX +#define ULAB_NUMPY_HAS_MINMAX (1) +#endif + +#ifndef ULAB_NUMPY_HAS_POLYFIT +#define ULAB_NUMPY_HAS_POLYFIT (1) +#endif + +#ifndef ULAB_NUMPY_HAS_POLYVAL +#define ULAB_NUMPY_HAS_POLYVAL (1) +#endif + +#ifndef ULAB_NUMPY_HAS_ROLL +#define ULAB_NUMPY_HAS_ROLL (1) +#endif + +#ifndef ULAB_NUMPY_HAS_SORT +#define ULAB_NUMPY_HAS_SORT (1) +#endif + +#ifndef ULAB_NUMPY_HAS_STD +#define ULAB_NUMPY_HAS_STD (1) +#endif + +#ifndef ULAB_NUMPY_HAS_SUM +#define ULAB_NUMPY_HAS_SUM (1) +#endif + +#ifndef ULAB_NUMPY_HAS_TRACE +#define ULAB_NUMPY_HAS_TRACE (1) +#endif + +#ifndef ULAB_NUMPY_HAS_TRAPZ +#define ULAB_NUMPY_HAS_TRAPZ (1) +#endif + +// vectorised versions of the functions of the math python module, with +// the exception of the functions listed in scipy.special +#ifndef ULAB_NUMPY_HAS_ACOS +#define ULAB_NUMPY_HAS_ACOS (1) +#endif + +#ifndef ULAB_NUMPY_HAS_ACOSH +#define ULAB_NUMPY_HAS_ACOSH (1) +#endif + +#ifndef ULAB_NUMPY_HAS_ARCTAN2 +#define ULAB_NUMPY_HAS_ARCTAN2 (1) +#endif + +#ifndef ULAB_NUMPY_HAS_AROUND +#define ULAB_NUMPY_HAS_AROUND (1) +#endif + +#ifndef ULAB_NUMPY_HAS_ASIN +#define ULAB_NUMPY_HAS_ASIN (1) +#endif + +#ifndef ULAB_NUMPY_HAS_ASINH +#define ULAB_NUMPY_HAS_ASINH (1) +#endif + +#ifndef ULAB_NUMPY_HAS_ATAN +#define ULAB_NUMPY_HAS_ATAN (1) +#endif + +#ifndef ULAB_NUMPY_HAS_ATANH +#define ULAB_NUMPY_HAS_ATANH (1) +#endif + +#ifndef ULAB_NUMPY_HAS_CEIL +#define ULAB_NUMPY_HAS_CEIL (1) +#endif + +#ifndef ULAB_NUMPY_HAS_COS +#define ULAB_NUMPY_HAS_COS (1) +#endif + +#ifndef ULAB_NUMPY_HAS_COSH +#define ULAB_NUMPY_HAS_COSH (1) +#endif + +#ifndef ULAB_NUMPY_HAS_DEGREES +#define ULAB_NUMPY_HAS_DEGREES (1) +#endif + +#ifndef ULAB_NUMPY_HAS_EXP +#define ULAB_NUMPY_HAS_EXP (1) +#endif + +#ifndef ULAB_NUMPY_HAS_EXPM1 +#define ULAB_NUMPY_HAS_EXPM1 (1) +#endif + +#ifndef ULAB_NUMPY_HAS_FLOOR +#define ULAB_NUMPY_HAS_FLOOR (1) +#endif + +#ifndef ULAB_NUMPY_HAS_LOG +#define ULAB_NUMPY_HAS_LOG (1) +#endif + +#ifndef ULAB_NUMPY_HAS_LOG10 +#define ULAB_NUMPY_HAS_LOG10 (1) +#endif + +#ifndef ULAB_NUMPY_HAS_LOG2 +#define ULAB_NUMPY_HAS_LOG2 (1) +#endif + +#ifndef ULAB_NUMPY_HAS_RADIANS +#define ULAB_NUMPY_HAS_RADIANS (1) +#endif + +#ifndef ULAB_NUMPY_HAS_SIN +#define ULAB_NUMPY_HAS_SIN (1) +#endif + +#ifndef ULAB_NUMPY_HAS_SINH +#define ULAB_NUMPY_HAS_SINH (1) +#endif + +#ifndef ULAB_NUMPY_HAS_SQRT +#define ULAB_NUMPY_HAS_SQRT (1) +#endif + +#ifndef ULAB_NUMPY_HAS_TAN +#define ULAB_NUMPY_HAS_TAN (1) +#endif + +#ifndef ULAB_NUMPY_HAS_TANH +#define ULAB_NUMPY_HAS_TANH (1) +#endif + +#ifndef ULAB_NUMPY_HAS_VECTORIZE +#define ULAB_NUMPY_HAS_VECTORIZE (1) +#endif + +#ifndef ULAB_SCIPY_HAS_LINALG_MODULE +#define ULAB_SCIPY_HAS_LINALG_MODULE (1) +#endif + +#ifndef ULAB_SCIPY_LINALG_HAS_CHO_SOLVE +#define ULAB_SCIPY_LINALG_HAS_CHO_SOLVE (1) +#endif + +#ifndef ULAB_SCIPY_LINALG_HAS_SOLVE_TRIANGULAR +#define ULAB_SCIPY_LINALG_HAS_SOLVE_TRIANGULAR (1) +#endif + +#ifndef ULAB_SCIPY_HAS_SIGNAL_MODULE +#define ULAB_SCIPY_HAS_SIGNAL_MODULE (1) +#endif + +#ifndef ULAB_SCIPY_SIGNAL_HAS_SPECTROGRAM +#define ULAB_SCIPY_SIGNAL_HAS_SPECTROGRAM (1) +#endif + +#ifndef ULAB_SCIPY_SIGNAL_HAS_SOSFILT +#define ULAB_SCIPY_SIGNAL_HAS_SOSFILT (1) +#endif + +#ifndef ULAB_SCIPY_HAS_OPTIMIZE_MODULE +#define ULAB_SCIPY_HAS_OPTIMIZE_MODULE (1) +#endif + +#ifndef ULAB_SCIPY_OPTIMIZE_HAS_BISECT +#define ULAB_SCIPY_OPTIMIZE_HAS_BISECT (1) +#endif + +#ifndef ULAB_SCIPY_OPTIMIZE_HAS_CURVE_FIT +#define ULAB_SCIPY_OPTIMIZE_HAS_CURVE_FIT (0) // not fully implemented +#endif + +#ifndef ULAB_SCIPY_OPTIMIZE_HAS_FMIN +#define ULAB_SCIPY_OPTIMIZE_HAS_FMIN (1) +#endif + +#ifndef ULAB_SCIPY_OPTIMIZE_HAS_NEWTON +#define ULAB_SCIPY_OPTIMIZE_HAS_NEWTON (1) +#endif + +#ifndef ULAB_SCIPY_HAS_SPECIAL_MODULE +#define ULAB_SCIPY_HAS_SPECIAL_MODULE (1) +#endif + +#ifndef ULAB_SCIPY_SPECIAL_HAS_ERF +#define ULAB_SCIPY_SPECIAL_HAS_ERF (1) +#endif + +#ifndef ULAB_SCIPY_SPECIAL_HAS_ERFC +#define ULAB_SCIPY_SPECIAL_HAS_ERFC (1) +#endif + +#ifndef ULAB_SCIPY_SPECIAL_HAS_GAMMA +#define ULAB_SCIPY_SPECIAL_HAS_GAMMA (1) +#endif + +#ifndef ULAB_SCIPY_SPECIAL_HAS_GAMMALN +#define ULAB_SCIPY_SPECIAL_HAS_GAMMALN (1) +#endif + +// user-defined module; source of the module and +// its sub-modules should be placed in code/user/ +#ifndef ULAB_HAS_USER_MODULE +#define ULAB_HAS_USER_MODULE (0) +#endif + +#ifndef ULAB_HAS_UTILS_MODULE +#define ULAB_HAS_UTILS_MODULE (1) +#endif + +#ifndef ULAB_UTILS_HAS_FROM_INT16_BUFFER +#define ULAB_UTILS_HAS_FROM_INT16_BUFFER (1) +#endif + +#ifndef ULAB_UTILS_HAS_FROM_UINT16_BUFFER +#define ULAB_UTILS_HAS_FROM_UINT16_BUFFER (1) +#endif + +#ifndef ULAB_UTILS_HAS_FROM_INT32_BUFFER +#define ULAB_UTILS_HAS_FROM_INT32_BUFFER (1) +#endif + +#ifndef ULAB_UTILS_HAS_FROM_UINT32_BUFFER +#define ULAB_UTILS_HAS_FROM_UINT32_BUFFER (1) +#endif + +#endif diff --git a/python/port/mod/ulab/ulab_create.c b/python/port/mod/ulab/ulab_create.c new file mode 100644 index 000000000..2cb8f2325 --- /dev/null +++ b/python/port/mod/ulab/ulab_create.c @@ -0,0 +1,706 @@ +/* + * This file is part of the micropython-ulab project, + * + * https://github.com/v923z/micropython-ulab + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jeff Epler for Adafruit Industries + * 2019-2021 Zoltán Vörös + * 2020 Taku Fukada +*/ + +#include +#include +#include +#include +#include +#include + +#include "ulab.h" +#include "ulab_create.h" + +#if ULAB_NUMPY_HAS_ONES | ULAB_NUMPY_HAS_ZEROS | ULAB_NUMPY_HAS_FULL | ULAB_NUMPY_HAS_EMPTY +static mp_obj_t create_zeros_ones_full(mp_obj_t oshape, uint8_t dtype, mp_obj_t value) { + if(!mp_obj_is_int(oshape) && !mp_obj_is_type(oshape, &mp_type_tuple) && !mp_obj_is_type(oshape, &mp_type_list)) { + mp_raise_TypeError(translate("input argument must be an integer, a tuple, or a list")); + } + ndarray_obj_t *ndarray = NULL; + if(mp_obj_is_int(oshape)) { + size_t n = mp_obj_get_int(oshape); + ndarray = ndarray_new_linear_array(n, dtype); + } else if(mp_obj_is_type(oshape, &mp_type_tuple) || mp_obj_is_type(oshape, &mp_type_list)) { + uint8_t len = (uint8_t)mp_obj_get_int(mp_obj_len_maybe(oshape)); + if(len > ULAB_MAX_DIMS) { + mp_raise_TypeError(translate("too many dimensions")); + } + size_t *shape = m_new(size_t, ULAB_MAX_DIMS); + memset(shape, 0, ULAB_MAX_DIMS * sizeof(size_t)); + size_t i = 0; + mp_obj_iter_buf_t iter_buf; + mp_obj_t item, iterable = mp_getiter(oshape, &iter_buf); + while((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION){ + shape[ULAB_MAX_DIMS - len + i] = (size_t)mp_obj_get_int(item); + i++; + } + ndarray = ndarray_new_dense_ndarray(len, shape, dtype); + } + if(value != mp_const_none) { + if(dtype == NDARRAY_BOOL) { + dtype = NDARRAY_UINT8; + if(mp_obj_is_true(value)) { + value = mp_obj_new_int(1); + } else { + value = mp_obj_new_int(0); + } + } + for(size_t i=0; i < ndarray->len; i++) { + ndarray_set_value(dtype, ndarray->array, i, value); + } + } + // if zeros calls the function, we don't have to do anything + return MP_OBJ_FROM_PTR(ndarray); +} +#endif + +#if ULAB_NUMPY_HAS_ARANGE | ULAB_NUMPY_HAS_LINSPACE +static ndarray_obj_t *create_linspace_arange(mp_float_t start, mp_float_t step, size_t len, uint8_t dtype) { + mp_float_t value = start; + + ndarray_obj_t *ndarray = ndarray_new_linear_array(len, dtype); + if(ndarray->boolean == NDARRAY_BOOLEAN) { + uint8_t *array = (uint8_t *)ndarray->array; + for(size_t i=0; i < len; i++, value += step) { + *array++ = value == MICROPY_FLOAT_CONST(0.0) ? 0 : 1; + } + } else if(dtype == NDARRAY_UINT8) { + ARANGE_LOOP(uint8_t, ndarray, len, step); + } else if(dtype == NDARRAY_INT8) { + ARANGE_LOOP(int8_t, ndarray, len, step); + } else if(dtype == NDARRAY_UINT16) { + ARANGE_LOOP(uint16_t, ndarray, len, step); + } else if(dtype == NDARRAY_INT16) { + ARANGE_LOOP(int16_t, ndarray, len, step); + } else { + ARANGE_LOOP(mp_float_t, ndarray, len, step); + } + return ndarray; +} +#endif + +#if ULAB_NUMPY_HAS_ARANGE +//| @overload +//| def arange(stop: _float, step: _float = 1, *, dtype: _DType = ulab.float) -> ulab.ndarray: ... +//| @overload +//| def arange(start: _float, stop: _float, step: _float = 1, *, dtype: _DType = ulab.float) -> ulab.ndarray: +//| """ +//| .. param: start +//| First value in the array, optional, defaults to 0 +//| .. param: stop +//| Final value in the array +//| .. param: step +//| Difference between consecutive elements, optional, defaults to 1.0 +//| .. param: dtype +//| Type of values in the array +//| +//| Return a new 1-D array with elements ranging from ``start`` to ``stop``, with step size ``step``.""" +//| ... +//| + +mp_obj_t create_arange(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = mp_const_none } }, + { MP_QSTR_, MP_ARG_OBJ, { .u_rom_obj = mp_const_none } }, + { MP_QSTR_, MP_ARG_OBJ, { .u_rom_obj = mp_const_none } }, + { MP_QSTR_dtype, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = mp_const_none } }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + uint8_t dtype = NDARRAY_FLOAT; + mp_float_t start, stop, step; + if(n_args == 1) { + start = 0.0; + stop = mp_obj_get_float(args[0].u_obj); + step = 1.0; + if(mp_obj_is_int(args[0].u_obj)) dtype = NDARRAY_INT16; + } else if(n_args == 2) { + start = mp_obj_get_float(args[0].u_obj); + stop = mp_obj_get_float(args[1].u_obj); + step = 1.0; + if(mp_obj_is_int(args[0].u_obj) && mp_obj_is_int(args[1].u_obj)) dtype = NDARRAY_INT16; + } else if(n_args == 3) { + start = mp_obj_get_float(args[0].u_obj); + stop = mp_obj_get_float(args[1].u_obj); + step = mp_obj_get_float(args[2].u_obj); + if(mp_obj_is_int(args[0].u_obj) && mp_obj_is_int(args[1].u_obj) && mp_obj_is_int(args[2].u_obj)) dtype = NDARRAY_INT16; + } else { + mp_raise_TypeError(translate("wrong number of arguments")); + } + if((MICROPY_FLOAT_C_FUN(fabs)(stop) > 32768) || (MICROPY_FLOAT_C_FUN(fabs)(start) > 32768) || (MICROPY_FLOAT_C_FUN(fabs)(step) > 32768)) { + dtype = NDARRAY_FLOAT; + } + if(args[3].u_obj != mp_const_none) { + dtype = (uint8_t)mp_obj_get_int(args[3].u_obj); + } + ndarray_obj_t *ndarray; + if((stop - start)/step < 0) { + ndarray = ndarray_new_linear_array(0, dtype); + } else { + size_t len = (size_t)(MICROPY_FLOAT_C_FUN(ceil)((stop - start)/step)); + ndarray = create_linspace_arange(start, step, len, dtype); + } + return MP_OBJ_FROM_PTR(ndarray); +} + +MP_DEFINE_CONST_FUN_OBJ_KW(create_arange_obj, 1, create_arange); +#endif + +#if ULAB_NUMPY_HAS_CONCATENATE +//| def concatenate(arrays: Tuple[ulab.ndarray], *, axis: int = 0) -> ulab.ndarray: +//| """ +//| .. param: arrays +//| tuple of ndarrays +//| .. param: axis +//| axis along which the arrays will be joined +//| +//| Join a sequence of arrays along an existing axis.""" +//| ... +//| + +mp_obj_t create_concatenate(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = mp_const_none } }, + { MP_QSTR_axis, MP_ARG_KW_ONLY | MP_ARG_INT, { .u_int = 0 } }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + if(!mp_obj_is_type(args[0].u_obj, &mp_type_tuple)) { + mp_raise_TypeError(translate("first argument must be a tuple of ndarrays")); + } + int8_t axis = (int8_t)args[1].u_int; + size_t *shape = m_new(size_t, ULAB_MAX_DIMS); + memset(shape, 0, sizeof(size_t)*ULAB_MAX_DIMS); + mp_obj_tuple_t *ndarrays = MP_OBJ_TO_PTR(args[0].u_obj); + + // first check, whether the arrays are compatible + ndarray_obj_t *_ndarray = MP_OBJ_TO_PTR(ndarrays->items[0]); + uint8_t dtype = _ndarray->dtype; + uint8_t ndim = _ndarray->ndim; + if(axis < 0) { + axis += ndim; + } + if((axis < 0) || (axis >= ndim)) { + mp_raise_ValueError(translate("wrong axis specified")); + } + // shift axis + axis = ULAB_MAX_DIMS - ndim + axis; + for(uint8_t j=0; j < ULAB_MAX_DIMS; j++) { + shape[j] = _ndarray->shape[j]; + } + + for(uint8_t i=1; i < ndarrays->len; i++) { + _ndarray = MP_OBJ_TO_PTR(ndarrays->items[i]); + // check, whether the arrays are compatible + if((dtype != _ndarray->dtype) || (ndim != _ndarray->ndim)) { + mp_raise_ValueError(translate("input arrays are not compatible")); + } + for(uint8_t j=0; j < ULAB_MAX_DIMS; j++) { + if(j == axis) { + shape[j] += _ndarray->shape[j]; + } else { + if(shape[j] != _ndarray->shape[j]) { + mp_raise_ValueError(translate("input arrays are not compatible")); + } + } + } + } + + ndarray_obj_t *target = ndarray_new_dense_ndarray(ndim, shape, dtype); + uint8_t *tpos = (uint8_t *)target->array; + uint8_t *tarray; + + for(uint8_t p=0; p < ndarrays->len; p++) { + // reset the pointer along the axis + ndarray_obj_t *source = MP_OBJ_TO_PTR(ndarrays->items[p]); + uint8_t *sarray = (uint8_t *)source->array; + tarray = tpos; + + #if ULAB_MAX_DIMS > 3 + size_t i = 0; + do { + #endif + #if ULAB_MAX_DIMS > 2 + size_t j = 0; + do { + #endif + #if ULAB_MAX_DIMS > 1 + size_t k = 0; + do { + #endif + size_t l = 0; + do { + memcpy(tarray, sarray, source->itemsize); + tarray += target->strides[ULAB_MAX_DIMS - 1]; + sarray += source->strides[ULAB_MAX_DIMS - 1]; + l++; + } while(l < source->shape[ULAB_MAX_DIMS - 1]); + #if ULAB_MAX_DIMS > 1 + tarray -= target->strides[ULAB_MAX_DIMS - 1] * source->shape[ULAB_MAX_DIMS-1]; + tarray += target->strides[ULAB_MAX_DIMS - 2]; + sarray -= source->strides[ULAB_MAX_DIMS - 1] * source->shape[ULAB_MAX_DIMS-1]; + sarray += source->strides[ULAB_MAX_DIMS - 2]; + k++; + } while(k < source->shape[ULAB_MAX_DIMS - 2]); + #endif + #if ULAB_MAX_DIMS > 2 + tarray -= target->strides[ULAB_MAX_DIMS - 2] * source->shape[ULAB_MAX_DIMS-2]; + tarray += target->strides[ULAB_MAX_DIMS - 3]; + sarray -= source->strides[ULAB_MAX_DIMS - 2] * source->shape[ULAB_MAX_DIMS-2]; + sarray += source->strides[ULAB_MAX_DIMS - 3]; + j++; + } while(j < source->shape[ULAB_MAX_DIMS - 3]); + #endif + #if ULAB_MAX_DIMS > 3 + tarray -= target->strides[ULAB_MAX_DIMS - 3] * source->shape[ULAB_MAX_DIMS-3]; + tarray += target->strides[ULAB_MAX_DIMS - 4]; + sarray -= source->strides[ULAB_MAX_DIMS - 3] * source->shape[ULAB_MAX_DIMS-3]; + sarray += source->strides[ULAB_MAX_DIMS - 4]; + i++; + } while(i < source->shape[ULAB_MAX_DIMS - 4]); + #endif + if(p < ndarrays->len - 1) { + tpos += target->strides[axis] * source->shape[axis]; + } + } + return MP_OBJ_FROM_PTR(target); +} + +MP_DEFINE_CONST_FUN_OBJ_KW(create_concatenate_obj, 1, create_concatenate); +#endif + +#if ULAB_NUMPY_HAS_DIAG +//| def diag(a: ulab.ndarray, *, k: int = 0) -> ulab.ndarray: +//| """ +//| .. param: a +//| an ndarray +//| .. param: k +//| Offset of the diagonal from the main diagonal. Can be positive or negative. +//| +//| Return specified diagonals.""" +//| ... +//| +mp_obj_t create_diag(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = mp_const_none } }, + { MP_QSTR_k, MP_ARG_KW_ONLY | MP_ARG_INT, { .u_int = 0 } }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + if(!mp_obj_is_type(args[0].u_obj, &ulab_ndarray_type)) { + mp_raise_TypeError(translate("input must be an ndarray")); + } + ndarray_obj_t *source = MP_OBJ_TO_PTR(args[0].u_obj); + if(source->ndim == 1) { // return a rank-2 tensor with the prescribed diagonal + ndarray_obj_t *target = ndarray_new_dense_ndarray(2, ndarray_shape_vector(0, 0, source->len, source->len), source->dtype); + uint8_t *sarray = (uint8_t *)source->array; + uint8_t *tarray = (uint8_t *)target->array; + for(size_t i=0; i < source->len; i++) { + memcpy(tarray, sarray, source->itemsize); + sarray += source->strides[ULAB_MAX_DIMS - 1]; + tarray += (source->len + 1) * target->itemsize; + } + return MP_OBJ_FROM_PTR(target); + } + if(source->ndim > 2) { + mp_raise_TypeError(translate("input must be a tensor of rank 2")); + } + int32_t k = args[1].u_int; + size_t len = 0; + uint8_t *sarray = (uint8_t *)source->array; + if(k < 0) { // move the pointer "vertically" + if(-k < (int32_t)source->shape[ULAB_MAX_DIMS - 2]) { + sarray -= k * source->strides[ULAB_MAX_DIMS - 2]; + len = MIN(source->shape[ULAB_MAX_DIMS - 2] + k, source->shape[ULAB_MAX_DIMS - 1]); + } + } else { // move the pointer "horizontally" + if(k < (int32_t)source->shape[ULAB_MAX_DIMS - 1]) { + sarray += k * source->strides[ULAB_MAX_DIMS - 1]; + len = MIN(source->shape[ULAB_MAX_DIMS - 1] - k, source->shape[ULAB_MAX_DIMS - 2]); + } + } + + if(len == 0) { + mp_raise_ValueError(translate("offset is too large")); + } + + ndarray_obj_t *target = ndarray_new_linear_array(len, source->dtype); + uint8_t *tarray = (uint8_t *)target->array; + + for(size_t i=0; i < len; i++) { + memcpy(tarray, sarray, source->itemsize); + sarray += source->strides[ULAB_MAX_DIMS - 2]; + sarray += source->strides[ULAB_MAX_DIMS - 1]; + tarray += source->itemsize; + } + return MP_OBJ_FROM_PTR(target); +} + +MP_DEFINE_CONST_FUN_OBJ_KW(create_diag_obj, 1, create_diag); +#endif /* ULAB_NUMPY_HAS_DIAG */ + +#if ULAB_MAX_DIMS > 1 +#if ULAB_NUMPY_HAS_EYE +//| def eye(size: int, *, M: Optional[int] = None, k: int = 0, dtype: _DType = ulab.float) -> ulab.ndarray: +//| """Return a new square array of size, with the diagonal elements set to 1 +//| and the other elements set to 0.""" +//| ... +//| + +mp_obj_t create_eye(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_INT, { .u_int = 0 } }, + { MP_QSTR_M, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = mp_const_none } }, + { MP_QSTR_k, MP_ARG_KW_ONLY | MP_ARG_INT, { .u_int = 0 } }, + { MP_QSTR_dtype, MP_ARG_KW_ONLY | MP_ARG_INT, { .u_int = NDARRAY_FLOAT } }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + size_t n = args[0].u_int, m; + size_t k = args[2].u_int > 0 ? (size_t)args[2].u_int : (size_t)(-args[2].u_int); + uint8_t dtype = args[3].u_int; + if(args[1].u_rom_obj == mp_const_none) { + m = n; + } else { + m = mp_obj_get_int(args[1].u_rom_obj); + } + ndarray_obj_t *ndarray = ndarray_new_dense_ndarray(2, ndarray_shape_vector(0, 0, n, m), dtype); + if(dtype == NDARRAY_BOOL) { + dtype = NDARRAY_UINT8; + } + mp_obj_t one = mp_obj_new_int(1); + size_t i = 0; + if((args[2].u_int >= 0)) { + while(k < m) { + ndarray_set_value(dtype, ndarray->array, i*m+k, one); + k++; + i++; + } + } else { + while(k < n) { + ndarray_set_value(dtype, ndarray->array, k*m+i, one); + k++; + i++; + } + } + return MP_OBJ_FROM_PTR(ndarray); +} + +MP_DEFINE_CONST_FUN_OBJ_KW(create_eye_obj, 1, create_eye); +#endif /* ULAB_NUMPY_HAS_EYE */ +#endif /* ULAB_MAX_DIMS > 1 */ + +#if ULAB_NUMPY_HAS_FULL +//| def full(shape: Union[int, Tuple[int, ...]], fill_value: Union[_float, _bool], *, dtype: _DType = ulab.float) -> ulab.ndarray: +//| """ +//| .. param: shape +//| Shape of the array, either an integer (for a 1-D array) or a tuple of integers (for tensors of higher rank) +//| .. param: fill_value +//| scalar, the value with which the array is filled +//| .. param: dtype +//| Type of values in the array +//| +//| Return a new array of the given shape with all elements set to 0.""" +//| ... +//| + +mp_obj_t create_full(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_obj = MP_OBJ_NULL } }, + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_obj = MP_OBJ_NULL } }, + { MP_QSTR_dtype, MP_ARG_KW_ONLY | MP_ARG_INT, { .u_int = NDARRAY_FLOAT } }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + uint8_t dtype = args[2].u_int; + + return create_zeros_ones_full(args[0].u_obj, dtype, args[1].u_obj); +} + +MP_DEFINE_CONST_FUN_OBJ_KW(create_full_obj, 0, create_full); +#endif + + +#if ULAB_NUMPY_HAS_LINSPACE +//| def linspace( +//| start: _float, +//| stop: _float, +//| *, +//| dtype: _DType = ulab.float, +//| num: int = 50, +//| endpoint: _bool = True, +//| retstep: _bool = False +//| ) -> ulab.ndarray: +//| """ +//| .. param: start +//| First value in the array +//| .. param: stop +//| Final value in the array +//| .. param int: num +//| Count of values in the array. +//| .. param: dtype +//| Type of values in the array +//| .. param bool: endpoint +//| Whether the ``stop`` value is included. Note that even when +//| endpoint=True, the exact ``stop`` value may not be included due to the +//| inaccuracy of floating point arithmetic. +// .. param bool: retstep, +//| If True, return (`samples`, `step`), where `step` is the spacing between samples. +//| +//| Return a new 1-D array with ``num`` elements ranging from ``start`` to ``stop`` linearly.""" +//| ... +//| + +mp_obj_t create_linspace(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = mp_const_none } }, + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = mp_const_none } }, + { MP_QSTR_num, MP_ARG_INT, { .u_int = 50 } }, + { MP_QSTR_endpoint, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = mp_const_true } }, + { MP_QSTR_retstep, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = mp_const_false } }, + { MP_QSTR_dtype, MP_ARG_KW_ONLY | MP_ARG_INT, { .u_int = NDARRAY_FLOAT } }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + if(args[2].u_int < 2) { + mp_raise_ValueError(translate("number of points must be at least 2")); + } + size_t len = (size_t)args[2].u_int; + mp_float_t start, step; + start = mp_obj_get_float(args[0].u_obj); + uint8_t typecode = args[5].u_int; + if(args[3].u_obj == mp_const_true) step = (mp_obj_get_float(args[1].u_obj)-start)/(len-1); + else step = (mp_obj_get_float(args[1].u_obj)-start)/len; + ndarray_obj_t *ndarray = create_linspace_arange(start, step, len, typecode); + if(args[4].u_obj == mp_const_false) { + return MP_OBJ_FROM_PTR(ndarray); + } else { + mp_obj_t tuple[2]; + tuple[0] = ndarray; + tuple[1] = mp_obj_new_float(step); + return mp_obj_new_tuple(2, tuple); + } +} + +MP_DEFINE_CONST_FUN_OBJ_KW(create_linspace_obj, 2, create_linspace); +#endif + +#if ULAB_NUMPY_HAS_LOGSPACE +//| def logspace( +//| start: _float, +//| stop: _float, +//| *, +//| dtype: _DType = ulab.float, +//| num: int = 50, +//| endpoint: _bool = True, +//| base: _float = 10.0 +//| ) -> ulab.ndarray: +//| """ +//| .. param: start +//| First value in the array +//| .. param: stop +//| Final value in the array +//| .. param int: num +//| Count of values in the array. Defaults to 50. +//| .. param: base +//| The base of the log space. The step size between the elements in +//| ``ln(samples) / ln(base)`` (or ``log_base(samples)``) is uniform. Defaults to 10.0. +//| .. param: dtype +//| Type of values in the array +//| .. param bool: endpoint +//| Whether the ``stop`` value is included. Note that even when +//| endpoint=True, the exact ``stop`` value may not be included due to the +//| inaccuracy of floating point arithmetic. Defaults to True. +//| +//| Return a new 1-D array with ``num`` evenly spaced elements on a log scale. +//| The sequence starts at ``base ** start``, and ends with ``base ** stop``.""" +//| ... +//| + +const mp_obj_float_t create_float_const_ten = {{&mp_type_float}, MICROPY_FLOAT_CONST(10.0)}; + +mp_obj_t create_logspace(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = mp_const_none } }, + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = mp_const_none } }, + { MP_QSTR_num, MP_ARG_INT, { .u_int = 50 } }, + { MP_QSTR_base, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_PTR(&create_float_const_ten) } }, + { MP_QSTR_endpoint, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = mp_const_true } }, + { MP_QSTR_dtype, MP_ARG_KW_ONLY | MP_ARG_INT, { .u_int = NDARRAY_FLOAT } }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + if(args[2].u_int < 2) { + mp_raise_ValueError(translate("number of points must be at least 2")); + } + size_t len = (size_t)args[2].u_int; + mp_float_t start, step, quotient; + start = mp_obj_get_float(args[0].u_obj); + uint8_t dtype = args[5].u_int; + mp_float_t base = mp_obj_get_float(args[3].u_obj); + if(args[4].u_obj == mp_const_true) step = (mp_obj_get_float(args[1].u_obj) - start)/(len - 1); + else step = (mp_obj_get_float(args[1].u_obj) - start) / len; + quotient = MICROPY_FLOAT_C_FUN(pow)(base, step); + ndarray_obj_t *ndarray = ndarray_new_linear_array(len, dtype); + + mp_float_t value = MICROPY_FLOAT_C_FUN(pow)(base, start); + if(ndarray->dtype == NDARRAY_UINT8) { + uint8_t *array = (uint8_t *)ndarray->array; + if(ndarray->boolean) { + memset(array, 1, len); + } else { + for(size_t i=0; i < len; i++, value *= quotient) *array++ = (uint8_t)value; + } + } else if(ndarray->dtype == NDARRAY_INT8) { + int8_t *array = (int8_t *)ndarray->array; + for(size_t i=0; i < len; i++, value *= quotient) *array++ = (int8_t)value; + } else if(ndarray->dtype == NDARRAY_UINT16) { + uint16_t *array = (uint16_t *)ndarray->array; + for(size_t i=0; i < len; i++, value *= quotient) *array++ = (uint16_t)value; + } else if(ndarray->dtype == NDARRAY_INT16) { + int16_t *array = (int16_t *)ndarray->array; + for(size_t i=0; i < len; i++, value *= quotient) *array++ = (int16_t)value; + } else { + mp_float_t *array = (mp_float_t *)ndarray->array; + for(size_t i=0; i < len; i++, value *= quotient) *array++ = value; + } + return MP_OBJ_FROM_PTR(ndarray); +} + +MP_DEFINE_CONST_FUN_OBJ_KW(create_logspace_obj, 2, create_logspace); +#endif + +#if ULAB_NUMPY_HAS_ONES +//| def ones(shape: Union[int, Tuple[int, ...]], *, dtype: _DType = ulab.float) -> ulab.ndarray: +//| """ +//| .. param: shape +//| Shape of the array, either an integer (for a 1-D array) or a tuple of 2 integers (for a 2-D array) +//| .. param: dtype +//| Type of values in the array +//| +//| Return a new array of the given shape with all elements set to 1.""" +//| ... +//| + +mp_obj_t create_ones(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_obj = MP_OBJ_NULL } }, + { MP_QSTR_dtype, MP_ARG_KW_ONLY | MP_ARG_INT, { .u_int = NDARRAY_FLOAT } }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + uint8_t dtype = args[1].u_int; + mp_obj_t one = mp_obj_new_int(1); + return create_zeros_ones_full(args[0].u_obj, dtype, one); +} + +MP_DEFINE_CONST_FUN_OBJ_KW(create_ones_obj, 0, create_ones); +#endif + +#if ULAB_NUMPY_HAS_ZEROS +//| def zeros(shape: Union[int, Tuple[int, ...]], *, dtype: _DType = ulab.float) -> ulab.ndarray: +//| """ +//| .. param: shape +//| Shape of the array, either an integer (for a 1-D array) or a tuple of 2 integers (for a 2-D array) +//| .. param: dtype +//| Type of values in the array +//| +//| Return a new array of the given shape with all elements set to 0.""" +//| ... +//| + +mp_obj_t create_zeros(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_obj = MP_OBJ_NULL } }, + { MP_QSTR_dtype, MP_ARG_KW_ONLY | MP_ARG_INT, { .u_int = NDARRAY_FLOAT } }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + uint8_t dtype = args[1].u_int; + return create_zeros_ones_full(args[0].u_obj, dtype, mp_const_none); +} + +MP_DEFINE_CONST_FUN_OBJ_KW(create_zeros_obj, 0, create_zeros); +#endif + +#if ULAB_NUMPY_HAS_FROMBUFFER +mp_obj_t create_frombuffer(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = mp_const_none } }, + { MP_QSTR_dtype, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_INT(NDARRAY_FLOAT) } }, + { MP_QSTR_count, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_INT(-1) } }, + { MP_QSTR_offset, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_INT(0) } }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + uint8_t dtype = mp_obj_get_int(args[1].u_obj); + size_t offset = mp_obj_get_int(args[3].u_obj); + + mp_buffer_info_t bufinfo; + if(mp_get_buffer(args[0].u_obj, &bufinfo, MP_BUFFER_READ)) { + size_t sz = 1; + if(dtype != NDARRAY_BOOL) { // mp_binary_get_size doesn't work with Booleans + sz = mp_binary_get_size('@', dtype, NULL); + } + if(bufinfo.len < offset) { + mp_raise_ValueError(translate("offset must be non-negative and no greater than buffer length")); + } + size_t len = (bufinfo.len - offset) / sz; + if((len * sz) != (bufinfo.len - offset)) { + mp_raise_ValueError(translate("buffer size must be a multiple of element size")); + } + if(mp_obj_get_int(args[2].u_obj) > 0) { + size_t count = mp_obj_get_int(args[2].u_obj); + if(len < count) { + mp_raise_ValueError(translate("buffer is smaller than requested size")); + } else { + len = count; + } + } + ndarray_obj_t *ndarray = m_new_obj(ndarray_obj_t); + ndarray->base.type = &ulab_ndarray_type; + ndarray->dtype = dtype == NDARRAY_BOOL ? NDARRAY_UINT8 : dtype; + ndarray->boolean = dtype == NDARRAY_BOOL ? NDARRAY_BOOLEAN : NDARRAY_NUMERIC; + ndarray->ndim = 1; + ndarray->len = len; + ndarray->itemsize = sz; + ndarray->shape[ULAB_MAX_DIMS - 1] = len; + ndarray->strides[ULAB_MAX_DIMS - 1] = sz; + + uint8_t *buffer = bufinfo.buf; + ndarray->array = buffer + offset; + return MP_OBJ_FROM_PTR(ndarray); + } + return mp_const_none; +} + +MP_DEFINE_CONST_FUN_OBJ_KW(create_frombuffer_obj, 1, create_frombuffer); +#endif diff --git a/python/port/mod/ulab/ulab_create.h b/python/port/mod/ulab/ulab_create.h new file mode 100644 index 000000000..9aefc0b2b --- /dev/null +++ b/python/port/mod/ulab/ulab_create.h @@ -0,0 +1,78 @@ +/* + * This file is part of the micropython-ulab project, + * + * https://github.com/v923z/micropython-ulab + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jeff Epler for Adafruit Industries + * 2019-2021 Zoltán Vörös +*/ + +#ifndef _CREATE_ +#define _CREATE_ + +#include "ulab.h" +#include "ndarray.h" + +#if ULAB_NUMPY_HAS_ARANGE +mp_obj_t create_arange(size_t , const mp_obj_t *, mp_map_t *); +MP_DECLARE_CONST_FUN_OBJ_KW(create_arange_obj); +#endif + +#if ULAB_NUMPY_HAS_CONCATENATE +mp_obj_t create_concatenate(size_t , const mp_obj_t *, mp_map_t *); +MP_DECLARE_CONST_FUN_OBJ_KW(create_concatenate_obj); +#endif + +#if ULAB_NUMPY_HAS_DIAG +mp_obj_t create_diag(size_t , const mp_obj_t *, mp_map_t *); +MP_DECLARE_CONST_FUN_OBJ_KW(create_diag_obj); +#endif + +#if ULAB_MAX_DIMS > 1 +#if ULAB_NUMPY_HAS_EYE +mp_obj_t create_eye(size_t , const mp_obj_t *, mp_map_t *); +MP_DECLARE_CONST_FUN_OBJ_KW(create_eye_obj); +#endif +#endif + +#if ULAB_NUMPY_HAS_FULL +mp_obj_t create_full(size_t , const mp_obj_t *, mp_map_t *); +MP_DECLARE_CONST_FUN_OBJ_KW(create_full_obj); +#endif + +#if ULAB_NUMPY_HAS_LINSPACE +mp_obj_t create_linspace(size_t , const mp_obj_t *, mp_map_t *); +MP_DECLARE_CONST_FUN_OBJ_KW(create_linspace_obj); +#endif + +#if ULAB_NUMPY_HAS_LOGSPACE +mp_obj_t create_logspace(size_t , const mp_obj_t *, mp_map_t *); +MP_DECLARE_CONST_FUN_OBJ_KW(create_logspace_obj); +#endif + +#if ULAB_NUMPY_HAS_ONES +mp_obj_t create_ones(size_t , const mp_obj_t *, mp_map_t *); +MP_DECLARE_CONST_FUN_OBJ_KW(create_ones_obj); +#endif + +#if ULAB_NUMPY_HAS_ZEROS +mp_obj_t create_zeros(size_t , const mp_obj_t *, mp_map_t *); +MP_DECLARE_CONST_FUN_OBJ_KW(create_zeros_obj); +#endif + +#if ULAB_NUMPY_HAS_FROMBUFFER +mp_obj_t create_frombuffer(size_t , const mp_obj_t *, mp_map_t *); +MP_DECLARE_CONST_FUN_OBJ_KW(create_frombuffer_obj); +#endif + +#define ARANGE_LOOP(type_, ndarray, len, step) \ +({\ + type_ *array = (type_ *)(ndarray)->array;\ + for (size_t i = 0; i < (len); i++, (value) += (step)) {\ + *array++ = (type_)value;\ + }\ +}) + +#endif diff --git a/python/port/mod/ulab/ulab_tools.c b/python/port/mod/ulab/ulab_tools.c new file mode 100644 index 000000000..dd93787ba --- /dev/null +++ b/python/port/mod/ulab/ulab_tools.c @@ -0,0 +1,233 @@ +/* + * This file is part of the micropython-ulab project, + * + * https://github.com/v923z/micropython-ulab + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Zoltán Vörös + */ + + +#include +#include + +#include "ulab.h" +#include "ndarray.h" +#include "ulab_tools.h" + +// The following five functions return a float from a void type +// The value in question is supposed to be located at the head of the pointer + +mp_float_t ndarray_get_float_uint8(void *data) { + // Returns a float value from an uint8_t type + return (mp_float_t)(*(uint8_t *)data); +} + +mp_float_t ndarray_get_float_int8(void *data) { + // Returns a float value from an int8_t type + return (mp_float_t)(*(int8_t *)data); +} + +mp_float_t ndarray_get_float_uint16(void *data) { + // Returns a float value from an uint16_t type + return (mp_float_t)(*(uint16_t *)data); +} + +mp_float_t ndarray_get_float_int16(void *data) { + // Returns a float value from an int16_t type + return (mp_float_t)(*(int16_t *)data); +} + + +mp_float_t ndarray_get_float_float(void *data) { + // Returns a float value from an mp_float_t type + return *((mp_float_t *)data); +} + +// returns a single function pointer, depending on the dtype +void *ndarray_get_float_function(uint8_t dtype) { + if(dtype == NDARRAY_UINT8) { + return ndarray_get_float_uint8; + } else if(dtype == NDARRAY_INT8) { + return ndarray_get_float_int8; + } else if(dtype == NDARRAY_UINT16) { + return ndarray_get_float_uint16; + } else if(dtype == NDARRAY_INT16) { + return ndarray_get_float_int16; + } else { + return ndarray_get_float_float; + } +} + +mp_float_t ndarray_get_float_index(void *data, uint8_t dtype, size_t index) { + // returns a single float value from an array located at index + if(dtype == NDARRAY_UINT8) { + return (mp_float_t)((uint8_t *)data)[index]; + } else if(dtype == NDARRAY_INT8) { + return (mp_float_t)((int8_t *)data)[index]; + } else if(dtype == NDARRAY_UINT16) { + return (mp_float_t)((uint16_t *)data)[index]; + } else if(dtype == NDARRAY_INT16) { + return (mp_float_t)((int16_t *)data)[index]; + } else { + return (mp_float_t)((mp_float_t *)data)[index]; + } +} + +mp_float_t ndarray_get_float_value(void *data, uint8_t dtype) { + // Returns a float value from an arbitrary data type + // The value in question is supposed to be located at the head of the pointer + if(dtype == NDARRAY_UINT8) { + return (mp_float_t)(*(uint8_t *)data); + } else if(dtype == NDARRAY_INT8) { + return (mp_float_t)(*(int8_t *)data); + } else if(dtype == NDARRAY_UINT16) { + return (mp_float_t)(*(uint16_t *)data); + } else if(dtype == NDARRAY_INT16) { + return (mp_float_t)(*(int16_t *)data); + } else { + return *((mp_float_t *)data); + } +} + +#if NDARRAY_BINARY_USES_FUN_POINTER | ULAB_NUMPY_HAS_WHERE +uint8_t ndarray_upcast_dtype(uint8_t ldtype, uint8_t rdtype) { + // returns a single character that corresponds to the broadcasting rules + // - if one of the operarands is a float, the result is always float + // - operation on identical types preserves type + // + // uint8 + int8 => int16 + // uint8 + int16 => int16 + // uint8 + uint16 => uint16 + // int8 + int16 => int16 + // int8 + uint16 => uint16 + // uint16 + int16 => float + + if(ldtype == rdtype) { + // if the two dtypes are equal, the result is also of that type + return ldtype; + } else if(((ldtype == NDARRAY_UINT8) && (rdtype == NDARRAY_INT8)) || + ((ldtype == NDARRAY_INT8) && (rdtype == NDARRAY_UINT8)) || + ((ldtype == NDARRAY_UINT8) && (rdtype == NDARRAY_INT16)) || + ((ldtype == NDARRAY_INT16) && (rdtype == NDARRAY_UINT8)) || + ((ldtype == NDARRAY_INT8) && (rdtype == NDARRAY_INT16)) || + ((ldtype == NDARRAY_INT16) && (rdtype == NDARRAY_INT8))) { + return NDARRAY_INT16; + } else if(((ldtype == NDARRAY_UINT8) && (rdtype == NDARRAY_UINT16)) || + ((ldtype == NDARRAY_UINT16) && (rdtype == NDARRAY_UINT8)) || + ((ldtype == NDARRAY_INT8) && (rdtype == NDARRAY_UINT16)) || + ((ldtype == NDARRAY_UINT16) && (rdtype == NDARRAY_INT8))) { + return NDARRAY_UINT16; + } + return NDARRAY_FLOAT; +} + +// The following five functions are the inverse of the ndarray_get_... functions, +// and write a floating point datum into a void pointer + +void ndarray_set_float_uint8(void *data, mp_float_t datum) { + *((uint8_t *)data) = (uint8_t)datum; +} + +void ndarray_set_float_int8(void *data, mp_float_t datum) { + *((int8_t *)data) = (int8_t)datum; +} + +void ndarray_set_float_uint16(void *data, mp_float_t datum) { + *((uint16_t *)data) = (uint16_t)datum; +} + +void ndarray_set_float_int16(void *data, mp_float_t datum) { + *((int16_t *)data) = (int16_t)datum; +} + +void ndarray_set_float_float(void *data, mp_float_t datum) { + *((mp_float_t *)data) = datum; +} + +// returns a single function pointer, depending on the dtype +void *ndarray_set_float_function(uint8_t dtype) { + if(dtype == NDARRAY_UINT8) { + return ndarray_set_float_uint8; + } else if(dtype == NDARRAY_INT8) { + return ndarray_set_float_int8; + } else if(dtype == NDARRAY_UINT16) { + return ndarray_set_float_uint16; + } else if(dtype == NDARRAY_INT16) { + return ndarray_set_float_int16; + } else { + return ndarray_set_float_float; + } +} +#endif /* NDARRAY_BINARY_USES_FUN_POINTER */ + +shape_strides tools_reduce_axes(ndarray_obj_t *ndarray, mp_obj_t axis) { + // TODO: replace numerical_reduce_axes with this function, wherever applicable + // This function should be used, whenever a tensor is contracted; + // The shape and strides at `axis` are moved to the zeroth position, + // everything else is aligned to the right + if(!mp_obj_is_int(axis) & (axis != mp_const_none)) { + mp_raise_TypeError(translate("axis must be None, or an integer")); + } + shape_strides _shape_strides; + + size_t *shape = m_new(size_t, ULAB_MAX_DIMS + 1); + _shape_strides.shape = shape; + int32_t *strides = m_new(int32_t, ULAB_MAX_DIMS + 1); + _shape_strides.strides = strides; + + _shape_strides.increment = 0; + // this is the contracted dimension (won't be overwritten for axis == None) + _shape_strides.ndim = 0; + + memcpy(_shape_strides.shape, ndarray->shape, sizeof(size_t) * ULAB_MAX_DIMS); + memcpy(_shape_strides.strides, ndarray->strides, sizeof(int32_t) * ULAB_MAX_DIMS); + + if(axis == mp_const_none) { + return _shape_strides; + } + + uint8_t index = ULAB_MAX_DIMS - 1; // value of index for axis == mp_const_none (won't be overwritten) + + if(axis != mp_const_none) { // i.e., axis is an integer + int8_t ax = mp_obj_get_int(axis); + if(ax < 0) ax += ndarray->ndim; + if((ax < 0) || (ax > ndarray->ndim - 1)) { + mp_raise_ValueError(translate("index out of range")); + } + index = ULAB_MAX_DIMS - ndarray->ndim + ax; + _shape_strides.ndim = ndarray->ndim - 1; + } + + // move the value stored at index to the leftmost position, and align everything else to the right + _shape_strides.shape[0] = ndarray->shape[index]; + _shape_strides.strides[0] = ndarray->strides[index]; + for(uint8_t i = 0; i < index; i++) { + // entries to the right of index must be shifted by one position to the left + _shape_strides.shape[i + 1] = ndarray->shape[i]; + _shape_strides.strides[i + 1] = ndarray->strides[i]; + } + + if(_shape_strides.ndim != 0) { + _shape_strides.increment = 1; + } + + return _shape_strides; +} + + +#if ULAB_MAX_DIMS > 1 +ndarray_obj_t *tools_object_is_square(mp_obj_t obj) { + // Returns an ndarray, if the object is a square ndarray, + // raises the appropriate exception otherwise + if(!mp_obj_is_type(obj, &ulab_ndarray_type)) { + mp_raise_TypeError(translate("size is defined for ndarrays only")); + } + ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(obj); + if((ndarray->shape[ULAB_MAX_DIMS - 1] != ndarray->shape[ULAB_MAX_DIMS - 2]) || (ndarray->ndim != 2)) { + mp_raise_ValueError(translate("input must be square matrix")); + } + return ndarray; +} +#endif diff --git a/python/port/mod/ulab/ulab_tools.h b/python/port/mod/ulab/ulab_tools.h new file mode 100644 index 000000000..378e4f0ca --- /dev/null +++ b/python/port/mod/ulab/ulab_tools.h @@ -0,0 +1,37 @@ +/* + * This file is part of the micropython-ulab project, + * + * https://github.com/v923z/micropython-ulab + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Zoltán Vörös +*/ + +#ifndef _TOOLS_ +#define _TOOLS_ + +#include "ndarray.h" + +#define SWAP(t, a, b) { t tmp = a; a = b; b = tmp; } + +typedef struct _shape_strides_t { + uint8_t increment; + uint8_t ndim; + size_t *shape; + int32_t *strides; +} shape_strides; + +mp_float_t ndarray_get_float_uint8(void *); +mp_float_t ndarray_get_float_int8(void *); +mp_float_t ndarray_get_float_uint16(void *); +mp_float_t ndarray_get_float_int16(void *); +mp_float_t ndarray_get_float_float(void *); +void *ndarray_get_float_function(uint8_t ); + +uint8_t ndarray_upcast_dtype(uint8_t , uint8_t ); +void *ndarray_set_float_function(uint8_t ); + +shape_strides tools_reduce_axes(ndarray_obj_t *, mp_obj_t ); +ndarray_obj_t *tools_object_is_square(mp_obj_t ); +#endif diff --git a/python/port/mod/ulab/user/user.c b/python/port/mod/ulab/user/user.c new file mode 100644 index 000000000..be7c6d0a0 --- /dev/null +++ b/python/port/mod/ulab/user/user.c @@ -0,0 +1,95 @@ + +/* + * This file is part of the micropython-ulab project, + * + * https://github.com/v923z/micropython-ulab + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Zoltán Vörös +*/ + +#include +#include +#include +#include +#include +#include +#include "user.h" + +#if ULAB_HAS_USER_MODULE + +//| """This module should hold arbitrary user-defined functions.""" +//| + +static mp_obj_t user_square(mp_obj_t arg) { + // the function takes a single dense ndarray, and calculates the + // element-wise square of its entries + + // raise a TypeError exception, if the input is not an ndarray + if(!mp_obj_is_type(arg, &ulab_ndarray_type)) { + mp_raise_TypeError(translate("input must be an ndarray")); + } + ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(arg); + + // make sure that the input is a dense array + if(!ndarray_is_dense(ndarray)) { + mp_raise_TypeError(translate("input must be a dense ndarray")); + } + + // if the input is a dense array, create `results` with the same number of + // dimensions, shape, and dtype + ndarray_obj_t *results = ndarray_new_dense_ndarray(ndarray->ndim, ndarray->shape, ndarray->dtype); + + // since in a dense array the iteration over the elements is trivial, we + // can cast the data arrays ndarray->array and results->array to the actual type + if(ndarray->dtype == NDARRAY_UINT8) { + uint8_t *array = (uint8_t *)ndarray->array; + uint8_t *rarray = (uint8_t *)results->array; + for(size_t i=0; i < ndarray->len; i++, array++) { + *rarray++ = (*array) * (*array); + } + } else if(ndarray->dtype == NDARRAY_INT8) { + int8_t *array = (int8_t *)ndarray->array; + int8_t *rarray = (int8_t *)results->array; + for(size_t i=0; i < ndarray->len; i++, array++) { + *rarray++ = (*array) * (*array); + } + } else if(ndarray->dtype == NDARRAY_UINT16) { + uint16_t *array = (uint16_t *)ndarray->array; + uint16_t *rarray = (uint16_t *)results->array; + for(size_t i=0; i < ndarray->len; i++, array++) { + *rarray++ = (*array) * (*array); + } + } else if(ndarray->dtype == NDARRAY_INT16) { + int16_t *array = (int16_t *)ndarray->array; + int16_t *rarray = (int16_t *)results->array; + for(size_t i=0; i < ndarray->len; i++, array++) { + *rarray++ = (*array) * (*array); + } + } else { // if we end up here, the dtype is NDARRAY_FLOAT + mp_float_t *array = (mp_float_t *)ndarray->array; + mp_float_t *rarray = (mp_float_t *)results->array; + for(size_t i=0; i < ndarray->len; i++, array++) { + *rarray++ = (*array) * (*array); + } + } + // at the end, return a micrppython object + return MP_OBJ_FROM_PTR(results); +} + +MP_DEFINE_CONST_FUN_OBJ_1(user_square_obj, user_square); + +static const mp_rom_map_elem_t ulab_user_globals_table[] = { + { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_user) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_square), (mp_obj_t)&user_square_obj }, +}; + +static MP_DEFINE_CONST_DICT(mp_module_ulab_user_globals, ulab_user_globals_table); + +mp_obj_module_t ulab_user_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t*)&mp_module_ulab_user_globals, +}; + +#endif diff --git a/python/port/mod/ulab/user/user.h b/python/port/mod/ulab/user/user.h new file mode 100644 index 000000000..4ec366af1 --- /dev/null +++ b/python/port/mod/ulab/user/user.h @@ -0,0 +1,20 @@ + +/* + * This file is part of the micropython-ulab project, + * + * https://github.com/v923z/micropython-ulab + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Zoltán Vörös +*/ + +#ifndef _USER_ +#define _USER_ + +#include "../ulab.h" +#include "../ndarray.h" + +extern mp_obj_module_t ulab_user_module; + +#endif diff --git a/python/port/mod/ulab/utils/utils.c b/python/port/mod/ulab/utils/utils.c new file mode 100644 index 000000000..6ff787b3b --- /dev/null +++ b/python/port/mod/ulab/utils/utils.c @@ -0,0 +1,215 @@ +/* + * This file is part of the micropython-ulab project, + * + * https://github.com/v923z/micropython-ulab + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Zoltán Vörös +*/ + +#include +#include +#include +#include +#include +#include +#include "utils.h" + +#if ULAB_HAS_UTILS_MODULE + +enum UTILS_BUFFER_TYPE { + UTILS_INT16_BUFFER, + UTILS_UINT16_BUFFER, + UTILS_INT32_BUFFER, + UTILS_UINT32_BUFFER, +}; + +#if ULAB_UTILS_HAS_FROM_INT16_BUFFER | ULAB_UTILS_HAS_FROM_UINT16_BUFFER | ULAB_UTILS_HAS_FROM_INT32_BUFFER | ULAB_UTILS_HAS_FROM_UINT32_BUFFER +static mp_obj_t utils_from_intbuffer_helper(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args, uint8_t buffer_type) { + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = mp_const_none } } , + { MP_QSTR_count, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_INT(-1) } }, + { MP_QSTR_offset, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_INT(0) } }, + { MP_QSTR_out, MP_ARG_OBJ, { .u_rom_obj = mp_const_none } }, + { MP_QSTR_byteswap, MP_ARG_OBJ, { .u_rom_obj = mp_const_false } }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + ndarray_obj_t *ndarray = NULL; + + if(args[3].u_obj != mp_const_none) { + ndarray = MP_OBJ_TO_PTR(args[3].u_obj); + if((ndarray->dtype != NDARRAY_FLOAT) || !ndarray_is_dense(ndarray)) { + mp_raise_TypeError(translate("out must be a float dense array")); + } + } + + size_t offset = mp_obj_get_int(args[2].u_obj); + + mp_buffer_info_t bufinfo; + if(mp_get_buffer(args[0].u_obj, &bufinfo, MP_BUFFER_READ)) { + if(bufinfo.len < offset) { + mp_raise_ValueError(translate("offset is too large")); + } + uint8_t sz = sizeof(int16_t); + #if ULAB_UTILS_HAS_FROM_INT32_BUFFER | ULAB_UTILS_HAS_FROM_UINT32_BUFFER + if((buffer_type == UTILS_INT32_BUFFER) || (buffer_type == UTILS_UINT32_BUFFER)) { + sz = sizeof(int32_t); + } + #endif + + size_t len = (bufinfo.len - offset) / sz; + if((len * sz) != (bufinfo.len - offset)) { + mp_raise_ValueError(translate("buffer size must be a multiple of element size")); + } + if(mp_obj_get_int(args[1].u_obj) > 0) { + size_t count = mp_obj_get_int(args[1].u_obj); + if(len < count) { + mp_raise_ValueError(translate("buffer is smaller than requested size")); + } else { + len = count; + } + } + if(args[3].u_obj == mp_const_none) { + ndarray = ndarray_new_linear_array(len, NDARRAY_FLOAT); + } else { + if(ndarray->len < len) { + mp_raise_ValueError(translate("out array is too small")); + } + } + uint8_t *buffer = bufinfo.buf; + + mp_float_t *array = (mp_float_t *)ndarray->array; + if(args[4].u_obj == mp_const_true) { + // swap the bytes before conversion + uint8_t *tmpbuff = m_new(uint8_t, sz); + #if ULAB_UTILS_HAS_FROM_INT16_BUFFER | ULAB_UTILS_HAS_FROM_UINT16_BUFFER + if((buffer_type == UTILS_INT16_BUFFER) || (buffer_type == UTILS_UINT16_BUFFER)) { + for(size_t i = 0; i < len; i++) { + tmpbuff += sz; + for(uint8_t j = 0; j < sz; j++) { + memcpy(--tmpbuff, buffer++, 1); + } + if(buffer_type == UTILS_INT16_BUFFER) { + *array++ = (mp_float_t)(*(int16_t *)tmpbuff); + } else { + *array++ = (mp_float_t)(*(uint16_t *)tmpbuff); + } + } + } + #endif + #if ULAB_UTILS_HAS_FROM_INT32_BUFFER | ULAB_UTILS_HAS_FROM_UINT32_BUFFER + if((buffer_type == UTILS_INT32_BUFFER) || (buffer_type == UTILS_UINT32_BUFFER)) { + for(size_t i = 0; i < len; i++) { + tmpbuff += sz; + for(uint8_t j = 0; j < sz; j++) { + memcpy(--tmpbuff, buffer++, 1); + } + if(buffer_type == UTILS_INT32_BUFFER) { + *array++ = (mp_float_t)(*(int32_t *)tmpbuff); + } else { + *array++ = (mp_float_t)(*(uint32_t *)tmpbuff); + } + } + } + #endif + } else { + #if ULAB_UTILS_HAS_FROM_INT16_BUFFER + if(buffer_type == UTILS_INT16_BUFFER) { + for(size_t i = 0; i < len; i++) { + *array++ = (mp_float_t)(*(int16_t *)buffer); + buffer += sz; + } + } + #endif + #if ULAB_UTILS_HAS_FROM_UINT16_BUFFER + if(buffer_type == UTILS_UINT16_BUFFER) { + for(size_t i = 0; i < len; i++) { + *array++ = (mp_float_t)(*(uint16_t *)buffer); + buffer += sz; + } + } + #endif + #if ULAB_UTILS_HAS_FROM_INT32_BUFFER + if(buffer_type == UTILS_INT32_BUFFER) { + for(size_t i = 0; i < len; i++) { + *array++ = (mp_float_t)(*(int32_t *)buffer); + buffer += sz; + } + } + #endif + #if ULAB_UTILS_HAS_FROM_UINT32_BUFFER + if(buffer_type == UTILS_UINT32_BUFFER) { + for(size_t i = 0; i < len; i++) { + *array++ = (mp_float_t)(*(uint32_t *)buffer); + buffer += sz; + } + } + #endif + } + return MP_OBJ_FROM_PTR(ndarray); + } + return mp_const_none; +} + +#ifdef ULAB_UTILS_HAS_FROM_INT16_BUFFER +static mp_obj_t utils_from_int16_buffer(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + return utils_from_intbuffer_helper(n_args, pos_args, kw_args, UTILS_INT16_BUFFER); +} + +MP_DEFINE_CONST_FUN_OBJ_KW(utils_from_int16_buffer_obj, 1, utils_from_int16_buffer); +#endif + +#ifdef ULAB_UTILS_HAS_FROM_UINT16_BUFFER +static mp_obj_t utils_from_uint16_buffer(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + return utils_from_intbuffer_helper(n_args, pos_args, kw_args, UTILS_UINT16_BUFFER); +} + +MP_DEFINE_CONST_FUN_OBJ_KW(utils_from_uint16_buffer_obj, 1, utils_from_uint16_buffer); +#endif + +#ifdef ULAB_UTILS_HAS_FROM_INT32_BUFFER +static mp_obj_t utils_from_int32_buffer(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + return utils_from_intbuffer_helper(n_args, pos_args, kw_args, UTILS_INT32_BUFFER); +} + +MP_DEFINE_CONST_FUN_OBJ_KW(utils_from_int32_buffer_obj, 1, utils_from_int32_buffer); +#endif + +#ifdef ULAB_UTILS_HAS_FROM_UINT32_BUFFER +static mp_obj_t utils_from_uint32_buffer(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + return utils_from_intbuffer_helper(n_args, pos_args, kw_args, UTILS_UINT32_BUFFER); +} + +MP_DEFINE_CONST_FUN_OBJ_KW(utils_from_uint32_buffer_obj, 1, utils_from_uint32_buffer); +#endif + +#endif + +static const mp_rom_map_elem_t ulab_utils_globals_table[] = { + { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_utils) }, + #if ULAB_UTILS_HAS_FROM_INT16_BUFFER + { MP_OBJ_NEW_QSTR(MP_QSTR_from_int16_buffer), (mp_obj_t)&utils_from_int16_buffer_obj }, + #endif + #if ULAB_UTILS_HAS_FROM_UINT16_BUFFER + { MP_OBJ_NEW_QSTR(MP_QSTR_from_uint16_buffer), (mp_obj_t)&utils_from_uint16_buffer_obj }, + #endif + #if ULAB_UTILS_HAS_FROM_INT32_BUFFER + { MP_OBJ_NEW_QSTR(MP_QSTR_from_int32_buffer), (mp_obj_t)&utils_from_int32_buffer_obj }, + #endif + #if ULAB_UTILS_HAS_FROM_UINT32_BUFFER + { MP_OBJ_NEW_QSTR(MP_QSTR_from_uint32_buffer), (mp_obj_t)&utils_from_uint32_buffer_obj }, + #endif +}; + +static MP_DEFINE_CONST_DICT(mp_module_ulab_utils_globals, ulab_utils_globals_table); + +mp_obj_module_t ulab_utils_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t*)&mp_module_ulab_utils_globals, +}; + +#endif diff --git a/python/port/mod/ulab/utils/utils.h b/python/port/mod/ulab/utils/utils.h new file mode 100644 index 000000000..3b258bf24 --- /dev/null +++ b/python/port/mod/ulab/utils/utils.h @@ -0,0 +1,19 @@ +/* + * This file is part of the micropython-ulab project, + * + * https://github.com/v923z/micropython-ulab + * + * The MIT License (MIT) + * + * Copyright (c) 2020-2021 Zoltán Vörös +*/ + +#ifndef _UTILS_ +#define _UTILS_ + +#include "../ulab.h" +#include "../ndarray.h" + +extern mp_obj_module_t ulab_utils_module; + +#endif diff --git a/python/port/mpconfigport.h b/python/port/mpconfigport.h index fa3d71da6..07a6e3e99 100644 --- a/python/port/mpconfigport.h +++ b/python/port/mpconfigport.h @@ -136,6 +136,7 @@ extern const struct _mp_obj_module_t modpyplot_module; extern const struct _mp_obj_module_t modtime_module; extern const struct _mp_obj_module_t modos_module; extern const struct _mp_obj_module_t modturtle_module; +extern const struct _mp_obj_module_t ulab_user_cmodule; #define MICROPY_PORT_BUILTIN_MODULES \ { MP_ROM_QSTR(MP_QSTR_ion), MP_ROM_PTR(&modion_module) }, \ @@ -145,6 +146,7 @@ extern const struct _mp_obj_module_t modturtle_module; { MP_ROM_QSTR(MP_QSTR_time), MP_ROM_PTR(&modtime_module) }, \ { MP_ROM_QSTR(MP_QSTR_os), MP_ROM_PTR(&modos_module) }, \ { MP_ROM_QSTR(MP_QSTR_turtle), MP_ROM_PTR(&modturtle_module) }, \ + { MP_ROM_QSTR(MP_QSTR_ulab), MP_ROM_PTR(&ulab_user_cmodule) }, \ // Enable setjmp in debug mode. This is to avoid some optimizations done // specifically for x86_64 using inline assembly, which makes the debug binary diff --git a/python/port/port.cpp b/python/port/port.cpp index 1f83a01dd..e90b9de0b 100644 --- a/python/port/port.cpp +++ b/python/port/port.cpp @@ -61,6 +61,7 @@ extern "C" { #include "mphalport.h" #include "mod/turtle/modturtle.h" #include "mod/matplotlib/pyplot/modpyplot.h" +#include "mod/ulab/ulab.h" } #include From 837fcd9bcc491e491f8aa392cd5c09d47e1927b9 Mon Sep 17 00:00:00 2001 From: ArtichOwO Date: Wed, 23 Jun 2021 23:08:46 +0200 Subject: [PATCH 19/48] [MPY/MOD/ULAB] Added N100 compatibility N100: - doesn't have Scipy; - uses function pointers --- python/port/mod/ulab/ulab.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/python/port/mod/ulab/ulab.h b/python/port/mod/ulab/ulab.h index e38310020..647672d8b 100644 --- a/python/port/mod/ulab/ulab.h +++ b/python/port/mod/ulab/ulab.h @@ -35,7 +35,11 @@ // Determines, whether scipy is defined in ulab. The sub-modules and functions // of scipy have to be defined separately #ifndef ULAB_HAS_SCIPY +#if defined(DEVICE_N0100) #define ULAB_HAS_SCIPY (0) +#else +#define ULAB_HAS_SCIPY (1) +#endif #endif // The maximum number of dimensions the firmware should be able to support @@ -82,8 +86,12 @@ // 2 kB in the two-dimensional case, and around 4 kB in the four-dimensional case. #ifndef NDARRAY_BINARY_USES_FUN_POINTER +#if defined(DEVICE_N0100) +#define NDARRAY_BINARY_USES_FUN_POINTER (1) +#else #define NDARRAY_BINARY_USES_FUN_POINTER (0) #endif +#endif #ifndef NDARRAY_HAS_BINARY_OP_ADD #define NDARRAY_HAS_BINARY_OP_ADD (1) From a3720d3d76f1188a696c526c9e9be47c7994d232 Mon Sep 17 00:00:00 2001 From: Pixelpunker Date: Mon, 28 Jun 2021 16:09:49 +0200 Subject: [PATCH 20/48] Some corrections and many additions to German translation, mostly Python toolbox --- apps/code/base.de.i18n | 10 +- apps/code/catalog.de.i18n | 298 +++++++++++++++++----------------- apps/code/catalog.en.i18n | 2 +- apps/external/base.de.i18n | 12 +- apps/on_boarding/base.de.i18n | 16 +- apps/settings/base.de.i18n | 10 +- apps/shared.de.i18n | 6 +- apps/toolbox.de.i18n | 48 +++--- apps/usb/base.de.i18n | 6 +- 9 files changed, 204 insertions(+), 204 deletions(-) diff --git a/apps/code/base.de.i18n b/apps/code/base.de.i18n index 40cf856fa..4306f4d17 100644 --- a/apps/code/base.de.i18n +++ b/apps/code/base.de.i18n @@ -1,15 +1,15 @@ AddScript = "Skript hinzufügen" AllowedCharactersaz09 = "Erlaubte Zeichen: a-z, 0-9, _" Autocomplete = "Autovervollständigung" -AutoImportScript = "Automatischer Import in Konsole" -BuiltinsAndKeywords = "Native Funktionen und Schlüsselwörter" +AutoImportScript = "Automatischer Import in die Konsole" +BuiltinsAndKeywords = "Eingebaute Funktionen und Schlüsselwörter" Console = "Interaktive Konsole" DeleteScript = "Skript löschen" DuplicateScript = "Skript duplizieren" ExecuteScript = "Skript ausführen" FunctionsAndVariables = "Funktionen und Variablen" ImportedModulesAndScripts = "Importierte Module und Skripte" -NoWordAvailableHere = "Kein Wort ist hier verfübar." -ScriptInProgress = "Aktuelle Skript" +NoWordAvailableHere = "Hier ist kein Wort verfügbar." +ScriptInProgress = "Aktuelles Skript" ScriptOptions = "Skriptoptionen" -ScriptSize = "Script size" +ScriptSize = "Skriptgröße" diff --git a/apps/code/catalog.de.i18n b/apps/code/catalog.de.i18n index 9cb321ab8..eeb532011 100644 --- a/apps/code/catalog.de.i18n +++ b/apps/code/catalog.de.i18n @@ -3,175 +3,175 @@ PythonPercent = "Modulo" Python1J = "Imaginäres i" PythonLF = "Zeilenvorschub" PythonTab = "Tabulator" -PythonAmpersand = "Bitweise und" -PythonSymbolExp = "Bitweise exklusiv oder" -PythonVerticalBar = "Bitweise oder" +PythonAmpersand = "Bitweises und" +PythonSymbolExp = "Bitweises exklusives oder" +PythonVerticalBar = "Bitweises oder" PythonImag = "Imaginärteil von z" PythonReal = "Realteil von z" PythonSingleQuote = "Einfaches Anführungszeichen" -PythonAbs = "Absolute/r Wert/Größe" -PythonAcos = "Arkuskosinus" -PythonAcosh = "Hyperbelkosinus" -PythonAppend = "Hängt x an das Ende der Liste" -PythonArrow = "Arrow from (x,y) to (x+dx,y+dy)" -PythonAsin = "Arkussinus" -PythonAsinh = "Hyperbelsinus" +PythonAbs = "Absoluter Wert/Absolute Größe" +PythonAcos = "Bogenkosinus" +PythonAcosh = "Bogenhyperbolischer Kosinus" +PythonAppend = "x an das Ende der Liste anfügen" +PythonArrow = "Pfeil von (x,y) nach (x+dx,y+dy)" +PythonAsin = "Sinusbogen" +PythonAsinh = "Kreisbogen hyperbolischer Sinus" PythonAtan = "Arkustangens" -PythonAtan2 = "Gib atan(y/x)" +PythonAtan2 = "Rückgabe atan(y/x)" PythonAtanh = "Hyperbeltangens" -PythonAxis = "Set axes to (xmin,xmax,ymin,ymax)" -PythonBar = "Draw a bar plot with x values" -PythonBin = "Ganzzahl nach binär konvertieren" -PythonCeil = "Aufrundung" -PythonChoice = "Zufallszahl aus der Liste" -PythonClear = "Leere die Liste" +PythonAxis = "Achsen auf (xmin,xmax,ymin,ymax) setzen" +PythonBar = "Balkendiagramm mit x-Werten zeichnen" +PythonBin = "Ganzzahl in Binärwert umwandeln" +PythonCeil = "Aufrunden" +PythonChoice = "Zufällige Zahl in der Liste" +PythonClear = "Liste leeren" PythonCmathFunction = "cmath-Modul-Funktionspräfix" -PythonColor = "Definiere eine RGB-Farbe" -PythonColorBlack = "Black color" -PythonColorBlue = "Blue color" -PythonColorBrown = "Brown color" -PythonColorGray = "Gray color" -PythonColorGreen = "Green color" -PythonColorOrange = "Orange color" -PythonColorPink = "Pink color" -PythonColorPurple = "Purple color" -PythonColorRed = "Red color" -PythonColorWhite = "White color" -PythonColorYellow = "Yellow color" -PythonComplex = "a+ib zurückgeben" -PythonCopySign = "x mit dem Vorzeichen von y" +PythonColor = "Eine RGB-Farbe definieren" +PythonColorBlack = "Farbe Schwarz" +PythonColorBlue = "Farbe Blau" +PythonColorBrown = "Farbe Braun" +PythonColorGray = "Farbe Grau" +PythonColorGreen = "Farbe Grün" +PythonColorOrange = "Farbe Orange" +PythonColorPink = "Farbe Rosa" +PythonColorPurple = "Farbe Violett" +PythonColorRed = "Farbe Rot" +PythonColorWhite = "Farbe Weiß" +PythonColorYellow = "Farbe Gelb" +PythonComplex = "Gib a+ib zurück" +PythonCopySign = "Gib x mit dem Vorzeichen von y zurück" PythonCos = "Kosinus" -PythonCosh = "Hyperbolic cosine" -PythonCount = "Zählt wie oft x vorkommt" -PythonDegrees = "x von Radian zu Grad umwandeln" +PythonCosh = "Hyperbolischer Kosinus" +PythonCount = "Zählt die Vorkommen von x" +PythonDegrees = "x von Bogenmaß in Grad umrechnen" PythonDivMod = "Quotient und Rest" -PythonDrawLine = "Draw a line" -PythonDrawString = "Schreibt Text bei (x,y)" +PythonDrawLine = "Eine Linie zeichnen" +PythonDrawString = "Einen Text bei Pixel (x,y) darstellen" PythonErf = "Fehlerfunktion" -PythonErfc = "Complementary error function" -PythonEval = "Return the evaluated expression" +PythonErfc = "Komplementäre Fehlerfunktion" +PythonEval = "Rückgabe des ausgewerteten Ausdrucks" PythonExp = "Exponentialfunktion" PythonExpm1 = "Berechne exp(x)-1" PythonFabs = "Absoluter Wert" -PythonFillRect = "Malt ein Rechteck bei Pixel (x,y)" -PythonFloat = "Wandelt x zu float um" -PythonFloor = "Floor" +PythonFillRect = "Malt ein gefülltes Rechteck bei Pixel (x,y)" +PythonFloat = "x in einen Fließkommawert umwandeln" +PythonFloor = "Abrunden" PythonFmod = "a modulo b" -PythonFrExp = "Mantissa and exponent of x: (m,e)" -PythonGamma = "Gamma function" -PythonGetPixel = "Return pixel (x,y) color" -PythonGetrandbits = "Integer with k random bits" -PythonGrid = "Toggle the visibility of the grid" -PythonHex = "Ganzzahl zu Hexadecimal" -PythonHist = "Draw the histogram of x" -PythonImportCmath = "cmath Modul importieren" -PythonImportIon = "ion Modul importieren" -PythonImportKandinsky = "kandinsky Modul importieren" -PythonImportRandom = "random Modul importieren" -PythonImportMath = "math Modul importieren" -PythonImportMatplotlibPyplot = "Import matplotlib.pyplot module" -PythonImportOs = "os Modul importieren" -PythonOsUname = "Informieren Sie sich über das System" +PythonFrExp = "Mantisse und Exponent von x: (m,e)" +PythonGamma = "Gamma-Funktion" +PythonGetPixel = "Farbe von Pixel (x,y) zurückgeben" +PythonGetrandbits = "Ganzzahl mit k Zufallsbits" +PythonGrid = "Schaltet die Sichtbarkeit des Gitters um" +PythonHex = "Ganzzahl in Hexadezimal umwandeln" +PythonHist = "Zeichnet das Histogramm von x" +PythonImportCmath = "cmath-Modul importieren" +PythonImportIon = "Ion-Modul importieren" +PythonImportKandinsky = "Kandinsky-Modul importieren" +PythonImportRandom = "Random-Modul importieren" +PythonImportMath = "Math-Modul importieren" +PythonImportMatplotlibPyplot = "Matplotlib.pyplot-Modul importieren" +PythonImportOs = "OS-Modul importieren" +PythonOsUname = "Informationen über das System holen" PythonOsRemove = "Datei namens Dateiname entfernen" PythonOsRename = "Datei mit altem Namen in neuen Namen umbenennen" PythonOsListdir = "Dateien im Speicher auflisten" -PythonImportTime = "time Modul importieren" -PythonImportTurtle = "turtle Modul importieren" -PythonIndex = "Index, bei dem x zuerst vorkommt" -PythonInput = "Eingabeaufforderung" -PythonInsert = "x bei index i in der Liste einsetzen" -PythonInt = "x zu Ganzzahl" -PythonIonFunction = "ion module function prefix" -PythonIsFinite = "Prüft ob x endlich ist" -PythonIsInfinite = "Prüft ob x unendlich ist" -PythonIsNaN = "Prüft ob x NaN ist" -PythonIsKeyDown = "true wenn k gedrückt ist" -PythonKandinskyFunction = "kandinsky module function prefix" -PythonLdexp = "Return x*(2**i), inverse of frexp" -PythonLength = "Length of an object" -PythonLgamma = "Log-gamma function" -PythonLog = "Logarithm to base a" -PythonLog10 = "Logarithm to base 10" -PythonLog2 = "Logarithm to base 2" -PythonMathFunction = "math module function prefix" -PythonMatplotlibPyplotFunction = "matplotlib.pyplot module prefix" +PythonImportTime = "Time-Modul importieren" +PythonImportTurtle = "Turtle-Modul importieren" +PythonIndex = "Index des ersten x-Vorkommens" +PythonInput = "Einen Wert abfragen" +PythonInsert = "x an Index i in die Liste einfügen" +PythonInt = "x in eine ganze Zahl umwandeln" +PythonIonFunction = "Ion-Modul-Funktionspräfix" +PythonIsFinite = "Prüfen, ob x endlich ist" +PythonIsInfinite = "Prüfen, ob x unendlich ist" +PythonIsNaN = "Prüfen, ob x keine Zahl ist" +PythonIsKeyDown = "Gibt Wahr zurück, wenn die Taste k gedrückt ist" +PythonKandinskyFunction = "Kandinsky-Modul Funktionspräfix" +PythonLdexp = "Liefert x*(2**i), die Inverse von frexp" +PythonLength = "Länge eines Objekts" +PythonLgamma = "Log-Gamma-Funktion" +PythonLog = "Logarithmus zur Basis a" +PythonLog10 = "Logarithmus zur Basis 10" +PythonLog2 = "Logarithmus zur Basis 2" +PythonMathFunction = "Funktionspräfix des Math-Moduls" +PythonMatplotlibPyplotFunction = "matplotlib.pyplot Modul-Präfix" PythonMax = "Maximum" PythonMin = "Minimum" -PythonModf = "Fractional and integer parts of x" -PythonMonotonic = "Value of a monotonic clock" -PythonOct = "Convert integer to octal" -PythonPhase = "Phase of z" -PythonPlot = "Plot y versus x as lines" -PythonPolar = "z in polar coordinates" -PythonPop = "Remove and return the last item" -PythonPower = "x raised to the power y" -PythonPrint = "Print object" -PythonRadians = "Convert x from degrees to radians" -PythonRandint = "Random integer in [a,b]" -PythonRandom = "Floating point number in [0,1[" -PythonRandomFunction = "random module function prefix" -PythonRandrange = "Random number in range(start,stop)" -PythonRangeStartStop = "List from start to stop-1" -PythonRangeStop = "List from 0 to stop-1" -PythonRect = "Convert to cartesian coordinates" -PythonRemove = "Remove the first occurrence of x" -PythonReverse = "Reverse the elements of the list" -PythonRound = "Round to n digits" -PythonScatter = "Draw a scatter plot of y versus x" -PythonSeed = "Initialize random number generator" -PythonSetPixel = "Color pixel (x,y)" -PythonShow = "Display the figure" -PythonSin = "Sine" -PythonSinh = "Hyperbolic sine" -PythonSleep = "Suspend the execution for t seconds" -PythonSort = "Sort the list" -PythonSqrt = "Wurzel" -PythonSum = "Sum the items of a list" +PythonModf = "Bruch- und ganzzahlige Anteile von x" +PythonMonotonic = "Wert einer monotonen Uhr" +PythonOct = "Ganzzahl in Oktal umwandeln" +PythonPhase = "Phase von z" +PythonPlot = "Plotten von y gegen x als Linien" +PythonPolar = "z in Polarkoordinaten" +PythonPop = "Letztes Element entfernen und zurückgeben" +PythonPower = "x erhöht mit der Potenz y" +PythonPrint = "Objekt drucken" +PythonRadians = "x von Grad in Bogenmaß umrechnen" +PythonRandint = "Zufällige Ganzzahl in [a,b]" +PythonRandom = "Fließkommazahl in [0,1]" +PythonRandomFunction = "Random-Modul Funktionspräfix" +PythonRandrange = "Zufallszahl im Bereich(start,stop)" +PythonRangeStartStop = "Liste von Start bis Stop-1" +PythonRangeStop = "Liste von 0 bis Stop-1" +PythonRect = "In kartesische Koordinaten umrechnen" +PythonRemove = "Entferne das erste Vorkommen von x" +PythonReverse = "Kehrt die Elemente der Liste um" +PythonRound = "Runden auf n Stellen" +PythonScatter = "Ein Streudiagramm von y gegen x zeichnen" +PythonSeed = "Zufallszahlengenerator initialisieren" +PythonSetPixel = "Pixel (x,y) einfärben" +PythonShow = "Figur anzeigen" +PythonSin = "Sinus" +PythonSinh = "Hyperbolischer Sinus" +PythonSleep = "Aussetzen der Ausführung für t Sekunden" +PythonSort = "Die Liste sortieren" +PythonSqrt = "Quadratwurzel" +PythonSum = "Summe der Elemente einer Liste" PythonTan = "Tangens" -PythonTanh = "Hyperbolic tangent" -PythonText = "Display a text at (x,y) coordinates" -PythonTimeFunction = "time module function prefix" -PythonTrunc = "x truncated to an integer" -PythonTurtleBackward = "Move backward by x pixels" -PythonTurtleCircle = "Circle of radius r pixels" -PythonTurtleColor = "Stiftfarbe setzen" -PythonTurtleColorMode = "Set the color mode to 1.0 or 255" -PythonTurtleForward = "Move forward by x pixels" -PythonTurtleFunction = "turtle module function prefix" -PythonTurtleGoto = "Move to (x,y) coordinates" -PythonTurtleHeading = "Return the current heading" -PythonTurtleHideturtle = "Hide the turtle" -PythonTurtleIsdown = "Return True if the pen is down" -PythonTurtleLeft = "Turn left by a degrees" -PythonTurtlePendown = "Pull the pen down" -PythonTurtlePensize = "Set the line thickness to x pixels" -PythonTurtlePenup = "Pull the pen up" -PythonTurtlePosition = "Return the current (x,y) location" -PythonTurtleReset = "Reset the drawing" -PythonTurtleRight = "Turn right by a degrees" -PythonTurtleSetheading = "Set the orientation to a degrees" -PythonTurtleSetposition = "Positionne la tortue" -PythonTurtleShowturtle = "Show the turtle" -PythonTurtleSpeed = "Drawing speed between 0 and 10" -PythonTurtleWrite = "Display a text" -PythonUniform = "Floating point number in [a,b]" -PythonImportTime = "Import time module" -PythonTimePrefix = "time module function prefix" -PythonTimeSleep = "Wait for n second" -PythonMonotonic = "Return monotonic time" +PythonTanh = "Hyperbolischer Tangens" +PythonText = "Einen Text an (x,y) Koordinaten anzeigen" +PythonTimeFunction = "Time-Modul-Funktionspräfix" +PythonTrunc = "x abgeschnitten auf eine ganze Zahl" +PythonTurtleBackward = "Um x Pixel rückwärts bewegen" +PythonTurtleCircle = "Kreis mit Radius r Pixel" +PythonTurtleColor = "Setzt die Stiftfarbe" +PythonTurtleColorMode = "Setzt den Farbmodus auf 1.0 oder 255" +PythonTurtleForward = "Um x Pixel vorwärts bewegen" +PythonTurtleFunction = "Turtle-Modul-Funktionspräfix" +PythonTurtleGoto = "Bewegen zu (x,y) Koordinaten" +PythonTurtleHeading = "Liefert den aktuellen Kurs" +PythonTurtleHideturtle = "Versteckt den Igel" +PythonTurtleIsdown = "Gibt Wahr zurück, wenn der Stift unten ist" +PythonTurtleLeft = "Nach links um ein Grad drehen" +PythonTurtlePendown = "Den Stift nach unten ziehen" +PythonTurtlePensize = "Linienstärke auf x Pixel setzen" +PythonTurtlePenup = "Den Stift nach oben ziehen" +PythonTurtlePosition = "Aktuelle (x,y) Position zurückgeben" +PythonTurtleReset = "Die Zeichnung zurücksetzen" +PythonTurtleRight = "Um ein Grad nach rechts drehen" +PythonTurtleSetheading = "Die Ausrichtung auf einen Grad setzen" +PythonTurtleSetposition = "Den Igel auf Position setzen" +PythonTurtleShowturtle = "Den Igel anzeigen" +PythonTurtleSpeed = "Zeichengeschwindigkeit zwischen 0 und 10" +PythonTurtleWrite = "Einen Text anzeigen" +PythonUniform = "Fließkommazahl in [a,b]" +PythonImportTime = "Time-Modul importieren" +PythonTimePrefix = "Zeitmodul-Funktionspräfix" +PythonTimeSleep = "Warte für n Sekunden" +PythonMonotonic = "Monotone Zeit zurückgeben" PythonFileOpen = "Öffnet eine Datei" -PythonFileSeekable = "Ist eine Datei durchsuchbar?" -PythonFileSeek = "Dateicursor verschieben" -PythonFileTell = "Cursorposition der Datei abrufen" +PythonFileSeekable = "Gibt an, ob eine Datei durchsucht werden kann" +PythonFileSeek = "Bewegt den Cursor einer Datei" +PythonFileTell = "Ermittelt die Position des Cursors einer Datei" PythonFileClose = "Schließt eine Datei" -PythonFileClosed = "Wenn Datei geschlossen wurde" -PythonFileRead = "Bis zu size Bytes lesen" -PythonFileWrite = "Schreibe b in die Datei" -PythonFileReadline = "Lies eine Zeile" +PythonFileClosed = "Wahr, wenn Datei geschlossen wurde" +PythonFileRead = "Lesen bis zu einer Größe von Bytes" +PythonFileWrite = "Schreibe b in Datei" +PythonFileReadline = "Liest eine Zeile oder bis zu einer Größe von Bytes" PythonFileReadlines = "Liest eine Liste von Zeilen" -PythonFileTruncate = "Größe der Datei ändern" +PythonFileTruncate = "Verkleinert die Datei auf Größe" PythonFileWritelines = "Schreibt eine Liste von Zeilen" -PythonFileName = "Dateiname" -PythonFileMode = "Dateiöffnungsmodus" -PythonFileReadable = "Ist die Datei lesbar?" -PythonFileWritable = "Ist die Datei beschreibbar?" +PythonFileName = "Enthält den Namen der Datei" +PythonFileMode = "Enthält den Öffnungsmodus der Datei" +PythonFileReadable = "Gibt an, ob eine Datei gelesen werden kann" +PythonFileWritable = "Gibt an, ob eine Datei geschrieben werden kann" diff --git a/apps/code/catalog.en.i18n b/apps/code/catalog.en.i18n index 819887dbb..8227192a8 100644 --- a/apps/code/catalog.en.i18n +++ b/apps/code/catalog.en.i18n @@ -103,7 +103,7 @@ PythonPower = "x raised to the power y" PythonPrint = "Print object" PythonRadians = "Convert x from degrees to radians" PythonRandint = "Random integer in [a,b]" -PythonRandom = "Floating point number in [0,1[" +PythonRandom = "Floating point number in [0,1]" PythonRandomFunction = "random module function prefix" PythonRandrange = "Random number in range(start,stop)" PythonRangeStartStop = "List from start to stop-1" diff --git a/apps/external/base.de.i18n b/apps/external/base.de.i18n index fb566db70..28dd31e4e 100644 --- a/apps/external/base.de.i18n +++ b/apps/external/base.de.i18n @@ -1,9 +1,9 @@ ExternalApp = "External" ExternalAppCapital = "EXTERNAL" -ExternalAppApiMismatch = "API mismatch" -ExternalAppExecError = "Cannot execute file" -ExternalNotCompatible = "External ist nicht kompatibel" +ExternalAppApiMismatch = "API stimmt nicht überein" +ExternalAppExecError = "Datei kann nicht ausgeführt werden" +ExternalNotCompatible = "Externe App ist nicht kompatibel" WithSimulator = "mit dem Simulator" -WithN0100 = "mit n0100" -GetMoreAppsAt = "Weitere Apps erhalten Sie auf" -NoAppsInstalled = "Keine Apps installiert" +WithN0100 = "mit N0100" +GetMoreAppsAt = "Weitere Apps abrufen bei" +NoAppsInstalled = "Keine Apps installiert" \ No newline at end of file diff --git a/apps/on_boarding/base.de.i18n b/apps/on_boarding/base.de.i18n index bbf9040ad..6828f5faa 100644 --- a/apps/on_boarding/base.de.i18n +++ b/apps/on_boarding/base.de.i18n @@ -1,13 +1,13 @@ -UpdateAvailable = "UPDATE VERFÜGBAR" +UpdateAvailable = "AKTUALISIERUNG VERFÜGBAR" UpdateMessage1 = "Wichtige Verbesserungen für Ihren" -UpdateMessage2 = "Rechner stehen zur Verfügung." -UpdateMessage3 = "Verbinden Sie sich mit Ihrem Computer auf" +UpdateMessage2 = "Taschenrechner stehen zur Verfügung." +UpdateMessage3 = "Verbinden Sie sich mit Ihrem Computer mit" UpdateMessage4 = "www.numworks.com/update." -BetaVersion = "BETA VERSION" +BetaVersion = "VORABVERSION" BetaVersionMessage1 = "" -BetaVersionMessage2 = "Your device runs a beta software." -BetaVersionMessage3 = "You might run into bugs or glitches." -BetaVersionMessage4 = "" -BetaVersionMessage5 = "Please send any feedback to" +BetaVersionMessage2 = "Auf Ihrem Gerät läuft eine Vorabversion." +BetaVersionMessage3 = "Sie könnten auf Fehler oder Störungen stoßen." +BetaVersionMeldung4 = "" +BetaVersionMessage5 = "Bitte senden Sie jegliches Feedback an" BetaVersionMessage6 = "contact@numworks.com" Skip = "Überspringen" diff --git a/apps/settings/base.de.i18n b/apps/settings/base.de.i18n index 0f469929b..8ccd4adef 100644 --- a/apps/settings/base.de.i18n +++ b/apps/settings/base.de.i18n @@ -9,8 +9,8 @@ ComplexFormat = "Komplexe Zahlen" ExamMode = "Prüfungsmodus" ExamModeActive = "Modus erneut starten" ToDeactivateExamMode1 = "Um den Prüfungsmodus auszuschalten," -ToDeactivateExamMode2 = "schließen Sie den Rechner an einen" -ToDeactivateExamMode3 = "Computer oder eine Steckdose an." +ToDeactivateExamMode2 = "schließen Sie den Taschenrechner an" +ToDeactivateExamMode3 = "einen Computer an." # --------------------- Please do not edit these messages --------------------- ExamModeWarning1 = "Caution: compliance of this" ExamModeWarning2 = "unofficial software's exam mode" @@ -40,9 +40,9 @@ FontSizes = "Python-Schriftgröße" LargeFont = "Große " SmallFont = "Kleine " SerialNumber = "Seriennummer" -UpdatePopUp = "Erinnerung: Update" -BetaPopUp = "Beta pop-up" -Contributors = "Beiträger" +UpdatePopUp = "Erinnerung: Aktualisierung" +BetaPopUp = "Erinnerung: Vorabversion" +Contributors = "Mitwirkende" Accessibility = "Barrierefreiheit" AccessibilityInvertColors = "Farbumkehrung" AccessibilityMagnify = "Lupe" diff --git a/apps/shared.de.i18n b/apps/shared.de.i18n index 9d3c2927b..e1ac6bd17 100644 --- a/apps/shared.de.i18n +++ b/apps/shared.de.i18n @@ -43,13 +43,13 @@ FunctionColumn = "0(0) Spalte" FunctionOptions = "Funktionsoptionen" Goto = "Gehe zu" GraphTab = "Graph" -HardwareTestLaunch1 = "Sie sind dabei den Hardwaretest zu" +HardwareTestLaunch1 = "Sie sind dabei, den Hardwaretest zu" HardwareTestLaunch2 = "starten. Um ihn wieder zu verlassen," -HardwareTestLaunch3 = "müssen Sie einen Reset durchfuhren," +HardwareTestLaunch3 = "müssen Sie einen Reset durchführen," HardwareTestLaunch4 = "der Ihre Daten löschen wird." IntervalSet = "Intervall einstellen" Language = "Sprache" -LowBattery = "Batterie erschöpft" +LowBattery = "Akku erschöpft" Mean = "Mittelwert" Move = " Verschieben: " NameCannotStartWithNumber = "Ein Name darf nicht mit einer Zahl beginnen" diff --git a/apps/toolbox.de.i18n b/apps/toolbox.de.i18n index c0745498a..e3f21d58f 100644 --- a/apps/toolbox.de.i18n +++ b/apps/toolbox.de.i18n @@ -25,15 +25,15 @@ UnitDistanceParsec = "Parsec" UnitDistanceMile = "Meile" UnitDistanceYard = "Yard" UnitDistanceFoot = "Fuß" -UnitDistanceInch = "Inch" +UnitDistanceInch = "Zoll" UnitMassMenu = "Masse" UnitMassGramKilo = "Kilogramm" UnitMassGram = "Gramm" UnitMassGramMilli = "Milligramm" UnitMassGramMicro = "Mikrogramm" UnitMassGramNano = "Nanogramm" -UnitDistanceImperialMenu = "US Customary" -UnitMassImperialMenu = "US Customary" +UnitDistanceImperialMenu = "Angloamerikanisch" +UnitMassImperialMenu = "Angloamerikanisch" UnitMassTonne = "Tonne" UnitMassOunce = "Unze" UnitMassPound = "Pfund" @@ -95,7 +95,7 @@ UnitVolumePint = "Pint" UnitVolumeQuart = "Quart" UnitVolumeGallon = "Gallone" UnitMetricMenu = "Metrisch" -UnitImperialMenu = "Empire" +UnitImperialMenu = "Angloamerikanisch" Toolbox = "Werkzeugkasten" AbsoluteValue = "Betragsfunktion" NthRoot = "n-te Wurzel" @@ -137,34 +137,34 @@ Vectors = "Vektoren" Dot = "Skalarprodukt" Cross = "Kreuzprodukt" NormVector = "Norm" -Sort = "Sortieren aufsteigend" -InvSort = "Sortieren absteigend" +Sort = "Aufsteigend sortieren" +InvSort = "Absteigend sortieren" Maximum = "Maximalwert" Minimum = "Mindestwert" Floor = "Untergrenze" FracPart = "Bruchteil" Ceiling = "Obergrenze" Rounding = "Runden" -HyperbolicCosine = "Kosinus hyperbolicus" -HyperbolicSine = "Sinus hyperbolicus" -HyperbolicTangent = "Tangens hyperbolicus" -InverseHyperbolicCosine = "Areakosinus hyperbolicus" -InverseHyperbolicSine = "Areasinus hyperbolicus" -InverseHyperbolicTangent = "Areatangens hyperbolicus" -Prediction95 = "Schwankungsbereich 95%" -Prediction = "Einfacher Schwankungsbereich" -Confidence = "Konfidenzintervall" +HyperbolicCosine = "Hyperbolischer Kosinus" +HyperbolicSine = "Hyperbolischer Sinus" +HyperbolischerTangens = "Hyperbolischer Tangens" +InverseHyperbolicCosine = "Inverser hyperbolischer Kosinus" +InverseHyperbolicSine = "Inverser hyperbolischer Sinus" +InverseHyperbolicTangent = "Inverser hyperbolischer Tangens" +Vorhersage95 = "Vorhersageintervall 95%" +Vorhersage = "Einfaches Vorhersageintervall" +Konfidenz = "Konfidenzintervall" RandomAndApproximation = "Zufall und Näherung" -RandomFloat = "Dezimalzahl in [0,1]" +RandomFloat = "Fließkommazahl in [0,1]" RandomInteger = "Zufällige ganze Zahl in [a,b]" -PrimeFactorDecomposition = "Primfaktorzerlegung" -NormCDF = "P(X Date: Mon, 28 Jun 2021 16:31:52 +0200 Subject: [PATCH 21/48] Fix build error "BetaVersionMessage4" --- apps/on_boarding/base.de.i18n | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/on_boarding/base.de.i18n b/apps/on_boarding/base.de.i18n index 6828f5faa..ccb8aa1b7 100644 --- a/apps/on_boarding/base.de.i18n +++ b/apps/on_boarding/base.de.i18n @@ -7,7 +7,7 @@ BetaVersion = "VORABVERSION" BetaVersionMessage1 = "" BetaVersionMessage2 = "Auf Ihrem Gerät läuft eine Vorabversion." BetaVersionMessage3 = "Sie könnten auf Fehler oder Störungen stoßen." -BetaVersionMeldung4 = "" +BetaVersionMessage4 = "" BetaVersionMessage5 = "Bitte senden Sie jegliches Feedback an" BetaVersionMessage6 = "contact@numworks.com" Skip = "Überspringen" From ed54927c5b9c01326d493cc81b1b56f3cce8d9ff Mon Sep 17 00:00:00 2001 From: Pixelpunker Date: Mon, 28 Jun 2021 16:41:40 +0200 Subject: [PATCH 22/48] Fix for 4 translation keys --- apps/toolbox.de.i18n | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/toolbox.de.i18n b/apps/toolbox.de.i18n index e3f21d58f..18823164d 100644 --- a/apps/toolbox.de.i18n +++ b/apps/toolbox.de.i18n @@ -147,13 +147,13 @@ Ceiling = "Obergrenze" Rounding = "Runden" HyperbolicCosine = "Hyperbolischer Kosinus" HyperbolicSine = "Hyperbolischer Sinus" -HyperbolischerTangens = "Hyperbolischer Tangens" +HyperbolicTangent = "Hyperbolischer Tangens" InverseHyperbolicCosine = "Inverser hyperbolischer Kosinus" InverseHyperbolicSine = "Inverser hyperbolischer Sinus" InverseHyperbolicTangent = "Inverser hyperbolischer Tangens" -Vorhersage95 = "Vorhersageintervall 95%" -Vorhersage = "Einfaches Vorhersageintervall" -Konfidenz = "Konfidenzintervall" +Prediction95 = "Vorhersageintervall 95%" +Prediction = "Einfaches Vorhersageintervall" +Confidence = "Konfidenzintervall" RandomAndApproximation = "Zufall und Näherung" RandomFloat = "Fließkommazahl in [0,1]" RandomInteger = "Zufällige ganze Zahl in [a,b]" From 786a3273da46edd01c2a259bf2e27a29fe6472dc Mon Sep 17 00:00:00 2001 From: Pixelpunker Date: Tue, 29 Jun 2021 17:11:10 +0200 Subject: [PATCH 23/48] All translated german strings limited to 35 characters --- apps/code/base.de.i18n | 2 +- apps/code/catalog.de.i18n | 52 +++++++++++++++++------------------ apps/graph/base.de.i18n | 2 +- apps/on_boarding/base.de.i18n | 8 +++--- apps/regression/base.de.i18n | 2 +- apps/sequence/base.de.i18n | 4 +-- apps/settings/base.de.i18n | 12 ++++---- apps/shared.de.i18n | 14 +++++----- apps/solver/base.de.i18n | 6 ++-- apps/toolbox.de.i18n | 2 +- apps/usb/base.de.i18n | 4 +-- 11 files changed, 54 insertions(+), 54 deletions(-) diff --git a/apps/code/base.de.i18n b/apps/code/base.de.i18n index 4306f4d17..748494831 100644 --- a/apps/code/base.de.i18n +++ b/apps/code/base.de.i18n @@ -2,7 +2,7 @@ AddScript = "Skript hinzufügen" AllowedCharactersaz09 = "Erlaubte Zeichen: a-z, 0-9, _" Autocomplete = "Autovervollständigung" AutoImportScript = "Automatischer Import in die Konsole" -BuiltinsAndKeywords = "Eingebaute Funktionen und Schlüsselwörter" +BuiltinsAndKeywords = "Native Funktionen & Schlüsselwörter" Console = "Interaktive Konsole" DeleteScript = "Skript löschen" DuplicateScript = "Skript duplizieren" diff --git a/apps/code/catalog.de.i18n b/apps/code/catalog.de.i18n index eeb532011..d7325bd4c 100644 --- a/apps/code/catalog.de.i18n +++ b/apps/code/catalog.de.i18n @@ -19,8 +19,8 @@ PythonAsinh = "Kreisbogen hyperbolischer Sinus" PythonAtan = "Arkustangens" PythonAtan2 = "Rückgabe atan(y/x)" PythonAtanh = "Hyperbeltangens" -PythonAxis = "Achsen auf (xmin,xmax,ymin,ymax) setzen" -PythonBar = "Balkendiagramm mit x-Werten zeichnen" +PythonAxis = "Achsen auf (xmin,xmax,ymin,ymax)" +PythonBar = "Balkendiagramm mit x-Werten" PythonBin = "Ganzzahl in Binärwert umwandeln" PythonCeil = "Aufrunden" PythonChoice = "Zufällige Zahl in der Liste" @@ -39,21 +39,21 @@ PythonColorRed = "Farbe Rot" PythonColorWhite = "Farbe Weiß" PythonColorYellow = "Farbe Gelb" PythonComplex = "Gib a+ib zurück" -PythonCopySign = "Gib x mit dem Vorzeichen von y zurück" +PythonCopySign = "Gib x mit Vorzeichen von y zurück" PythonCos = "Kosinus" PythonCosh = "Hyperbolischer Kosinus" PythonCount = "Zählt die Vorkommen von x" PythonDegrees = "x von Bogenmaß in Grad umrechnen" PythonDivMod = "Quotient und Rest" PythonDrawLine = "Eine Linie zeichnen" -PythonDrawString = "Einen Text bei Pixel (x,y) darstellen" +PythonDrawString = "Text bei Pixel (x,y) darstellen" PythonErf = "Fehlerfunktion" PythonErfc = "Komplementäre Fehlerfunktion" -PythonEval = "Rückgabe des ausgewerteten Ausdrucks" +PythonEval = "Rückgabe ausgewerteter Ausdruck" PythonExp = "Exponentialfunktion" PythonExpm1 = "Berechne exp(x)-1" PythonFabs = "Absoluter Wert" -PythonFillRect = "Malt ein gefülltes Rechteck bei Pixel (x,y)" +PythonFillRect = "Gefülltes Rechteck bei Pixel (x,y)" PythonFloat = "x in einen Fließkommawert umwandeln" PythonFloor = "Abrunden" PythonFmod = "a modulo b" @@ -61,7 +61,7 @@ PythonFrExp = "Mantisse und Exponent von x: (m,e)" PythonGamma = "Gamma-Funktion" PythonGetPixel = "Farbe von Pixel (x,y) zurückgeben" PythonGetrandbits = "Ganzzahl mit k Zufallsbits" -PythonGrid = "Schaltet die Sichtbarkeit des Gitters um" +PythonGrid = "Sichtbarkeit des Gitters umschalten" PythonHex = "Ganzzahl in Hexadezimal umwandeln" PythonHist = "Zeichnet das Histogramm von x" PythonImportCmath = "cmath-Modul importieren" @@ -73,7 +73,7 @@ PythonImportMatplotlibPyplot = "Matplotlib.pyplot-Modul importieren" PythonImportOs = "OS-Modul importieren" PythonOsUname = "Informationen über das System holen" PythonOsRemove = "Datei namens Dateiname entfernen" -PythonOsRename = "Datei mit altem Namen in neuen Namen umbenennen" +PythonOsRename = "Datei umbenennen von Alt nach Neu" PythonOsListdir = "Dateien im Speicher auflisten" PythonImportTime = "Time-Modul importieren" PythonImportTurtle = "Turtle-Modul importieren" @@ -85,9 +85,9 @@ PythonIonFunction = "Ion-Modul-Funktionspräfix" PythonIsFinite = "Prüfen, ob x endlich ist" PythonIsInfinite = "Prüfen, ob x unendlich ist" PythonIsNaN = "Prüfen, ob x keine Zahl ist" -PythonIsKeyDown = "Gibt Wahr zurück, wenn die Taste k gedrückt ist" +PythonIsKeyDown = "Wahr, wenn die Taste k gedrückt ist" PythonKandinskyFunction = "Kandinsky-Modul Funktionspräfix" -PythonLdexp = "Liefert x*(2**i), die Inverse von frexp" +PythonLdexp = "Liefert x*(2**i), Inverse von frexp" PythonLength = "Länge eines Objekts" PythonLgamma = "Log-Gamma-Funktion" PythonLog = "Logarithmus zur Basis a" @@ -97,13 +97,13 @@ PythonMathFunction = "Funktionspräfix des Math-Moduls" PythonMatplotlibPyplotFunction = "matplotlib.pyplot Modul-Präfix" PythonMax = "Maximum" PythonMin = "Minimum" -PythonModf = "Bruch- und ganzzahlige Anteile von x" +PythonModf = "Bruch- und Ganzzahl-Anteile von x" PythonMonotonic = "Wert einer monotonen Uhr" PythonOct = "Ganzzahl in Oktal umwandeln" PythonPhase = "Phase von z" PythonPlot = "Plotten von y gegen x als Linien" PythonPolar = "z in Polarkoordinaten" -PythonPop = "Letztes Element entfernen und zurückgeben" +PythonPop = "Letztes Element abnehmen" PythonPower = "x erhöht mit der Potenz y" PythonPrint = "Objekt drucken" PythonRadians = "x von Grad in Bogenmaß umrechnen" @@ -113,35 +113,35 @@ PythonRandomFunction = "Random-Modul Funktionspräfix" PythonRandrange = "Zufallszahl im Bereich(start,stop)" PythonRangeStartStop = "Liste von Start bis Stop-1" PythonRangeStop = "Liste von 0 bis Stop-1" -PythonRect = "In kartesische Koordinaten umrechnen" +PythonRect = "In kartesische Koordinaten" PythonRemove = "Entferne das erste Vorkommen von x" PythonReverse = "Kehrt die Elemente der Liste um" PythonRound = "Runden auf n Stellen" -PythonScatter = "Ein Streudiagramm von y gegen x zeichnen" -PythonSeed = "Zufallszahlengenerator initialisieren" +PythonScatter = "Streudiagramm von y gg. x zeichnen" +PythonSeed = "Zufallszahlengenerator initiieren" PythonSetPixel = "Pixel (x,y) einfärben" PythonShow = "Figur anzeigen" PythonSin = "Sinus" PythonSinh = "Hyperbolischer Sinus" -PythonSleep = "Aussetzen der Ausführung für t Sekunden" +PythonSleep = "Ausführung aussetzen für t Sekunden" PythonSort = "Die Liste sortieren" PythonSqrt = "Quadratwurzel" PythonSum = "Summe der Elemente einer Liste" PythonTan = "Tangens" PythonTanh = "Hyperbolischer Tangens" -PythonText = "Einen Text an (x,y) Koordinaten anzeigen" +PythonText = "Text an (x,y) Koordinaten anzeigen" PythonTimeFunction = "Time-Modul-Funktionspräfix" PythonTrunc = "x abgeschnitten auf eine ganze Zahl" PythonTurtleBackward = "Um x Pixel rückwärts bewegen" PythonTurtleCircle = "Kreis mit Radius r Pixel" PythonTurtleColor = "Setzt die Stiftfarbe" -PythonTurtleColorMode = "Setzt den Farbmodus auf 1.0 oder 255" +PythonTurtleColorMode = "Setzt Farbmodus auf 1.0 oder 255" PythonTurtleForward = "Um x Pixel vorwärts bewegen" PythonTurtleFunction = "Turtle-Modul-Funktionspräfix" PythonTurtleGoto = "Bewegen zu (x,y) Koordinaten" PythonTurtleHeading = "Liefert den aktuellen Kurs" PythonTurtleHideturtle = "Versteckt den Igel" -PythonTurtleIsdown = "Gibt Wahr zurück, wenn der Stift unten ist" +PythonTurtleIsdown = "Wahr, wenn der Stift unten ist" PythonTurtleLeft = "Nach links um ein Grad drehen" PythonTurtlePendown = "Den Stift nach unten ziehen" PythonTurtlePensize = "Linienstärke auf x Pixel setzen" @@ -149,10 +149,10 @@ PythonTurtlePenup = "Den Stift nach oben ziehen" PythonTurtlePosition = "Aktuelle (x,y) Position zurückgeben" PythonTurtleReset = "Die Zeichnung zurücksetzen" PythonTurtleRight = "Um ein Grad nach rechts drehen" -PythonTurtleSetheading = "Die Ausrichtung auf einen Grad setzen" +PythonTurtleSetheading = "Ausrichtung auf einen Grad setzen" PythonTurtleSetposition = "Den Igel auf Position setzen" PythonTurtleShowturtle = "Den Igel anzeigen" -PythonTurtleSpeed = "Zeichengeschwindigkeit zwischen 0 und 10" +PythonTurtleSpeed = "Zeichengeschwindigkeit von 0 bis 10" PythonTurtleWrite = "Einen Text anzeigen" PythonUniform = "Fließkommazahl in [a,b]" PythonImportTime = "Time-Modul importieren" @@ -160,18 +160,18 @@ PythonTimePrefix = "Zeitmodul-Funktionspräfix" PythonTimeSleep = "Warte für n Sekunden" PythonMonotonic = "Monotone Zeit zurückgeben" PythonFileOpen = "Öffnet eine Datei" -PythonFileSeekable = "Gibt an, ob eine Datei durchsucht werden kann" +PythonFileSeekable = "Kann Datei durchsucht werden?" PythonFileSeek = "Bewegt den Cursor einer Datei" -PythonFileTell = "Ermittelt die Position des Cursors einer Datei" +PythonFileTell = "Position des Cursors ermitteln" PythonFileClose = "Schließt eine Datei" PythonFileClosed = "Wahr, wenn Datei geschlossen wurde" PythonFileRead = "Lesen bis zu einer Größe von Bytes" PythonFileWrite = "Schreibe b in Datei" -PythonFileReadline = "Liest eine Zeile oder bis zu einer Größe von Bytes" +PythonFileReadline = "Liest Zeile oder Anzahl Bytes" PythonFileReadlines = "Liest eine Liste von Zeilen" PythonFileTruncate = "Verkleinert die Datei auf Größe" PythonFileWritelines = "Schreibt eine Liste von Zeilen" PythonFileName = "Enthält den Namen der Datei" PythonFileMode = "Enthält den Öffnungsmodus der Datei" -PythonFileReadable = "Gibt an, ob eine Datei gelesen werden kann" -PythonFileWritable = "Gibt an, ob eine Datei geschrieben werden kann" +PythonFileReadable = "Kann Datei gelesen werden?" +PythonFileWritable = "Kann Datei geschrieben werden?" diff --git a/apps/graph/base.de.i18n b/apps/graph/base.de.i18n index c816e3d06..68aef0b67 100644 --- a/apps/graph/base.de.i18n +++ b/apps/graph/base.de.i18n @@ -29,5 +29,5 @@ NoIntersectionFound = "Kein Schnittpunkt gefunden" NoPreimageFound = "Kein Urbild gefunden" DerivativeFunctionColumn = "Spalte der Ableitungsfunktion" HideDerivativeColumn = "Ableitungsfunktion ausblenden" -AllowedCharactersAZaz09 = "Erlaubte Zeichen: A..Z, a..z, 0..9, _" +AllowedCharactersAZaz09 = "Erlaubt: A..Z, a..z, 0..9, _" ReservedName = "Reserviertes Wort" diff --git a/apps/on_boarding/base.de.i18n b/apps/on_boarding/base.de.i18n index ccb8aa1b7..d0e9992c5 100644 --- a/apps/on_boarding/base.de.i18n +++ b/apps/on_boarding/base.de.i18n @@ -1,13 +1,13 @@ UpdateAvailable = "AKTUALISIERUNG VERFÜGBAR" UpdateMessage1 = "Wichtige Verbesserungen für Ihren" UpdateMessage2 = "Taschenrechner stehen zur Verfügung." -UpdateMessage3 = "Verbinden Sie sich mit Ihrem Computer mit" +UpdateMessage3 = "Verbinden Sie sich mit Ihrem PC mit" UpdateMessage4 = "www.numworks.com/update." BetaVersion = "VORABVERSION" BetaVersionMessage1 = "" -BetaVersionMessage2 = "Auf Ihrem Gerät läuft eine Vorabversion." -BetaVersionMessage3 = "Sie könnten auf Fehler oder Störungen stoßen." +BetaVersionMessage2 = "Gerät führt eine Vorabversion aus." +BetaVersionMessage3 = "Sie könnten auf Fehler stoßen." BetaVersionMessage4 = "" -BetaVersionMessage5 = "Bitte senden Sie jegliches Feedback an" +BetaVersionMessage5 = "Bitte senden Sie Feedback an" BetaVersionMessage6 = "contact@numworks.com" Skip = "Überspringen" diff --git a/apps/regression/base.de.i18n b/apps/regression/base.de.i18n index 619e8a0ba..e7175d5f8 100644 --- a/apps/regression/base.de.i18n +++ b/apps/regression/base.de.i18n @@ -5,7 +5,7 @@ MeanDot = "mittel" RegressionCurve = "Regressionskurve" XPrediction = "Berechne Y" YPrediction = "Berechne X" -ValueNotReachedByRegression = "Wert in diesem Fenster nicht erreicht" +ValueNotReachedByRegression = "Wert im Fenster nicht erreicht" NumberOfDots = "Punktanzahl" Covariance = "Kovarianz" Linear = "Lineare" diff --git a/apps/sequence/base.de.i18n b/apps/sequence/base.de.i18n index 278615512..88f518ca9 100644 --- a/apps/sequence/base.de.i18n +++ b/apps/sequence/base.de.i18n @@ -2,7 +2,7 @@ SequenceApp = "Folge" SequenceAppCapital = "FOLGE" SequenceTab = "Folgen" AddSequence = "Folge hinzufügen" -ChooseSequenceType = "Das Bildungsgesetz der Folge auswählen" +ChooseSequenceType = "Bildungsgesetz der Folge auswählen" SequenceType = "Bildungsgesetz der Folge" Explicit = "Explizit" SingleRecurrence = "Rekursion 1. Ordnung" @@ -17,6 +17,6 @@ NEnd = "Endwert" TermSum = "Summe der Terme" SelectFirstTerm = "Erster Term " SelectLastTerm = "Letzter Term " -ValueNotReachedBySequence = "Der Wert wird von der Folge nicht erreicht" +ValueNotReachedBySequence = "Wert wird von Folge nicht erreicht" NColumn = "n-te Spalte" FirstTermIndex = "Anfangsindex" diff --git a/apps/settings/base.de.i18n b/apps/settings/base.de.i18n index 8ccd4adef..5c00526fc 100644 --- a/apps/settings/base.de.i18n +++ b/apps/settings/base.de.i18n @@ -62,9 +62,9 @@ SymbolArgFunction = "Leer " SymbolArgDefaultFunction = "Argument " PythonFont = "Python Schriftart" MemUse = "Speicher" -DateTime = "Date/time" -ActivateClock = "Activate clock" -Date = "Date" -Time = "Time" -RTCWarning1 = "Enabling the clock drains the battery faster" -RTCWarning2 = "when the calculator is powered off." +DateTime = "Datum/Uhrzeit" +ActivateClock = "Uhr aktivieren" +Date = "Datum" +Time = "Uhrzeit" +RTCWarning1 = "Das Aktivieren der Uhr verkürzt die" +RTCWarning2 = "Akkulaufzeit im Bereitschaftsmodus." diff --git a/apps/shared.de.i18n b/apps/shared.de.i18n index e1ac6bd17..721941691 100644 --- a/apps/shared.de.i18n +++ b/apps/shared.de.i18n @@ -4,7 +4,7 @@ ActivateExamMode = "Prüfungsmodus starten" ActiveExamModeMessage1 = "Alle Ihre Daten werden " ActiveExamModeMessage2 = "gelöscht, wenn Sie den " ActiveExamModeMessage3 = "Prüfungsmodus einschalten." -ActiveDutchExamModeMessage1 = "Alle Ihre Daten werden gelöscht, wenn" +ActiveDutchExamModeMessage1 = "Alle Daten werden gelöscht, wenn" ActiveDutchExamModeMessage2 = "Sie den Prüfungsmodus einschalten. " ActiveDutchExamModeMessage3 = "Python wird nicht verfügbar sein." Axis = "Achse" @@ -25,7 +25,7 @@ CountryNL = "Niederlande " CountryPT = "Portugal " CountryUS = "Vereinigte Staaten " CountryWW = "International " -CountryWarning1 = "Diese Einstellung definiert die verwendeten" +CountryWarning1 = "Einstellung definiert verwendete" CountryWarning2 = "mathematischen Konventionen." DataNotSuitable = "Daten nicht geeignet" DataTab = "Daten" @@ -44,7 +44,7 @@ FunctionOptions = "Funktionsoptionen" Goto = "Gehe zu" GraphTab = "Graph" HardwareTestLaunch1 = "Sie sind dabei, den Hardwaretest zu" -HardwareTestLaunch2 = "starten. Um ihn wieder zu verlassen," +HardwareTestLaunch2 = "starten. Um ihn zu verlassen," HardwareTestLaunch3 = "müssen Sie einen Reset durchführen," HardwareTestLaunch4 = "der Ihre Daten löschen wird." IntervalSet = "Intervall einstellen" @@ -52,7 +52,7 @@ Language = "Sprache" LowBattery = "Akku erschöpft" Mean = "Mittelwert" Move = " Verschieben: " -NameCannotStartWithNumber = "Ein Name darf nicht mit einer Zahl beginnen" +NameCannotStartWithNumber = "Name darf nicht mit Zahl beginnen" NameTaken = "Dieser Name ist bereits vergeben" NameTooLong = "Der Name ist zu lang" Navigate = "Navigieren" @@ -70,14 +70,14 @@ PoolMemoryFull1 = "Der Arbeitsspeicher ist voll." PoolMemoryFull2 = "Versuchen Sie es erneut." Rename = "Umbenennen" Sci = "wiss" -SortValues = "Nach steigenden Werten sortieren" -SortSizes = "Nach steigenden Frequenzen sortieren" +SortValues = "Nach Werten sortieren" +SortSizes = "Nach Frequenzen sortieren" SquareSum = "Quadratsumme" StatTab = "Stats" StandardDeviation = "Standardabweichung" Step = "Schrittwert" StorageMemoryFull1 = "Der Speicher ist voll. Löschen Sie" -StorageMemoryFull2 = "einige Daten und versuchen Sie es erneut." +StorageMemoryFull2 = "einige Daten, dann erneut versuchen." StoreExpressionNotAllowed = "'store' ist verboten" SyntaxError = "Syntaxfehler" Sym = "sym" diff --git a/apps/solver/base.de.i18n b/apps/solver/base.de.i18n index f88fe1a28..f3a416738 100644 --- a/apps/solver/base.de.i18n +++ b/apps/solver/base.de.i18n @@ -4,7 +4,7 @@ AddEquation = "Gleichung hinzufügen" ResolveEquation = "Lösen der Gleichung" ResolveSystem = "Lösen des Gleichungssystems" UseEquationModel = "Verwenden Sie ein Gleichungsmodell" -RequireEquation = "Die Eingabe muss eine Gleichung sein" +RequireEquation = "Eingabe muss eine Gleichung sein" UndefinedEquation = "Nicht definierte Gleichung" UnrealEquation = "Nicht reelle Gleichung" TooManyVariables = "Es gibt zu viele Unbekannte" @@ -17,8 +17,8 @@ NoSolutionEquation = "Die Gleichung hat keine Lösung" NoSolutionInterval = "Keine Lösung im Intervall gefunden" EnterEquation = "Geben Sie eine Gleichung ein" InfiniteNumberOfSolutions = "Es gibt unendlich viele Lösungen" -ApproximateSolutionIntervalInstruction0= "Geben Sie das Intervall für die Suche" -ApproximateSolutionIntervalInstruction1= "nach einer ungefähren Lösung ein" +ApproximateSolutionIntervalInstruction0= "Geben Sie das Suchintervall" +ApproximateSolutionIntervalInstruction1= "für eine ungefähre Lösung ein" OnlyFirstSolutionsDisplayed0 = "Es werden nur die ersten" OnlyFirstSolutionsDisplayed1 = "zehn Lösungen angezeigt." PolynomeHasNoRealSolution0 = "Das Polynom hat" diff --git a/apps/toolbox.de.i18n b/apps/toolbox.de.i18n index 18823164d..e39e80693 100644 --- a/apps/toolbox.de.i18n +++ b/apps/toolbox.de.i18n @@ -161,7 +161,7 @@ PrimeFactorDecomposition = "Ganzzahlige Faktorisierung" NormCDF = "P(X Date: Tue, 29 Jun 2021 20:40:19 +0200 Subject: [PATCH 24/48] Removed "Omega or Epsilon" check --- python/port/mod/os/modos_table.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/python/port/mod/os/modos_table.c b/python/port/mod/os/modos_table.c index 20106bb2d..a97009720 100644 --- a/python/port/mod/os/modos_table.c +++ b/python/port/mod/os/modos_table.c @@ -10,11 +10,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(modos_remove_obj, modos_remove); MP_DEFINE_CONST_FUN_OBJ_2(modos_rename_obj, modos_rename); MP_DEFINE_CONST_FUN_OBJ_0(modos_listdir_obj, modos_listdir); -#ifdef OMEGA_VERSION STATIC const MP_DEFINE_STR_OBJ(modos_sysname_obj, "Omega"); -#else -STATIC const MP_DEFINE_STR_OBJ(modos_sysname_obj, "Epsilon"); -#endif STATIC const mp_rom_map_elem_t modos_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_os) }, From 24f3ccf007c59eec4a352a746c87f5c55b7c10e5 Mon Sep 17 00:00:00 2001 From: ArtichOwO Date: Tue, 29 Jun 2021 20:41:12 +0200 Subject: [PATCH 25/48] Removed username from "uname()", and "getlogin()" now uses "Ion::username()" --- python/port/mod/os/modos.cpp | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/python/port/mod/os/modos.cpp b/python/port/mod/os/modos.cpp index b32d9a3d8..a8333006d 100644 --- a/python/port/mod/os/modos.cpp +++ b/python/port/mod/os/modos.cpp @@ -7,6 +7,7 @@ extern "C" { #include } +#include #include #ifndef OMEGA_VERSION @@ -32,19 +33,12 @@ STATIC const MP_DEFINE_STR_OBJ(modos_uname_info_machine_obj, "NumWorks N0100"); STATIC const MP_DEFINE_STR_OBJ(modos_uname_info_machine_obj, "NumWorks Simulator"); #endif -#if defined(OMEGA_USERNAME) -STATIC const MP_DEFINE_STR_OBJ(modos_uname_info_username_obj, MP_STRINGIFY(OMEGA_USERNAME)); -#else -STATIC const MP_DEFINE_STR_OBJ(modos_uname_info_username_obj, ""); -#endif - STATIC const mp_rom_map_elem_t modos_uname_info_table[] = { { MP_ROM_QSTR(MP_QSTR_sysname), &modos_uname_info_sysname_obj }, { MP_ROM_QSTR(MP_QSTR_nodename), &modos_uname_info_nodename_obj }, { MP_ROM_QSTR(MP_QSTR_release), &modos_uname_info_release_obj }, { MP_ROM_QSTR(MP_QSTR_version), &modos_uname_info_version_obj }, { MP_ROM_QSTR(MP_QSTR_machine), &modos_uname_info_machine_obj }, - { MP_ROM_QSTR(MP_QSTR_username), &modos_uname_info_username_obj }, }; STATIC MP_DEFINE_CONST_DICT(modos_uname_info_obj, modos_uname_info_table); @@ -54,7 +48,7 @@ mp_obj_t modos_uname(void) { } mp_obj_t modos_getlogin(void) { - return (mp_obj_t)&modos_uname_info_username_obj; + return mp_obj_new_str((const char *)Ion::username(), MIN(strlen((const char *)Ion::username()), 16)); } mp_obj_t modos_remove(mp_obj_t o_file_name) { From 649f48919eef5cc89fd3df0df5f2f01e9ff9fdf5 Mon Sep 17 00:00:00 2001 From: Laury Date: Tue, 6 Jul 2021 14:54:55 +0200 Subject: [PATCH 26/48] [home] Added support for a wallpaper in a special format (.obm) --- apps/home/Makefile | 1 + apps/home/app_cell.cpp | 9 ++- apps/home/app_cell.h | 4 +- apps/home/controller.cpp | 27 ++++++- apps/home/controller.h | 5 +- .../selectable_table_view_with_background.cpp | 16 ++++ .../selectable_table_view_with_background.h | 25 ++++++ escher/Makefile | 2 + escher/include/escher.h | 2 + escher/include/escher/background_view.h | 20 +++++ escher/include/escher/icon_view.h | 20 +++++ escher/include/escher/scroll_view.h | 41 +++++----- escher/src/background_view.cpp | 65 +++++++++++++++ escher/src/icon_view.cpp | 79 +++++++++++++++++++ escher/src/scroll_view.cpp | 6 +- 15 files changed, 292 insertions(+), 30 deletions(-) create mode 100644 apps/home/selectable_table_view_with_background.cpp create mode 100644 apps/home/selectable_table_view_with_background.h create mode 100644 escher/include/escher/background_view.h create mode 100644 escher/include/escher/icon_view.h create mode 100644 escher/src/background_view.cpp create mode 100644 escher/src/icon_view.cpp diff --git a/apps/home/Makefile b/apps/home/Makefile index c76c45808..880302a52 100644 --- a/apps/home/Makefile +++ b/apps/home/Makefile @@ -3,6 +3,7 @@ app_home_src = $(addprefix apps/home/,\ app_cell.cpp \ apps_layout.py \ controller.cpp \ + selectable_table_view_with_background.cpp \ ) apps_src += $(app_home_src) diff --git a/apps/home/app_cell.cpp b/apps/home/app_cell.cpp index a6e5a1554..777bc14f6 100644 --- a/apps/home/app_cell.cpp +++ b/apps/home/app_cell.cpp @@ -8,6 +8,7 @@ namespace Home { AppCell::AppCell() : HighlightCell(), m_nameView(KDFont::SmallFont, (I18n::Message)0, 0.5f, 0.5f, Palette::HomeCellText, Palette::HomeCellBackground), + m_backgroundView(nullptr), m_visible(true), m_external_app(false) { } @@ -15,8 +16,8 @@ AppCell::AppCell() : void AppCell::drawRect(KDContext * ctx, KDRect rect) const { KDSize nameSize = m_nameView.minimalSizeForOptimalDisplay(); - ctx->fillRect(KDRect(0, bounds().height()-nameSize.height() - 2*k_nameHeightMargin, bounds().width(), nameSize.height()+2*k_nameHeightMargin), Palette::HomeBackground); -} + m_backgroundView->drawRect(ctx, KDRect(0, bounds().height()-nameSize.height() - 2*k_nameHeightMargin, bounds().width(), nameSize.height()+2*k_nameHeightMargin)); + } int AppCell::numberOfSubviews() const { return m_visible ? 2 : 0; @@ -70,6 +71,10 @@ void AppCell::setVisible(bool visible) { } } +void AppCell::setBackgroundView(const BackgroundView * backgroundView) { + m_backgroundView = backgroundView; +} + void AppCell::reloadCell() { m_nameView.setTextColor(isHighlighted() ? (m_external_app ? Palette::HomeCellTextExternalActive : Palette::HomeCellTextActive) : (m_external_app ? Palette::HomeCellTextExternal : Palette::HomeCellText)); m_nameView.setBackgroundColor(isHighlighted() ? Palette::HomeCellBackgroundActive : Palette::HomeCellBackground); diff --git a/apps/home/app_cell.h b/apps/home/app_cell.h index 89c22e008..f9ff029bd 100644 --- a/apps/home/app_cell.h +++ b/apps/home/app_cell.h @@ -15,6 +15,7 @@ public: void layoutSubviews(bool force = false) override; void setVisible(bool visible); + void setBackgroundView(const BackgroundView * backgroundView); void reloadCell() override; void setAppDescriptor(::App::Descriptor * appDescriptor); void setExtAppDescriptor(const char* name, const Image* icon); @@ -25,8 +26,9 @@ private: static constexpr KDCoordinate k_iconHeight = 56; static constexpr KDCoordinate k_nameWidthMargin = 4; static constexpr KDCoordinate k_nameHeightMargin = 1; - ImageView m_iconView; + IconView m_iconView; MessageTextView m_nameView; + const BackgroundView * m_backgroundView; bool m_visible; bool m_external_app; }; diff --git a/apps/home/controller.cpp b/apps/home/controller.cpp index 2724bd172..df725f10f 100644 --- a/apps/home/controller.cpp +++ b/apps/home/controller.cpp @@ -18,11 +18,11 @@ extern "C" { namespace Home { Controller::ContentView::ContentView(Controller * controller, SelectableTableViewDataSource * selectionDataSource) : - m_selectableTableView(controller, controller, selectionDataSource, controller) + m_selectableTableView(controller, controller, &m_backgroundView, selectionDataSource, controller), + m_backgroundView() { m_selectableTableView.setVerticalCellOverlap(0); m_selectableTableView.setMargins(0, k_sideMargin, k_bottomMargin, k_sideMargin); - m_selectableTableView.setBackgroundColor(Palette::HomeBackground); static_cast(m_selectableTableView.decorator())->verticalBar()->setMargin(k_indicatorMargin); } @@ -31,7 +31,7 @@ SelectableTableView * Controller::ContentView::selectableTableView() { } void Controller::ContentView::drawRect(KDContext * ctx, KDRect rect) const { - ctx->fillRect(bounds(), Palette::HomeBackground); + m_selectableTableView.drawRect(ctx, rect); } void Controller::ContentView::reloadBottomRow(SimpleTableViewDataSource * dataSource, int numberOfIcons, int numberOfColumns) { @@ -45,6 +45,10 @@ void Controller::ContentView::reloadBottomRow(SimpleTableViewDataSource * dataSo } } +BackgroundView * Controller::ContentView::backgroundView() { + return &m_backgroundView; +} + int Controller::ContentView::numberOfSubviews() const { return 1; } @@ -56,6 +60,8 @@ View * Controller::ContentView::subviewAtIndex(int index) { void Controller::ContentView::layoutSubviews(bool force) { m_selectableTableView.setFrame(bounds(), force); + m_backgroundView.setFrame(KDRect(0, Metric::TitleBarHeight, Ion::Display::Width, Ion::Display::Height-Metric::TitleBarHeight), force); + m_backgroundView.updateDataValidity(); } Controller::Controller(Responder * parentResponder, SelectableTableViewDataSource * selectionDataSource, ::App * app) : @@ -63,6 +69,21 @@ Controller::Controller(Responder * parentResponder, SelectableTableViewDataSourc m_view(this, selectionDataSource) { m_app = app; + for (int i = 0; i < k_maxNumberOfCells; i++) { + m_cells[i].setBackgroundView(m_view.backgroundView()); + } + + m_view.backgroundView()->setDefaultColor(Palette::HomeBackground); + + +#ifdef HOME_DISPLAY_EXTERNALS + int index = External::Archive::indexFromName("wallpaper.obm"); + if(index > -1) { + External::Archive::File image; + External::Archive::fileAtIndex(index, image); + m_view.backgroundView()->setBackgroundImage(image.data); + } +#endif } bool Controller::handleEvent(Ion::Events::Event event) { diff --git a/apps/home/controller.h b/apps/home/controller.h index 90a5604a3..3301fac78 100644 --- a/apps/home/controller.h +++ b/apps/home/controller.h @@ -2,6 +2,7 @@ #define HOME_CONTROLLER_H #include +#include "selectable_table_view_with_background.h" #include "app_cell.h" namespace Home { @@ -35,11 +36,13 @@ private: SelectableTableView * selectableTableView(); void drawRect(KDContext * ctx, KDRect rect) const override; void reloadBottomRow(SimpleTableViewDataSource * dataSource, int numberOfIcons, int numberOfColumns); + BackgroundView * backgroundView(); private: int numberOfSubviews() const override; View * subviewAtIndex(int index) override; void layoutSubviews(bool force = false) override; - SelectableTableView m_selectableTableView; + SelectableTableViewWithBackground m_selectableTableView; + BackgroundView m_backgroundView; }; static constexpr KDCoordinate k_sideMargin = 4; static constexpr KDCoordinate k_bottomMargin = 14; diff --git a/apps/home/selectable_table_view_with_background.cpp b/apps/home/selectable_table_view_with_background.cpp new file mode 100644 index 000000000..66854589f --- /dev/null +++ b/apps/home/selectable_table_view_with_background.cpp @@ -0,0 +1,16 @@ +#include "selectable_table_view_with_background.h" + +SelectableTableViewWithBackground::SelectableTableViewWithBackground(Responder * parentResponder, TableViewDataSource * dataSource, BackgroundView * backgroundView, SelectableTableViewDataSource * selectionDataSource, SelectableTableViewDelegate * delegate) : + SelectableTableView(parentResponder, dataSource, selectionDataSource, delegate), + m_backgroundInnerView(this, backgroundView) +{ + +} + +void SelectableTableViewWithBackground::BackgroundInnerView::drawRect(KDContext * ctx, KDRect rect) const { + m_backgroundView->drawRect(ctx, rect); +} + +void SelectableTableViewWithBackground::BackgroundInnerView::setBackgroundView(const uint8_t * data) { + m_backgroundView->setBackgroundImage(data); +} \ No newline at end of file diff --git a/apps/home/selectable_table_view_with_background.h b/apps/home/selectable_table_view_with_background.h new file mode 100644 index 000000000..b722cf20b --- /dev/null +++ b/apps/home/selectable_table_view_with_background.h @@ -0,0 +1,25 @@ +#ifndef ESCHER_SELECTABLE_TABLE_VIEW_WITH_BACKGROUND_H +#define ESCHER_SELECTABLE_TABLE_VIEW_WITH_BACKGROUND_H + +#include +#include + +class SelectableTableViewWithBackground : public SelectableTableView { +public: + SelectableTableViewWithBackground(Responder * parentResponder, TableViewDataSource * dataSource, BackgroundView * backgroundView, + SelectableTableViewDataSource * selectionDataSource = nullptr, SelectableTableViewDelegate * delegate = nullptr); + View * subviewAtIndex(int index) override { return (index == 0) ? &m_backgroundInnerView : decorator()->indicatorAtIndex(index); } +protected: + virtual InnerView * getInnerView() { return &m_backgroundInnerView; } + class BackgroundInnerView : public ScrollView::InnerView { + public: + BackgroundInnerView(ScrollView * scrollView, BackgroundView * backgroundView): InnerView(scrollView), m_backgroundView(backgroundView) {} + void drawRect(KDContext * ctx, KDRect rect) const override; + void setBackgroundView(const uint8_t * data); + private: + BackgroundView * m_backgroundView; + }; + BackgroundInnerView m_backgroundInnerView; +}; + +#endif diff --git a/escher/Makefile b/escher/Makefile index 5a4a0fe87..a8bb8491c 100644 --- a/escher/Makefile +++ b/escher/Makefile @@ -22,6 +22,7 @@ endif escher_src += $(addprefix escher/src/,\ alternate_empty_view_controller.cpp \ app.cpp \ + background_view.cpp \ bank_view_controller.cpp \ bordered.cpp \ buffer_text_view.cpp \ @@ -46,6 +47,7 @@ escher_src += $(addprefix escher/src/,\ expression_view.cpp \ highlight_cell.cpp \ gauge_view.cpp \ + icon_view.cpp \ image_view.cpp \ input_event_handler.cpp \ invocation.cpp \ diff --git a/escher/include/escher.h b/escher/include/escher.h index e07ce1abb..62ceb7e1a 100644 --- a/escher/include/escher.h +++ b/escher/include/escher.h @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -26,6 +27,7 @@ #include #include #include +#include #include #include #include diff --git a/escher/include/escher/background_view.h b/escher/include/escher/background_view.h new file mode 100644 index 000000000..c7db13c69 --- /dev/null +++ b/escher/include/escher/background_view.h @@ -0,0 +1,20 @@ +#ifndef ESCHER_BACKGROUND_VIEW_H +#define ESCHER_BACKGROUND_VIEW_H + +#include +#include + +class BackgroundView : public View { +public: + BackgroundView(); + void drawRect(KDContext * ctx, KDRect rect) const override; + void setBackgroundImage(const uint8_t * data); + void setDefaultColor(KDColor defaultColor); + void updateDataValidity(); +private: + const uint8_t * m_data; + bool m_isDataValid; + KDColor m_defaultColor; +}; + +#endif \ No newline at end of file diff --git a/escher/include/escher/icon_view.h b/escher/include/escher/icon_view.h new file mode 100644 index 000000000..4f03a140a --- /dev/null +++ b/escher/include/escher/icon_view.h @@ -0,0 +1,20 @@ +#ifndef ESCHER_ICON_VIEW_H +#define ESCHER_ICON_VIEW_H + +#include +#include + +class IconView : public View { +//Unlike the ImageView class, IconView displays an image with rounded corners +public: + IconView(); + void setImage(const Image * image); + void setImage(const uint8_t *data, size_t dataLength); + void drawRect(KDContext * ctx, KDRect rect) const override; +private: + const Image * m_image; + const uint8_t * m_data; + size_t m_dataLength; +}; + +#endif diff --git a/escher/include/escher/scroll_view.h b/escher/include/escher/scroll_view.h index 34225273e..8a2761059 100644 --- a/escher/include/escher/scroll_view.h +++ b/escher/include/escher/scroll_view.h @@ -96,25 +96,6 @@ public: void scrollToContentPoint(KDPoint p, bool allowOverscroll = false); void scrollToContentRect(KDRect rect, bool allowOverscroll = false); // Minimal scrolling to make this rect visible protected: - 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(bool force = false) override; - virtual KDSize contentSize() const { return m_contentView->minimalSizeForOptimalDisplay(); } -#if ESCHER_VIEW_LOGGING - const char * className() const override; - void logAttributes(std::ostream &os) const override; -#endif - View * m_contentView; -private: - ScrollViewDataSource * m_dataSource; - 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) {} @@ -127,13 +108,33 @@ private: } const ScrollView * m_scrollView; }; + + 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(bool force = false) override; + virtual KDSize contentSize() const { return m_contentView->minimalSizeForOptimalDisplay(); } + virtual InnerView * getInnerView() { return &m_innerView; } +#if ESCHER_VIEW_LOGGING + const char * className() const override; + void logAttributes(std::ostream &os) const override; +#endif + View * m_contentView; + InnerView m_innerView; +private: + ScrollViewDataSource * m_dataSource; + int numberOfSubviews() const override { return 1 + const_cast(this)->decorator()->numberOfIndicators(); } + View * subviewAtIndex(int index) override { return (index == 0) ? &m_innerView : decorator()->indicatorAtIndex(index); } KDCoordinate m_topMargin; KDCoordinate m_rightMargin; KDCoordinate m_bottomMargin; KDCoordinate m_leftMargin; - InnerView m_innerView; Decorator::Type m_decoratorType; union Decorators { public: diff --git a/escher/src/background_view.cpp b/escher/src/background_view.cpp new file mode 100644 index 000000000..3711185c0 --- /dev/null +++ b/escher/src/background_view.cpp @@ -0,0 +1,65 @@ +#include +#include +#include +#include //AND THIS +#include "apps/home/controller.h" +#ifdef HOME_DISPLAY_EXTERNALS +#include "apps/external/external_icon.h" +#include "apps/external/archive.h" +#include +#endif + +//To store the Omega backgrounds, we use a specific file in the "OmegaBitMap" format. +//Here is its header +struct OBMHeader +{ + uint32_t signature; //Normally it is 32145 + int32_t width; + int32_t height; + const KDColor image_data; +}; + +BackgroundView::BackgroundView(): + m_data(nullptr), + m_isDataValid(false), + m_defaultColor(Palette::BackgroundHard) +{ + +} + +void BackgroundView::setBackgroundImage(const uint8_t * data) { + m_data = data; + updateDataValidity(); + markRectAsDirty(bounds()); +} + +void BackgroundView::setDefaultColor(KDColor defaultColor) { + m_defaultColor = defaultColor; +} + +void BackgroundView::drawRect(KDContext * ctx, KDRect rect) const { + if(!m_isDataValid) { + ctx->fillRect(rect, m_defaultColor); + return; + } + + OBMHeader* h = (OBMHeader*)m_data; + + int yrectToImage = ctx->origin().y() - m_frame.y(); + int xrectToImage = ctx->origin().x() - m_frame.x(); + + for (int line = rect.y(); line <= rect.bottom(); line++) { + int offset = ((line + yrectToImage) * h->width) + (rect.x() + xrectToImage); + ctx->fillRectWithPixels(KDRect(rect.x(), line, rect.width(), 1), &(h->image_data) + offset, nullptr); + } +} + +void BackgroundView::updateDataValidity() { + if(m_data == nullptr || KDIonContext::sharedContext()->gammaEnabled) { + m_isDataValid = false; + return; + } + + OBMHeader* h = (OBMHeader*)m_data; + m_isDataValid = h->signature==466512775 && h->height==m_frame.height() && h->width==m_frame.width(); +} \ No newline at end of file diff --git a/escher/src/icon_view.cpp b/escher/src/icon_view.cpp new file mode 100644 index 000000000..0637d62c6 --- /dev/null +++ b/escher/src/icon_view.cpp @@ -0,0 +1,79 @@ +#include +extern "C" { +#include +} +#include +#include + +IconView::IconView() : + View(), + m_image(nullptr) +{ +} + +constexpr static int iconBufferSize = 3080; +// Icon file is 55 x 56 = 3080 + +void IconView::drawRect(KDContext * ctx, KDRect rect) const { + const uint8_t* data; + size_t size; + + if (m_image != nullptr) { + assert(bounds().width() == m_image->width()); + assert(bounds().height() == m_image->height()); + assert(bounds().width() == 55); + assert(bounds().height() == 56); + + data = m_image->compressedPixelData(); + size = m_image->compressedPixelDataSize(); + } else if (m_data != nullptr) { + data = m_data; + size = m_dataLength; + } else { + return; + } + + KDColor pixelBuffer[iconBufferSize]; + assert(Ion::stackSafe()); // That's a VERY big buffer we're allocating on the stack + + Ion::decompress( + data, + reinterpret_cast(pixelBuffer), + size, + iconBufferSize * sizeof(KDColor) + ); + + //We push the first 6 lines of the image so that they are truncated on the sides + ctx->fillRectWithPixels(KDRect(6, 0, m_frame.width()-12, 1),pixelBuffer+6, nullptr); + ctx->fillRectWithPixels(KDRect(4, 1, m_frame.width()-8, 1),pixelBuffer+6+55, nullptr); + ctx->fillRectWithPixels(KDRect(3, 2, m_frame.width()-6, 1),pixelBuffer+3+(2*55), nullptr); + ctx->fillRectWithPixels(KDRect(2, 3, m_frame.width()-4, 1),pixelBuffer+2+(3*55), nullptr); + ctx->fillRectWithPixels(KDRect(1, 4, m_frame.width()-2, 1),pixelBuffer+1+(4*55), nullptr); + ctx->fillRectWithPixels(KDRect(1, 5, m_frame.width()-2, 1),pixelBuffer+1+(5*55), nullptr); + + //Then we push the rectangular part of the image + ctx->fillRectWithPixels(KDRect(0, 6, m_frame.width(), 44),pixelBuffer+(6*55), nullptr); + + //Finaly we push the last 5 lines of the image so that they are truncated on the sides + ctx->fillRectWithPixels(KDRect(1, 50, m_frame.width()-2, 1),pixelBuffer+1+(50*55), nullptr); + ctx->fillRectWithPixels(KDRect(1, 51, m_frame.width()-2, 1),pixelBuffer+1+(51*55), nullptr); + ctx->fillRectWithPixels(KDRect(2, 52, m_frame.width()-4, 1),pixelBuffer+2+(52*55), nullptr); + ctx->fillRectWithPixels(KDRect(3, 53, m_frame.width()-6, 1),pixelBuffer+3+(53*55), nullptr); + ctx->fillRectWithPixels(KDRect(4, 54, m_frame.width()-8, 1),pixelBuffer+4+(54*55), nullptr); + ctx->fillRectWithPixels(KDRect(6, 55, m_frame.width()-12, 1),pixelBuffer+6+(55*55), nullptr); +} + +void IconView::setImage(const uint8_t *data, size_t dataLength) { + if (data != m_data && dataLength != m_dataLength) { + m_data = data; + m_dataLength = dataLength; + markRectAsDirty(bounds()); + } +} + +void IconView::setImage(const Image * image) { + if (image != m_image) { + m_image = image; + markRectAsDirty(bounds()); + } +} diff --git a/escher/src/scroll_view.cpp b/escher/src/scroll_view.cpp index 9cb783d99..a653ae4cf 100644 --- a/escher/src/scroll_view.cpp +++ b/escher/src/scroll_view.cpp @@ -9,12 +9,12 @@ extern "C" { ScrollView::ScrollView(View * contentView, ScrollViewDataSource * dataSource) : View(), m_contentView(contentView), + m_innerView(this), m_dataSource(dataSource), m_topMargin(0), m_rightMargin(0), m_bottomMargin(0), m_leftMargin(0), - m_innerView(this), m_decorators(), m_backgroundColor(Palette::BackgroundApps) { @@ -24,12 +24,12 @@ ScrollView::ScrollView(View * contentView, ScrollViewDataSource * dataSource) : ScrollView::ScrollView(ScrollView&& other) : m_contentView(other.m_contentView), + m_innerView(this), 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); @@ -144,7 +144,7 @@ void ScrollView::layoutSubviews(bool force) { if (!r2.isEmpty()) { markRectAsDirty(r2); } - m_innerView.setFrame(innerFrame, force); + getInnerView()->setFrame(innerFrame, force); KDPoint absoluteOffset = contentOffset().opposite().translatedBy(KDPoint(m_leftMargin - innerFrame.x(), m_topMargin - innerFrame.y())); KDRect contentFrame = KDRect(absoluteOffset, contentSize()); m_contentView->setFrame(contentFrame, force); From dc8a1d48d9e29fd2fc87ddd8075f6d7047a1aadb Mon Sep 17 00:00:00 2001 From: Maxime FRIESS Date: Tue, 6 Jul 2021 16:23:28 +0200 Subject: [PATCH 27/48] [escher] Remove unnecessary include --- escher/include/escher/background_view.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/escher/include/escher/background_view.h b/escher/include/escher/background_view.h index c7db13c69..697d7f27d 100644 --- a/escher/include/escher/background_view.h +++ b/escher/include/escher/background_view.h @@ -2,7 +2,6 @@ #define ESCHER_BACKGROUND_VIEW_H #include -#include class BackgroundView : public View { public: @@ -17,4 +16,4 @@ private: KDColor m_defaultColor; }; -#endif \ No newline at end of file +#endif From 0366c4cd00c9d55bcf8ce2e2ba6bf251b3179386 Mon Sep 17 00:00:00 2001 From: Laury Date: Tue, 6 Jul 2021 14:54:55 +0200 Subject: [PATCH 28/48] [home] Added support for a wallpaper in a special format (.obm) --- apps/home/Makefile | 1 + apps/home/app_cell.cpp | 9 ++- apps/home/app_cell.h | 4 +- apps/home/controller.cpp | 27 ++++++- apps/home/controller.h | 5 +- .../selectable_table_view_with_background.cpp | 16 ++++ .../selectable_table_view_with_background.h | 25 ++++++ escher/Makefile | 2 + escher/include/escher.h | 2 + escher/include/escher/background_view.h | 19 +++++ escher/include/escher/icon_view.h | 20 +++++ escher/include/escher/scroll_view.h | 41 +++++----- escher/src/background_view.cpp | 65 +++++++++++++++ escher/src/icon_view.cpp | 79 +++++++++++++++++++ escher/src/scroll_view.cpp | 6 +- 15 files changed, 291 insertions(+), 30 deletions(-) create mode 100644 apps/home/selectable_table_view_with_background.cpp create mode 100644 apps/home/selectable_table_view_with_background.h create mode 100644 escher/include/escher/background_view.h create mode 100644 escher/include/escher/icon_view.h create mode 100644 escher/src/background_view.cpp create mode 100644 escher/src/icon_view.cpp diff --git a/apps/home/Makefile b/apps/home/Makefile index c76c45808..880302a52 100644 --- a/apps/home/Makefile +++ b/apps/home/Makefile @@ -3,6 +3,7 @@ app_home_src = $(addprefix apps/home/,\ app_cell.cpp \ apps_layout.py \ controller.cpp \ + selectable_table_view_with_background.cpp \ ) apps_src += $(app_home_src) diff --git a/apps/home/app_cell.cpp b/apps/home/app_cell.cpp index a6e5a1554..777bc14f6 100644 --- a/apps/home/app_cell.cpp +++ b/apps/home/app_cell.cpp @@ -8,6 +8,7 @@ namespace Home { AppCell::AppCell() : HighlightCell(), m_nameView(KDFont::SmallFont, (I18n::Message)0, 0.5f, 0.5f, Palette::HomeCellText, Palette::HomeCellBackground), + m_backgroundView(nullptr), m_visible(true), m_external_app(false) { } @@ -15,8 +16,8 @@ AppCell::AppCell() : void AppCell::drawRect(KDContext * ctx, KDRect rect) const { KDSize nameSize = m_nameView.minimalSizeForOptimalDisplay(); - ctx->fillRect(KDRect(0, bounds().height()-nameSize.height() - 2*k_nameHeightMargin, bounds().width(), nameSize.height()+2*k_nameHeightMargin), Palette::HomeBackground); -} + m_backgroundView->drawRect(ctx, KDRect(0, bounds().height()-nameSize.height() - 2*k_nameHeightMargin, bounds().width(), nameSize.height()+2*k_nameHeightMargin)); + } int AppCell::numberOfSubviews() const { return m_visible ? 2 : 0; @@ -70,6 +71,10 @@ void AppCell::setVisible(bool visible) { } } +void AppCell::setBackgroundView(const BackgroundView * backgroundView) { + m_backgroundView = backgroundView; +} + void AppCell::reloadCell() { m_nameView.setTextColor(isHighlighted() ? (m_external_app ? Palette::HomeCellTextExternalActive : Palette::HomeCellTextActive) : (m_external_app ? Palette::HomeCellTextExternal : Palette::HomeCellText)); m_nameView.setBackgroundColor(isHighlighted() ? Palette::HomeCellBackgroundActive : Palette::HomeCellBackground); diff --git a/apps/home/app_cell.h b/apps/home/app_cell.h index 89c22e008..f9ff029bd 100644 --- a/apps/home/app_cell.h +++ b/apps/home/app_cell.h @@ -15,6 +15,7 @@ public: void layoutSubviews(bool force = false) override; void setVisible(bool visible); + void setBackgroundView(const BackgroundView * backgroundView); void reloadCell() override; void setAppDescriptor(::App::Descriptor * appDescriptor); void setExtAppDescriptor(const char* name, const Image* icon); @@ -25,8 +26,9 @@ private: static constexpr KDCoordinate k_iconHeight = 56; static constexpr KDCoordinate k_nameWidthMargin = 4; static constexpr KDCoordinate k_nameHeightMargin = 1; - ImageView m_iconView; + IconView m_iconView; MessageTextView m_nameView; + const BackgroundView * m_backgroundView; bool m_visible; bool m_external_app; }; diff --git a/apps/home/controller.cpp b/apps/home/controller.cpp index 2724bd172..df725f10f 100644 --- a/apps/home/controller.cpp +++ b/apps/home/controller.cpp @@ -18,11 +18,11 @@ extern "C" { namespace Home { Controller::ContentView::ContentView(Controller * controller, SelectableTableViewDataSource * selectionDataSource) : - m_selectableTableView(controller, controller, selectionDataSource, controller) + m_selectableTableView(controller, controller, &m_backgroundView, selectionDataSource, controller), + m_backgroundView() { m_selectableTableView.setVerticalCellOverlap(0); m_selectableTableView.setMargins(0, k_sideMargin, k_bottomMargin, k_sideMargin); - m_selectableTableView.setBackgroundColor(Palette::HomeBackground); static_cast(m_selectableTableView.decorator())->verticalBar()->setMargin(k_indicatorMargin); } @@ -31,7 +31,7 @@ SelectableTableView * Controller::ContentView::selectableTableView() { } void Controller::ContentView::drawRect(KDContext * ctx, KDRect rect) const { - ctx->fillRect(bounds(), Palette::HomeBackground); + m_selectableTableView.drawRect(ctx, rect); } void Controller::ContentView::reloadBottomRow(SimpleTableViewDataSource * dataSource, int numberOfIcons, int numberOfColumns) { @@ -45,6 +45,10 @@ void Controller::ContentView::reloadBottomRow(SimpleTableViewDataSource * dataSo } } +BackgroundView * Controller::ContentView::backgroundView() { + return &m_backgroundView; +} + int Controller::ContentView::numberOfSubviews() const { return 1; } @@ -56,6 +60,8 @@ View * Controller::ContentView::subviewAtIndex(int index) { void Controller::ContentView::layoutSubviews(bool force) { m_selectableTableView.setFrame(bounds(), force); + m_backgroundView.setFrame(KDRect(0, Metric::TitleBarHeight, Ion::Display::Width, Ion::Display::Height-Metric::TitleBarHeight), force); + m_backgroundView.updateDataValidity(); } Controller::Controller(Responder * parentResponder, SelectableTableViewDataSource * selectionDataSource, ::App * app) : @@ -63,6 +69,21 @@ Controller::Controller(Responder * parentResponder, SelectableTableViewDataSourc m_view(this, selectionDataSource) { m_app = app; + for (int i = 0; i < k_maxNumberOfCells; i++) { + m_cells[i].setBackgroundView(m_view.backgroundView()); + } + + m_view.backgroundView()->setDefaultColor(Palette::HomeBackground); + + +#ifdef HOME_DISPLAY_EXTERNALS + int index = External::Archive::indexFromName("wallpaper.obm"); + if(index > -1) { + External::Archive::File image; + External::Archive::fileAtIndex(index, image); + m_view.backgroundView()->setBackgroundImage(image.data); + } +#endif } bool Controller::handleEvent(Ion::Events::Event event) { diff --git a/apps/home/controller.h b/apps/home/controller.h index 90a5604a3..3301fac78 100644 --- a/apps/home/controller.h +++ b/apps/home/controller.h @@ -2,6 +2,7 @@ #define HOME_CONTROLLER_H #include +#include "selectable_table_view_with_background.h" #include "app_cell.h" namespace Home { @@ -35,11 +36,13 @@ private: SelectableTableView * selectableTableView(); void drawRect(KDContext * ctx, KDRect rect) const override; void reloadBottomRow(SimpleTableViewDataSource * dataSource, int numberOfIcons, int numberOfColumns); + BackgroundView * backgroundView(); private: int numberOfSubviews() const override; View * subviewAtIndex(int index) override; void layoutSubviews(bool force = false) override; - SelectableTableView m_selectableTableView; + SelectableTableViewWithBackground m_selectableTableView; + BackgroundView m_backgroundView; }; static constexpr KDCoordinate k_sideMargin = 4; static constexpr KDCoordinate k_bottomMargin = 14; diff --git a/apps/home/selectable_table_view_with_background.cpp b/apps/home/selectable_table_view_with_background.cpp new file mode 100644 index 000000000..66854589f --- /dev/null +++ b/apps/home/selectable_table_view_with_background.cpp @@ -0,0 +1,16 @@ +#include "selectable_table_view_with_background.h" + +SelectableTableViewWithBackground::SelectableTableViewWithBackground(Responder * parentResponder, TableViewDataSource * dataSource, BackgroundView * backgroundView, SelectableTableViewDataSource * selectionDataSource, SelectableTableViewDelegate * delegate) : + SelectableTableView(parentResponder, dataSource, selectionDataSource, delegate), + m_backgroundInnerView(this, backgroundView) +{ + +} + +void SelectableTableViewWithBackground::BackgroundInnerView::drawRect(KDContext * ctx, KDRect rect) const { + m_backgroundView->drawRect(ctx, rect); +} + +void SelectableTableViewWithBackground::BackgroundInnerView::setBackgroundView(const uint8_t * data) { + m_backgroundView->setBackgroundImage(data); +} \ No newline at end of file diff --git a/apps/home/selectable_table_view_with_background.h b/apps/home/selectable_table_view_with_background.h new file mode 100644 index 000000000..b722cf20b --- /dev/null +++ b/apps/home/selectable_table_view_with_background.h @@ -0,0 +1,25 @@ +#ifndef ESCHER_SELECTABLE_TABLE_VIEW_WITH_BACKGROUND_H +#define ESCHER_SELECTABLE_TABLE_VIEW_WITH_BACKGROUND_H + +#include +#include + +class SelectableTableViewWithBackground : public SelectableTableView { +public: + SelectableTableViewWithBackground(Responder * parentResponder, TableViewDataSource * dataSource, BackgroundView * backgroundView, + SelectableTableViewDataSource * selectionDataSource = nullptr, SelectableTableViewDelegate * delegate = nullptr); + View * subviewAtIndex(int index) override { return (index == 0) ? &m_backgroundInnerView : decorator()->indicatorAtIndex(index); } +protected: + virtual InnerView * getInnerView() { return &m_backgroundInnerView; } + class BackgroundInnerView : public ScrollView::InnerView { + public: + BackgroundInnerView(ScrollView * scrollView, BackgroundView * backgroundView): InnerView(scrollView), m_backgroundView(backgroundView) {} + void drawRect(KDContext * ctx, KDRect rect) const override; + void setBackgroundView(const uint8_t * data); + private: + BackgroundView * m_backgroundView; + }; + BackgroundInnerView m_backgroundInnerView; +}; + +#endif diff --git a/escher/Makefile b/escher/Makefile index 5a4a0fe87..a8bb8491c 100644 --- a/escher/Makefile +++ b/escher/Makefile @@ -22,6 +22,7 @@ endif escher_src += $(addprefix escher/src/,\ alternate_empty_view_controller.cpp \ app.cpp \ + background_view.cpp \ bank_view_controller.cpp \ bordered.cpp \ buffer_text_view.cpp \ @@ -46,6 +47,7 @@ escher_src += $(addprefix escher/src/,\ expression_view.cpp \ highlight_cell.cpp \ gauge_view.cpp \ + icon_view.cpp \ image_view.cpp \ input_event_handler.cpp \ invocation.cpp \ diff --git a/escher/include/escher.h b/escher/include/escher.h index e07ce1abb..62ceb7e1a 100644 --- a/escher/include/escher.h +++ b/escher/include/escher.h @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -26,6 +27,7 @@ #include #include #include +#include #include #include #include diff --git a/escher/include/escher/background_view.h b/escher/include/escher/background_view.h new file mode 100644 index 000000000..697d7f27d --- /dev/null +++ b/escher/include/escher/background_view.h @@ -0,0 +1,19 @@ +#ifndef ESCHER_BACKGROUND_VIEW_H +#define ESCHER_BACKGROUND_VIEW_H + +#include + +class BackgroundView : public View { +public: + BackgroundView(); + void drawRect(KDContext * ctx, KDRect rect) const override; + void setBackgroundImage(const uint8_t * data); + void setDefaultColor(KDColor defaultColor); + void updateDataValidity(); +private: + const uint8_t * m_data; + bool m_isDataValid; + KDColor m_defaultColor; +}; + +#endif diff --git a/escher/include/escher/icon_view.h b/escher/include/escher/icon_view.h new file mode 100644 index 000000000..4f03a140a --- /dev/null +++ b/escher/include/escher/icon_view.h @@ -0,0 +1,20 @@ +#ifndef ESCHER_ICON_VIEW_H +#define ESCHER_ICON_VIEW_H + +#include +#include + +class IconView : public View { +//Unlike the ImageView class, IconView displays an image with rounded corners +public: + IconView(); + void setImage(const Image * image); + void setImage(const uint8_t *data, size_t dataLength); + void drawRect(KDContext * ctx, KDRect rect) const override; +private: + const Image * m_image; + const uint8_t * m_data; + size_t m_dataLength; +}; + +#endif diff --git a/escher/include/escher/scroll_view.h b/escher/include/escher/scroll_view.h index 34225273e..8a2761059 100644 --- a/escher/include/escher/scroll_view.h +++ b/escher/include/escher/scroll_view.h @@ -96,25 +96,6 @@ public: void scrollToContentPoint(KDPoint p, bool allowOverscroll = false); void scrollToContentRect(KDRect rect, bool allowOverscroll = false); // Minimal scrolling to make this rect visible protected: - 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(bool force = false) override; - virtual KDSize contentSize() const { return m_contentView->minimalSizeForOptimalDisplay(); } -#if ESCHER_VIEW_LOGGING - const char * className() const override; - void logAttributes(std::ostream &os) const override; -#endif - View * m_contentView; -private: - ScrollViewDataSource * m_dataSource; - 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) {} @@ -127,13 +108,33 @@ private: } const ScrollView * m_scrollView; }; + + 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(bool force = false) override; + virtual KDSize contentSize() const { return m_contentView->minimalSizeForOptimalDisplay(); } + virtual InnerView * getInnerView() { return &m_innerView; } +#if ESCHER_VIEW_LOGGING + const char * className() const override; + void logAttributes(std::ostream &os) const override; +#endif + View * m_contentView; + InnerView m_innerView; +private: + ScrollViewDataSource * m_dataSource; + int numberOfSubviews() const override { return 1 + const_cast(this)->decorator()->numberOfIndicators(); } + View * subviewAtIndex(int index) override { return (index == 0) ? &m_innerView : decorator()->indicatorAtIndex(index); } KDCoordinate m_topMargin; KDCoordinate m_rightMargin; KDCoordinate m_bottomMargin; KDCoordinate m_leftMargin; - InnerView m_innerView; Decorator::Type m_decoratorType; union Decorators { public: diff --git a/escher/src/background_view.cpp b/escher/src/background_view.cpp new file mode 100644 index 000000000..3711185c0 --- /dev/null +++ b/escher/src/background_view.cpp @@ -0,0 +1,65 @@ +#include +#include +#include +#include //AND THIS +#include "apps/home/controller.h" +#ifdef HOME_DISPLAY_EXTERNALS +#include "apps/external/external_icon.h" +#include "apps/external/archive.h" +#include +#endif + +//To store the Omega backgrounds, we use a specific file in the "OmegaBitMap" format. +//Here is its header +struct OBMHeader +{ + uint32_t signature; //Normally it is 32145 + int32_t width; + int32_t height; + const KDColor image_data; +}; + +BackgroundView::BackgroundView(): + m_data(nullptr), + m_isDataValid(false), + m_defaultColor(Palette::BackgroundHard) +{ + +} + +void BackgroundView::setBackgroundImage(const uint8_t * data) { + m_data = data; + updateDataValidity(); + markRectAsDirty(bounds()); +} + +void BackgroundView::setDefaultColor(KDColor defaultColor) { + m_defaultColor = defaultColor; +} + +void BackgroundView::drawRect(KDContext * ctx, KDRect rect) const { + if(!m_isDataValid) { + ctx->fillRect(rect, m_defaultColor); + return; + } + + OBMHeader* h = (OBMHeader*)m_data; + + int yrectToImage = ctx->origin().y() - m_frame.y(); + int xrectToImage = ctx->origin().x() - m_frame.x(); + + for (int line = rect.y(); line <= rect.bottom(); line++) { + int offset = ((line + yrectToImage) * h->width) + (rect.x() + xrectToImage); + ctx->fillRectWithPixels(KDRect(rect.x(), line, rect.width(), 1), &(h->image_data) + offset, nullptr); + } +} + +void BackgroundView::updateDataValidity() { + if(m_data == nullptr || KDIonContext::sharedContext()->gammaEnabled) { + m_isDataValid = false; + return; + } + + OBMHeader* h = (OBMHeader*)m_data; + m_isDataValid = h->signature==466512775 && h->height==m_frame.height() && h->width==m_frame.width(); +} \ No newline at end of file diff --git a/escher/src/icon_view.cpp b/escher/src/icon_view.cpp new file mode 100644 index 000000000..0637d62c6 --- /dev/null +++ b/escher/src/icon_view.cpp @@ -0,0 +1,79 @@ +#include +extern "C" { +#include +} +#include +#include + +IconView::IconView() : + View(), + m_image(nullptr) +{ +} + +constexpr static int iconBufferSize = 3080; +// Icon file is 55 x 56 = 3080 + +void IconView::drawRect(KDContext * ctx, KDRect rect) const { + const uint8_t* data; + size_t size; + + if (m_image != nullptr) { + assert(bounds().width() == m_image->width()); + assert(bounds().height() == m_image->height()); + assert(bounds().width() == 55); + assert(bounds().height() == 56); + + data = m_image->compressedPixelData(); + size = m_image->compressedPixelDataSize(); + } else if (m_data != nullptr) { + data = m_data; + size = m_dataLength; + } else { + return; + } + + KDColor pixelBuffer[iconBufferSize]; + assert(Ion::stackSafe()); // That's a VERY big buffer we're allocating on the stack + + Ion::decompress( + data, + reinterpret_cast(pixelBuffer), + size, + iconBufferSize * sizeof(KDColor) + ); + + //We push the first 6 lines of the image so that they are truncated on the sides + ctx->fillRectWithPixels(KDRect(6, 0, m_frame.width()-12, 1),pixelBuffer+6, nullptr); + ctx->fillRectWithPixels(KDRect(4, 1, m_frame.width()-8, 1),pixelBuffer+6+55, nullptr); + ctx->fillRectWithPixels(KDRect(3, 2, m_frame.width()-6, 1),pixelBuffer+3+(2*55), nullptr); + ctx->fillRectWithPixels(KDRect(2, 3, m_frame.width()-4, 1),pixelBuffer+2+(3*55), nullptr); + ctx->fillRectWithPixels(KDRect(1, 4, m_frame.width()-2, 1),pixelBuffer+1+(4*55), nullptr); + ctx->fillRectWithPixels(KDRect(1, 5, m_frame.width()-2, 1),pixelBuffer+1+(5*55), nullptr); + + //Then we push the rectangular part of the image + ctx->fillRectWithPixels(KDRect(0, 6, m_frame.width(), 44),pixelBuffer+(6*55), nullptr); + + //Finaly we push the last 5 lines of the image so that they are truncated on the sides + ctx->fillRectWithPixels(KDRect(1, 50, m_frame.width()-2, 1),pixelBuffer+1+(50*55), nullptr); + ctx->fillRectWithPixels(KDRect(1, 51, m_frame.width()-2, 1),pixelBuffer+1+(51*55), nullptr); + ctx->fillRectWithPixels(KDRect(2, 52, m_frame.width()-4, 1),pixelBuffer+2+(52*55), nullptr); + ctx->fillRectWithPixels(KDRect(3, 53, m_frame.width()-6, 1),pixelBuffer+3+(53*55), nullptr); + ctx->fillRectWithPixels(KDRect(4, 54, m_frame.width()-8, 1),pixelBuffer+4+(54*55), nullptr); + ctx->fillRectWithPixels(KDRect(6, 55, m_frame.width()-12, 1),pixelBuffer+6+(55*55), nullptr); +} + +void IconView::setImage(const uint8_t *data, size_t dataLength) { + if (data != m_data && dataLength != m_dataLength) { + m_data = data; + m_dataLength = dataLength; + markRectAsDirty(bounds()); + } +} + +void IconView::setImage(const Image * image) { + if (image != m_image) { + m_image = image; + markRectAsDirty(bounds()); + } +} diff --git a/escher/src/scroll_view.cpp b/escher/src/scroll_view.cpp index 9cb783d99..a653ae4cf 100644 --- a/escher/src/scroll_view.cpp +++ b/escher/src/scroll_view.cpp @@ -9,12 +9,12 @@ extern "C" { ScrollView::ScrollView(View * contentView, ScrollViewDataSource * dataSource) : View(), m_contentView(contentView), + m_innerView(this), m_dataSource(dataSource), m_topMargin(0), m_rightMargin(0), m_bottomMargin(0), m_leftMargin(0), - m_innerView(this), m_decorators(), m_backgroundColor(Palette::BackgroundApps) { @@ -24,12 +24,12 @@ ScrollView::ScrollView(View * contentView, ScrollViewDataSource * dataSource) : ScrollView::ScrollView(ScrollView&& other) : m_contentView(other.m_contentView), + m_innerView(this), 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); @@ -144,7 +144,7 @@ void ScrollView::layoutSubviews(bool force) { if (!r2.isEmpty()) { markRectAsDirty(r2); } - m_innerView.setFrame(innerFrame, force); + getInnerView()->setFrame(innerFrame, force); KDPoint absoluteOffset = contentOffset().opposite().translatedBy(KDPoint(m_leftMargin - innerFrame.x(), m_topMargin - innerFrame.y())); KDRect contentFrame = KDRect(absoluteOffset, contentSize()); m_contentView->setFrame(contentFrame, force); From ef45fab63d7f3229250edd8d0b698cf9ce6db9a9 Mon Sep 17 00:00:00 2001 From: Fevret Date: Tue, 29 Jun 2021 21:00:09 +0200 Subject: [PATCH 29/48] [themes] Improved epsilon-dark icons --- themes/themes/local/epsilon_dark.json | 2 +- .../local/epsilon_dark/apps/atomic_icon.png | Bin 0 -> 3240 bytes .../epsilon_dark/apps/calculation_icon.png | Bin 0 -> 985 bytes .../local/epsilon_dark/apps/code_icon.png | Bin 0 -> 1680 bytes .../local/epsilon_dark/apps/external_icon.png | Bin 0 -> 2553 bytes .../local/epsilon_dark/apps/graph_icon.png | Bin 0 -> 1094 bytes .../epsilon_dark/apps/probability_icon.png | Bin 0 -> 1738 bytes .../local/epsilon_dark/apps/regression_icon.png | Bin 0 -> 1482 bytes .../themes/local/epsilon_dark/apps/rpn_icon.png | Bin 0 -> 1337 bytes .../local/epsilon_dark/apps/sequence_icon.png | Bin 0 -> 633 bytes .../local/epsilon_dark/apps/settings_icon.png | Bin 0 -> 1693 bytes .../local/epsilon_dark/apps/solver_icon.png | Bin 0 -> 2900 bytes .../local/epsilon_dark/apps/stat_icon.png | Bin 0 -> 1340 bytes themes/themes/local/epsilon_dark/exam_icon.png | Bin 0 -> 550 bytes themes/themes/local/epsilon_dark/logo_icon.png | Bin 0 -> 8619 bytes .../epsilon_dark/probability/binomial_icon.png | Bin 0 -> 29317 bytes .../epsilon_dark/probability/calcul1_icon.png | Bin 0 -> 54562 bytes .../epsilon_dark/probability/calcul2_icon.png | Bin 0 -> 54116 bytes .../epsilon_dark/probability/calcul3_icon.png | Bin 0 -> 53757 bytes .../epsilon_dark/probability/calcul4_icon.png | Bin 0 -> 53389 bytes .../probability/chi_squared_icon.png | Bin 0 -> 28508 bytes .../probability/exponential_icon.png | Bin 0 -> 31374 bytes .../epsilon_dark/probability/fisher_icon.png | Bin 0 -> 367 bytes .../probability/focused_binomial_icon.png | Bin 0 -> 40684 bytes .../probability/focused_calcul1_icon.png | Bin 0 -> 55124 bytes .../probability/focused_calcul2_icon.png | Bin 0 -> 55587 bytes .../probability/focused_calcul3_icon.png | Bin 0 -> 55933 bytes .../probability/focused_calcul4_icon.png | Bin 0 -> 56315 bytes .../probability/focused_chi_squared_icon.png | Bin 0 -> 28150 bytes .../probability/focused_exponential_icon.png | Bin 0 -> 39927 bytes .../probability/focused_fisher_icon.png | Bin 0 -> 405 bytes .../probability/focused_geometric_icon.png | Bin 0 -> 83138 bytes .../probability/focused_normal_icon.png | Bin 0 -> 39664 bytes .../probability/focused_poisson_icon.png | Bin 0 -> 39002 bytes .../probability/focused_student_icon.png | Bin 0 -> 27754 bytes .../probability/focused_uniform_icon.png | Bin 0 -> 40270 bytes .../epsilon_dark/probability/geometric_icon.png | Bin 0 -> 80419 bytes .../epsilon_dark/probability/normal_icon.png | Bin 0 -> 31826 bytes .../epsilon_dark/probability/poisson_icon.png | Bin 0 -> 30799 bytes .../epsilon_dark/probability/student_icon.png | Bin 0 -> 27348 bytes .../epsilon_dark/probability/uniform_icon.png | Bin 0 -> 29675 bytes 41 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 themes/themes/local/epsilon_dark/apps/atomic_icon.png create mode 100644 themes/themes/local/epsilon_dark/apps/calculation_icon.png create mode 100644 themes/themes/local/epsilon_dark/apps/code_icon.png create mode 100644 themes/themes/local/epsilon_dark/apps/external_icon.png create mode 100644 themes/themes/local/epsilon_dark/apps/graph_icon.png create mode 100644 themes/themes/local/epsilon_dark/apps/probability_icon.png create mode 100644 themes/themes/local/epsilon_dark/apps/regression_icon.png create mode 100644 themes/themes/local/epsilon_dark/apps/rpn_icon.png create mode 100644 themes/themes/local/epsilon_dark/apps/sequence_icon.png create mode 100644 themes/themes/local/epsilon_dark/apps/settings_icon.png create mode 100644 themes/themes/local/epsilon_dark/apps/solver_icon.png create mode 100644 themes/themes/local/epsilon_dark/apps/stat_icon.png create mode 100644 themes/themes/local/epsilon_dark/exam_icon.png create mode 100644 themes/themes/local/epsilon_dark/logo_icon.png create mode 100644 themes/themes/local/epsilon_dark/probability/binomial_icon.png create mode 100644 themes/themes/local/epsilon_dark/probability/calcul1_icon.png create mode 100644 themes/themes/local/epsilon_dark/probability/calcul2_icon.png create mode 100644 themes/themes/local/epsilon_dark/probability/calcul3_icon.png create mode 100644 themes/themes/local/epsilon_dark/probability/calcul4_icon.png create mode 100644 themes/themes/local/epsilon_dark/probability/chi_squared_icon.png create mode 100644 themes/themes/local/epsilon_dark/probability/exponential_icon.png create mode 100644 themes/themes/local/epsilon_dark/probability/fisher_icon.png create mode 100644 themes/themes/local/epsilon_dark/probability/focused_binomial_icon.png create mode 100644 themes/themes/local/epsilon_dark/probability/focused_calcul1_icon.png create mode 100644 themes/themes/local/epsilon_dark/probability/focused_calcul2_icon.png create mode 100644 themes/themes/local/epsilon_dark/probability/focused_calcul3_icon.png create mode 100644 themes/themes/local/epsilon_dark/probability/focused_calcul4_icon.png create mode 100644 themes/themes/local/epsilon_dark/probability/focused_chi_squared_icon.png create mode 100644 themes/themes/local/epsilon_dark/probability/focused_exponential_icon.png create mode 100644 themes/themes/local/epsilon_dark/probability/focused_fisher_icon.png create mode 100644 themes/themes/local/epsilon_dark/probability/focused_geometric_icon.png create mode 100644 themes/themes/local/epsilon_dark/probability/focused_normal_icon.png create mode 100644 themes/themes/local/epsilon_dark/probability/focused_poisson_icon.png create mode 100644 themes/themes/local/epsilon_dark/probability/focused_student_icon.png create mode 100644 themes/themes/local/epsilon_dark/probability/focused_uniform_icon.png create mode 100644 themes/themes/local/epsilon_dark/probability/geometric_icon.png create mode 100644 themes/themes/local/epsilon_dark/probability/normal_icon.png create mode 100644 themes/themes/local/epsilon_dark/probability/poisson_icon.png create mode 100644 themes/themes/local/epsilon_dark/probability/student_icon.png create mode 100644 themes/themes/local/epsilon_dark/probability/uniform_icon.png diff --git a/themes/themes/local/epsilon_dark.json b/themes/themes/local/epsilon_dark.json index 665d2d0b8..22c951642 100644 --- a/themes/themes/local/epsilon_dark.json +++ b/themes/themes/local/epsilon_dark.json @@ -1,6 +1,6 @@ { "name": "Epsilon Dark", - "icons": "epsilon_light", + "icons": "epsilon_dark", "colors": { "PrimaryText": "ffffff", "SecondaryText": "585858", diff --git a/themes/themes/local/epsilon_dark/apps/atomic_icon.png b/themes/themes/local/epsilon_dark/apps/atomic_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..aefe2fa424f17ba3155c9b965e210b8057e13af8 GIT binary patch literal 3240 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 Hu0mjfrikMt literal 0 HcmV?d00001 diff --git a/themes/themes/local/epsilon_dark/apps/code_icon.png b/themes/themes/local/epsilon_dark/apps/code_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..3b7f629fbd5c514ce5a17c93df10d7c7519080b1 GIT binary patch literal 1680 zcmV;B25P`JHnQ0YEVWm&=95#zt)2x)lzG1EMI-EVATCk_3;(gXZRD96EFe9UUFBBuz7C zGMRAX$Pw(?wJYh&XmRGu8SLG=7okvSvgD+>yu3WLwzk48LOzMQ0}0>yc|D%{HZX8@gB(hFi2Th8NGh}`jU&{O?7oO#KVUV z?_aZK&C}%!Kw)7ah{xlhHk&O8NtCa7& zV7|!c=%~7a1dwHl)8GAw_8aaw$5@rS0()NF30r0cH-lV$s*6|JCXJcRCaiYmLlE?Z z?jP{t^J8Bl5|MaFQZVoca^vPLRu!+q-xBuVBOUN>CLVcZ~lB?J5>mL!DBoqPYCM$IzA zfdD={_}QZV0tuTN9Q2{>h8x|zeRv$y|EHfAXcB^kA#{HgU2aZfP=XmWtr*l+$#NTh z=9qO}P2?z%?E2)~heC-ci=@U1o#{9_slm4soGk3e={-XOzvsggF z31l__?G;E|bB`Z~;Pd~hTikqE>na8b{H+;1)y~ zOF{zxji{E1Yc3ENnVZ5nQdr=?8?Wqu!E*{E*qvAqDX4lm?mRa-s$MZ3xhr#E&(DR? zkXU|TwWgt{z=6^dJA|$;A@qI=kOZ?rIjnj)t^&5fqX#Ci;rY$5=VgNg`;>_|I{C(d zP0nmo3=e}mx(d>M4kXmCUS|e`O0%EhBw{YrVotb`^_6AVy>lB3y{92uIt9oxyR@t$9BFS9UMR4>Ohmu1UTcCyr9H>j+%1j1m;f_jJrwG;tFeb7=JmWm4K+wZQupVgECe6~S<|->nRVy0|&{WM-x>x^OL&yOy$2 zvz)3qHWBaMxt;4C{(PaL8FJxk2}RLVT~f3f`}VvHNB*iMwgpgg0p#-ipr*vM=z0*s z9!CX(Ptr0$5V3MaHpI3!LH@h@WFnq5&k2B-2C3@cl$O=z!Wh>?GpI?sD^5k%?pH>- zRe8+`c?_iY`x)k9WG5CUWcv$qYhym<<^_#F82n*MgxKj9&D;oZGWS$($}XWf0aDoq zh}3=q`I)yDs0A)jjJm7P>{`(58YP92S3L)s<{FR1@0=MQbEyUT>#%KtiPabl17Uyk zV`?E6N#Hf0$o3r&y+1?GJ|G$GfOz`=sI3~r`y0rZ4-iv9^SASm6m26A2v8nJ4uyeV zAl&&Vy7Z4V&uJ}I4OU2N-v!OAnzt5=A6X<_U0pOMCx=Tw67)cLa16wMVMa2oVooqa z&fNyt`67^#%|!>p=ktM_IB|kDG&FDvst^u<4BrF^xIsq#0m8oMMk^6O4OYNp2erCD zGuDCHssKTK-8dJTnwmgdE|+q!Eg4V4&K1@r*}*n}9iKgW_U!U>5eMdn`BDy(8jVJ@ zx3{CXxHu`r>Y=l<6B{;cKsX$Z&U5TTeSLk*L(t0!rl0v>elT?s_jFoLEnlE#T0AGKv!7Ij&Yb=cNPiMlQEK8qQN3@MW4l9FuY0~X}W z&ip&y&dfjm|L@O8B$6vQN#$yc*(fQ_+t^BIfui6Kp>EjP%_J(UAu|LPl(v zmPQMVM*V>xM?iS7yE((0E*E^;OF$;$v6~mQ*C4zYm&^6SHQCu^#p2RpLjC5Z3nSxG zafI@640L(}$;%bGv$`U_-FpVt}zyhZ&narP6p2HXRN~Bx1VU z-adB{<|+eiJvcmyFTT2uD2&W6xeA>YW|JN*&5d+m z1hp0u{(kk-jL6N+FM;F4`1LQ}he*Vrv#XcZzj5+4I#btgJ;3t~C%w{PU}zj?Pk##> z$Dy}x5RXR2QK2iviDPZ_1xAqeoJ$6>d@lX@uk;OPg4$|5w3-s!zSjePVC#JBXm7*e zmInOs(gzvIsy6B1^93+DGas!x+wmGspK6bmIj`57DL0CuLPf^pM#iS-6^p4^8`@f% zGJgKplbM9N(eYTW5MJ)yL!k%#F4|z)Db01^8SNftgUZmG?*H*9)*QUT)Tb; zXFJ-_&|smm^7HfXx62=AB#S7WZ=X9w`;Ux{V{Cj9<=PTz$Y(^(HgCtk@Dn`f8G^~E z!|a?L^Y+aBHeM`&$LoQ$|1riUrqEPZ1%_qNH#i2T%af5TgI-IO8@Y&$naa)-yewDO zH?V3QN~kAqvD#Dtug^yZ>hb#0%5pf}xYwP%*DjMu1-08%z~jQXcC<9rV|ir_a+wq- zPaMUStN+1_EzyhEFOa1fIimKAoKdeub7LJoyz);>&u%G~A6`6?=$_fgV3gqX09SE(l7_t65GcB48 z0)AnUy0qk2Hu@jn ztBZu^;B+{l)t1mQS*9DC4wPw&p($1;$kCLjscqTTT$gY!j4)PKpX1r|*9S7$(|Vm2 zxjAz91AY)b9*>8{Y}9IDPAs#ww)R5Xk_Ze0g7EJM+7h$P?RKN~Oak`Y{`<3Gt`hE&lelr~9xe9|PvDtt1#a!nQ}hrPLQFgI z7o8(}g^>IRHeFr}jy#DlMv^gNlJ%iK=<(f2OE zQdR(dxeFmaANmV#VAB%>=SUu8C6G`34y8pugSy2`Ujje#5kwnT;nn;GZq2rL9lijm z{dIs5W;JqaYisn+SGPOSKeV;x`yP$KP*I9pxfJ%L%pA!=MIO`&Iph<+hbXY6WyMR4 zkVu=rJO7osW7rAF;-4Uid6z+v3G;j{!rNn{;J44o*t%5YB7er8&c@w%Ha(xLeOeT& z6%YsAQDOw5z#!y~%w3!yXFzxt(Z^^{(~jtFnTGgjNB*>*I0AW&3`@&vs5a|SZ8A`!dEWjB{lFm9gI=md_qG%w0%C{nA?Sd%D>6>-!tjYXld0F5<2z2VU}5h!a@~*Po{;0nK5D(Xx{C7hafgk7{~uJcHUYQ(H5Z|+FN^VKgfQP6&IvEa^b{>0mK1VX|*d(a9C+~)un6^utgLK zZPGLeN&Ft$vw9}Yk`Om>(n)OhCq=4bH+g=Y@iWi356kEC0ufl2g&+tZgg_XEQI{kN zV+@+6fpd<9g@wpahU7@9)9Fa5tU#iJj==*-A#d?q+`JgA4^%47?o)7bVU(kHLYUJ~Ia7y>1xOMAxCbh|nk6Xhk zmFnCAQVDtStfl?9kQZ0;QR(DL%jqeIVJu8|KtLHi1XvD&=KwZ7^i;_)zXi=c2Dd(d zIb|^4029&K+}<3Y#S@kj(41pv%~#;&d$3S#F_DNI3vB3LccC?Rl?(MVS2-3~$kkpz zYrUS!_svI61jyBYhu-*8PQ`hlyRJL-tKwX95t>uRV*MBJz*edhC#D&WoItVp2a2t~ zCi}IJ(*9S#Zv%k=geDL;fan0CZ(FwUzDfla6c&(>PM*@Ey7Ec2jJm|{haYg{fKiy22Ak)hUAB)we5hp^xcscj` zD~hS*7S4VJ8=7u3Vt)hfiSj_FVXDUr+^Zw+91fo__EIB;Y2_Br9@EM;_@?y^a6c;w zOAQmt<*Wl}-&gqFNw?xi857Hu>dyw>prV(iV+xr_PIo?ni@qd6#cz9ea4V7`CXy?i z{Wkc9x!>J$r4+rzaxd*oKF=ikJr5H??>cEozb zmMe*~ZwFK_CQt}ZN-ViW?;RK~bPxPSqSJ8_V#!@|UY`FXx=AD>#F8uEe6Qxr?s>;7 zh$Sbdq})xv&5Rf(TIZ;|8uff6g~XE!n%yaxG2+S56RU(C6-*2DgXz@uHmak6&i``p~Lxv_h?DE73<@oAO-o zmsAyms^P5<74)S-(9$+3F~$UPu^l_!ovCKmUMIv(eAy6_Pl}XX&&CXy*PF1G~)3jqR}T8!O=Y`}+|N4`Xuj zQO&@trOwU{jF10}d-wiWR&p8KrAuGn;ls%q>w4+x>T1LF>p$4#Y3`rPm%o0c!K5Z8 zBDi?*bNiYBxVmrOn@FYjhIY&;&CbT5stmDM%(7fx-@v8-%jw|3A@e^H4u?fB*jfvH za#YdiBiP3oD6!bnm-av*tW`&3*B~iE$f|WJ)%5gJ zD28FIFA;$5RyVfP&*PJK`tbc&0N@#<CimGt=o3YjF$K10hqLC?32*&}}4Nkh?@NO5ry0EKw8LGkGxCN9lME?61lsaoc zT0)t!CBX)FxN-Tz-D_5rfZjk8e1RZ@aCRf%IGhA?G2qID-jb~#n1+^6E4UDL&~ZYJ z!U+?OagGndDs-;d_gUh1K%ZMto`8cG z>1B87{Q+>!3un(2$T`cI^M&Kg;a>Ze#vcQjSkWp(=;oh&&0J6vRgO|^!AUS*9fiy7 z&)oqQ{#v`Dk;%6~80x&<+H*^(1JY>3YEr`tV9c*s$8=AT-zE8jA?XB;06GFEvccrY z-X*D1`f(Wzr2D6KKvt7ei}uVSgN5ZR6}!403CsR)LSk57jGoL zB=~#{CS0|ERm0^vaeT1Nyk^v>2#%QZ&Vo^09&@KstsJqO+*NdhycS&M@v`Vwx`e$X z5K7H?S8Y&nz;YbDAp=ygf(g=99PDz-H3gyY2O2G}sG@IG!Pzd^r^Db38kqz1nTp$! zma_s2L9nSAh9Rs8U7@OjlWs^j&JJd`T&cgSX)fES6_}Xng4ge_$~7y7%Vhz_--yHO zZ-hn{fOMqdj^sp4*Occe3@xEHD@&-*dX-yF2&l}!$HzFJHJ7$YE>SX>mGjQ+@u1#Y zx%Dc7lOxHwflr6xXbF0(Q^@pG^;B6&>j@(5N735eWk0iARS3t?-Lg@r`fCH%18oKrZ>z3ES8Z0lqfTa%^ooM8-nmR zG@FB34qjPsa^y6sL^R*rj%1U<6lHhV+U!IO5yd%3ork;v+|7A-Ua$zuQF-1XF z&vx^=l=-(NTn_BB6LFjwSX}ahZZEj&DPp5p(liZS-LKo(f5mXJGKGOF=i(R{Tzuij z^#@z>uw_>+w+CI_y%1z|3l@cwQ8D1rG5XOQj_zJ8R&2v00lRy)gED3v!x!jC!M8ke zG!AcDz@;-uG}Jk>e#xlLsI#xAR82#7?@m04JVJba)?Pa7XP!O#-U{g44n)@t$d{7_ zH^7bR(hvKwH0xwljG1hUQes+-F$P6pV3e(R@ly2toGi!~HAjO92B8-FN~}}~vn^r= zf-0;?mh@3V7I~YbEPq3tuOx?OV3jbLkDAOfeOS-J6=_pk$%0t zCfI1Hssh^Cxph;*$>&6y+YT9B@%apT61xgAOd z^YfLTTGf(fW}YLJikm$$;l{?s5DxFh{QUZ*FE2w9?9ie8xPAL}9!_d>^gHv1Xfy&{ zuf4tGYLfkpMyD_|bP(6BjoKwwe87F=$Wgf69!yWqpuWByE|(jODeLwDS|yTem<)pH zbP9jn#0{{R3 literal 0 HcmV?d00001 diff --git a/themes/themes/local/epsilon_dark/apps/regression_icon.png b/themes/themes/local/epsilon_dark/apps/regression_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..3f86d36536f84dfd6b838884f3da9882056ff545 GIT binary patch literal 1482 zcmV;*1vUDKP)U_%HPC~*U3JkcDkaNqvgs;P zBc;ozn?`I3B2b8ENH7G#C-E;S7_h;PvBw`=-ARZ-JlOLw@wi7q!#v;n=AL=)&ABu0 zssJRwgWld=3=It-kw{>7cbCs3t5B&_P^;CrbmyYTpl9bbOllhTgXpkA*BbN%}DoFKNbxsA!GIkdN(N^47eX!KMp%O>LS z_<@F(FMo#{H-5=Paqthre z97S{E!9uMHJ4Cf>Se8wDaUnKxFl}4L%U}qfUOb!fT6(<&2rlycVBz)4`pA(`FR!@K zbIyvqG(x>=8su-U9^h@yDmfDB$*FmqJ$(u##YJiDLr2a44cCCeIzXj;b!@nXP|;al z`>c_RMq{`)VS~kF#BsAxpidOR!Z!5%@C>THVeUxh)XoRVI;Y*5fNu3R)T|Tnd=tXP zuMjQi6zqTh!2+}zHQHK)y~6B)h}^kzXW#ijkTi1I?MW2ce}+1|0wki`ko?=^iCL7F6{G&7S#rQ)Ac>q7ABdH`=nggrps#U|6h zo6c>j8)d~NTMBG*cIb3@_^7s0av#B95T3%5Ftq*(gTVlwZ-`rK)Gi0I$a%K{@V^Y9 ztNo;?y|aS|(x1&{gWK(v)z={TXOUa(9pC> zRnb7dU%B%Adkst|%F9b}_3Dqj)tPNPxW{=aC@bfk(sv)TMg3g{n7eR92+l!#Gz zCGo-AI@jIZ4Zq)y&CSi6TZ0tu`uaKofdI#24@m{wPNx$yGczz6ja-IKrG2y^ucQJv zPQz3xP$?xnFIHDq(cIh&lWG5_4c^lGzP>)(ym=EYmx~+8ot+&;mEY4y6%-VptgH;x k)zzSf;_&eBej@D2`4L1i?oF z2#3SLM9t35LaO5d5}J~dA`|JKIhrM}*UNZQfofK!dL;y}*UOL70T>>>j=H)p*o;vcC@U+4*_@`>mOF7Pfh)6I zDXZdm<*pW|j)5`ApfKOi&;*z3G8!A}72EXp51_rh1D%~`v29yEii%#4wTU@#$Bs4O z=+Q>D5Dmx4li%Xdp~EZ{6c@h)hr_PeGUmiNom)^)kdITReqb{f3e74B+qJ6#xw+2U z_7!vD!X;#h+S)o47CybCFvP0Qg@gQzmOUnu348bMU-DlHSJCp)rQg``^yzkN-MWSC zqqyDwpsMP_pnVhxu4YbJ%T-psLV<84 zZr!^3+417VUVj~}5(t|uTh^W{v!u<}yu4ggSJ$xomYA5pl0D_b4I3VmwdYD4L3@=a zPPC%F{!11)No+~U%WRu3l-2bdudIsW6~Wi$T=h8~Srx}Ckxk->8-c7+K))W)rUDax z0+){fH(D0kXwre~a==;u40i#2yMz0%z5&>agX_j!!1-5Xw~MT}j3QCk@EKsv1El8x z8{Y-G#{u^lv5m63y$aCV zfu4_6c3q%|0RWGG16ZF?6c?R5XWK1?`hnxYY2Z#6?-NZ`fyj#^VYcT*VFUaBdw-N# z$oCXb-<-)n>OIOQ|92!|lyoW2Q9>r>ZY>=p33Cac-?SKj43CMk)>A8MIu~b(u+Nb& zf_zOMu)j}9c{$>Uv1&WF^UP)Na zo4>N7*=&a5+v&d_7NZfFD-nyqhD6<(RI_+7Iu3l$0=7ZS?l9Oq_(-kc#H_ vAAmt0g^@2467@-&4qr8YCwrS-tJUIP;9c482BfvA00000NkvXXu0mjfEPIMS literal 0 HcmV?d00001 diff --git a/themes/themes/local/epsilon_dark/apps/sequence_icon.png b/themes/themes/local/epsilon_dark/apps/sequence_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..0db57ab0b70dba8b063837c9bdf29ff95975dd53 GIT binary patch literal 633 zcmV-<0*3vGP)nHCcnONAO-k`aV&`jIL`h+h##T}oZ1NsLN?*b{g+I*@5kXN=MiICx!(H?Q}Xmk!Rv|yPbE*N+c5TE}yvVb_>UGq!o7LRG^Octr!Mjc;9`b z2glB>*K4U}j0er2WmzyB$7$~Od$Fti2NH<{be^!+*SetPv#Hf;(CHjwVcUO%6~KaY z>0z3+Q%ILCPC55SiIbI3i!%kN;Ud=WAI$sf&o%C6sLln#^cVYE|NG8uHXH7uYu4Nw z>kc&qx-CD9e?Ii{m7zQgo@v~O`h_AZYUXa?(T>PMb?zn}?1(Isp~#AwIUYeeA`8_y zE&)0s3uP#>V$NnWZs{T##x->-LlM<5cql`W6(vqqlsH)ltvJe}n7Xcmm1v;*U%r2O zYGK#$6)!Iz(QJOZxSGUigTcV%#WiF_58?^VGoYZJ9*su5trsRpPb?OLk;!D5*=*K( z6Bg(nL$}*~OYIGZL+@dpX^Q(4`C4c@Qev-AD7+m7^kTVON}stTVHB?wH5-3n)fgEEX{ykG)Q=Rx9Q~|7@sJ@pv4mR0_FV4!vFv<#PGF3;=%tyt3E3 T9^4G900000NkvXXu0mjfosJz$ literal 0 HcmV?d00001 diff --git a/themes/themes/local/epsilon_dark/apps/settings_icon.png b/themes/themes/local/epsilon_dark/apps/settings_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..5bf925d96e7b210448b9bc9c828191564ea20254 GIT binary patch literal 1693 zcmV;O24eY%P)K-p*;w`)#MPtzEnJbS*r|mY(M8`M&48m+yVP z_izBh=mU=9P+VMs6)RUGpaX8VilCmUDmM%_Xn*8W|x+)>`t`J8+a#XW{Y_Lv=+7UVHTwG&VM} z@ey(de?Nw%ljpLYKs-#TDZ~9!E7>TX{>^@5&K*IFaOC*kaJkypXO82z@K@jMf=Bgb zd|_r`SgsZM!@}T~zx)0jMT~IMt)lFnD)6<_9?pX0n2+UDqKB9At?H&v8*V)S-sz@j?tGxey{H2pk@$tw#BTBFM7L_LQ!!>)6|H z7@eKh!Sj5^$Z2{ANzG2gM1jY%PtC>T$_cPo6!1L9%A%3^!|~*T*|1t=HZGGGG&yoP zSrYNY<1=Bg$Wg}#f`C~M-pAZElP;PRIjU>OLZT?LvMn0y_5x@bCMU)lFt4r_IXOz? zIvCX8QN7F!V}S^f_2$=2LvF4Wk`#BEq~P`X;oNsPd2%0o`ZWT9K;)WSjmpvb&zCXF zMFUyW>-FNjkJp3a1V%I+pd?l`sUmrDeSfTJ8UlVlS}%2=$T2pmTT&uvZM%x9ib;?a zCFVm&HwGPZl-@Z(zz*kr=AzMJimZF!&=EE+liR_dXC_ODf`Fhuh%eS{#Ko2?k*pXB zh0*zM7dCI-gRbjtHZE4e843*AUudxuL*#Dco*TA0AqfI(Rx3C@46o{k77DX?5NHy zIXVGQf-+Qxfx&u3R+8&3%mvJzH3dcXaS(U`x~}0&b1M%0aU42T{0VqV){!G(RN6ha zXf7+v4HAMNqkMt`MfS1yX46kS;|3;}Rl!1TNPA}8tlD103{Zxq?`KS(oRH$kI&!q! zqVeNmT1Ap1++E^GNO25W6{Hj0io*vfPA#g)GE*G9C}3a1vE<41RR!q;xmqZsW9?_l zU{RtAVS?Z9$MO%?!Dh9grn)M5asx>Uvq6f(=kwvb>mnwW7DtsztO^6f|GLAc!>rU@x48nH#6yi%pWq|0wO~0Npa;51Q84d5e|jG@jT0viY!6t5y0^* zmvx)?t%^gGB&ImXEe*v%7E4rdWM%nfmg30T!r2VPk)2w_B&5x9W=NJ)krr}BWlsgL z!(ISMlvrtX*42t%4;^I(NkbuUO($m%dw$VeD2g08vn0tfvBVBXVFA9~Ax3Ev3xkn7 z5y^7#A5@e@mn`(4PMmh3rmBibmUJ~q=t1*Tn3@Gcvh3b{5I(=ZUrfTqu zSyF+fYvAc=n*~F%gg72+)@(%6sb=_m0rnJC)sJIMr?KYCt>8I6?M)I=5~9tfz^tTY z6uTrzNV5E}s{xuul7(8$TrbIz?k0)NYJrfOBQGs3F!R;xAq#@j0~X3Q8W@%b zG7jfl7g#d5oj~htKJP+~q+#)kiy|xU_X79g#V=!2VL5a?jN!xcSUZ@qTsAZl4Nxr8 zp6O9l_O6*E2{_)k4^HRKUgM&^dS6~%fkz*khw{p)Xt{J5*Sc;X5HN4EPZji_^GD>N ntn@DI+_3>Wwr}fyO91}?wKcCLG+w)000000NkvXXu0mjf#W*95 literal 0 HcmV?d00001 diff --git a/themes/themes/local/epsilon_dark/apps/solver_icon.png b/themes/themes/local/epsilon_dark/apps/solver_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..efcea828f5ad5bf3f93398606257a0a2a8638459 GIT binary patch literal 2900 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 literal 0 HcmV?d00001 diff --git a/themes/themes/local/epsilon_dark/apps/stat_icon.png b/themes/themes/local/epsilon_dark/apps/stat_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..5773a8ea864e4e89e9050aee643da4c362e37f6a GIT binary patch literal 1340 zcmV-C1;hG@P)XWK^4u$U`oauuZr(zEy8y#9(J6?cfK)1pm9MVio0TuiqvClUuInO^ zNFbd~gX1{P^E{7z$Z;GN7A|3E_Y~=<7?sFdgJeCT5?1fvp-rx_H0Q*w{JbC8M3=JP^l^pMd4Y@Zw8z=FF87>5FpY;61qzEF^YTO1(@#S(bR=-r5A zS-10CH}b!@qe@qc6Qk}>5O~Z!GlkJJ4ElX zox;Y#f$tIxUTNPp%4i#TD%cN0H<>HeTji*u?V%=dRn<_)7xxqvb+kP+aiyZn{`SHTNQNH% zk}4L9)s40-7e`-6t3|7}Upv}2s-nTR?9kui9QMl5Z02)Cw$Fm#dG79HNCbimNuHV4Y^4C=fyir69 zieZ>gWEK6#5>59}wQ@8V5_WE4bYEcvwV}2bFFa8e(kLQ^MJ_2YjpiS~l*Rd>cI_+Ri;sbuM)>|Tj`)(M*lpfPP6cMjZ-*u=Aeh+hR{+zw`D>q|HeG?1Z|@JTUG|mYT-Sl< zI5tEvRx`+EX}5h@oa2Cp+uT90)1j@$4-TfgQrysB8k`^q-u&xtm0hzNuq_M8et>7U zylS{>v`)v=OITcdcc=XN=)?C=ENpd!u>4jQKi^nqubDVSQSj?8*N?owTYqgL^V^SD zU0r4Gxyl=YRO)U_U3?Bh!zU1n^|W)Gp|DI7*?<2=_MZ)0yS7r!liZ!xA3Z%in45bQ zifZ6kGKF}&4}utr{Fep?fd+5e77SfuZfNK#ve`|nt*uq{S|iTq)TswBKmQssnctDi y<)C-axsE1!c=#T8o{6QUtH|ZDHB}AAarh4%X9{F5bIUyd00004nJh<1Q5_`(0heTRmgQeK{)pHQH=>?*z{1(9wmjY}d~TQ50I*z(0PCMqXbv3%#A z)7J9O@2viEbIG&)U6Bdrn{3%NH0C%xlxZq@v6`!K3TJ1ggok%{+G?lk3rbik{dcX~ z+F0d&(6hUFhU*fpQ;#luH()SGN;tB((|W3r;Qe<@JbKP~(sJdCT|fIju)W-qwuq>#Etj_Lng3mJw`SnnEe1o1c=IR*6M}5k$kB)w3o6H86O(`njxgN@xNA Dmx{@8 literal 0 HcmV?d00001 diff --git a/themes/themes/local/epsilon_dark/logo_icon.png b/themes/themes/local/epsilon_dark/logo_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..6d14c3a47c34092a239ee98fa9efc694c81eeef3 GIT binary patch literal 8619 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 literal 0 HcmV?d00001 diff --git a/themes/themes/local/epsilon_dark/probability/binomial_icon.png b/themes/themes/local/epsilon_dark/probability/binomial_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..4f9ee7063d410fb5b3949804d6f94e255481eafb GIT binary patch literal 29317 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 literal 0 HcmV?d00001 diff --git a/themes/themes/local/epsilon_dark/probability/calcul2_icon.png b/themes/themes/local/epsilon_dark/probability/calcul2_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..218102ba0c85851aed09e77d95a6fd4e36105911 GIT binary patch literal 54116 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 KZmV4N>i-Ayd~Q_$ literal 0 HcmV?d00001 diff --git a/themes/themes/local/epsilon_dark/probability/calcul3_icon.png b/themes/themes/local/epsilon_dark/probability/calcul3_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..f67ffe379d1bec953ce70f05a515159d6235a2da GIT binary patch literal 53757 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 Xx$r-4{{RR`VQ+n3?ZcHnxaI!=3r6wQ literal 0 HcmV?d00001 diff --git a/themes/themes/local/epsilon_dark/probability/calcul4_icon.png b/themes/themes/local/epsilon_dark/probability/calcul4_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..8040f094f17e48b9a9f81d5e672f76c09b7bc512 GIT binary patch literal 53389 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

nyPkW_yR_oj*ZkubKl;%Jo^||b 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 buykUAGr;LpY^G!dy{)d6k>(=}`@{ba8mjOk literal 0 HcmV?d00001 diff --git a/themes/themes/local/epsilon_dark/probability/exponential_icon.png b/themes/themes/local/epsilon_dark/probability/exponential_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..adc24dce5c06eab7858a60875c4632a6db93fde1 GIT binary patch literal 31374 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 N002ovPDHLkV1mj1qUrzu literal 0 HcmV?d00001 diff --git a/themes/themes/local/epsilon_dark/probability/focused_binomial_icon.png b/themes/themes/local/epsilon_dark/probability/focused_binomial_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..28b315c0c01c5d047785c4d14122d0e1a445a3a4 GIT binary patch literal 40684 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 literal 0 HcmV?d00001 diff --git a/themes/themes/local/epsilon_dark/probability/focused_calcul1_icon.png b/themes/themes/local/epsilon_dark/probability/focused_calcul1_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..11ca66e1b5080c9c3912c2978a38a4f0c616e2bb GIT binary patch literal 55124 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 literal 0 HcmV?d00001 diff --git a/themes/themes/local/epsilon_dark/probability/focused_calcul2_icon.png b/themes/themes/local/epsilon_dark/probability/focused_calcul2_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..89cb0c89e8aba2ff2763d60dc7172cf8099b0ea5 GIT binary patch literal 55587 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 CWp$JQ literal 0 HcmV?d00001 diff --git a/themes/themes/local/epsilon_dark/probability/focused_calcul3_icon.png b/themes/themes/local/epsilon_dark/probability/focused_calcul3_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..5a9ee752aca5459288c5308ccb0b69c0168a43e2 GIT binary patch literal 55933 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 HF4a-y>sv7K4xdp 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 literal 0 HcmV?d00001 diff --git a/themes/themes/local/epsilon_dark/probability/focused_exponential_icon.png b/themes/themes/local/epsilon_dark/probability/focused_exponential_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..aa3da9a8ec81a8bab002f0b1e096c65e9ead3718 GIT binary patch literal 39927 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 literal 0 HcmV?d00001 diff --git a/themes/themes/local/epsilon_dark/probability/focused_geometric_icon.png b/themes/themes/local/epsilon_dark/probability/focused_geometric_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..e4802b8e1a3a82a1b52512829d294d84001832fe GIT binary patch literal 83138 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 literal 0 HcmV?d00001 diff --git a/themes/themes/local/epsilon_dark/probability/focused_normal_icon.png b/themes/themes/local/epsilon_dark/probability/focused_normal_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..3c62147caef9fb8cbd0e886af16ba1d3e72839a8 GIT binary patch literal 39664 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 SpTj?USDdT%*UVq~wf_SZYU02E literal 0 HcmV?d00001 diff --git a/themes/themes/local/epsilon_dark/probability/focused_student_icon.png b/themes/themes/local/epsilon_dark/probability/focused_student_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..039553c5173bc363ee87966a7bca9370ac4dab76 GIT binary patch literal 27754 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 literal 0 HcmV?d00001 diff --git a/themes/themes/local/epsilon_dark/probability/focused_uniform_icon.png b/themes/themes/local/epsilon_dark/probability/focused_uniform_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..ea7524a63d8b357c832475e3975ff8e14da8a96e GIT binary patch literal 40270 zcmeHQZ-^vGRmGkM~8`0~PU`==mZd|5avG zS59@!y1iQi)1969;>C+!y!Yb8i^#}#Z{K?5`RBg$T&L4{{^kwm1b)8=|G)T!&%@6z z{r-pl1;2iDdgG0>)A_M4(*K|9{Pz1VbUN?9lia;GzjypTnjuy%T4cRR@1v-@8AbPkY_&!-o&| zAFBJ4*=ZM9meo~o7h@SH^lIh%i01#x53QMMUA5Ej~Nt8|&vmkOGfFv)9TA$8OqOx~d zumW-+qj?gq;6tCR1TQ*IRurR8crm6^ysHlZwBBGAdGpEa?qo7L;_7-k??Y)YVCpG- z<=6|7G40r|9(L=6O4TX(MUFR*jzG0_8DUvLcM<7ndQa27rl6jpRG4te8joWzqtKgs zITu4#u#CxFr1h}T)6^=LCE7ZlNtncsT5%~UD?AEy?F}ZQ$?SL%Mn~#lS9Dug%9#WU zsVCrSB@(9AaC+#2#9P)7KTlv=r0Tb$w{thh1vJ zKv>qE^=x(@Ylzqe(cR&|1_$9_$Iy*FF&$)T1~{O#42WtE2DWQEeFtgaV+@B7S22c~ zp$u)uMC%zi19zyCfu#+IY4uG4BA~vD`)H{3@jzS40IJlTfo2aC*CeWC+qQ0yzD+D$ zb6tY*S_ZaiVpTzgi!}`)-B29|qrPSPA0pj!9Yr+;uI;YJz#X`nilFtXseygb(d)C zG2jDKI0=%lS8A`K#Ia$;q3k11lMym3+4Brjw!DbMrV-(QkhP6l!CH$gRkJ+VC%(m5 z`=O`Gk%EY#;@Brw!+N9B3#5#Po}V zl({F*16?!;vP=YaX~mmLO(P9m3t;A>`Uo=i_uiLQZ$Y+q_W z1m21v!yuEpC*+5+7po+W4D6#Q+|Y+u$k4F~ynuCPjsCV+>!A{=dL;Xn;pfU1>2eeh z92&8w`Bt#0)TXW|ni+#r1r6lxX-iJk2M-sA8lf7JjWXMEs$hAs5m^u{L}BjvESQo+ z;0QepYnG~R8i)8XjM}Dd0@hmY`Bb9GmTm+=U}*%bazm_xP$#iY!?9S+O~!&?C&0vu z0z(Y|sEt{NaX?h+kHAZ_ZuCP8!)^>-iWq@sqD>7wL@Ggn;>#Y1Nv0uYfaKUyRR}~N z62zNI9fpRFVjY9Y;M`>32H- zgAH<%+0xh;`!Uu{D+Jb?54(#6Oa~Oh?c1hnD@1i&jF7FOp{?jJ?Q&eh8R%>EjB{}n z7v_|fq78>IIYPE!4PDcPxsIau6?eFn3uG1cLn;n36+j9+FNSakA=3bHbcbkzfz{X4 z!RCz2f$pffZ4TX`V(ROszJ)~EvP2e4$8AM*ux*%wp|gJaS1_`c8JWmcUDFs?x~n>d zvfk`+MyQW8m=gC*h`n4LFe18T4w2S(VOFl{ZflIN<$EC{6l8qeVEx&jkF+lFtlOj zJ{%A3K7VA?1XKT>2fBt(W-C)E4^`vzX&3b?i{r?ILz zvWt>nhh;M2MnW@Wk7`Ln$cN)B-S@LEo?8dQB)FeMGbuUqPNVreN~Ou-K{T7g!~Vfw z_T+IiV|2IZZEqpo*%|#6OctYPF-xVB zuiNb5tKRGsZoqSN4XaXc#_rzm7IS`Axduu7?C!~v$LVCWU@7*Vgw1OZqDoV5mOmd( zW@p}93PJX1avqIR%9ZguiO!~@gw`^jdE*qyK<zC^ z@^1H&@#HM=M$!Isd@4ob87$nSb7ΞpTs5O5l#Axd)A#Nkys3&f5Jc(GzBA;UZH- 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 N&^}~+*1YYv{vYBDe;@z= literal 0 HcmV?d00001 diff --git a/themes/themes/local/epsilon_dark/probability/poisson_icon.png b/themes/themes/local/epsilon_dark/probability/poisson_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..c56aafa340c26f8a9e11b87e3b5f819a01fb9804 GIT binary patch literal 30799 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 TYa%T4I_Ut)(_RMjGv-)~_SF>zp0RUjN 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 literal 0 HcmV?d00001 diff --git a/themes/themes/local/epsilon_dark/probability/uniform_icon.png b/themes/themes/local/epsilon_dark/probability/uniform_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..3e801cfb8e6781367f4c6cd05d55d93514f158f2 GIT binary patch literal 29675 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 RRXld?1b6*!d%yRG{|~eac}oBQ literal 0 HcmV?d00001 From c8afdcacab7ee06f13a08c1de5b78147187c4a28 Mon Sep 17 00:00:00 2001 From: Hyperengined <71342876+Hyperengined@users.noreply.github.com> Date: Mon, 22 Mar 2021 10:15:52 +0100 Subject: [PATCH 30/48] [ion/battery] Doubled hysteresis to avoid sudden fluctuactions --- ion/src/device/shared/drivers/battery.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ion/src/device/shared/drivers/battery.cpp b/ion/src/device/shared/drivers/battery.cpp index 779454096..c0f4877bd 100644 --- a/ion/src/device/shared/drivers/battery.cpp +++ b/ion/src/device/shared/drivers/battery.cpp @@ -30,7 +30,7 @@ Charge level() { constexpr static int numberOfChargeStates = 4; constexpr static int numberOfThresholds = numberOfChargeStates - 1; - constexpr float hysteresis = 0.02f; + constexpr float hysteresis = 0.04f; const float thresholds[numberOfThresholds] = {3.6f + hysteresis, 3.7f, 3.8f}; // We do not want to lower the threshold for empty battery, so we add the hysteresis to it int nextLevel = -1; for (int i = 0; i < numberOfThresholds; i++) { From 5a6dfd8a20b4627df2a0b79027f4991be9612654 Mon Sep 17 00:00:00 2001 From: M4x1m3 Date: Wed, 7 Jul 2021 12:09:08 +0200 Subject: [PATCH 31/48] [ci] Remove duplicate tests action --- .github/workflows/unit-workflow.yml | 14 -------------- 1 file changed, 14 deletions(-) delete mode 100644 .github/workflows/unit-workflow.yml diff --git a/.github/workflows/unit-workflow.yml b/.github/workflows/unit-workflow.yml deleted file mode 100644 index 38135e988..000000000 --- a/.github/workflows/unit-workflow.yml +++ /dev/null @@ -1,14 +0,0 @@ -name: Unit tests -on: [pull_request_target] - -jobs: - units: - runs-on: ubuntu-latest - steps: - - run: sudo apt-get install build-essential imagemagick libfreetype6-dev libjpeg-dev libpng-dev pkg-config - - uses: actions/checkout@v2 - with: - submodules: 'recursive' - - run: make -j2 PLATFORM=simulator test.bin - - run: output/release/simulator/linux/test.bin - From 6fcab3952f85f9c858463815873454a529155a8e Mon Sep 17 00:00:00 2001 From: Laury Date: Thu, 11 Mar 2021 19:47:22 +0100 Subject: [PATCH 32/48] [python] Add some useful functions in kandinsky --- apps/code/catalog.de.i18n | 4 + apps/code/catalog.en.i18n | 4 + apps/code/catalog.es.i18n | 4 + apps/code/catalog.fr.i18n | 4 + apps/code/catalog.it.i18n | 4 + apps/code/catalog.nl.i18n | 4 + apps/code/catalog.pt.i18n | 4 + apps/code/catalog.universal.i18n | 4 + apps/code/python_toolbox.cpp | 10 ++- kandinsky/Makefile | 2 + kandinsky/include/kandinsky/context.h | 8 ++ kandinsky/src/context_circle.cpp | 49 ++++++++++++ kandinsky/src/context_polygon.cpp | 68 +++++++++++++++++ python/port/genhdr/qstrdefs.in.h | 4 + python/port/mod/kandinsky/modkandinsky.cpp | 74 +++++++++++++++++++ python/port/mod/kandinsky/modkandinsky.h | 4 + .../port/mod/kandinsky/modkandinsky_table.c | 8 ++ 17 files changed, 258 insertions(+), 1 deletion(-) create mode 100644 kandinsky/src/context_circle.cpp create mode 100644 kandinsky/src/context_polygon.cpp diff --git a/apps/code/catalog.de.i18n b/apps/code/catalog.de.i18n index ab998c81b..5da1a10aa 100644 --- a/apps/code/catalog.de.i18n +++ b/apps/code/catalog.de.i18n @@ -45,6 +45,8 @@ PythonCosh = "Hyperbolic cosine" PythonCount = "Count the occurrences of x" PythonDegrees = "Convert x from radians to degrees" PythonDivMod = "Quotient and remainder" +PythonDrawCircle = "Draw a circle" +PythonDrawLine = "Draw a line" PythonDrawString = "Display a text from pixel (x,y)" PythonErf = "Error function" PythonErfc = "Complementary error function" @@ -52,6 +54,8 @@ PythonEval = "Return the evaluated expression" PythonExp = "Exponential function" PythonExpm1 = "Compute exp(x)-1" PythonFabs = "Absolute value" +PythonFillCircle = "Fill a circle" +PythonFillPolygon = "Fill a polygon" PythonFillRect = "Fill a rectangle at pixel (x,y)" PythonFloat = "Convert x to a float" PythonFloor = "Floor" diff --git a/apps/code/catalog.en.i18n b/apps/code/catalog.en.i18n index eaffc09a7..279c77ab4 100644 --- a/apps/code/catalog.en.i18n +++ b/apps/code/catalog.en.i18n @@ -45,6 +45,8 @@ PythonCosh = "Hyperbolic cosine" PythonCount = "Count the occurrences of x" PythonDegrees = "Convert x from radians to degrees" PythonDivMod = "Quotient and remainder" +PythonDrawCircle = "Draw a circle" +PythonDrawLine = "Draw a line" PythonDrawString = "Display a text from pixel (x,y)" PythonErf = "Error function" PythonErfc = "Complementary error function" @@ -52,6 +54,8 @@ PythonEval = "Return the evaluated expression" PythonExp = "Exponential function" PythonExpm1 = "Compute exp(x)-1" PythonFabs = "Absolute value" +PythonFillCircle = "Fill a circle" +PythonFillPolygon = "Fill a polygon" PythonFillRect = "Fill a rectangle at pixel (x,y)" PythonFloat = "Convert x to a float" PythonFloor = "Floor" diff --git a/apps/code/catalog.es.i18n b/apps/code/catalog.es.i18n index eaffc09a7..279c77ab4 100644 --- a/apps/code/catalog.es.i18n +++ b/apps/code/catalog.es.i18n @@ -45,6 +45,8 @@ PythonCosh = "Hyperbolic cosine" PythonCount = "Count the occurrences of x" PythonDegrees = "Convert x from radians to degrees" PythonDivMod = "Quotient and remainder" +PythonDrawCircle = "Draw a circle" +PythonDrawLine = "Draw a line" PythonDrawString = "Display a text from pixel (x,y)" PythonErf = "Error function" PythonErfc = "Complementary error function" @@ -52,6 +54,8 @@ PythonEval = "Return the evaluated expression" PythonExp = "Exponential function" PythonExpm1 = "Compute exp(x)-1" PythonFabs = "Absolute value" +PythonFillCircle = "Fill a circle" +PythonFillPolygon = "Fill a polygon" PythonFillRect = "Fill a rectangle at pixel (x,y)" PythonFloat = "Convert x to a float" PythonFloor = "Floor" diff --git a/apps/code/catalog.fr.i18n b/apps/code/catalog.fr.i18n index f98473ed8..2b288652b 100644 --- a/apps/code/catalog.fr.i18n +++ b/apps/code/catalog.fr.i18n @@ -45,6 +45,8 @@ PythonCosh = "Cosinus hyperbolique" PythonCount = "Compte les occurrences de x" PythonDegrees = "Conversion de radians en degrés" PythonDivMod = "Quotient et reste" +PythonDrawCircle = "Trace un cercle" +PythonDrawLine = "Trace une ligne" PythonDrawString = "Affiche un texte au pixel (x,y)" PythonErf = "Fonction d'erreur" PythonErfc = "Fonction d'erreur complémentaire" @@ -52,6 +54,8 @@ PythonEval = "Evalue l'expression en argument " PythonExp = "Fonction exponentielle" PythonExpm1 = "Calcul de exp(x)-1" PythonFabs = "Valeur absolue" +PythonFillCircle = "Remplit un cercle" +PythonFillPolygon = "Remplit un polygone" PythonFillRect = "Remplit un rectangle" PythonFloat = "Conversion en flottant" PythonFloor = "Partie entière" diff --git a/apps/code/catalog.it.i18n b/apps/code/catalog.it.i18n index 51223cbdc..a9a32222d 100644 --- a/apps/code/catalog.it.i18n +++ b/apps/code/catalog.it.i18n @@ -45,6 +45,8 @@ PythonCosh = "Coseno iperbolico" PythonCount = "Conta le ricorrenze di x" PythonDegrees = "Conversione di radianti in gradi" PythonDivMod = "Quoziente e resto" +PythonDrawCircle = "Disegnare un cerchio" +PythonDrawLine = "Disegna una linea" PythonDrawString = "Visualizza il testo dal pixel x,y" PythonErf = "Funzione d'errore" PythonErfc = "Funzione d'errore complementare" @@ -52,6 +54,8 @@ PythonEval = "Valuta l'espressione nell'argomento " PythonExp = "Funzione esponenziale" PythonExpm1 = "Calcola exp(x)-1" PythonFabs = "Valore assoluto" +PythonFillCircle = "Riempire un cerchio" +PythonFillPolygon = "Riempire un poligono" PythonFillRect = "Riempie un rettangolo" PythonFloat = "Conversione in flottanti" PythonFloor = "Parte intera" diff --git a/apps/code/catalog.nl.i18n b/apps/code/catalog.nl.i18n index 84be69878..95fab0586 100644 --- a/apps/code/catalog.nl.i18n +++ b/apps/code/catalog.nl.i18n @@ -45,6 +45,8 @@ PythonCosh = "Cosinus hyperbolicus" PythonCount = "Tel voorkomen van x" PythonDegrees = "Zet x om van radialen naar graden" PythonDivMod = "Quotiënt en rest" +PythonDrawCircle = "Teken een cirkel" +PythonDrawLine = "Teken een lijn" PythonDrawString = "Geef een tekst weer van pixel (x,y)" PythonErf = "Error functie" PythonErfc = "Complementaire error functie" @@ -52,6 +54,8 @@ PythonEval = "Geef de geëvalueerde uitdrukking" PythonExp = "Exponentiële functie" PythonExpm1 = "Bereken exp(x)-1" PythonFabs = "Absolute waarde" +PythonFillCircle = "Vul een cirkel" +PythonFillPolygon = "Vul een veelhoek" PythonFillRect = "Vul een rechthoek bij pixel (x,y)" PythonFloat = "Zet x om in een float" PythonFloor = "Vloer" diff --git a/apps/code/catalog.pt.i18n b/apps/code/catalog.pt.i18n index 4f50cadaa..8820cfc43 100644 --- a/apps/code/catalog.pt.i18n +++ b/apps/code/catalog.pt.i18n @@ -45,6 +45,8 @@ PythonCosh = "Cosseno hiperbólico" PythonCount = "Contar as ocorrências de x" PythonDegrees = "Converter x de radianos para graus" PythonDivMod = "Quociente e resto" +PythonDrawCircle = "Desenha um círculo" +PythonDrawLine = "Desenhe uma linha" PythonDrawString = "Mostrar o texto do pixel (x,y)" PythonErf = "Função erro" PythonErfc = "Função erro complementar" @@ -52,6 +54,8 @@ PythonEval = "Devolve a expressão avaliada" PythonExp = "Função exponencial" PythonExpm1 = "Calcular exp(x)-1" PythonFabs = "Valor absoluto" +PythonFillCircle = "Preencher um círculo" +PythonFillPolygon = "Preencher um polígono" PythonFillRect = "Preencher um retângulo em (x,y)" PythonFloat = "Converter x num flutuante" PythonFloor = "Parte inteira" diff --git a/apps/code/catalog.universal.i18n b/apps/code/catalog.universal.i18n index ff0cfed7a..d950bf9ff 100644 --- a/apps/code/catalog.universal.i18n +++ b/apps/code/catalog.universal.i18n @@ -50,6 +50,8 @@ PythonCommandCount = "list.count(x)" PythonCommandCountWithoutArg = ".count(\x11)" PythonCommandDegrees = "degrees(x)" PythonCommandDivMod = "divmod(a,b)" +PythonCommandDrawCircle = "draw_circle(x,y,r,color)" +PythonCommandDrawLine = "draw_line(x1,y1,x2,y2,color)" PythonCommandDrawString = "draw_string(\"text\",x,y)" PythonCommandConstantE = "e" PythonCommandErf = "erf(x)" @@ -59,6 +61,8 @@ PythonCommandExp = "exp(x)" PythonCommandExpComplex = "exp(z)" PythonCommandExpm1 = "expm1(x)" PythonCommandFabs = "fabs(x)" +PythonCommandFillCircle = "fill_circle(x,y,r,color)" +PythonCommandFillPolygon = "fill_polygon([(x1,y1),...],color)" PythonCommandFillRect = "fill_rect(x,y,width,height,color)" PythonCommandFloat = "float(x)" PythonCommandFloor = "floor(x)" diff --git a/apps/code/python_toolbox.cpp b/apps/code/python_toolbox.cpp index bcc0bef7b..a5801bec2 100644 --- a/apps/code/python_toolbox.cpp +++ b/apps/code/python_toolbox.cpp @@ -192,7 +192,11 @@ const ToolboxMessageTree KandinskyModuleChildren[] = { ToolboxMessageTree::Leaf(I18n::Message::PythonCommandSetPixel, I18n::Message::PythonSetPixel), ToolboxMessageTree::Leaf(I18n::Message::PythonCommandColor, I18n::Message::PythonColor), ToolboxMessageTree::Leaf(I18n::Message::PythonCommandDrawString, I18n::Message::PythonDrawString), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFillRect, I18n::Message::PythonFillRect) + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandDrawLine, I18n::Message::PythonDrawLine), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandDrawCircle, I18n::Message::PythonDrawCircle), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFillRect, I18n::Message::PythonFillRect), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFillCircle, I18n::Message::PythonFillCircle), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFillPolygon, I18n::Message::PythonFillPolygon) }; const ToolboxMessageTree IonModuleChildren[] = { @@ -305,6 +309,8 @@ const ToolboxMessageTree catalogChildren[] = { ToolboxMessageTree::Leaf(I18n::Message::PythonCommandCosh, I18n::Message::PythonCosh), ToolboxMessageTree::Leaf(I18n::Message::PythonCommandDegrees, I18n::Message::PythonDegrees), ToolboxMessageTree::Leaf(I18n::Message::PythonCommandDivMod, I18n::Message::PythonDivMod), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandDrawCircle, I18n::Message::PythonDrawCircle), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandDrawLine, I18n::Message::PythonDrawLine), ToolboxMessageTree::Leaf(I18n::Message::PythonCommandDrawString, I18n::Message::PythonDrawString), ToolboxMessageTree::Leaf(I18n::Message::PythonCommandConstantE, I18n::Message::PythonConstantE, false), ToolboxMessageTree::Leaf(I18n::Message::PythonCommandErf, I18n::Message::PythonErf), @@ -313,6 +319,8 @@ const ToolboxMessageTree catalogChildren[] = { ToolboxMessageTree::Leaf(I18n::Message::PythonCommandExp, I18n::Message::PythonExp), ToolboxMessageTree::Leaf(I18n::Message::PythonCommandExpm1, I18n::Message::PythonExpm1), ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFabs, I18n::Message::PythonFabs), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFillCircle, I18n::Message::PythonFillCircle), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFillPolygon, I18n::Message::PythonFillPolygon), ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFillRect, I18n::Message::PythonFillRect), ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFloat, I18n::Message::PythonFloat), ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFloor, I18n::Message::PythonFloor), diff --git a/kandinsky/Makefile b/kandinsky/Makefile index 1e1ad24ff..38776bfd4 100644 --- a/kandinsky/Makefile +++ b/kandinsky/Makefile @@ -2,8 +2,10 @@ SFLAGS += -Ikandinsky/include kandinsky_src += $(addprefix kandinsky/src/,\ color.cpp \ + context_circle.cpp \ context_line.cpp \ context_pixel.cpp \ + context_polygon.cpp \ context_rect.cpp \ context_text.cpp \ font.cpp \ diff --git a/kandinsky/include/kandinsky/context.h b/kandinsky/include/kandinsky/context.h index fbcc6bf4f..846e4c108 100644 --- a/kandinsky/include/kandinsky/context.h +++ b/kandinsky/include/kandinsky/context.h @@ -23,11 +23,19 @@ public: // Line. Not anti-aliased. void drawLine(KDPoint p1, KDPoint p2, KDColor c); + // Circle + void drawCircle(KDPoint c, KDCoordinate r, KDColor color); + void fillCircle(KDPoint c, KDCoordinate r, KDColor color); + // Rect void fillRect(KDRect rect, KDColor color); void fillRectWithPixels(KDRect rect, const KDColor * pixels, KDColor * workingBuffer); void blendRectWithMask(KDRect rect, KDColor color, const uint8_t * mask, KDColor * workingBuffer); void strokeRect(KDRect rect, KDColor color); + + //Polygon + static const int k_polygonMaxNumberOfPoints = 32; + void fillPolygon(KDCoordinate pointsX[], KDCoordinate pointsY[], int numberOfPoints, KDColor color); protected: KDContext(KDPoint origin, KDRect clippingRect) : m_origin(origin), diff --git a/kandinsky/src/context_circle.cpp b/kandinsky/src/context_circle.cpp new file mode 100644 index 000000000..4e18c2b56 --- /dev/null +++ b/kandinsky/src/context_circle.cpp @@ -0,0 +1,49 @@ +#include +#include +#include + +void KDContext::drawCircle(KDPoint c, KDCoordinate r, KDColor color) { + + //The used algorithm is the Bresenham's arc tracing algorithm + + KDCoordinate x, y, m; + x = 0; + y = r; + m = 5 - 4*r; + + while(x <= y) { + setPixel(c.translatedBy(KDPoint(x,y)), color); + setPixel(c.translatedBy(KDPoint(y,x)), color); + setPixel(c.translatedBy(KDPoint(-x,y)), color); + setPixel(c.translatedBy(KDPoint(-y,x)), color); + setPixel(c.translatedBy(KDPoint(x,-y)), color); + setPixel(c.translatedBy(KDPoint(y,-x)), color); + setPixel(c.translatedBy(KDPoint(-x,-y)), color); + setPixel(c.translatedBy(KDPoint(-y,-x)), color); + + if(m > 0) { + y = y - 1 ; + m = m - 8*y ; + } + + x = x + 1 ; + m = m + 8*x + 4 ; + } +} + +void KDContext::fillCircle(KDPoint c, KDCoordinate r, KDColor color) { + KDCoordinate left = c.x()-r; + KDCoordinate right = c.x()+r; + if(left < 0) { + left = 0; + } + if(right > m_clippingRect.width()) { + right = m_clippingRect.width(); + } + + for(KDCoordinate x=left; x<=right; x++) { + KDCoordinate semiHeight = sqrt((r*r)-((c.x()-x)*(c.x()-x))); + + fillRect(KDRect(x, c.y()-semiHeight, 1, semiHeight * 2 ), color); + } +} diff --git a/kandinsky/src/context_polygon.cpp b/kandinsky/src/context_polygon.cpp new file mode 100644 index 000000000..8c95ababb --- /dev/null +++ b/kandinsky/src/context_polygon.cpp @@ -0,0 +1,68 @@ +#include +#include +#include + +void KDContext::fillPolygon(KDCoordinate pointsX[], KDCoordinate pointsY[], int numberOfPoints, KDColor color) +{ + //The used algorithm is the scan-line algorithm + + KDCoordinate top = KDCOORDINATE_MAX; + KDCoordinate bottom = KDCOORDINATE_MIN; + KDCoordinate right = m_clippingRect.width(); + KDCoordinate left = 0; + + for (int i = 0; i < numberOfPoints; i++) { + if (pointsY[i] < top) { + top = pointsY[i]; + } + if (pointsY[i] > bottom) { + bottom = pointsY[i]; + } + } + + if (top < 0) { + top = 0; + } + if (bottom > m_clippingRect.height()) { + bottom = m_clippingRect.height(); + } + + + for (int y=top; y<=bottom; y++) { + + int switches=0; + KDCoordinate switchesX[KDContext::k_polygonMaxNumberOfPoints]; + int lastPointIndex = numberOfPoints-1; + + for (int i=0; i=y) || (pointsY[lastPointIndex]< y && pointsY[i]>=y)) { + switchesX[switches++] = (int) round(pointsX[i]+1.0*(y-pointsY[i])/(pointsY[lastPointIndex]-pointsY[i])*(pointsX[lastPointIndex]-pointsX[i])); + } + lastPointIndex=i; + } + + //Sorting switches by a bubble sort + int i=0; + while (iswitchesX[i+1]) { + KDCoordinate temp = switchesX[i]; + switchesX[i]=switchesX[i+1]; + switchesX[i+1]=temp; + if (i) i--; + } + else { + i++; + } + } + + for (i=0; i=right) { + break; + } + + if (switchesX[i+1]>left) { + fillRect( KDRect( switchesX[ i ] , y, switchesX[ i+1 ] - switchesX[ i ], 1 ), color ) ; + } + } + } +} diff --git a/python/port/genhdr/qstrdefs.in.h b/python/port/genhdr/qstrdefs.in.h index fa1aa21b7..06ea392ca 100644 --- a/python/port/genhdr/qstrdefs.in.h +++ b/python/port/genhdr/qstrdefs.in.h @@ -373,8 +373,12 @@ Q(KEY_EXE) // Kandinsky QSTRs Q(kandinsky) Q(color) +Q(draw_line) Q(draw_string) +Q(draw_circle) Q(fill_rect) +Q(fill_circle) +Q(fill_polygon) Q(get_pixel) Q(set_pixel) diff --git a/python/port/mod/kandinsky/modkandinsky.cpp b/python/port/mod/kandinsky/modkandinsky.cpp index f6f797bc7..a1d93bcc7 100644 --- a/python/port/mod/kandinsky/modkandinsky.cpp +++ b/python/port/mod/kandinsky/modkandinsky.cpp @@ -65,6 +65,34 @@ mp_obj_t modkandinsky_draw_string(size_t n_args, const mp_obj_t * args) { return mp_const_none; } +mp_obj_t modkandinsky_draw_line(size_t n_args, const mp_obj_t * args) { + mp_int_t x1 = mp_obj_get_int(args[0]); + mp_int_t y1 = mp_obj_get_int(args[1]); + mp_int_t x2 = mp_obj_get_int(args[2]); + mp_int_t y2 = mp_obj_get_int(args[3]); + KDPoint p1 = KDPoint(x1, y1); + KDPoint p2 = KDPoint(x2, y2); + KDColor color = MicroPython::Color::Parse(args[4]); + MicroPython::ExecutionEnvironment::currentExecutionEnvironment()->displaySandbox(); + KDIonContext::sharedContext()->drawLine(p1, p2, color); + return mp_const_none; +} + +mp_obj_t modkandinsky_draw_circle(size_t n_args, const mp_obj_t * args) { + mp_int_t cx = mp_obj_get_int(args[0]); + mp_int_t cy = mp_obj_get_int(args[1]); + mp_int_t r = mp_obj_get_int(args[2]); + if(r<0) + { + r = -r; + } + KDPoint center = KDPoint(cx, cy); + KDColor color = MicroPython::Color::Parse(args[3]); + MicroPython::ExecutionEnvironment::currentExecutionEnvironment()->displaySandbox(); + KDIonContext::sharedContext()->drawCircle(center, r, color); + return mp_const_none; +} + mp_obj_t modkandinsky_fill_rect(size_t n_args, const mp_obj_t * args) { mp_int_t x = mp_obj_get_int(args[0]); mp_int_t y = mp_obj_get_int(args[1]); @@ -84,3 +112,49 @@ mp_obj_t modkandinsky_fill_rect(size_t n_args, const mp_obj_t * args) { KDIonContext::sharedContext()->fillRect(rect, color); return mp_const_none; } + +mp_obj_t modkandinsky_fill_circle(size_t n_args, const mp_obj_t * args) { + mp_int_t cx = mp_obj_get_int(args[0]); + mp_int_t cy = mp_obj_get_int(args[1]); + mp_int_t r = mp_obj_get_int(args[2]); + if(r<0) + { + r = -r; + } + KDPoint center = KDPoint(cx, cy); + KDColor color = MicroPython::Color::Parse(args[3]); + MicroPython::ExecutionEnvironment::currentExecutionEnvironment()->displaySandbox(); + KDIonContext::sharedContext()->fillCircle(center, r, color); + return mp_const_none; +} + +mp_obj_t modkandinsky_fill_polygon(size_t n_args, const mp_obj_t * args) { + KDCoordinate pointsX[KDContext::k_polygonMaxNumberOfPoints]; + KDCoordinate pointsY[KDContext::k_polygonMaxNumberOfPoints]; + + size_t itemLength; + mp_obj_t * items; + + mp_obj_get_array(args[0], &itemLength, &items); + + if (itemLength < 3) { + mp_raise_ValueError("polygon must have at least 3 points"); + } + else if (itemLength > KDContext::k_polygonMaxNumberOfPoints) { + mp_raise_ValueError("polygon is defined by too many points"); + } + + for(unsigned int i=0; idisplaySandbox(); + KDIonContext::sharedContext()->fillPolygon(pointsX, pointsY, itemLength, color); + return mp_const_none; +} \ No newline at end of file diff --git a/python/port/mod/kandinsky/modkandinsky.h b/python/port/mod/kandinsky/modkandinsky.h index 95f0366aa..12252a81e 100644 --- a/python/port/mod/kandinsky/modkandinsky.h +++ b/python/port/mod/kandinsky/modkandinsky.h @@ -4,4 +4,8 @@ mp_obj_t modkandinsky_color(size_t n_args, const mp_obj_t *args); mp_obj_t modkandinsky_get_pixel(mp_obj_t x, mp_obj_t y); mp_obj_t modkandinsky_set_pixel(mp_obj_t x, mp_obj_t y, mp_obj_t color); mp_obj_t modkandinsky_draw_string(size_t n_args, const mp_obj_t *args); +mp_obj_t modkandinsky_draw_line(size_t n_args, const mp_obj_t *args); +mp_obj_t modkandinsky_draw_circle(size_t n_args, const mp_obj_t *args); mp_obj_t modkandinsky_fill_rect(size_t n_args, const mp_obj_t *args); +mp_obj_t modkandinsky_fill_circle(size_t n_args, const mp_obj_t *args); +mp_obj_t modkandinsky_fill_polygon(size_t n_args, const mp_obj_t *args); \ No newline at end of file diff --git a/python/port/mod/kandinsky/modkandinsky_table.c b/python/port/mod/kandinsky/modkandinsky_table.c index ff916f915..862ad99e3 100644 --- a/python/port/mod/kandinsky/modkandinsky_table.c +++ b/python/port/mod/kandinsky/modkandinsky_table.c @@ -4,7 +4,11 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(modkandinsky_color_obj, 1, 3, modkand STATIC MP_DEFINE_CONST_FUN_OBJ_2(modkandinsky_get_pixel_obj, modkandinsky_get_pixel); STATIC MP_DEFINE_CONST_FUN_OBJ_3(modkandinsky_set_pixel_obj, modkandinsky_set_pixel); STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(modkandinsky_draw_string_obj, 3, 5, modkandinsky_draw_string); +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(modkandinsky_draw_line_obj, 5, 5, modkandinsky_draw_line); +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(modkandinsky_draw_circle_obj, 4, 4, modkandinsky_draw_circle); STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(modkandinsky_fill_rect_obj, 5, 5, modkandinsky_fill_rect); +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(modkandinsky_fill_circle_obj, 4, 4, modkandinsky_fill_circle); +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(modkandinsky_fill_polygon_obj, 2, 2, modkandinsky_fill_polygon); STATIC const mp_rom_map_elem_t modkandinsky_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_kandinsky) }, @@ -12,7 +16,11 @@ STATIC const mp_rom_map_elem_t modkandinsky_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_get_pixel), (mp_obj_t)&modkandinsky_get_pixel_obj }, { MP_ROM_QSTR(MP_QSTR_set_pixel), (mp_obj_t)&modkandinsky_set_pixel_obj }, { MP_ROM_QSTR(MP_QSTR_draw_string), (mp_obj_t)&modkandinsky_draw_string_obj }, + { MP_ROM_QSTR(MP_QSTR_draw_line), (mp_obj_t)&modkandinsky_draw_line_obj }, + { MP_ROM_QSTR(MP_QSTR_draw_circle), (mp_obj_t)&modkandinsky_draw_circle_obj }, { MP_ROM_QSTR(MP_QSTR_fill_rect), (mp_obj_t)&modkandinsky_fill_rect_obj }, + { MP_ROM_QSTR(MP_QSTR_fill_circle), (mp_obj_t)&modkandinsky_fill_circle_obj }, + { MP_ROM_QSTR(MP_QSTR_fill_polygon), (mp_obj_t)&modkandinsky_fill_polygon_obj }, }; STATIC MP_DEFINE_CONST_DICT(modkandinsky_module_globals, modkandinsky_module_globals_table); From eb02fb5baa5a7d54543bdbf4f26a66ad5e3358b4 Mon Sep 17 00:00:00 2001 From: ArtichOwO Date: Wed, 14 Jul 2021 19:52:32 +0200 Subject: [PATCH 33/48] [ion/mac] Added missing source --- ion/src/simulator/macos/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/ion/src/simulator/macos/Makefile b/ion/src/simulator/macos/Makefile index a640a6571..cdc8015d6 100644 --- a/ion/src/simulator/macos/Makefile +++ b/ion/src/simulator/macos/Makefile @@ -15,6 +15,7 @@ ion_src += $(addprefix ion/src/simulator/shared/, \ collect_registers.cpp \ haptics.cpp \ journal.cpp \ + store_script.cpp \ ) ifeq ($(EPSILON_TELEMETRY),1) From fb4fc4862d74ef88868e15470772bb0761627b94 Mon Sep 17 00:00:00 2001 From: Laury Date: Tue, 31 Aug 2021 15:48:11 +0200 Subject: [PATCH 34/48] [python] Enable operator overload --- python/port/genhdr/qstrdefs.in.h | 44 ++++++++++++++++++++++++++++++++ python/src/py/mpconfig.h | 10 ++++---- 2 files changed, 49 insertions(+), 5 deletions(-) diff --git a/python/port/genhdr/qstrdefs.in.h b/python/port/genhdr/qstrdefs.in.h index 7d65a07c2..20dec6a7a 100644 --- a/python/port/genhdr/qstrdefs.in.h +++ b/python/port/genhdr/qstrdefs.in.h @@ -49,6 +49,7 @@ Q(GeneratorExit) Q(ImportError) Q(IndentationError) Q(IndexError) +Q(keepends) Q(KeyError) Q(KeyboardInterrupt) Q(LookupError) @@ -67,47 +68,88 @@ Q(UnicodeError) Q(ValueError) Q(ZeroDivisionError) Q(_0x0a_) +Q(__abs__) Q(__add__) +Q(__and__) Q(__bool__) Q(__build_class__) Q(__call__) Q(__class__) Q(__contains__) Q(__delitem__) +Q(__divmod__) Q(__enter__) Q(__eq__) Q(__exit__) +Q(__floordiv__) Q(__ge__) Q(__getattr__) Q(__getitem__) Q(__gt__) Q(__hash__) Q(__iadd__) +Q(__iand__) +Q(__ifloordiv__) +Q(__ilshift__) +Q(__imatmul__) +Q(__imod__) Q(__import__) +Q(__imul__) Q(__init__) Q(__int__) +Q(__invert__) +Q(__ior__) +Q(__ipow__) +Q(__irshift__) Q(__isub__) Q(__iter__) +Q(__itruediv__) +Q(__ixor__) Q(__le__) Q(__len__) +Q(__lshift__) Q(__lt__) Q(__main__) +Q(__matmul__) +Q(__mod__) Q(__module__) +Q(__mul__) Q(__name__) #if __EMSCRIPTEN__ Q(__ne__) #endif +Q(__neg__) Q(__new__) Q(__next__) +Q(__or__) Q(__path__) +Q(__pos__) +Q(__pow__) Q(__qualname__) +Q(__radd__) +Q(__rand__) Q(__repl_print__) Q(__repr__) Q(__reversed__) +Q(__rfloordiv__) +Q(__rlshift__) +Q(__rmatmul__) +Q(__rmod__) +Q(__rmul__) +Q(__ror__) +Q(__rpow__) +Q(__rrshift__) +Q(__rshift__) +Q(__rsub__) +Q(__rtruediv__) +Q(__rxor__) Q(__setitem__) Q(__str__) Q(__sub__) +Q(__sub__) Q(__traceback__) +Q(__truediv__) +Q(__xor__) Q(_brace_open__colon__hash_b_brace_close_) Q(_lt_dictcomp_gt_) Q(_lt_genexpr_gt_) @@ -141,6 +183,7 @@ Q(bytecode) Q(bytes) Q(callable) Q(ceil) +Q(center) Q(choice) Q(chr) Q(classmethod) @@ -293,6 +336,7 @@ Q(slice) Q(sort) Q(sorted) Q(split) +Q(splitlines) Q(sqrt) Q(start) Q(startswith) diff --git a/python/src/py/mpconfig.h b/python/src/py/mpconfig.h index 1e786f753..7c213c668 100644 --- a/python/src/py/mpconfig.h +++ b/python/src/py/mpconfig.h @@ -830,7 +830,7 @@ typedef double mp_float_t; // Whether str.center() method provided #ifndef MICROPY_PY_BUILTINS_STR_CENTER -#define MICROPY_PY_BUILTINS_STR_CENTER (0) +#define MICROPY_PY_BUILTINS_STR_CENTER (1) #endif // Whether str.count() method provided @@ -850,7 +850,7 @@ typedef double mp_float_t; // Whether str.splitlines() method provided #ifndef MICROPY_PY_BUILTINS_STR_SPLITLINES -#define MICROPY_PY_BUILTINS_STR_SPLITLINES (0) +#define MICROPY_PY_BUILTINS_STR_SPLITLINES (1) #endif // Whether to support bytearray object @@ -934,20 +934,20 @@ typedef double mp_float_t; // "Reverse" methods are controlled by // MICROPY_PY_REVERSE_SPECIAL_METHODS below. #ifndef MICROPY_PY_ALL_SPECIAL_METHODS -#define MICROPY_PY_ALL_SPECIAL_METHODS (0) +#define MICROPY_PY_ALL_SPECIAL_METHODS (1) #endif // Whether to support all inplace arithmetic operarion methods // (__imul__, etc.) #ifndef MICROPY_PY_ALL_INPLACE_SPECIAL_METHODS -#define MICROPY_PY_ALL_INPLACE_SPECIAL_METHODS (0) +#define MICROPY_PY_ALL_INPLACE_SPECIAL_METHODS (1) #endif // Whether to support reverse arithmetic operarion methods // (__radd__, etc.). Additionally gated by // MICROPY_PY_ALL_SPECIAL_METHODS. #ifndef MICROPY_PY_REVERSE_SPECIAL_METHODS -#define MICROPY_PY_REVERSE_SPECIAL_METHODS (0) +#define MICROPY_PY_REVERSE_SPECIAL_METHODS (1) #endif // Whether to support compile function From 210a83b04d78ee7bd51d22ec90934457f566f9ff Mon Sep 17 00:00:00 2001 From: Laury Date: Tue, 31 Aug 2021 16:17:36 +0200 Subject: [PATCH 35/48] [python/kandinsky] Added get_palette() function --- apps/code/catalog.de.i18n | 61 ++++++++++--------- apps/code/catalog.en.i18n | 1 + apps/code/catalog.es.i18n | 1 + apps/code/catalog.fr.i18n | 1 + apps/code/catalog.hu.i18n | 1 + apps/code/catalog.it.i18n | 1 + apps/code/catalog.nl.i18n | 1 + apps/code/catalog.pt.i18n | 1 + apps/code/catalog.universal.i18n | 1 + apps/code/python_toolbox.cpp | 3 +- python/port/genhdr/qstrdefs.in.h | 7 +++ python/port/mod/kandinsky/modkandinsky.cpp | 13 ++++ python/port/mod/kandinsky/modkandinsky.h | 1 + .../port/mod/kandinsky/modkandinsky_table.c | 2 + 14 files changed, 64 insertions(+), 31 deletions(-) diff --git a/apps/code/catalog.de.i18n b/apps/code/catalog.de.i18n index b002d9025..1721b0fea 100644 --- a/apps/code/catalog.de.i18n +++ b/apps/code/catalog.de.i18n @@ -63,6 +63,7 @@ PythonFloor = "Floor" PythonFmod = "a modulo b" PythonFrExp = "Mantissa and exponent of x: (m,e)" PythonGamma = "Gamma function" +PythonGetPalette = "Themenpalette erhalten" PythonGetPixel = "Return pixel (x,y) color" PythonGetrandbits = "Integer with k random bits" PythonGrid = "Toggle the visibility of the grid" @@ -101,36 +102,36 @@ PythonMathFunction = "math module function prefix" PythonMatplotlibPyplotFunction = "matplotlib.pyplot module prefix" PythonMax = "Maximum" PythonMin = "Minimum" -PythonModf = "Fractional and integer parts of x" -PythonMonotonic = "Value of a monotonic clock" -PythonOct = "Convert integer to octal" -PythonPhase = "Phase of z" -PythonPlot = "Plot y versus x as lines" -PythonPolar = "z in polar coordinates" -PythonPop = "Remove and return the last item" -PythonPower = "x raised to the power y" -PythonPrint = "Print object" -PythonRadians = "Convert x from degrees to radians" -PythonRandint = "Random integer in [a,b]" -PythonRandom = "Floating point number in [0,1[" -PythonRandomFunction = "random module function prefix" -PythonRandrange = "Random number in range(start,stop)" -PythonRangeStartStop = "List from start to stop-1" -PythonRangeStop = "List from 0 to stop-1" -PythonRect = "Convert to cartesian coordinates" -PythonRemove = "Remove the first occurrence of x" -PythonReverse = "Reverse the elements of the list" -PythonRound = "Round to n digits" -PythonScatter = "Draw a scatter plot of y versus x" -PythonSeed = "Initialize random number generator" -PythonSetPixel = "Color pixel (x,y)" -PythonShow = "Display the figure" -PythonSin = "Sine" -PythonSinh = "Hyperbolic sine" -PythonSleep = "Suspend the execution for t seconds" -PythonSort = "Sort the list" -PythonSqrt = "Wurzel" -PythonSum = "Sum the items of a list" +PythonModf = "Bruch- und Ganzzahl-Anteile von x" +PythonMonotonic = "Wert einer monotonen Uhr" +PythonOct = "Ganzzahl in Oktal umwandeln" +PythonPhase = "Phase von z" +PythonPlot = "Plotten von y gegen x als Linien" +PythonPolar = "z in Polarkoordinaten" +PythonPop = "Letztes Element abnehmen" +PythonPower = "x erhöht mit der Potenz y" +PythonPrint = "Objekt drucken" +PythonRadians = "x von Grad in Bogenmaß umrechnen" +PythonRandint = "Zufällige Ganzzahl in [a,b]" +PythonRandom = "Fließkommazahl in [0,1]" +PythonRandomFunction = "Random-Modul Funktionspräfix" +PythonRandrange = "Zufallszahl im Bereich(start,stop)" +PythonRangeStartStop = "Liste von Start bis Stop-1" +PythonRangeStop = "Liste von 0 bis Stop-1" +PythonRect = "In kartesische Koordinaten" +PythonRemove = "Entferne das erste Vorkommen von x" +PythonReverse = "Kehrt die Elemente der Liste um" +PythonRound = "Runden auf n Stellen" +PythonScatter = "Streudiagramm von y gg. x zeichnen" +PythonSeed = "Zufallszahlengenerator initiieren" +PythonSetPixel = "Pixel (x,y) einfärben" +PythonShow = "Figur anzeigen" +PythonSin = "Sinus" +PythonSinh = "Hyperbolischer Sinus" +PythonSleep = "Ausführung aussetzen für t Sekunden" +PythonSort = "Die Liste sortieren" +PythonSqrt = "Quadratwurzel" +PythonSum = "Summe der Elemente einer Liste" PythonTan = "Tangens" PythonTanh = "Hyperbolic tangent" PythonText = "Display a text at (x,y) coordinates" diff --git a/apps/code/catalog.en.i18n b/apps/code/catalog.en.i18n index 458ec1eeb..1a826ecf0 100644 --- a/apps/code/catalog.en.i18n +++ b/apps/code/catalog.en.i18n @@ -62,6 +62,7 @@ PythonFloor = "Floor" PythonFmod = "a modulo b" PythonFrExp = "Mantissa and exponent of x: (m,e)" PythonGamma = "Gamma function" +PythonGetPalette = "Get theme palette" PythonGetPixel = "Return pixel (x,y) color" PythonGetrandbits = "Integer with k random bits" PythonGrid = "Toggle the visibility of the grid" diff --git a/apps/code/catalog.es.i18n b/apps/code/catalog.es.i18n index 4ac5de287..ff6de6a86 100644 --- a/apps/code/catalog.es.i18n +++ b/apps/code/catalog.es.i18n @@ -62,6 +62,7 @@ PythonFloor = "Floor" PythonFmod = "a modulo b" PythonFrExp = "Mantissa and exponent of x: (m,e)" PythonGamma = "Gamma function" +PythonGetPalette = "Get theme palette" PythonGetPixel = "Return pixel (x,y) color" PythonGetrandbits = "Integer with k random bits" PythonGrid = "Toggle the visibility of the grid" diff --git a/apps/code/catalog.fr.i18n b/apps/code/catalog.fr.i18n index e3c3c22b8..05725e9c7 100644 --- a/apps/code/catalog.fr.i18n +++ b/apps/code/catalog.fr.i18n @@ -62,6 +62,7 @@ PythonFloor = "Partie entière" PythonFmod = "a modulo b" PythonFrExp = "Mantisse et exposant de x : (m,e)" PythonGamma = "Fonction gamma" +PythonGetPalette = "Obtient la palette du thème" PythonGetPixel = "Renvoie la couleur du pixel (x,y)" PythonGetrandbits = "Nombre aléatoire sur k bits" PythonGrid = "Affiche ou masque la grille" diff --git a/apps/code/catalog.hu.i18n b/apps/code/catalog.hu.i18n index 2749d3789..a9349ac9c 100644 --- a/apps/code/catalog.hu.i18n +++ b/apps/code/catalog.hu.i18n @@ -62,6 +62,7 @@ PythonFloor = "Egész része" PythonFmod = "a modulo b" PythonFrExp = "X mantissája és kiállítója" PythonGamma = "Gamma funkció" +PythonGetPalette = "Téma paletta beszerzése" PythonGetPixel = "Visszatéríti (x,y) színét" PythonGetrandbits = "Váletlenszám visszatérítése k biten" PythonGrid = "Rács megjelenítése/elrejtése" diff --git a/apps/code/catalog.it.i18n b/apps/code/catalog.it.i18n index e4fdd0f26..9cd02959a 100644 --- a/apps/code/catalog.it.i18n +++ b/apps/code/catalog.it.i18n @@ -62,6 +62,7 @@ PythonFloor = "Parte intera" PythonFmod = "a modulo b" PythonFrExp = "Mantissa ed esponente di x : (m,e)" PythonGamma = "Funzione gamma" +PythonGetPalette = "Ottieni la tavolozza del tema" PythonGetPixel = "Restituisce colore del pixel(x,y)" PythonGetrandbits = "Numero aleatorio con k bit" PythonGrid = "Attiva la visibilità della griglia" diff --git a/apps/code/catalog.nl.i18n b/apps/code/catalog.nl.i18n index 8a8d4265b..31b2f1d60 100644 --- a/apps/code/catalog.nl.i18n +++ b/apps/code/catalog.nl.i18n @@ -62,6 +62,7 @@ PythonFloor = "Vloer" PythonFmod = "a modulo b" PythonFrExp = "Mantisse en exponent van x: (m,e)" PythonGamma = "Gammafunctie" +PythonGetPalette = "Thema palet krijgen" PythonGetPixel = "Geef pixel (x,y) kleur (rgb)" PythonGetrandbits = "Integer met k willekeurige bits" PythonGrid = "Verander zichtbaarheid raster" diff --git a/apps/code/catalog.pt.i18n b/apps/code/catalog.pt.i18n index f432b28ce..a06882d6a 100644 --- a/apps/code/catalog.pt.i18n +++ b/apps/code/catalog.pt.i18n @@ -62,6 +62,7 @@ PythonFloor = "Parte inteira" PythonFmod = "a módulo b" PythonFrExp = "Coeficiente e expoente de x: (m, e)" PythonGamma = "Função gama" +PythonGetPalette = "Obter paleta temática" PythonGetPixel = "Devolve a cor do pixel (x,y)" PythonGetrandbits = "Número inteiro aleatório com k bits" PythonGrid = "Alterar visibilidade da grelha" diff --git a/apps/code/catalog.universal.i18n b/apps/code/catalog.universal.i18n index e6fa758a6..1b5b3684b 100644 --- a/apps/code/catalog.universal.i18n +++ b/apps/code/catalog.universal.i18n @@ -69,6 +69,7 @@ PythonCommandFloor = "floor(x)" PythonCommandFmod = "fmod(a,b)" PythonCommandFrExp = "frexp(x)" PythonCommandGamma = "gamma(x)" +PythonCommandGetPalette = "get_palette()" PythonCommandGetPixel = "get_pixel(x,y)" PythonCommandGetrandbits = "getrandbits(k)" PythonCommandGrid = "grid()" diff --git a/apps/code/python_toolbox.cpp b/apps/code/python_toolbox.cpp index 0036acdb3..c11a09632 100644 --- a/apps/code/python_toolbox.cpp +++ b/apps/code/python_toolbox.cpp @@ -197,7 +197,8 @@ const ToolboxMessageTree KandinskyModuleChildren[] = { ToolboxMessageTree::Leaf(I18n::Message::PythonCommandDrawCircle, I18n::Message::PythonDrawCircle), ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFillRect, I18n::Message::PythonFillRect), ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFillCircle, I18n::Message::PythonFillCircle), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFillPolygon, I18n::Message::PythonFillPolygon) + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandFillPolygon, I18n::Message::PythonFillPolygon), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandGetPalette, I18n::Message::PythonGetPalette) }; const ToolboxMessageTree IonModuleChildren[] = { diff --git a/python/port/genhdr/qstrdefs.in.h b/python/port/genhdr/qstrdefs.in.h index 20dec6a7a..65478063f 100644 --- a/python/port/genhdr/qstrdefs.in.h +++ b/python/port/genhdr/qstrdefs.in.h @@ -429,6 +429,13 @@ Q(large_font) Q(small_font) Q(wait_vblank) Q(get_keys) +Q(get_palette) + +Q(PrimaryText) +Q(SecondaryText) +Q(AccentText) +Q(Toolbar) +Q(HomeBackground) // Keys QSTRs Q(left) diff --git a/python/port/mod/kandinsky/modkandinsky.cpp b/python/port/mod/kandinsky/modkandinsky.cpp index ff55f3f15..29218376c 100644 --- a/python/port/mod/kandinsky/modkandinsky.cpp +++ b/python/port/mod/kandinsky/modkandinsky.cpp @@ -2,9 +2,11 @@ extern "C" { #include "modkandinsky.h" #include } +#include #include #include #include "port.h" +#include static mp_obj_t TupleForKDColor(KDColor c) { @@ -245,3 +247,14 @@ mp_obj_t modkandinsky_get_keys() { return result; } + +mp_obj_t modkandinsky_get_palette() { + mp_obj_t modkandinsky_palette_table = mp_obj_new_dict(0); + mp_obj_dict_store(modkandinsky_palette_table, MP_ROM_QSTR(MP_QSTR_PrimaryText), TupleForKDColor(Palette::PrimaryText)); + mp_obj_dict_store(modkandinsky_palette_table, MP_ROM_QSTR(MP_QSTR_SecondaryText), TupleForKDColor(Palette::SecondaryText)); + mp_obj_dict_store(modkandinsky_palette_table, MP_ROM_QSTR(MP_QSTR_AccentText), TupleForKDColor(Palette::AccentText)); + mp_obj_dict_store(modkandinsky_palette_table, MP_ROM_QSTR(MP_QSTR_Toolbar), TupleForKDColor(Palette::Toolbar)); + mp_obj_dict_store(modkandinsky_palette_table, MP_ROM_QSTR(MP_QSTR_HomeBackground), TupleForKDColor(Palette::HomeBackground)); + + return modkandinsky_palette_table; +} diff --git a/python/port/mod/kandinsky/modkandinsky.h b/python/port/mod/kandinsky/modkandinsky.h index 4d4024aef..3ce71155a 100644 --- a/python/port/mod/kandinsky/modkandinsky.h +++ b/python/port/mod/kandinsky/modkandinsky.h @@ -11,3 +11,4 @@ mp_obj_t modkandinsky_fill_circle(size_t n_args, const mp_obj_t *args); mp_obj_t modkandinsky_fill_polygon(size_t n_args, const mp_obj_t *args); mp_obj_t modkandinsky_wait_vblank(); mp_obj_t modkandinsky_get_keys(); +mp_obj_t modkandinsky_get_palette(); diff --git a/python/port/mod/kandinsky/modkandinsky_table.c b/python/port/mod/kandinsky/modkandinsky_table.c index 1cc76c73c..ce962e4c1 100644 --- a/python/port/mod/kandinsky/modkandinsky_table.c +++ b/python/port/mod/kandinsky/modkandinsky_table.c @@ -11,6 +11,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(modkandinsky_fill_circle_obj, 4, 4, m STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(modkandinsky_fill_polygon_obj, 2, 2, modkandinsky_fill_polygon); STATIC MP_DEFINE_CONST_FUN_OBJ_0(modkandinsky_wait_vblank_obj, modkandinsky_wait_vblank); STATIC MP_DEFINE_CONST_FUN_OBJ_0(modkandinsky_get_keys_obj, modkandinsky_get_keys); +STATIC MP_DEFINE_CONST_FUN_OBJ_0(modkandinsky_get_palette_obj, modkandinsky_get_palette); STATIC const mp_rom_map_elem_t modkandinsky_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_kandinsky) }, @@ -27,6 +28,7 @@ STATIC const mp_rom_map_elem_t modkandinsky_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_small_font), mp_const_false }, { MP_ROM_QSTR(MP_QSTR_wait_vblank), (mp_obj_t)&modkandinsky_wait_vblank_obj }, { MP_ROM_QSTR(MP_QSTR_get_keys), (mp_obj_t)&modkandinsky_get_keys_obj }, + { MP_ROM_QSTR(MP_QSTR_get_palette), (mp_obj_t)&modkandinsky_get_palette_obj }, }; STATIC MP_DEFINE_CONST_DICT(modkandinsky_module_globals, modkandinsky_module_globals_table); From c0739b0e062cd0c531d0dbade7ccea5ea9d1e9fe Mon Sep 17 00:00:00 2001 From: Laury Date: Tue, 31 Aug 2021 19:23:09 +0200 Subject: [PATCH 36/48] [atomic] updated submodule --- .gitmodules | 2 +- apps/atomic | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index 02641c8d5..9725262b5 100644 --- a/.gitmodules +++ b/.gitmodules @@ -3,4 +3,4 @@ url = https://github.com/Omega-Numworks/Omega-RPN.git [submodule "apps/atomic"] path = apps/atomic - url = https://github.com/Omega-Numworks/Omega-Atomic.git + url = https://github.com/Lauryy06/atomic diff --git a/apps/atomic b/apps/atomic index 64f2e38ed..2a621d6d4 160000 --- a/apps/atomic +++ b/apps/atomic @@ -1 +1 @@ -Subproject commit 64f2e38ed1e4b8a896b0488039fb866b7015ff3f +Subproject commit 2a621d6d48e98595f86628ce9fc479e8d6e4ac17 From 7b4a94296bd5879e4496d5139c0b93f535117b96 Mon Sep 17 00:00:00 2001 From: Laury Date: Wed, 1 Sep 2021 17:31:51 +0200 Subject: [PATCH 37/48] [calculation][poincare] Revert 'b8544e3' and improve equal simplification --- apps/calculation/app.cpp | 2 +- apps/shared/function_app.cpp | 10 +++++++--- apps/shared/text_field_delegate_app.cpp | 8 +++++++- apps/solver/app.cpp | 6 ------ apps/solver/app.h | 3 --- poincare/include/poincare/equal.h | 2 +- poincare/src/equal.cpp | 16 ++++++---------- 7 files changed, 22 insertions(+), 25 deletions(-) diff --git a/apps/calculation/app.cpp b/apps/calculation/app.cpp index 2c5c97d3c..ad344c316 100644 --- a/apps/calculation/app.cpp +++ b/apps/calculation/app.cpp @@ -72,7 +72,7 @@ bool App::isAcceptableExpression(const Poincare::Expression expression) { return false; } } - return !(expression.isUninitialized() || expression.type() == ExpressionNode::Type::Equal); + return !expression.isUninitialized(); } void App::didBecomeActive(Window * window) { diff --git a/apps/shared/function_app.cpp b/apps/shared/function_app.cpp index fbdc4f43a..a63ca3284 100644 --- a/apps/shared/function_app.cpp +++ b/apps/shared/function_app.cpp @@ -31,9 +31,13 @@ void FunctionApp::willBecomeInactive() { ::App::willBecomeInactive(); } - -bool FunctionApp::isAcceptableExpression(const Expression exp) { - return TextFieldDelegateApp::isAcceptableExpression(exp) && ExpressionCanBeSerialized(exp, false, Expression(), localContext()); +bool FunctionApp::isAcceptableExpression(const Poincare::Expression expression) { + /* We forbid functions whose type is equal because the input "2+f(3)" would be + * simplify to an expression with an nested equal node which makes no sense. */ + if (!TextFieldDelegateApp::ExpressionCanBeSerialized(expression, false, Expression(), localContext()) || expression.type() == ExpressionNode::Type::Equal) { + return false; + } + return TextFieldDelegateApp::isAcceptableExpression(expression); } } diff --git a/apps/shared/text_field_delegate_app.cpp b/apps/shared/text_field_delegate_app.cpp index e66a87422..ece157f9b 100644 --- a/apps/shared/text_field_delegate_app.cpp +++ b/apps/shared/text_field_delegate_app.cpp @@ -85,7 +85,13 @@ bool TextFieldDelegateApp::isFinishingEvent(Ion::Events::Event event) { } bool TextFieldDelegateApp::isAcceptableExpression(const Expression exp) { - return !(exp.isUninitialized() || exp.type() == ExpressionNode::Type::Store || exp.type() == ExpressionNode::Type::Equal); + if (exp.isUninitialized()) { + return false; + } + if (exp.type() == ExpressionNode::Type::Store) { + return false; + } + return true; } bool TextFieldDelegateApp::ExpressionCanBeSerialized(const Expression expression, bool replaceAns, Expression ansExpression, Context * context) { diff --git a/apps/solver/app.cpp b/apps/solver/app.cpp index 11f96a32a..1c88b7df5 100644 --- a/apps/solver/app.cpp +++ b/apps/solver/app.cpp @@ -68,10 +68,4 @@ void App::willBecomeInactive() { ::App::willBecomeInactive(); } - -bool App::isAcceptableExpression(const Poincare::Expression exp) { - return TextFieldDelegateApp::ExpressionCanBeSerialized(exp, false, Poincare::Expression(), localContext()) - && !(exp.isUninitialized() || exp.type() == Poincare::ExpressionNode::Type::Store); -} - } diff --git a/apps/solver/app.h b/apps/solver/app.h index a4919978e..898c3c6b8 100644 --- a/apps/solver/app.h +++ b/apps/solver/app.h @@ -47,9 +47,6 @@ public: void willBecomeInactive() override; TELEMETRY_ID("Solver"); private: - // TextFieldDelegateApp - bool isAcceptableExpression(const Poincare::Expression expression) override; - App(Snapshot * snapshot); SolutionsController m_solutionsController; IntervalController m_intervalController; diff --git a/poincare/include/poincare/equal.h b/poincare/include/poincare/equal.h index fa067652f..1f61b2c57 100644 --- a/poincare/include/poincare/equal.h +++ b/poincare/include/poincare/equal.h @@ -41,7 +41,7 @@ public: // For the equation A = B, create the reduced expression A-B Expression standardEquation(Context * context, Preferences::ComplexFormat complexFormat, Preferences::AngleUnit angleUnit, Preferences::UnitFormat unitFormat, ExpressionNode::ReductionTarget reductionTarget) const; // Expression - Expression shallowReduce(); + Expression shallowReduce(ExpressionNode::ReductionContext reductionContext); }; } diff --git a/poincare/src/equal.cpp b/poincare/src/equal.cpp index 4468a2e32..3e8ca67bd 100644 --- a/poincare/src/equal.cpp +++ b/poincare/src/equal.cpp @@ -23,7 +23,7 @@ extern "C" { namespace Poincare { Expression EqualNode::shallowReduce(ReductionContext reductionContext) { - return Equal(this).shallowReduce(); + return Equal(this).shallowReduce(reductionContext); } Layout EqualNode::createLayout(Preferences::PrintFloatMode floatDisplayMode, int numberOfSignificantDigits) const { @@ -49,19 +49,15 @@ Expression Equal::standardEquation(Context * context, Preferences::ComplexFormat return sub.reduce(ExpressionNode::ReductionContext(context, complexFormat, angleUnit, unitFormat, reductionTarget)); } -Expression Equal::shallowReduce() { - { - Expression e = Expression::defaultShallowReduce(); - if (e.isUndefined()) { - return e; - } - } - if (childAtIndex(0).isIdenticalTo(childAtIndex(1))) { +Expression Equal::shallowReduce(ExpressionNode::ReductionContext reductionContext) { + + Expression e = Equal::Builder(Subtraction::Builder(childAtIndex(0).clone(), childAtIndex(1).clone()).shallowReduce(reductionContext), Rational::Builder(0)); + if (e.childAtIndex(0).isIdenticalTo(e.childAtIndex(1))) { Expression result = Rational::Builder(1); replaceWithInPlace(result); return result; } - return *this; + return e; } } From 0df76f1680ada11199b930d468b7581973a31cb4 Mon Sep 17 00:00:00 2001 From: Laporte <40714786+Laporte12974@users.noreply.github.com> Date: Wed, 1 Sep 2021 17:58:06 +0200 Subject: [PATCH 38/48] [readme] Update readme * Update README.fr.md * Update README.md --- README.fr.md | 74 +++++++++++++++++++++++++++------------------------- README.md | 63 ++++++++++++++++++++++---------------------- 2 files changed, 70 insertions(+), 67 deletions(-) diff --git a/README.fr.md b/README.fr.md index 0eda582df..4ee47639e 100644 --- a/README.fr.md +++ b/README.fr.md @@ -1,37 +1,34 @@ -

+

cc by-nc-sa 4.0 - Issues + Issues
- Discord + Discord

> Don't understand french ? speak english ? here's the [english README](./README.md) ! ## À propos -Omega est un fork d'Epsilon, l'OS de Numworks tournant sur les calculatrices du même nom, qui apporte beaucoup de fonctionnalités en plus. Omega est fait pour ceux qui aimeraient ajouter certaines fonctionnalités ayant été rejetées par Numworks à leurs calculatrices (pour des raisons 100% compréhensibles !). [Essayez en ligne](https://getomega.web.app/simulator). +Upsilon est un fork d'Omega, un fork d'Epsilon, l'OS de Numworks tournant sur les calculatrices du même nom, qui apporte beaucoup de fonctionnalités en plus, mais qui fut archivé et fermé pour des raisons légales après un changement de politique de Numworks. Upsilon est fait pour ceux qui aimeraient voir un futur pour les OS créées par les utilisateurs pour Numworks, même après l'arrèt du projet initial. ### Quelques fonctionnalités supplémentaires -- Retour du calcul littéral -- Une application RPN -- Application Externes +- RDes améliorations du module python Kandinsky +- Un support pour fonds d'écrans personnalisés +- Des applications externes - Des thèmes -- Python amélioré (module os, méthode open...) -- Un tableau périodique et toutes les masses molaires des éléments dans la toolbox -- *Ainsi que d'autres à découvrir...* [Changelogs complets](https://github.com/Omega-Numworks/Omega/wiki/Changelog) | [Fonctionnalités princpales & captures d'écran](https://github.com/Omega-Numworks/Omega/wiki/Main-features). +- Des améliorations pour python +- Un tableau périodique légèrement modifié +- L'utilisation du signe "=" +- *Ainsi que tout ce qui a été ajouté sur Omega, et bien plus...* [Changelogs complets d'Omega](https://github.com/Omega-Numworks/Omega/wiki/Changelog) | [Fonctionnalités principales d'Omega & captures d'écran](https://github.com/Omega-Numworks/Omega/wiki/Main-features). ## Installation -### Automatique - -Vous pouvez installer Omega automatiquement depuis [notre site](https://getomega.web.app/) sur la page "installer". - -

Omega Banner Discord

- ### Manuelle +*A l'heure actuelle, seule l'installation manuelle est possible.* + Tout d'abord, suivez **la première étape** [ici](https://www.numworks.com/resources/engineering/software/build/), puis :
@@ -40,15 +37,15 @@ Tout d'abord, suivez **la première étape** [ici](https://www.numworks.com/reso (note : vous pouvez changer `EPSILON_I18N=fr` en `en`, `nl`, `pt`, `it`, `de`, `es` ou `hu`). ```bash -git clone --recursive https://github.com/Omega-Numworks/Omega.git -cd Omega +git clone --recursive https://github.com/Lauryy06/Upsilon.git +cd Upsilon git checkout omega-master make MODEL=n0100 clean make MODEL=n0100 EPSILON_I18N=fr OMEGA_USERNAME="{Votre nom ici, 15 caractères max}" -j4 make MODEL=n0100 epsilon_flash ``` -Important : N'oubliez pas l'argument `--recursive`, Omega a besoin de sous-modules. +Important : N'oubliez pas l'argument `--recursive`, Upsilon a besoin de sous-modules. Vous pouvez aussi changer le nombre de processus parallèles pendant la compilation en changeant la valeur suivant `-j`.
@@ -57,15 +54,15 @@ Vous pouvez aussi changer le nombre de processus parallèles pendant la compilat Modèle n0110 ```bash -git clone --recursive https://github.com/Omega-Numworks/Omega.git -cd Omega +git clone --recursive https://github.com/Lauryy06/Upsilon.git +cd Upsilon git checkout omega-master make clean make OMEGA_USERNAME="{Votre nom ici, 15 caractères max}" -j4 make epsilon_flash ``` -Important : N'oubliez pas l'argument `--recursive`, Omega a besoin de sous-modules. +Important : N'oubliez pas l'argument `--recursive`, Upsilon a besoin de sous-modules. Vous pouvez aussi changer le nombre de processus parallèles pendant la compilation en changeant la valeur suivant `-j`. @@ -73,11 +70,11 @@ Vous pouvez aussi changer le nombre de processus parallèles pendant la compilat
Fichiers binaires -Ces fichiers peuvent être utilisés pour distribuer Omega (pour que tout le monde puisse le flasher via [Webdfu_Numworks](https://ti-planet.github.io/webdfu_numworks/)). +Ces fichiers peuvent être utilisés pour distribuer Upsilon (pour que tout le monde puisse le flasher via [Webdfu_Numworks](https://ti-planet.github.io/webdfu_numworks/)). ```bash -git clone --recursive https://github.com/Omega-Numworks/Omega.git -cd Omega +git clone --recursive https://github.com/Lauryy06/Upsilon.git +cd Upsilon git checkout omega-master make clean make MODEL=n0100 OMEGA_USERNAME="" -j8 @@ -86,7 +83,7 @@ make OMEGA_USERNAME="" -j8 make OMEGA_USERNAME="" binpack -j8 ``` -Important : N'oubliez pas l'argument `--recursive`, Omega a besoin de sous-modules. +Important : N'oubliez pas l'argument `--recursive`, Upsilon a besoin de sous-modules. Vous pouvez aussi changer le nombre de processus parallèles pendant la compilation en changeant la valeur suivant `-j`.
@@ -104,11 +101,11 @@ cd emsdk source emsdk_env.sh ``` -Puis, compilez Omega : +Puis, compilez Upsilon : ```bash -git clone --recursive https://github.com/Omega-Numworks/Omega.git -cd Omega +git clone --recursive https://github.com/Lauryy06/Upsilon.git +cd Upsilon git checkout omega-master make clean make PLATFORM=simulator TARGET=web OMEGA_USERNAME="{Votre nom ici, 15 caractères max}" -j4 @@ -116,7 +113,7 @@ make PLATFORM=simulator TARGET=web OMEGA_USERNAME="{Votre nom ici, 15 caractère Le simulateur se trouve dans `output/release/simulator/web/simulator.zip` -Important : N'oubliez pas l'argument `--recursive`, Omega a besoin de sous-modules. +Important : N'oubliez pas l'argument `--recursive`, Upsilon a besoin de sous-modules. Vous pouvez aussi changer le nombre de processus parallèles pendant la compilation en changeant la valeur suivant `-j`. @@ -127,8 +124,8 @@ Vous pouvez aussi changer le nombre de processus parallèles pendant la compilat Vous aurez besoin de devkitPro et de devkitARM disponible dans votre `$PATH` (instructions [ici](https://devkitpro.org/wiki/Getting_Started) (en anglais)) ```bash -git clone --recursive https://github.com/Omega-Numworks/Omega.git -cd Omega +git clone --recursive https://github.com/Lauryy06/Upsilon.git +cd Upsilon git checkout --recursive omega-dev make PLATFORM=simulator TARGET=3ds -j ``` @@ -141,16 +138,18 @@ Vous pouvez ensuite copier epsilon.3dsx sur une carte SD pour l'exécuter depuis -Si vous avez besoin d'aide, n'hésitez pas à rejoindre notre serveur discord : https://discord.gg/X2TWhh9 +Si vous avez besoin d'aide, n'hésitez pas à rejoindre notre serveur discord : https://discord.gg/Q9buEMduXG -

Omega Banner Discord

+

Omega Banner Discord

--- ## Contribution -Pour contribuer, merci de lire le [Wiki](https://github.com/Omega-Numworks/Omega/wiki/Contributing) +Pour contribuer, merci de lire le [Wiki d'Omega](https://github.com/Omega-Numworks/Omega/wiki/Contributing), les mêmes règles s'appliquent ici. -## Nos autres projets +## Les autres projets + +Les anciens projets d'Omega, avant sa fermeture, qui ont été utilisés pour ce projet * [Omega Themes](https://github.com/Omega-Numworks/Omega-Themes) * [Omega Website](https://github.com/Omega-Numworks/Omega-Website) @@ -163,6 +162,8 @@ Pour contribuer, merci de lire le [Wiki](https://github.com/Omega-Numworks/Omega ## À propos d'Epsilon +Upsilon est un fork d'Omega, visant a continuer le projet des OS utilisateurs pour Numworks + Omega est un fork d'Epsilon, un système d'exploitation performant pour calculatrices graphiques. Il inclut huit applications pour les mathématiques de lycée et d'études supérieurs Vous pouvez essayer Epsilon depuis votre navigateur sur le [simulateur en ligne](https://www.numworks.com/simulator/). @@ -175,3 +176,4 @@ NumWorks SAS et Nintendo of America Inc ne sont en aucun cas associés avec ce p * NumWorks Epsilon est disponible sous [Lisense CC BY-NC-SA](https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode). * Omega est disponible sous [Lisense CC BY-NC-SA](https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode). +* Upsilon est disponible sous [Lisense CC BY-NC-SA](https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode). diff --git a/README.md b/README.md index 2620c8f49..ceb8bd5bc 100644 --- a/README.md +++ b/README.md @@ -1,37 +1,33 @@ -

+

cc by-nc-sa 4.0 - Issues + Issues
- Discord + Discord

> Vous ne comprenez pas l'anglais ? vous êtes francophone ? Regardez le [*LISEZ-MOI* français](./README.fr.md) ! ## About -Omega is a fork of Numworks' Epsilon, the OS that runs on their calculator, which brings many features to it. Omega is for the people who want to add features to the calculator, but cannot because they have been rejected by Numworks (for reasons that are 100% understandable!). [Try it online](https://getomega.web.app/simulator). +Upsilon is a fork of Omega, an user-made OS that runs on the Numworks calculator, which brings many features to it, but was discontinued because of a policy change from Numworks. Upsilon is for the people who want to see a future for user-made OSes for Numworks, even after the closure and archiving of Omega. ### Some new features -- Adding symbolic calculation back into the calculator -- An app for RPN +- Enhancements for the Kandinsky python module +- A support for wallpapers - Exernal apps -- A theme engine -- New python features (os module, open method...) -- A periodic table app + all of the molar masses for the elements in the toolbox -- *And much more to discover...* [Complete changelog](https://github.com/Omega-Numworks/Omega/wiki/Changelog) | [Main new features + screenshots](https://github.com/Omega-Numworks/Omega/wiki/Main-features). +- A custom theme +- Operator overload for python +- Improvements for the Periodic table application +- *And everything that has been added to Omega before its termination!* [See Omega's changelog here](https://github.com/Omega-Numworks/Omega/wiki/Changelog) | [Main Omega features + screenshots](https://github.com/Omega-Numworks/Omega/wiki/Main-features). ## Installation -### Automatic - -You can install Omega automatically on our website [here](https://getomega.web.app/) in the "install" page. - -

Omega Banner Discord

- ### Manual +*As of today, only the manual installation is available.* + First of all, follow **step 1** [here](https://www.numworks.com/resources/engineering/software/build/). Then:
@@ -40,8 +36,8 @@ First of all, follow **step 1** [here](https://www.numworks.com/resources/engine (note: you can change the `EPSILON_I18N=en` flag to `fr`, `nl`, `pt`, `it`, `de`, `es` or `hu`). ```bash -git clone --recursive https://github.com/Omega-Numworks/Omega.git -cd Omega +git clone --recursive https://github.com/Lauryy06/Upsilon.git +cd Upsilon git checkout omega-master make MODEL=n0100 clean make MODEL=n0100 EPSILON_I18N=en OMEGA_USERNAME="{Your name, max 15 characters}" -j4 @@ -57,7 +53,7 @@ Also, you can change the number of processes that run in parallel during the bui Model n0110 ```bash -git clone --recursive https://github.com/Omega-Numworks/Omega.git +git clone --recursive https://github.com/Lauryy06/Upsilon.git cd Omega git checkout omega-master make clean @@ -73,10 +69,10 @@ Also, you can change the number of processes that run in parallel during the bui
Bin files -These can be used to distribute Omega (so that it can be flashed by anyone with [Webdfu_Numworks](https://ti-planet.github.io/webdfu_numworks/)). +These can be used to distribute Upsilon (so that it can be flashed by anyone with [Webdfu_Numworks](https://ti-planet.github.io/webdfu_numworks/)). ```bash -git clone --recursive https://github.com/Omega-Numworks/Omega.git +git clone --recursive https://github.com/Lauryy06/Upsilon.git cd Omega git checkout omega-master make clean @@ -86,7 +82,7 @@ make OMEGA_USERNAME="" -j8 make OMEGA_USERNAME="" binpack -j8 ``` -Important: Don't forget the `--recursive` tag, because Omega relies on submodules. +Important: Don't forget the `--recursive` tag, because Upsilon relies on submodules. Also, you can change the number of processes that run in parallel during the build by changing the value of the `-j` flag.
@@ -104,11 +100,11 @@ cd emsdk source emsdk_env.sh ``` -Then, compile Omega : +Then, compile Upsilon : ```bash -git clone --recursive https://github.com/Omega-Numworks/Omega.git -cd Omega +git clone --recursive https://github.com/Lauryy06/Upsilon.git +cd Upsilon git checkout omega-master make clean make PLATFORM=simulator TARGET=web OMEGA_USERNAME="{Your name, max 15 characters}" -j4 @@ -116,7 +112,7 @@ make PLATFORM=simulator TARGET=web OMEGA_USERNAME="{Your name, max 15 characters The simulator is now in `output/release/simulator/web/simulator.zip` -Important: Don't forget the `--recursive` tag, because Omega relies on submodules. +Important: Don't forget the `--recursive` tag, because Upsilon relies on submodules. Also, you can change the number of processes that run in parallel during the build by changing the value of the `-j` flag.
@@ -127,8 +123,8 @@ Also, you can change the number of processes that run in parallel during the bui You need devkitPro and devkitARM installed and in your path (instructions [here](https://devkitpro.org/wiki/Getting_Started)) ```bash -git clone --recursive https://github.com/Omega-Numworks/Omega.git -cd Omega +git clone --recursive https://github.com/Lauryy06/Upsilon.git +cd Upsilon git checkout --recursive omega-dev make PLATFORM=simulator TARGET=3ds -j ``` @@ -140,17 +136,19 @@ You can then put epsilon.3dsx on a SD card to run it from the HBC or use 3dslink -If you need help, you can join our Discord server here : https://discord.gg/X2TWhh9 +If you need help, you can join our Discord server here : https://discord.gg/Q9buEMduXG -

Omega Banner Discord

+

Omega Banner Discord

--- ## Contributing -To contribute, please refer to the [Wiki](https://github.com/Omega-Numworks/Omega/wiki/Contributing) +To contribute, please refer to [Omega's Wiki](https://github.com/Omega-Numworks/Omega/wiki/Contributing), the same rules apply here. ## Related repositories +Here are the main links toward Omega's different websites and repositories, that have been used for the creation of Upsilon. + * [Omega Themes](https://github.com/Omega-Numworks/Omega-Themes) * [Omega Website](https://github.com/Omega-Numworks/Omega-Website) * [Omega RPN `APP`](https://github.com/Omega-Numworks/Omega-RPN) @@ -162,6 +160,8 @@ To contribute, please refer to the [Wiki](https://github.com/Omega-Numworks/Omeg ## About Epsilon +Upsilon is a fork of Omega, after the project's discontinuation. + Omega is a fork of Epsilon, a high-performance graphing calculator operating system. It includes eight apps that cover the high school mathematics curriculum. You can try Epsilon straight from your browser in the [online simulator](https://www.numworks.com/simulator/). @@ -174,3 +174,4 @@ NumWorks SAS and Nintendo of America Inc aren't associated in any shape or form * NumWorks Epsilon is released under a [CC BY-NC-SA License](https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode). * Omega is released under a [CC BY-NC-SA License](https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode). +* Upsilon is released under a [CC BY-NC-SA License](https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode). From dd757969f3857312cab188563c231e97d2691e08 Mon Sep 17 00:00:00 2001 From: Lauryy06 <80424145+Lauryy06@users.noreply.github.com> Date: Wed, 1 Sep 2021 18:00:24 +0200 Subject: [PATCH 39/48] [readme] Correct readme.fr --- README.fr.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.fr.md b/README.fr.md index 4ee47639e..00ab5f108 100644 --- a/README.fr.md +++ b/README.fr.md @@ -14,13 +14,13 @@ Upsilon est un fork d'Omega, un fork d'Epsilon, l'OS de Numworks tournant sur les calculatrices du même nom, qui apporte beaucoup de fonctionnalités en plus, mais qui fut archivé et fermé pour des raisons légales après un changement de politique de Numworks. Upsilon est fait pour ceux qui aimeraient voir un futur pour les OS créées par les utilisateurs pour Numworks, même après l'arrèt du projet initial. ### Quelques fonctionnalités supplémentaires -- RDes améliorations du module python Kandinsky +- Un module python kandinsky amélioré - Un support pour fonds d'écrans personnalisés - Des applications externes -- Des thèmes -- Des améliorations pour python -- Un tableau périodique légèrement modifié -- L'utilisation du signe "=" +- Un thème Upsilon +- La surcharge des opérateurs en python +- Un tableau périodique légèrement amélioré +- L'utilisation possible du signe "=" dans les calculs - *Ainsi que tout ce qui a été ajouté sur Omega, et bien plus...* [Changelogs complets d'Omega](https://github.com/Omega-Numworks/Omega/wiki/Changelog) | [Fonctionnalités principales d'Omega & captures d'écran](https://github.com/Omega-Numworks/Omega/wiki/Main-features). ## Installation From ebe6a7cc5f2c3c4bcbf1b9790459e4b87e2ab3b1 Mon Sep 17 00:00:00 2001 From: Yaya-Cout <67095734+Yaya-Cout@users.noreply.github.com> Date: Thu, 2 Sep 2021 17:58:23 +0200 Subject: [PATCH 40/48] Merge pull request #2 (Update some translations) --- apps/home/base.de.i18n | 2 +- apps/home/base.en.i18n | 2 +- apps/home/base.es.i18n | 2 +- apps/home/base.fr.i18n | 2 +- apps/home/base.hu.i18n | 2 +- apps/home/base.it.i18n | 2 +- apps/home/base.nl.i18n | 2 +- apps/home/base.pt.i18n | 2 +- apps/settings/base.de.i18n | 2 +- apps/settings/base.en.i18n | 2 +- apps/settings/base.es.i18n | 2 +- apps/settings/base.fr.i18n | 2 +- apps/settings/base.hu.i18n | 2 +- apps/settings/base.it.i18n | 2 +- apps/settings/base.nl.i18n | 2 +- apps/settings/base.pt.i18n | 2 +- apps/usb/base.de.i18n | 2 +- apps/usb/base.en.i18n | 2 +- apps/usb/base.es.i18n | 2 +- apps/usb/base.fr.i18n | 2 +- apps/usb/base.hu.i18n | 2 +- apps/usb/base.it.i18n | 2 +- apps/usb/base.nl.i18n | 2 +- apps/usb/base.pt.i18n | 2 +- 24 files changed, 24 insertions(+), 24 deletions(-) diff --git a/apps/home/base.de.i18n b/apps/home/base.de.i18n index 30ac1c9c2..a0fc91420 100644 --- a/apps/home/base.de.i18n +++ b/apps/home/base.de.i18n @@ -1,4 +1,4 @@ Apps = "Anwendungen" -AppsCapital = "OMEGA" +AppsCapital = "UPSILON" ForbidenAppInExamMode1 = "Diese Anwendung ist im" ForbidenAppInExamMode2 = "Prüfungsmodus nicht erlaubt." diff --git a/apps/home/base.en.i18n b/apps/home/base.en.i18n index dde4e2129..3468cca66 100644 --- a/apps/home/base.en.i18n +++ b/apps/home/base.en.i18n @@ -1,4 +1,4 @@ Apps = "Applications" -AppsCapital = "OMEGA" +AppsCapital = "UPSILON" ForbidenAppInExamMode1 = "This application is" ForbidenAppInExamMode2 = "forbidden in exam mode" diff --git a/apps/home/base.es.i18n b/apps/home/base.es.i18n index 93e7bdf49..3c262497c 100644 --- a/apps/home/base.es.i18n +++ b/apps/home/base.es.i18n @@ -1,4 +1,4 @@ Apps = "Aplicaciones" -AppsCapital = "OMEGA" +AppsCapital = "UPSILON" ForbidenAppInExamMode1 = "Esta aplicación está prohibida" ForbidenAppInExamMode2 = "en el modo de examen" diff --git a/apps/home/base.fr.i18n b/apps/home/base.fr.i18n index 8280b4584..4b4ab02dd 100644 --- a/apps/home/base.fr.i18n +++ b/apps/home/base.fr.i18n @@ -1,4 +1,4 @@ Apps = "Applications" -AppsCapital = "OMEGA" +AppsCapital = "UPSILON" ForbidenAppInExamMode1 = "Cette application n'est" ForbidenAppInExamMode2 = "pas autorisée en mode examen." diff --git a/apps/home/base.hu.i18n b/apps/home/base.hu.i18n index 6b0b18815..d526193eb 100644 --- a/apps/home/base.hu.i18n +++ b/apps/home/base.hu.i18n @@ -1,4 +1,4 @@ Apps = "Alkalmazások" -AppsCapital = "OMEGA" +AppsCapital = "UPSILON" ForbidenAppInExamMode1 = "Ez az alkalmazás" ForbidenAppInExamMode2 = "tilos vizsga módban" diff --git a/apps/home/base.it.i18n b/apps/home/base.it.i18n index 95373c040..4bd26ced3 100644 --- a/apps/home/base.it.i18n +++ b/apps/home/base.it.i18n @@ -1,4 +1,4 @@ Apps = "Applicazioni" -AppsCapital = "OMEGA" +AppsCapital = "UPSILON" ForbidenAppInExamMode1 = "Questa applicazione è" ForbidenAppInExamMode2 = "proibita nella modalità d'esame" diff --git a/apps/home/base.nl.i18n b/apps/home/base.nl.i18n index dd6a89919..496e8d6a3 100644 --- a/apps/home/base.nl.i18n +++ b/apps/home/base.nl.i18n @@ -1,4 +1,4 @@ Apps = "Applicaties" -AppsCapital = "OMEGA" +AppsCapital = "UPSILON" ForbidenAppInExamMode1 = "Deze applicatie is" ForbidenAppInExamMode2 = "uitgesloten in examenstand" diff --git a/apps/home/base.pt.i18n b/apps/home/base.pt.i18n index f3efc8ffd..6c074aa86 100644 --- a/apps/home/base.pt.i18n +++ b/apps/home/base.pt.i18n @@ -1,4 +1,4 @@ Apps = "Aplicações" -AppsCapital = "OMEGA" +AppsCapital = "UPSILON" ForbidenAppInExamMode1 = "Esta aplicação é" ForbidenAppInExamMode2 = "proibida no Modo de Exame" diff --git a/apps/settings/base.de.i18n b/apps/settings/base.de.i18n index 5c00526fc..7510e3334 100644 --- a/apps/settings/base.de.i18n +++ b/apps/settings/base.de.i18n @@ -33,7 +33,7 @@ Cartesian = "Kartesisch " Polar = "Polar " Brightness = "Helligkeit" SoftwareVersion = "Epsilon version" -OmegaVersion = "Omega version" +UpsilonVersion = "Upsilon version" Username = "Name" MicroPythonVersion = "µPythonversion" FontSizes = "Python-Schriftgröße" diff --git a/apps/settings/base.en.i18n b/apps/settings/base.en.i18n index b5d076904..0b4b81ebd 100644 --- a/apps/settings/base.en.i18n +++ b/apps/settings/base.en.i18n @@ -33,7 +33,7 @@ Cartesian = "Cartesian " Polar = "Polar " Brightness = "Brightness" SoftwareVersion = "Epsilon version" -OmegaVersion = "Omega version" +UpsilonVersion = "Upsilon version" Username = "Name" MicroPythonVersion = "µPython version" FontSizes = "Python font size" diff --git a/apps/settings/base.es.i18n b/apps/settings/base.es.i18n index 944709570..213ef0dc1 100644 --- a/apps/settings/base.es.i18n +++ b/apps/settings/base.es.i18n @@ -33,7 +33,7 @@ Cartesian = "Binómica " Polar = "Polar " Brightness = "Brillo" SoftwareVersion = "Versión de Epsilon" -OmegaVersion = "Versión de Omega" +UpsilonVersion = "Versión de Upsilon" Username = "Apellido" MicroPythonVersion = "Version de µPython" FontSizes = "Tipografía Python" diff --git a/apps/settings/base.fr.i18n b/apps/settings/base.fr.i18n index ac76c7dca..be2e08daf 100644 --- a/apps/settings/base.fr.i18n +++ b/apps/settings/base.fr.i18n @@ -33,7 +33,7 @@ Cartesian = "Algébrique " Polar = "Exponentielle " Brightness = "Luminosité" SoftwareVersion = "Version d'Epsilon" -OmegaVersion = "Version d'Omega" +UpsilonVersion = "Version d'Upsilon" Username = "Nom" MicroPythonVersion = "Version de µPython" FontSizes = "Police Python" diff --git a/apps/settings/base.hu.i18n b/apps/settings/base.hu.i18n index 19ed3a67f..db70d604f 100644 --- a/apps/settings/base.hu.i18n +++ b/apps/settings/base.hu.i18n @@ -33,7 +33,7 @@ Cartesian = "Kartéziánus " Polar = "Poláris " Brightness = "Fényerö" SoftwareVersion = "Epsilon verzió" -OmegaVersion = "Omega verzió" +UpsilonVersion = "Upsilon verzió" Username = "Felhasználónév" MicroPythonVersion = "µPython verzió" FontSizes = "Python betü méret" diff --git a/apps/settings/base.it.i18n b/apps/settings/base.it.i18n index 5c4dcc0a4..20fe834e8 100644 --- a/apps/settings/base.it.i18n +++ b/apps/settings/base.it.i18n @@ -33,7 +33,7 @@ Cartesian = "Algebrico " Polar = "Esponenziale " Brightness = "Luminosità" SoftwareVersion = "Epsilon version" -OmegaVersion = "Omega version" +UpsilonVersion = "Upsilon version" Username = "Name" MicroPythonVersion = "µPython version" FontSizes = "Carattere Python" diff --git a/apps/settings/base.nl.i18n b/apps/settings/base.nl.i18n index 75939d6a8..bf0cc43a8 100644 --- a/apps/settings/base.nl.i18n +++ b/apps/settings/base.nl.i18n @@ -33,7 +33,7 @@ Cartesian = "Cartesisch " Polar = "Polair " Brightness = "Helderheid" SoftwareVersion = "Epsilon version" -OmegaVersion = "Omega version" +UpsilonVersion = "Upsilon version" Username = "Name" MicroPythonVersion = "µPython version" FontSizes = "Python lettergrootte" diff --git a/apps/settings/base.pt.i18n b/apps/settings/base.pt.i18n index f33006f4a..167abd7c6 100644 --- a/apps/settings/base.pt.i18n +++ b/apps/settings/base.pt.i18n @@ -33,7 +33,7 @@ Cartesian = "Cartesiana " Polar = "Polar " Brightness = "Brilho" SoftwareVersion = "Versão do Epsilon" -OmegaVersion = "Versão do Omega" +UpsilonVersion = "Versão do Upsilon" Username = "Nome" MicroPythonVersion = "Versao do µPython" FontSizes = "Tipografia Python" diff --git a/apps/usb/base.de.i18n b/apps/usb/base.de.i18n index e381f9fb3..794b4d36b 100644 --- a/apps/usb/base.de.i18n +++ b/apps/usb/base.de.i18n @@ -1,7 +1,7 @@ USBConnected = "TASCHENRECHNER IST ANGESCHLOSSEN" ConnectedMessage1 = "Um Daten zu übertragen, verbinden" ConnectedMessage2 = "Sie sich von Ihrem Computer aus mit" -ConnectedMessage3 = "workshop.numworks.com." +ConnectedMessage3 = "getomega.dev/ide." ConnectedMessage4 = "Drücken Sie die Zurück-Taste am" ConnectedMessage5 = "Taschenrechner oder Kabel abziehen," ConnectedMessage6 = "um die Verbindung zu trennen." diff --git a/apps/usb/base.en.i18n b/apps/usb/base.en.i18n index 60eae2214..0eb615fa2 100644 --- a/apps/usb/base.en.i18n +++ b/apps/usb/base.en.i18n @@ -1,7 +1,7 @@ USBConnected = "THE CALCULATOR IS CONNECTED" ConnectedMessage1 = "To transfer data, browse" ConnectedMessage2 = "our page from your computer" -ConnectedMessage3 = "workshop.numworks.com" +ConnectedMessage3 = "getomega.dev/ide" ConnectedMessage4 = "Press the BACK key of your" ConnectedMessage5 = "calculator or unplug it to" ConnectedMessage6 = "disconnect it." diff --git a/apps/usb/base.es.i18n b/apps/usb/base.es.i18n index 7fa1e5cd7..cf9b3d7b5 100644 --- a/apps/usb/base.es.i18n +++ b/apps/usb/base.es.i18n @@ -1,7 +1,7 @@ USBConnected = "CALCULADORA CONECTADA" ConnectedMessage1 = "Para transferir datos, visite" ConnectedMessage2 = "nuestra página desde su ordenador" -ConnectedMessage3 = "workshop.numworks.com" +ConnectedMessage3 = "getomega.dev/ide" ConnectedMessage4 = "Pulse el botón RETURN de la" ConnectedMessage5 = "calculadora o desenchúfela para" ConnectedMessage6 = "desconectarla." \ No newline at end of file diff --git a/apps/usb/base.fr.i18n b/apps/usb/base.fr.i18n index aeda714e8..33762ca54 100644 --- a/apps/usb/base.fr.i18n +++ b/apps/usb/base.fr.i18n @@ -1,7 +1,7 @@ USBConnected = "LA CALCULATRICE EST CONNECTÉE" ConnectedMessage1 = "Pour transférer des données, connectez-" ConnectedMessage2 = "vous depuis votre ordinateur sur le site" -ConnectedMessage3 = "workshop.numworks.com" +ConnectedMessage3 = "getomega.dev/ide" ConnectedMessage4 = "Appuyez sur la touche RETOUR" ConnectedMessage5 = "de la calculatrice ou débranchez-la" ConnectedMessage6 = "pour la déconnecter." diff --git a/apps/usb/base.hu.i18n b/apps/usb/base.hu.i18n index 3afbc67ab..a8a3de172 100644 --- a/apps/usb/base.hu.i18n +++ b/apps/usb/base.hu.i18n @@ -1,7 +1,7 @@ USBConnected = "A SZÁMOLÓGÉP CSATLAKOZTATVA VAN" ConnectedMessage1 = "" ConnectedMessage2 = "Adat másolásához csatlakozzon" -ConnectedMessage3 = "fel workshop.numworks.com ra." +ConnectedMessage3 = "fel getomega.dev/ide ra." ConnectedMessage4 = "Nyomjon majd a VISSZA gombra" ConnectedMessage5 = "vagy huzza ki a kábelt azért" ConnectedMessage6 = "hogy a másolás véget érjen." diff --git a/apps/usb/base.it.i18n b/apps/usb/base.it.i18n index a0d7339a7..fdc2be0bd 100644 --- a/apps/usb/base.it.i18n +++ b/apps/usb/base.it.i18n @@ -1,7 +1,7 @@ USBConnected = "CALCOLATRICE CONNESSA" ConnectedMessage1 = "Per trasferire dei dati, connettetevi" ConnectedMessage2 = "dal vostro computer sul sito" -ConnectedMessage3 = "workshop.numworks.com" +ConnectedMessage3 = "getomega.dev/ide" ConnectedMessage4 = "Premere sul tasto INDIETRO della" ConnectedMessage5 = "calcolatrice o scollegatela per" ConnectedMessage6 = "disconnetterla." diff --git a/apps/usb/base.nl.i18n b/apps/usb/base.nl.i18n index ed8c78ca3..db29c1127 100644 --- a/apps/usb/base.nl.i18n +++ b/apps/usb/base.nl.i18n @@ -1,7 +1,7 @@ USBConnected = "DE REKENMACHINE IS AANGESLOTEN" ConnectedMessage1 = "Om gegevens op te slaan," ConnectedMessage2 = "ga naar onze webpagina" -ConnectedMessage3 = "workshop.numworks.com" +ConnectedMessage3 = "getomega.dev/ide" ConnectedMessage4 = "Druk op de TERUG toets van je" ConnectedMessage5 = "rekenmachine of verwijder de" ConnectedMessage6 = " kabel om hem los te koppelen." diff --git a/apps/usb/base.pt.i18n b/apps/usb/base.pt.i18n index 5e6c3cea7..408b24db7 100644 --- a/apps/usb/base.pt.i18n +++ b/apps/usb/base.pt.i18n @@ -1,7 +1,7 @@ USBConnected = "A CALCULADORA ESTÁ CONECTADA" ConnectedMessage1 = "Para transferir dados, navegue" ConnectedMessage2 = "na nossa página no seu computador" -ConnectedMessage3 = "workshop.numworks.com" +ConnectedMessage3 = "getomega.dev/ide" ConnectedMessage4 = "Pressione o botão RETURN na" ConnectedMessage5 = "calculadora ou desligue-a para a" ConnectedMessage6 = "desconectar." From 74635f8d88191aa21fd30ba4f743eb9c4aad9fc2 Mon Sep 17 00:00:00 2001 From: Laury Date: Fri, 3 Sep 2021 22:57:04 +0200 Subject: [PATCH 41/48] [code/ulab] Added ulab to toolbox --- apps/code/catalog.de.i18n | 98 ++++++++++++++ apps/code/catalog.en.i18n | 98 ++++++++++++++ apps/code/catalog.es.i18n | 98 ++++++++++++++ apps/code/catalog.fr.i18n | 98 ++++++++++++++ apps/code/catalog.hu.i18n | 98 ++++++++++++++ apps/code/catalog.it.i18n | 98 ++++++++++++++ apps/code/catalog.nl.i18n | 98 ++++++++++++++ apps/code/catalog.pt.i18n | 98 ++++++++++++++ apps/code/catalog.universal.i18n | 110 ++++++++++++++++ apps/code/python_toolbox.cpp | 122 ++++++++++++++++++ apps/code/toolbox.universal.i18n | 5 + .../include/escher/nested_menu_controller.h | 2 +- 12 files changed, 1022 insertions(+), 1 deletion(-) diff --git a/apps/code/catalog.de.i18n b/apps/code/catalog.de.i18n index 1e2e2e419..2c942a56e 100644 --- a/apps/code/catalog.de.i18n +++ b/apps/code/catalog.de.i18n @@ -74,6 +74,7 @@ PythonImportKandinsky = "Kandinsky-Modul importieren" PythonImportRandom = "Random-Modul importieren" PythonImportMath = "Math-Modul importieren" PythonImportMatplotlibPyplot = "Matplotlib.pyplot-Modul importieren" +PythonImportNumpy = "Ulab.numpy-Modul importieren" PythonImportOs = "OS-Modul importieren" PythonOsUname = "Informationen über das System holen" PythonOsGetlogin = "Benutzernamen holen" @@ -104,6 +105,103 @@ PythonMax = "Maximum" PythonMin = "Minimum" PythonModf = "Bruch- und Ganzzahl-Anteile von x" PythonMonotonic = "Wert einer monotonen Uhr" +PythonNumpyFunction = "numpy Modul-Präfix" +PythonNumpyFftFunction = "numpy.fft Modul-Präfix" +PythonNumpyLinalgFunction = "numpy.linalg Modul-Präfix" +PythonNumpyArray = "Konvertieren Sie ein Array in ndarray" +PythonNumpyArange = "Erstellen Sie eine Tabelle aus dem Bereich (i)" +PythonNumpyConcatenate = "Verketten Sie a und b" +PythonNumpyDiag = "Extrahiere oder konstruiere ein diagonales Array" +PythonNumpyZeros = "S-förmiges Array gefüllt mit 0" +PythonNumpyOnes = "S-förmiges Array gefüllt mit 1" +PythonNumpyEmpty = "Nicht initialisiertes Array der Form s" +PythonNumpyEye = "Tabelle mit Einsen auf der Diagonale und Nullen an anderer Stelle" +PythonNumpyFull = "S-förmiges Array gefüllt mit v" +PythonNumpyLinspace = "Zahlen, die über ein bestimmtes Intervall verteilt sind" +PythonNumpyLogspace = "Zahlen mit logarithmischem Abstand" +PythonNumpyCopy = "Kopie der Tabelle" +PythonNumpyDtype = "Tischtyp" +PythonNumpyFlat = "Flat-Array-Iterator" +PythonNumpyFlatten = "Abgeflachte Version der Tabelle" +PythonNumpyShape = "Holen Sie sich die Form des Arrays" +PythonNumpyReshape = "Array-Form durch s ersetzen" +PythonNumpySize = "Anzahl der Elemente im Array" +PythonNumpyTranspose = "Transponierte Version der Tabelle" +PythonNumpySortWithArguments = "Sortierte Version der Tabelle" +PythonNumpyNdinfo = "Informationen zu a . drucken" +PythonNumpyAll = "Testen Sie, ob alle Elemente von a trye sind" +PythonNumpyAny = "Teste, ob ein Element von a wahr ist" +PythonNumpyArgmax = "Index des Maximalwertes von a" +PythonNumpyArgmin = "Tiefgestellter Wert des Mindestwertes von a" +PythonNumpyArgsort = "Hinweise, die ein Array sortieren würden" +PythonNumpyClip = "Werte in einem Array ausschneiden" +PythonNumpyConvolve = "Diskrete lineare Faltung von a und b" +PythonNumpyDiff = "Abgeleitet von a" +PythonNumpyInterp = "Linear interpolierte Werte von a" +PythonNumpyDot = "Punktprodukt von a und b" +PythonNumpyCross = "Kreuzprodukt von a und b" +PythonNumpyEqual = "A == Element für Element" +PythonNumpyNot_equal = "A! = Element für Element" +PythonNumpyFlip = "Turnaround-Tabelle" +PythonNumpyIsfinite = "Testen Sie die Endlichkeit Element für Element" +PythonNumpyIsinf = "Teste die Unendlichkeit Element für Element" +PythonNumpyMean = "Durchschnitt d" +PythonNumpyMin = "Maximalwert von a" +PythonNumpyMax = "Mindestwert von a" +PythonNumpyMedian = "Medianwert von a" +PythonNumpyMinimum = "Minimale Array-Elemente pro Element" +PythonNumpyMaximum = "Maximum pro Element von Array-Elementen" +PythonNumpyPolyfit = "Polynomanpassung der kleinsten Quadrate" +PythonNumpyPolyval = "Bewerte ein Polynom bei bestimmten Werten" +PythonNumpyRoll = "Verschiebe den Inhalt von a um n" +PythonNumpySort = "Sortieren nach" +PythonNumpyStd = "Berechnen Sie die Standardabweichung von a" +PythonNumpySum = "Berechnen Sie die Summe von a" +PythonNumpyTrace = "Berechnen Sie die Summe der diagonalen Elemente von a" +PythonNumpyTrapz = "Integrieren Sie mit dem zusammengesetzten Trapezlineal" +PythonNumpyWhere = "Gibt Elemente aus x oder y gemäß c . zurück" +PythonNumpyVectorize = "Vektorisieren Sie die generische Python-Funktion f" +PythonNumpyAcos = "Wenden Sie acos Artikel für Artikel an" +PythonNumpyAcosh = "Wenden Sie acosh Artikel für Artikel an" +PythonNumpyArctan2 = "arctan2 Element für Element anwenden" +PythonNumpyAround = "Um das Element herum auftragen" +PythonNumpyAsin = "Element für Element anwenden" +PythonNumpyAsinh = "Wenden Sie asinh Element für Element an" +PythonNumpyAtan = "Wenden Sie ein Element für Element an" +PythonNumpyAtanh = "Wenden Sie atanh Element für Element an" +PythonNumpyCeil = "Bringen Sie die Decke nach Element an" +PythonNumpyCos = "Wenden Sie cos Element für Element an" +PythonNumpyCosh = "Wenden Sie cosh Element für Element an" +PythonNumpyDegrees = "Grade Element für Element anwenden" +PythonNumpyExp = "Exp pro Artikel anwenden" +PythonNumpyExpm1 = "Wenden Sie expm1 Element für Element an" +PythonNumpyFloor = "Boden nach Element auftragen" +PythonNumpyLog = "Tagebuch nach Artikel anwenden" +PythonNumpyLog10 = "Wenden Sie log10 Element für Element an" +PythonNumpyLog2 = "Wenden Sie log2 Element für Element an" +PythonNumpyRadians = "Wenden Sie Radiant pro Element an" +PythonNumpySin = "Wende Sünde nach Element an" +PythonNumpySinh = "Wenden Sie sinh Element für Element an" +PythonNumpySqrt = "Wenden Sie sqrt Element für Element an" +PythonNumpyTan = "Trage die Bräune nach Element auf" +PythonNumpyTanh = "Tanh pro Artikel auftragen" +PythonNumpyBool = "Bool Typ von numpy" +PythonNumpyFloat = "Float-Typ von numpy" +PythonNumpyUint8 = "Geben Sie uint8 von numpy . ein" +PythonNumpyInt8 = "Geben Sie int8 von numpy . ein" +PythonNumpyUint16 = "Geben Sie uint16 von numpy ein" +PythonNumpyInt16 = "Geben Sie int16 von numpy . ein" +PythonNumpyNan = "Nan-Darstellung von numpy" +PythonNumpyInf = "Inf-Darstellung von numpy" +PythonNumpyE = "2.718281828459045" +PythonNumpyPi = "3.141592653589793" +PythonNumpyFft = "Eindimensionale diskrete Fourier-Transformation" +PythonNumpyIfft = "Eindimensionale inverse diskrete Fourier-Transformation" +PythonNumpyDet = "Determinante von a" +PythonNumpyEig = "Eigenwerte und rechte Eigenvektoren von a" +PythonNumpyCholesky = "Cholesky-Zerlegung" +PythonNumpyInv = "Inverse Matrix a" +PythonNumpyNorm = "Matrix- oder Vektorstandard" PythonOct = "Ganzzahl in Oktal umwandeln" PythonPhase = "Phase von z" PythonPlot = "Plotten von y gegen x als Linien" diff --git a/apps/code/catalog.en.i18n b/apps/code/catalog.en.i18n index 169955b45..09de24ccb 100644 --- a/apps/code/catalog.en.i18n +++ b/apps/code/catalog.en.i18n @@ -74,6 +74,7 @@ PythonImportKandinsky = "Import kandinsky module" PythonImportRandom = "Import random module" PythonImportMath = "Import math module" PythonImportMatplotlibPyplot = "Import matplotlib.pyplot module" +PythonImportNumpy = "Import ulab.numpy module" PythonImportTime = "Import time module" PythonImportTurtle = "Import turtle module" PythonIndex = "Index of the first x occurrence" @@ -98,6 +99,103 @@ PythonMax = "Maximum" PythonMin = "Minimum" PythonModf = "Fractional and integer parts of x" PythonMonotonic = "Value of a monotonic clock" +PythonNumpyFunction = "numpy module prefix" +PythonNumpyFftFunction = "numpy.fft module prefix" +PythonNumpyLinalgFunction = "numpy.linalg module prefix" +PythonNumpyArray = "Convert an array to ndarray" +PythonNumpyArange = "Make a table from the range (i)" +PythonNumpyConcatenate = "Concatenate a and b" +PythonNumpyDiag = "Extract or construct a diagonal array" +PythonNumpyZeros = "S shape array filled with 0" +PythonNumpyOnes = "S shape array filled with 1" +PythonNumpyEmpty = "Uninitialized array of form s" +PythonNumpyEye = "Table with 1s on the diagonal and 0s elsewhere" +PythonNumpyFull = "S shape array filled with v" +PythonNumpyLinspace = "Numbers spaced over a specified interval" +PythonNumpyLogspace = "Numbers spaced on a logarithmic scale" +PythonNumpyCopy = "Copy of table" +PythonNumpyDtype = "Table type" +PythonNumpyFlat = "Flat array iterator" +PythonNumpyFlatten = "Flattened version of the table" +PythonNumpyShape = "Get the shape of the array" +PythonNumpyReshape = "Replace array shape with s" +PythonNumpySize = "Number of elements in the array" +PythonNumpyTranspose = "Transposed version of the table" +PythonNumpySortWithArguments = "Sorted version of the table" +PythonNumpyNdinfo = "Print information about a" +PythonNumpyAll = "Test if all elements of a are trye" +PythonNumpyAny = "Test if an element of a is true" +PythonNumpyArgmax = "Index of the maximum value of a" +PythonNumpyArgmin = "Subscript of the minimum value of a" +PythonNumpyArgsort = "Clues that would sort an array" +PythonNumpyClip = "Cut values in an array" +PythonNumpyConvolve = "Discrete linear convolution of a and b" +PythonNumpyDiff = "Derived from a" +PythonNumpyInterp = "Linearly interpolated values of a" +PythonNumpyDot = "Dot product of a and b" +PythonNumpyCross = "Cross product of a and b" +PythonNumpyEqual = "A == a element by element" +PythonNumpyNot_equal = "A! = A element by element" +PythonNumpyFlip = "Turnaround table" +PythonNumpyIsfinite = "Test the finiteness element by element" +PythonNumpyIsinf = "Test the infinity element by element" +PythonNumpyMean = "Average d" +PythonNumpyMin = "Maximum value of a" +PythonNumpyMax = "Minimum value of a" +PythonNumpyMedian = "Median value of a" +PythonNumpyMinimum = "Minimum array elements per element" +PythonNumpyMaximum = "Maximum per element of array elements" +PythonNumpyPolyfit = "Least squares polynomial fit" +PythonNumpyPolyval = "Evaluate a polynomial at specific values" +PythonNumpyRoll = "Shift the content of a by n" +PythonNumpySort = "Sort to" +PythonNumpyStd = "Calculate the standard deviation of a" +PythonNumpySum = "Calculate the sum of a" +PythonNumpyTrace = "Calculate the sum of the diagonal elements of a" +PythonNumpyTrapz = "Integrate using the composite trapezoidal ruler" +PythonNumpyWhere = "Returns elements chosen from x or y according to c" +PythonNumpyVectorize = "Vectorize the generic python function f" +PythonNumpyAcos = "Apply acos item by item" +PythonNumpyAcosh = "Apply acosh item by item" +PythonNumpyArctan2 = "Apply arctan2 element by element" +PythonNumpyAround = "Apply around the element" +PythonNumpyAsin = "Apply asin element by element" +PythonNumpyAsinh = "Apply asinh element by element" +PythonNumpyAtan = "Apply one item by item" +PythonNumpyAtanh = "Apply atanh element by element" +PythonNumpyCeil = "Apply the ceiling by element" +PythonNumpyCos = "Apply cos element by element" +PythonNumpyCosh = "Apply cosh element by element" +PythonNumpyDegrees = "Apply degrees element by element" +PythonNumpyExp = "Apply exp per item" +PythonNumpyExpm1 = "Apply expm1 element by element" +PythonNumpyFloor = "Apply soil by element" +PythonNumpyLog = "Apply journal by item" +PythonNumpyLog10 = "Apply log10 element by element" +PythonNumpyLog2 = "Apply log2 element by element" +PythonNumpyRadians = "Apply radians per element" +PythonNumpySin = "Apply sin by element" +PythonNumpySinh = "Apply sinh element by element" +PythonNumpySqrt = "Apply sqrt element by element" +PythonNumpyTan = "Apply the tan by element" +PythonNumpyTanh = "Apply tanh per item" +PythonNumpyBool = "Bool type of numpy" +PythonNumpyFloat = "Float type of numpy" +PythonNumpyUint8 = "Type uint8 of numpy" +PythonNumpyInt8 = "Type int8 of numpy" +PythonNumpyUint16 = "Type uint16 from numpy" +PythonNumpyInt16 = "Type int16 of numpy" +PythonNumpyNan = "Nan representation of numpy" +PythonNumpyInf = "Inf representation of numpy" +PythonNumpyE = "2.718281828459045" +PythonNumpyPi = "3.141592653589793" +PythonNumpyFft = "One-dimensional discrete fourier transform" +PythonNumpyIfft = "One-dimensional inverse discrete fourier transform" +PythonNumpyDet = "Determinant of a" +PythonNumpyEig = "Eigenvalues and right eigenvectors of a" +PythonNumpyCholesky = "Cholesky decomposition" +PythonNumpyInv = "Inverse matrix a" +PythonNumpyNorm = "Matrix or vector standard" PythonOct = "Convert integer to octal" PythonPhase = "Phase of z" PythonPlot = "Plot y versus x as lines" diff --git a/apps/code/catalog.es.i18n b/apps/code/catalog.es.i18n index ef181756a..48bff282b 100644 --- a/apps/code/catalog.es.i18n +++ b/apps/code/catalog.es.i18n @@ -74,6 +74,7 @@ PythonImportKandinsky = "Import kandinsky module" PythonImportRandom = "Import random module" PythonImportMath = "Import math module" PythonImportMatplotlibPyplot = "Import matplotlib.pyplot module" +PythonImportNumpy = "Import ulab.numpy module" PythonImportTime = "Import time module" PythonImportTurtle = "Import turtle module" PythonIndex = "Index of the first x occurrence" @@ -98,6 +99,103 @@ PythonMax = "Maximum" PythonMin = "Minimum" PythonModf = "Fractional and integer parts of x" PythonMonotonic = "Value of a monotonic clock" +PythonNumpyFunction = "numpy module prefix" +PythonNumpyFftFunction = "numpy.fft module prefix" +PythonNumpyLinalgFunction = "numpy.linalg module prefix" +PythonNumpyArray = "Convertir una matriz a ndarray" +PythonNumpyArange = "Haz una tabla de la gama (i)" +PythonNumpyConcatenate = "Concatenar ayb" +PythonNumpyDiag = "Extraer o construir una matriz diagonal" +PythonNumpyZeros = "Matriz en forma de S rellena con 0" +PythonNumpyOnes = "Matriz en forma de S llena con 1" +PythonNumpyEmpty = "Matriz no inicializada de formulario s" +PythonNumpyEye = "Tabla con 1 en la diagonal y 0 en el resto" +PythonNumpyFull = "Matriz en forma de S rellena con v" +PythonNumpyLinspace = "Números espaciados en un intervalo específico" +PythonNumpyLogspace = "Números espaciados en una escala logarítmica" +PythonNumpyCopy = "Copia de la tabla" +PythonNumpyDtype = "Tipo de mesa" +PythonNumpyFlat = "Iterador de matriz plana" +PythonNumpyFlatten = "Versión aplanada de la mesa." +PythonNumpyShape = "Obtén la forma de la matriz" +PythonNumpyReshape = "Reemplazar la forma de la matriz con s" +PythonNumpySize = "Número de elementos en la matriz" +PythonNumpyTranspose = "Versión transpuesta de la tabla" +PythonNumpySortWithArguments = "Versión ordenada de la tabla" +PythonNumpyNdinfo = "Imprimir información sobre un" +PythonNumpyAll = "Prueba si todos los elementos de a son probables" +PythonNumpyAny = "Prueba si un elemento de a es verdadero" +PythonNumpyArgmax = "Índice del valor máximo de un" +PythonNumpyArgmin = "Subíndice del valor mínimo de un" +PythonNumpyArgsort = "Pistas que ordenarían una matriz" +PythonNumpyClip = "Cortar valores en una matriz" +PythonNumpyConvolve = "Convolución lineal discreta de ayb" +PythonNumpyDiff = "Derivado de un" +PythonNumpyInterp = "Valores interpolados linealmente de a" +PythonNumpyDot = "Producto escalar de ayb" +PythonNumpyCross = "Producto cruzado de ayb" +PythonNumpyEqual = "A == un elemento por elemento" +PythonNumpyNot_equal = "A! = Un elemento por elemento" +PythonNumpyFlip = "Tabla de cambio" +PythonNumpyIsfinite = "Prueba la finitud elemento por elemento" +PythonNumpyIsinf = "Prueba el infinito elemento por elemento" +PythonNumpyMean = "Promedio d" +PythonNumpyMin = "Valor máximo de un" +PythonNumpyMax = "Valor mínimo de un" +PythonNumpyMedian = "Valor mediano de a" +PythonNumpyMinimum = "Elementos de matriz mínimos por elemento" +PythonNumpyMaximum = "Máximo por elemento de elementos de matriz" +PythonNumpyPolyfit = "Ajuste de polinomio de mínimos cuadrados" +PythonNumpyPolyval = "Evaluar un polinomio en valores específicos" +PythonNumpyRoll = "Cambiar el contenido de a por n" +PythonNumpySort = "Ordenar por" +PythonNumpyStd = "Calcule la desviación estándar de un" +PythonNumpySum = "Calcule la suma de a" +PythonNumpyTrace = "Calcule la suma de los elementos diagonales de un" +PythonNumpyTrapz = "Integrar usando la regla trapezoidal compuesta" +PythonNumpyWhere = "Devuelve elementos elegidos de xoy según c" +PythonNumpyVectorize = "Vectorizar la función genérica de python f" +PythonNumpyAcos = "Aplicar acos artículo por artículo" +PythonNumpyAcosh = "Aplicar un elemento por elemento" +PythonNumpyArctan2 = "Aplicar arctan2 elemento por elemento" +PythonNumpyAround = "Aplicar alrededor del elemento" +PythonNumpyAsin = "Aplicar asin elemento por elemento" +PythonNumpyAsinh = "Aplicar asinh elemento por elemento" +PythonNumpyAtan = "Aplicar un artículo por artículo" +PythonNumpyAtanh = "Aplicar atanh elemento por elemento" +PythonNumpyCeil = "Aplicar el techo por elemento" +PythonNumpyCos = "Aplicar cos elemento por elemento" +PythonNumpyCosh = "Aplicar cosh elemento por elemento" +PythonNumpyDegrees = "Aplicar grados elemento por elemento" +PythonNumpyExp = "Aplicar exp por artículo" +PythonNumpyExpm1 = "Aplicar expm1 elemento por elemento" +PythonNumpyFloor = "Aplicar suelo por elemento" +PythonNumpyLog = "Aplicar diario por artículo" +PythonNumpyLog10 = "Aplicar log10 elemento por elemento" +PythonNumpyLog2 = "Aplicar log2 elemento por elemento" +PythonNumpyRadians = "Aplicar radianes por elemento" +PythonNumpySin = "Aplicar el pecado por elemento" +PythonNumpySinh = "Aplicar sinh elemento por elemento" +PythonNumpySqrt = "Aplicar elemento sqrt por elemento" +PythonNumpyTan = "Aplicar el bronceado por elemento" +PythonNumpyTanh = "Aplicar tanh por artículo" +PythonNumpyBool = "Bool tipo de numpy" +PythonNumpyFloat = "Flotador tipo de numpy" +PythonNumpyUint8 = "Escriba uint8 de numpy" +PythonNumpyInt8 = "Escriba int8 de numpy" +PythonNumpyUint16 = "Escriba uint16 desde numpy" +PythonNumpyInt16 = "Escriba int16 de numpy" +PythonNumpyNan = "Nan representación de numpy" +PythonNumpyInf = "Inf representación de numpy" +PythonNumpyE = "2.718281828459045" +PythonNumpyPi = "3.141592653589793" +PythonNumpyFft = "Transformada de Fourier discreta unidimensional" +PythonNumpyIfft = "Transformada de Fourier discreta inversa unidimensional" +PythonNumpyDet = "Determinante de un" +PythonNumpyEig = "Autovalores y autovectores derechos de un" +PythonNumpyCholesky = "Descomposición de Cholesky" +PythonNumpyInv = "Matriz inversa a" +PythonNumpyNorm = "Matriz o estándar vectorial" PythonOct = "Convert integer to octal" PythonPhase = "Phase of z" PythonPlot = "Plot y versus x as lines" diff --git a/apps/code/catalog.fr.i18n b/apps/code/catalog.fr.i18n index efb491f41..5846f7c4a 100644 --- a/apps/code/catalog.fr.i18n +++ b/apps/code/catalog.fr.i18n @@ -74,6 +74,7 @@ PythonImportKandinsky = "Importation du module kandinsky" PythonImportRandom = "Importation du module random" PythonImportMath = "Importation du module math" PythonImportMatplotlibPyplot = "Importation de matplotlib.pyplot" +PythonImportNumpy = "Importation de ulab.numpy" PythonImportTurtle = "Importation du module turtle" PythonImportTime = "Importation du module time" PythonIndex = "Indice première occurrence de x" @@ -98,6 +99,103 @@ PythonMax = "Maximum" PythonMin = "Minimum" PythonModf = "Parties fractionnaire et entière" PythonMonotonic = "Renvoie la valeur de l'horloge" +PythonNumpyFunction = "Préfixe fonction du module numpy" +PythonNumpyFftFunction = "Préfixe fonction du module numpy.fft" +PythonNumpyLinalgFunction = "Préfixe fonction du module numpy.linalg" +PythonNumpyArray = "Convertir un tableau en ndarray" +PythonNumpyArange = "Faire un tableau à partir de la plage (i)" +PythonNumpyConcatenate = "Concaténer a et b" +PythonNumpyDiag = "Extraire ou construire un tableau diagonal" +PythonNumpyZeros = "Tableau de forme s rempli de 0" +PythonNumpyOnes = "Tableau de forme s rempli de 1" +PythonNumpyEmpty = "Tableau uninitialisé de forme s" +PythonNumpyEye = "Tableau avec des 1 sur la diagonale et des 0 ailleurs" +PythonNumpyFull = "Tableau de forme s rempli de v" +PythonNumpyLinspace = "Nombres espacés sur un intervalle spécifié" +PythonNumpyLogspace = "Nombres espacés sur une échelle logarithmique" +PythonNumpyCopy = "Copie du tableau" +PythonNumpyDtype = "Dtype du tableau" +PythonNumpyFlat = "Itérateur plat du tableau" +PythonNumpyFlatten = "Version aplatie du tableau" +PythonNumpyShape = "Obtenir la forme du tableau" +PythonNumpyReshape = "Remplacer la forme du tableau par s" +PythonNumpySize = "Nombre d'éléments dans le tableau" +PythonNumpyTranspose = "Version transposée du tableau" +PythonNumpySortWithArguments = "Version triée du tableau" +PythonNumpyNdinfo = "Imprimer des informations sur un" +PythonNumpyAll = "Tester si tous les éléments de a sont trye" +PythonNumpyAny = "Tester si un élément de a est vrai" +PythonNumpyArgmax = "Indice de la valeur maximale de a" +PythonNumpyArgmin = "Indice de la valeur minimale de a" +PythonNumpyArgsort = "Indices qui trieraient un tableau" +PythonNumpyClip = "Couper les valeurs dans un tableau" +PythonNumpyConvolve = "Convolution linéaire discrète de a et b" +PythonNumpyDiff = "Dérivée du a" +PythonNumpyInterp = "Valeurs interpolées linéairement de a" +PythonNumpyDot = "Produit scalaire de a et b" +PythonNumpyCross = "Produit vectoriel de a et b" +PythonNumpyEqual = "a == a élément par élément" +PythonNumpyNot_equal = "a != a élément par élément" +PythonNumpyFlip = "Tableau de retournement" +PythonNumpyIsfinite = "Testez la finitude élément par élément" +PythonNumpyIsinf = "Testez l'infinité élément par élément" +PythonNumpyMean = "Moyenne d" +PythonNumpyMin = "Valeur maximale de a" +PythonNumpyMax = "Valeur minimale de a" +PythonNumpyMedian = "Valeur médiane de a" +PythonNumpyMinimum = "Minimum d'éléments de tableau par élément" +PythonNumpyMaximum = "Maximum par élément d'éléments de tableau" +PythonNumpyPolyfit = "Ajustement polynomial des moindres carrés" +PythonNumpyPolyval = "Évaluer un polynôme à des valeurs spécifiques" +PythonNumpyRoll = "Décaler le contenu de a par n" +PythonNumpySort = "Trier a" +PythonNumpyStd = "Calculer l'écart type de a" +PythonNumpySum = "Calculer la somme de a" +PythonNumpyTrace = "Calculer la somme des éléments diagonaux de a" +PythonNumpyTrapz = "Intégrer à l'aide de la règle trapézoïdale composite" +PythonNumpyWhere = "Renvoie des éléments choisis parmi x ou y selon c" +PythonNumpyVectorize = "Vectoriser la fonction python générique f" +PythonNumpyAcos = "Appliquer acos élément par élément" +PythonNumpyAcosh = "Appliquer acosh élément par élément" +PythonNumpyArctan2 = "Appliquer arctan2 élément par élément" +PythonNumpyAround = "Appliquer autour de l'élément" +PythonNumpyAsin = "Appliquer asin élément par élément" +PythonNumpyAsinh = "Appliquer asinh élément par élément" +PythonNumpyAtan = "Appliquer un élément par élément" +PythonNumpyAtanh = "Appliquer atanh élément par élément" +PythonNumpyCeil = "Appliquer le plafond par élément" +PythonNumpyCos = "Appliquer cos élément par élément" +PythonNumpyCosh = "Appliquer cosh élément par élément" +PythonNumpyDegrees = "Appliquer des degrés élément par élément" +PythonNumpyExp = "Appliquer exp par élément" +PythonNumpyExpm1 = "Appliquer expm1 élément par élément" +PythonNumpyFloor = "Appliquer le sol par élément" +PythonNumpyLog = "Appliquer le journal par élément" +PythonNumpyLog10 = "Appliquer log10 élément par élément" +PythonNumpyLog2 = "Appliquer log2 élément par élément" +PythonNumpyRadians = "Appliquer des radians par élément" +PythonNumpySin = "Appliquer le péché par élément" +PythonNumpySinh = "Appliquer sinh élément par élément" +PythonNumpySqrt = "Appliquer sqrt élément par élément" +PythonNumpyTan = "Appliquer le bronzage par élément" +PythonNumpyTanh = "Appliquer tanh par élément" +PythonNumpyBool = "Type bool de numpy" +PythonNumpyFloat = "Type float de numpy" +PythonNumpyUint8 = "Tapez uint8 de numpy" +PythonNumpyInt8 = "Tapez int8 de numpy" +PythonNumpyUint16 = "Tapez uint16 de numpy" +PythonNumpyInt16 = "Tapez int16 de numpy" +PythonNumpyNan = "Nan représentation de numpy" +PythonNumpyInf = "Inf représentation de numpy" +PythonNumpyE = "2.718281828459045" +PythonNumpyPi = "3.141592653589793" +PythonNumpyFft = "Transformée de Fourier discrète à une dimension" +PythonNumpyIfft = "Transformée de Fourier discrète inverse unidimensionnelle" +PythonNumpyDet = "Déterminant de a" +PythonNumpyEig = "Valeurs propres et vecteurs propres droits de a" +PythonNumpyCholesky = "Décomposition de Cholesky" +PythonNumpyInv = "Matrice inverse a" +PythonNumpyNorm = "Norme matricielle ou vectorielle" PythonOct = "Conversion en octal" PythonPhase = "Argument de z" PythonPlot = "Trace y en fonction de x" diff --git a/apps/code/catalog.hu.i18n b/apps/code/catalog.hu.i18n index 90a82696f..f5365e633 100644 --- a/apps/code/catalog.hu.i18n +++ b/apps/code/catalog.hu.i18n @@ -74,6 +74,7 @@ PythonImportKandinsky = "Kandinsky modul importálása" PythonImportRandom = "Véletlenszerü modul importálása" PythonImportMath = "math modul importálása" PythonImportMatplotlibPyplot = "matplotlib.pyplot modul importálása" +PythonImportNumpy = "ulab.numpy modul importálása" PythonImportTurtle = "turtle modul importálása" PythonImportTime = "time modul importálása" PythonIndex = "Az elsö x esemény indexe" @@ -98,6 +99,103 @@ PythonMax = "Maximum" PythonMin = "Minimum" PythonModf = "x-nek tört és egész részei" PythonMonotonic = "Az óra értékét adja vissza" +PythonNumpyFunction = "numpy elötag" +PythonNumpyFftFunction = "numpy.fft elötag" +PythonNumpyLinalgFunction = "numpy.linalg elötag" +PythonNumpyArray = "Egy tömb konvertálása ndarray -re" +PythonNumpyArange = "Készítsen táblázatot az (i) tartományból" +PythonNumpyConcatenate = "Összekapcsolás a és b" +PythonNumpyDiag = "Bontson ki vagy készítsen átlós tömböt" +PythonNumpyZeros = "S alakú tömb 0 -val kitöltve" +PythonNumpyOnes = "S alakú tömb 1-el" +PythonNumpyEmpty = "Az űrlap inicializálatlan tömbje" +PythonNumpyEye = "Asztal 1 -es átlóval és 0 -val máshol" +PythonNumpyFull = "S alakú tömb tele v" +PythonNumpyLinspace = "Számok meghatározott intervallumon belül" +PythonNumpyLogspace = "A számok logaritmikus skálán helyezkednek el" +PythonNumpyCopy = "A táblázat másolata" +PythonNumpyDtype = "Táblázat típusa" +PythonNumpyFlat = "Lapos tömb iterátor" +PythonNumpyFlatten = "Az asztal lapított változata" +PythonNumpyShape = "Szerezd meg a tömb alakját" +PythonNumpyReshape = "Cserélje le a tömb alakját az s -vel" +PythonNumpySize = "A tömb elemeinek száma" +PythonNumpyTranspose = "A táblázat átültetett változata" +PythonNumpySortWithArguments = "A táblázat rendezett változata" +PythonNumpyNdinfo = "Információk nyomtatása a" +PythonNumpyAll = "Ellenőrizze, hogy egy elem minden eleme trye" +PythonNumpyAny = "Ellenőrizze, hogy az a eleme igaz -e" +PythonNumpyArgmax = "A maximális érték indexe a" +PythonNumpyArgmin = "A minimális értékének a indexe" +PythonNumpyArgsort = "Nyomok, amelyek rendeznek egy tömböt" +PythonNumpyClip = "Vágja le az értékeket egy tömbben" +PythonNumpyConvolve = "A és b diszkrét lineáris konvolúciója" +PythonNumpyDiff = "Származik a" +PythonNumpyInterp = "A lineárisan interpolált értékei" +PythonNumpyDot = "Az a és b pontszerű szorzata" +PythonNumpyCross = "Az a és b keresztterméke" +PythonNumpyEqual = "A == elemenként" +PythonNumpyNot_equal = "A! = Elemenként" +PythonNumpyFlip = "Fordulóasztal" +PythonNumpyIsfinite = "Tesztelje a végességet elemenként" +PythonNumpyIsinf = "Tesztelje a végtelen elemet elemenként" +PythonNumpyMean = "Átlagos d" +PythonNumpyMin = "Maximális értéke a" +PythonNumpyMax = "Minimális értéke a" +PythonNumpyMedian = "Medián értéke a" +PythonNumpyMinimum = "Minimális tömb elemek elemenként" +PythonNumpyMaximum = "Maximum tömb elemenként" +PythonNumpyPolyfit = "Legkevesebb négyzet polinom illeszkedés" +PythonNumpyPolyval = "Polinom értékelése meghatározott értékeken" +PythonNumpyRoll = "Az a tartalmának eltolása n -vel" +PythonNumpySort = "Rendezés ide" +PythonNumpyStd = "Számítsa ki a szórását a" +PythonNumpySum = "Számítsa ki a összegét" +PythonNumpyTrace = "Számítsa ki az a átlós elemeinek összegét!" +PythonNumpyTrapz = "Integrálja az összetett trapéz vonalzó használatával" +PythonNumpyWhere = "A c szerint x vagy y közül választott elemeket adja vissza" +PythonNumpyVectorize = "Vektorizálja az általános python függvényt f" +PythonNumpyAcos = "Alkalmazza az acos -t elemenként" +PythonNumpyAcosh = "Alkalmazza az elemeket elemenként" +PythonNumpyArctan2 = "Alkalmazza az arctan2 elemet elemenként" +PythonNumpyAround = "Alkalmazza az elem körül" +PythonNumpyAsin = "Alkalmazza az asszonyt elemenként" +PythonNumpyAsinh = "Alkalmazza az elemet elemenként" +PythonNumpyAtan = "Alkalmazzon egy elemet elemenként" +PythonNumpyAtanh = "Alkalmazza az atanh elemenként" +PythonNumpyCeil = "Alkalmazza a mennyezetet elemenként" +PythonNumpyCos = "Alkalmazza a cos elemet elemenként" +PythonNumpyCosh = "Alkalmazza a cosh elemet elemenként" +PythonNumpyDegrees = "Alkalmazza a fokokat elemenként" +PythonNumpyExp = "Alkalmazza az exp -ot elemenként" +PythonNumpyExpm1 = "Alkalmazza az expm1 elemet elemenként" +PythonNumpyFloor = "A talajt elemenként vigye fel" +PythonNumpyLog = "Napló alkalmazása tétel szerint" +PythonNumpyLog10 = "Alkalmazza a log10 elemet elemenként" +PythonNumpyLog2 = "Alkalmazza a log2 elemet elemenként" +PythonNumpyRadians = "Alkalmazzon radiánt elemenként" +PythonNumpySin = "Alkalmazza a bűnt elemenként" +PythonNumpySinh = "Alkalmazza a sinh elemet elemenként" +PythonNumpySqrt = "Alkalmazza az sqrt elemet elemenként" +PythonNumpyTan = "Vigye fel a barnulást elemenként" +PythonNumpyTanh = "Alkalmazzon tannt elemenként" +PythonNumpyBool = "Bull típusú numpy" +PythonNumpyFloat = "Lebegő típusú számológép" +PythonNumpyUint8 = "Írja be az uint8 számot" +PythonNumpyInt8 = "Írja be a numpy int8 típusát" +PythonNumpyUint16 = "Írja be az uint16 parancsot a numpy -ból" +PythonNumpyInt16 = "Írja be a numpy int16 típusát" +PythonNumpyNan = "A numpy nanos ábrázolása" +PythonNumpyInf = "A numpy inf ábrázolása" +PythonNumpyE = "2.718281828459045" +PythonNumpyPi = "3.141592653589793" +PythonNumpyFft = "Egydimenziós diszkrét Fourier-transzformáció" +PythonNumpyIfft = "Egydimenziós inverz diszkrét Fourier-transzformáció" +PythonNumpyDet = "Meghatározó a" +PythonNumpyEig = "Sajátértékek és jobb sajátvektorok a" +PythonNumpyCholesky = "Cholesky bomlás" +PythonNumpyInv = "Fordított mátrix a" +PythonNumpyNorm = "Mátrix vagy vektor standard" PythonOct = "Decimális szám konvertálása octális számra" PythonPhase = "z fázisa" PythonPlot = "y-t jelöli x függvényében" diff --git a/apps/code/catalog.it.i18n b/apps/code/catalog.it.i18n index da188e0be..9402ad69a 100644 --- a/apps/code/catalog.it.i18n +++ b/apps/code/catalog.it.i18n @@ -74,6 +74,7 @@ PythonImportKandinsky = "Importa modulo kandinsky" PythonImportRandom = "Importa modulo random" PythonImportMath = "Importa modulo math" PythonImportMatplotlibPyplot = "Importa modulo matplotlib.pyplot" +PythonImportNumpy = "Importa modulo ulab.numpy" PythonImportTurtle = "Importa del modulo turtle" PythonImportTime = "Importa del modulo time" PythonImportOs = "Importa modulo os" @@ -104,6 +105,103 @@ PythonMax = "Massimo" PythonMin = "Minimo" PythonModf = "Parti frazionarie e intere" PythonMonotonic = "Restituisce il valore dell'orologio" +PythonNumpyFunction = "Prefisso modulo numpy" +PythonNumpyFftFunction = "Prefisso modulo numpy.fft" +PythonNumpyLinalgFunction = "Prefisso modulo numpy.linalg" +PythonNumpyArray = "Converti un array in ndarray" +PythonNumpyArange = "Crea una tabella dall'intervallo (i)" +PythonNumpyConcatenate = "Concatena a e b" +PythonNumpyDiag = "Estrai o costruisci un array diagonale" +PythonNumpyZeros = "Matrice a forma di S riempita con 0" +PythonNumpyOnes = "Array a forma di S riempito con 1" +PythonNumpyEmpty = "Matrice non inizializzata della forma s" +PythonNumpyEye = "Tabella con 1 in diagonale e 0 altrove" +PythonNumpyFull = "Matrice a forma di S riempita con v" +PythonNumpyLinspace = "Numeri spaziati su un intervallo specificato" +PythonNumpyLogspace = "Numeri spaziati su una scala logaritmica" +PythonNumpyCopy = "Copia della tabella" +PythonNumpyDtype = "Tipo di tabella" +PythonNumpyFlat = "Iteratore flat array" +PythonNumpyFlatten = "Versione appiattita del tavolo" +PythonNumpyShape = "Ottieni la forma dell'array" +PythonNumpyReshape = "Sostituisci la forma dell'array con s" +PythonNumpySize = "Numero di elementi nell'array" +PythonNumpyTranspose = "Versione trasposta della tabella" +PythonNumpySortWithArguments = "Versione ordinata della tabella" +PythonNumpyNdinfo = "Stampa informazioni su a" +PythonNumpyAll = "Verifica se tutti gli elementi di a sono provati" +PythonNumpyAny = "Verifica se un elemento di a è vero" +PythonNumpyArgmax = "Indice del valore massimo di a" +PythonNumpyArgmin = "Pedice del valore minimo di a" +PythonNumpyArgsort = "Indizi che ordinerebbero un array" +PythonNumpyClip = "Taglia i valori in un array" +PythonNumpyConvolve = "Convoluzione lineare discreta di a e b" +PythonNumpyDiff = "Derivato da a" +PythonNumpyInterp = "Valori interpolati linearmente di a" +PythonNumpyDot = "Prodotto scalare di a e b" +PythonNumpyCross = "Prodotto incrociato di a e b" +PythonNumpyEqual = "A == un elemento per elemento" +PythonNumpyNot_equal = "A! = Un elemento per elemento" +PythonNumpyFlip = "Tavolo di turnaround" +PythonNumpyIsfinite = "Testa la finitezza elemento per elemento" +PythonNumpyIsinf = "Prova l'infinito elemento per elemento" +PythonNumpyMean = "d . medio" +PythonNumpyMin = "Valore massimo di a" +PythonNumpyMax = "Valore minimo di a" +PythonNumpyMedian = "Valore medio di a" +PythonNumpyMinimum = "Elementi minimi dell'array per elemento" +PythonNumpyMaximum = "Massimo per elemento di elementi dell'array" +PythonNumpyPolyfit = "Approssimazione polinomiale ai minimi quadrati" +PythonNumpyPolyval = "Valuta un polinomio a valori specifici" +PythonNumpyRoll = "Sposta il contenuto di a di n" +PythonNumpySort = "Ordina per" +PythonNumpyStd = "Calcola la deviazione standard di a" +PythonNumpySum = "Calcola la somma di a" +PythonNumpyTrace = "Calcola la somma degli elementi diagonali di a" +PythonNumpyTrapz = "Integrare utilizzando il righello trapezoidale composito" +PythonNumpyWhere = "Restituisce elementi scelti da x o y secondo c" +PythonNumpyVectorize = "Vettorizza la funzione Python generica f" +PythonNumpyAcos = "Applica acos articolo per articolo" +PythonNumpyAcosh = "Applica acosh articolo per articolo" +PythonNumpyArctan2 = "Applica arctan2 elemento per elemento" +PythonNumpyAround = "Applicare intorno all'elemento" +PythonNumpyAsin = "Applica asin elemento per elemento" +PythonNumpyAsinh = "Applica asinh elemento per elemento" +PythonNumpyAtan = "Applicare un articolo per articolo" +PythonNumpyAtanh = "Applicare atanh elemento per elemento" +PythonNumpyCeil = "Applicare il soffitto per elemento" +PythonNumpyCos = "Applica cos elemento per elemento" +PythonNumpyCosh = "Applicare cosh elemento per elemento" +PythonNumpyDegrees = "Applica gradi elemento per elemento" +PythonNumpyExp = "Applica esperienza per articolo" +PythonNumpyExpm1 = "Applica expm1 elemento per elemento" +PythonNumpyFloor = "Applicare terreno per elemento" +PythonNumpyLog = "Applica giornale per articolo" +PythonNumpyLog10 = "Applica log10 elemento per elemento" +PythonNumpyLog2 = "Applica log2 elemento per elemento" +PythonNumpyRadians = "Applica radianti per elemento" +PythonNumpySin = "Applica sin per elemento" +PythonNumpySinh = "Applica sinh elemento per elemento" +PythonNumpySqrt = "Applica sqrt elemento per elemento" +PythonNumpyTan = "Applicare l'abbronzatura per elemento" +PythonNumpyTanh = "Applicare tanh per articolo" +PythonNumpyBool = "Tipo bool di numpy" +PythonNumpyFloat = "Tipo galleggiante di numpy" +PythonNumpyUint8 = "Digita uint8 di numpy" +PythonNumpyInt8 = "Digita int8 di numpy" +PythonNumpyUint16 = "Digita uint16 da numpy" +PythonNumpyInt16 = "Digita int16 di numpy" +PythonNumpyNan = "Nan rappresentazione di numpy" +PythonNumpyInf = "Inf rappresentazione di numpy" +PythonNumpyE = "2.718281828459045" +PythonNumpyPi = "3.141592653589933" +PythonNumpyFft = "Trasformata di Fourier discreta unidimensionale" +PythonNumpyIfft = "Trasformata di Fourier discreta inversa unidimensionale" +PythonNumpyDet = "Determinante di a" +PythonNumpyEig = "Autovalori e autovettori giusti di a" +PythonNumpyCholesky = "Decomposizione Cholesky" +PythonNumpyInv = "matrice inversa a" +PythonNumpyNorm = "Matrice o standard vettoriale" PythonOct = "Conversione in ottale" PythonPhase = "Argomento di z" PythonPlot = "Disegna y in f. di x come linee" diff --git a/apps/code/catalog.nl.i18n b/apps/code/catalog.nl.i18n index fb6669df3..fa54b6bc1 100644 --- a/apps/code/catalog.nl.i18n +++ b/apps/code/catalog.nl.i18n @@ -74,6 +74,7 @@ PythonImportKandinsky = "Importeer kandinsky module" PythonImportRandom = "Importeer random module" PythonImportMath = "Importeer math module" PythonImportMatplotlibPyplot = "Importeer matplotlib.pyplot module" +PythonImportNumpy = "Importeer ulab.numpy module" PythonImportTime = "Importeer time module" PythonImportOs = "Importeer os module" PythonOsUname = " Krijg systeeminfo" @@ -104,6 +105,103 @@ PythonMax = "Maximum" PythonMin = "Minimum" PythonModf = "Fractionele en gehele delen van x" PythonMonotonic = "Waarde van een monotone klok" +PythonNumpyFunction = "numpy module prefix" +PythonNumpyFftFunction = "numpy.fft module prefix" +PythonNumpyLinalgFunction = "numpy.linalg module prefix" +PythonNumpyArray = "Converteer een array naar ndarray" +PythonNumpyArange = "Maak een tabel uit de reeks (i)" +PythonNumpyConcatenate = "Samenvoegen a en b" +PythonNumpyDiag = "Een diagonale array extraheren of construeren" +PythonNumpyZeros = "S-vormarray gevuld met 0" +PythonNumpyOnes = "S-vormige array gevuld met 1" +PythonNumpyEmpty = "Niet-geïnitialiseerde matrix van vorm s" +PythonNumpyEye = "Tabel met enen op de diagonaal en nullen elders" +PythonNumpyFull = "S-vormarray gevuld met v" +PythonNumpyLinspace = "Getallen verdeeld over een opgegeven interval" +PythonNumpyLogspace = "Getallen op een logaritmische schaal verdeeld" +PythonNumpyCopy = "Kopie van tabel" +PythonNumpyDtype = "Tafeltype:" +PythonNumpyFlat = "Flat array iterator" +PythonNumpyFlatten = "Afgeplatte versie van de tafel" +PythonNumpyShape = "De vorm van de array verkrijgen" +PythonNumpyReshape = "Vervang matrixvorm door s" +PythonNumpySize = "Aantal elementen in de array" +PythonNumpyTranspose = "Getransponeerde versie van de tabel" +PythonNumpySortWithArguments = "Gesorteerde versie van de tafel" +PythonNumpyNdinfo = "Informatie afdrukken over a" +PythonNumpyAll = "Test of alle elementen van a trye zijn" +PythonNumpyAny = "Test of een element van a waar is" +PythonNumpyArgmax = "Index van de maximale waarde van a" +PythonNumpyArgmin = "Subscript van de minimumwaarde van a" +PythonNumpyArgsort = "Aanwijzingen die een array zouden sorteren" +PythonNumpyClip = "Knip waarden in een array" +PythonNumpyConvolve = "Discrete lineaire convolutie van a en b" +PythonNumpyDiff = "Afgeleid van a" +PythonNumpyInterp = "Lineair geïnterpoleerde waarden van a" +PythonNumpyDot = "Puntproduct van a en b" +PythonNumpyCross = "Kruisproduct van a en b" +PythonNumpyEqual = "A == een element voor element" +PythonNumpyNot_equal = "A! = Een element voor element" +PythonNumpyFlip = "Omslagtabel" +PythonNumpyIsfinite = "Test de eindigheid element voor element" +PythonNumpyIsinf = "Test het oneindige element voor element" +PythonNumpyMean = "gemiddelde d" +PythonNumpyMin = "Maximale waarde van a" +PythonNumpyMax = "Minimale waarde van a" +PythonNumpyMedian = "Mediane waarde van a" +PythonNumpyMinimum = "Minimum array-elementen per element" +PythonNumpyMaximum = "Maximum per element van array-elementen" +PythonNumpyPolyfit = "Kleinste kwadraten polynoom fit" +PythonNumpyPolyval = "Een polynoom evalueren op specifieke waarden" +PythonNumpyRoll = "Verschuif de inhoud van a met n" +PythonNumpySort = "Sorteren op" +PythonNumpyStd = "Bereken de standaarddeviatie van a" +PythonNumpySum = "Bereken de som van a" +PythonNumpyTrace = "Bereken de som van de diagonale elementen van a" +PythonNumpyTrapz = "Integreer met behulp van de samengestelde trapeziumvormige liniaal" +PythonNumpyWhere = "Retourneert elementen gekozen uit x of y volgens c" +PythonNumpyVectorize = "Vectoriseer de generieke python-functie f" +PythonNumpyAcos = "Acos item per item toepassen" +PythonNumpyAcosh = "Acosh item voor item toepassen" +PythonNumpyArctan2 = "Arctan2 element voor element toepassen" +PythonNumpyAround = "Toepassen rond het element" +PythonNumpyAsin = "Asin element voor element toepassen" +PythonNumpyAsinh = "Asinh element voor element toepassen" +PythonNumpyAtan = "Eén item per item toepassen" +PythonNumpyAtanh = "Atanh element voor element toepassen" +PythonNumpyCeil = "Breng het plafond per element aan" +PythonNumpyCos = "Pas co element voor element toe" +PythonNumpyCosh = "Cosh element voor element toepassen" +PythonNumpyDegrees = "Graden element voor element toepassen" +PythonNumpyExp = "exp per item toepassen" +PythonNumpyExpm1 = "expm1 element voor element toepassen" +PythonNumpyFloor = "Grond per element aanbrengen" +PythonNumpyLog = "Journaal per item toepassen" +PythonNumpyLog10 = "Pas log10 element voor element toe" +PythonNumpyLog2 = "Log2 element voor element toepassen" +PythonNumpyRadians = "Pas radialen toe per element" +PythonNumpySin = "Zonde per element toepassen" +PythonNumpySinh = "Sinh element voor element toepassen" +PythonNumpySqrt = "Sqrt element voor element toepassen" +PythonNumpyTan = "Breng de kleur aan per element" +PythonNumpyTanh = "Tanh toepassen per item" +PythonNumpyBool = "Bool type numpy" +PythonNumpyFloat = "Float type numpy" +PythonNumpyUint8 = "Typ uint8 van numpy" +PythonNumpyInt8 = "Typ int8 van numpy" +PythonNumpyUint16 = "Typ uint16 van numpy" +PythonNumpyInt16 = "Typ int16 van numpy" +PythonNumpyNan = "Nan vertegenwoordiging van numpy" +PythonNumpyInf = "Inf representatie van numpy" +PythonNumpyE = "2.718281828459045" +PythonNumpyPi = "3.141592653589793" +PythonNumpyFft = "Eendimensionale discrete fouriertransformatie" +PythonNumpyIfft = "Eendimensionale inverse discrete fouriertransformatie" +PythonNumpyDet = "Determinant van a" +PythonNumpyEig = "Eigenwaarden en rechter eigenvectoren van a" +PythonNumpyCholesky = "Cholesky-decompositie" +PythonNumpyInv = "Inverse matrix a" +PythonNumpyNorm = "Matrix- of vectorstandaard" PythonOct = "Integer omzetten naar octaal" PythonPhase = "Fase van z in radialen" PythonPlot = "Plot y versus x als lijnen" diff --git a/apps/code/catalog.pt.i18n b/apps/code/catalog.pt.i18n index 2cfc976cd..be87d2285 100644 --- a/apps/code/catalog.pt.i18n +++ b/apps/code/catalog.pt.i18n @@ -74,6 +74,7 @@ PythonImportKandinsky = "Importar módulo kandinsky" PythonImportRandom = "Importar módulo random" PythonImportMath = "Importar módulo math" PythonImportMatplotlibPyplot = "Importar módulo matplotlib.pyplot" +PythonImportNumpy = "Importar módulo ulab.numpy" PythonImportTime = "Importar módulo time" PythonImportTurtle = "Importar módulo turtle" PythonIndex = "Índice da primeira ocorrência de x" @@ -98,6 +99,103 @@ PythonMax = "Máximo" PythonMin = "Mínimo" PythonModf = "Partes inteira e frácionária de x" PythonMonotonic = "Devolve o valor do relógio" +PythonNumpyFunction = "Prefixo do módulo numpy" +PythonNumpyFftFunction = "Prefixo do módulo numpy.fft" +PythonNumpyLinalgFunction = "Prefixo do módulo numpy.linalg" +PythonNumpyArray = "Converter uma matriz em ndarray" +PythonNumpyArange = "Faça uma mesa do intervalo (i)" +PythonNumpyConcatenate = "Concatenar a e b" +PythonNumpyDiag = "Extraia ou construa uma matriz diagonal" +PythonNumpyZeros = "Matriz de forma S preenchida com 0" +PythonNumpyOnes = "Matriz em forma de S preenchida com 1" +PythonNumpyEmpty = "Matriz não inicializada de formulários" +PythonNumpyEye = "Tabela com 1s na diagonal e 0s em outros lugares" +PythonNumpyFull = "Matriz de forma S preenchida com v" +PythonNumpyLinspace = "Números espaçados em um intervalo especificado" +PythonNumpyLogspace = "Números espaçados em escala logarítmica" +PythonNumpyCopy = "Cópia da tabela" +PythonNumpyDtype = "Tipo de mesa" +PythonNumpyFlat = "Iterador de matriz plana" +PythonNumpyFlatten = "Versão achatada da mesa" +PythonNumpyShape = "Obtenha a forma da matriz" +PythonNumpyReshape = "Substitua a forma da matriz por s" +PythonNumpySize = "Número de elementos na matriz" +PythonNumpyTranspose = "Versão transposta da tabela" +PythonNumpySortWithArguments = "Versão ordenada da tabela" +PythonNumpyNdinfo = "Imprimir informações sobre um" +PythonNumpyAll = "Teste se todos os elementos de um são trye" +PythonNumpyAny = "Teste se um elemento de a é verdadeiro" +PythonNumpyArgmax = "Índice do valor máximo de um" +PythonNumpyArgmin = "Subscrito do valor mínimo de um" +PythonNumpyArgsort = "Pistas que classificariam um array" +PythonNumpyClip = "Corte os valores em uma matriz" +PythonNumpyConvolve = "Convolução linear discreta de a e b" +PythonNumpyDiff = "Derivado de um" +PythonNumpyInterp = "Valores linearmente interpolados de um" +PythonNumpyDot = "Produto escalar de a e b" +PythonNumpyCross = "Produto cruzado de a e b" +PythonNumpyEqual = "A == um elemento por elemento" +PythonNumpyNot_equal = "A! = Um elemento por elemento" +PythonNumpyFlip = "Mesa giratória" +PythonNumpyIsfinite = "Teste a finitude elemento por elemento" +PythonNumpyIsinf = "Teste o infinito elemento por elemento" +PythonNumpyMean = "D médio" +PythonNumpyMin = "Valor máximo de a" +PythonNumpyMax = "Valor mínimo de a" +PythonNumpyMedian = "Valor mediano de a" +PythonNumpyMinimum = "Elementos mínimos da matriz por elemento" +PythonNumpyMaximum = "Máximo por elemento de elementos da matriz" +PythonNumpyPolyfit = "Ajuste polinomial de mínimos quadrados" +PythonNumpyPolyval = "Avalie um polinômio em valores específicos" +PythonNumpyRoll = "Mudar o conteúdo de a por n" +PythonNumpySort = "Classificar para" +PythonNumpyStd = "Calcule o desvio padrão de um" +PythonNumpySum = "Calcule a soma de um" +PythonNumpyTrace = "Calcule a soma dos elementos diagonais de um" +PythonNumpyTrapz = "Integre usando a régua trapezoidal composta" +PythonNumpyWhere = "Retorna elementos escolhidos de x ou y de acordo com c" +PythonNumpyVectorize = "Vectorize a função python genérica f" +PythonNumpyAcos = "Aplicar acos item por item" +PythonNumpyAcosh = "Aplicar acosh item por item" +PythonNumpyArctan2 = "Aplicar arctan2 elemento por elemento" +PythonNumpyAround = "Aplicar ao redor do elemento" +PythonNumpyAsin = "Aplicar asin elemento a elemento" +PythonNumpyAsinh = "Aplicar asinh elemento por elemento" +PythonNumpyAtan = "Aplicar um item por item" +PythonNumpyAtanh = "Aplicar atanh elemento por elemento" +PythonNumpyCeil = "Aplicar o teto por elemento" +PythonNumpyCos = "Aplicar cos elemento por elemento" +PythonNumpyCosh = "Aplicar cosh elemento por elemento" +PythonNumpyDegrees = "Aplicar graus elemento a elemento" +PythonNumpyExp = "Aplicar exp por item" +PythonNumpyExpm1 = "Aplicar expm1 elemento a elemento" +PythonNumpyFloor = "Aplicar solo por elemento" +PythonNumpyLog = "Aplicar diário por item" +PythonNumpyLog10 = "Aplicar log10 elemento a elemento" +PythonNumpyLog2 = "Aplicar log2 elemento por elemento" +PythonNumpyRadians = "Aplicar radianos por elemento" +PythonNumpySin = "Aplicar pecado por elemento" +PythonNumpySinh = "Aplicar sinh elemento a elemento" +PythonNumpySqrt = "Aplicar sqrt elemento a elemento" +PythonNumpyTan = "Aplicar o bronzeado por elemento" +PythonNumpyTanh = "Aplicar tanh por item" +PythonNumpyBool = "Tipo Bool de entorpecido" +PythonNumpyFloat = "Tipo flutuante de entorpecimento" +PythonNumpyUint8 = "Digite uint8 de numpy" +PythonNumpyInt8 = "Digite int8 de numpy" +PythonNumpyUint16 = "Digite uint16 de numpy" +PythonNumpyInt16 = "Digite int16 de numpy" +PythonNumpyNan = "Representação Nan de numpy" +PythonNumpyInf = "Representação de inf de numpy" +PythonNumpyE = "2.718281828459045" +PythonNumpyPi = "3,141592653589793" +PythonNumpyFft = "Transformada discreta de Fourier unidimensional" +PythonNumpyIfft = "Transformada de Fourier discreta inversa unidimensional" +PythonNumpyDet = "Determinante de um" +PythonNumpyEig = "Valores próprios e vetores próprios direitos de um" +PythonNumpyCholesky = "Decomposição de Cholesky" +PythonNumpyInv = "Matriz inversa a" +PythonNumpyNorm = "Matriz ou padrão vetorial" PythonOct = "Converter número inteiro em octal" PythonPhase = "Argumento de z" PythonPlot = "Desenhar y em função de x" diff --git a/apps/code/catalog.universal.i18n b/apps/code/catalog.universal.i18n index fd3d42945..106d57dbb 100644 --- a/apps/code/catalog.universal.i18n +++ b/apps/code/catalog.universal.i18n @@ -90,6 +90,7 @@ PythonCommandImportIon = "import ion" PythonCommandImportKandinsky = "import kandinsky" PythonCommandImportMath = "import math" PythonCommandImportMatplotlibPyplot = "import matplotlib.pyplot" +PythonCommandImportFromNumpy = "from ulab import numpy as np" PythonCommandImportRandom = "import random" PythonCommandImportOs = "import os" PythonCommandImportFromOs = "from os import *" @@ -170,6 +171,115 @@ PythonCommandMax = "max(list)" PythonCommandMin = "min(list)" PythonCommandModf = "modf(x)" PythonCommandMonotonic = "monotonic()" +PythonCommandNumpyArray = "np.array(a)" +PythonCommandNumpyArange = "np.arange(i)" +PythonCommandNumpyConcatenate = "np.concatenate(a,b)" +PythonCommandNumpyDiag = "np.diag(a)" +PythonCommandNumpyZeros = "np.zeros(s)" +PythonCommandNumpyOnes = "np.ones(s)" +PythonCommandNumpyEmpty = "np.empty(s)" +PythonCommandNumpyEye = "np.eye(n, m)" +PythonCommandNumpyFull = "np.full(s, v)" +PythonCommandNumpyLinspace = "np.linspace(s, e)" +PythonCommandNumpyLogspace = "np.logspace(s, e)" +PythonCommandNumpyCopy = "ndarray.copy()" +PythonCommandNumpyCopyWithoutArg = ".copy()" +PythonCommandNumpyDtype = "ndarray.dtype" +PythonCommandNumpyDtypeWithoutArg = ".dtype" +PythonCommandNumpyFlat = "ndarray.flat" +PythonCommandNumpyFlatWithoutArg = ".flat" +PythonCommandNumpyFlatten = "ndarray.flatten()" +PythonCommandNumpyFlattenWithoutArg = ".flatten()" +PythonCommandNumpyShape = "ndarray.shape" +PythonCommandNumpyShapeWithoutArg = ".shape" +PythonCommandNumpyReshape = "ndarray.reshape(s)" +PythonCommandNumpyReshapeWithoutArg = ".reshape(\x11)" +PythonCommandNumpySize = "ndarray.size" +PythonCommandNumpySizeWithoutArg = ".size" +PythonCommandNumpyTranspose = "ndarray.transpose()" +PythonCommandNumpyTransposeWithoutArg = ".transpose()" +PythonCommandNumpySort = "ndarray.sort()" +PythonCommandNumpySortWithoutArg = ".sort()" +PythonCommandNumpyNdinfo = "np.ndinfo(a)" +PythonCommandNumpyAll = "np.all(a)" +PythonCommandNumpyAny = "np.any(a)" +PythonCommandNumpyArgmax = "np.argmax(a)" +PythonCommandNumpyArgmin = "np.argmin(a)" +PythonCommandNumpyArgsort = "np.argsort(a)" +PythonCommandNumpyClip = "np.clip(a, min, max)" +PythonCommandNumpyConvolve = "np.convolve(a, b)" +PythonCommandNumpyDiff = "np.diff(a)" +PythonCommandNumpyInterp = "np.interp(a)" +PythonCommandNumpyDot = "np.dot(a, b)" +PythonCommandNumpyCross = "np.cross(a, b)" +PythonCommandNumpyEqual = "np.equal(a, b)" +PythonCommandNumpyNot_equal = "np.not_equal(a, b)" +PythonCommandNumpyFlip = "np.flip(a)" +PythonCommandNumpyIsfinite = "np.isfinite(a)" +PythonCommandNumpyIsinf = "np.isinf(a)" +PythonCommandNumpyMean = "np.mean(a)" +PythonCommandNumpyMin = "np.min(a)" +PythonCommandNumpyMax = "np.max(a)" +PythonCommandNumpyMedian = "np.median(a)" +PythonCommandNumpyMinimum = "np.minimum(a, b)" +PythonCommandNumpyMaximum = "np.maximum(a, b)" +PythonCommandNumpyPolyfit = "np.polyfit(a, b, y)" +PythonCommandNumpyPolyval = "np.polyval(p, x)" +PythonCommandNumpyRoll = "np.roll(a, n)" +PythonCommandNumpySortWithArguments = "np.sort(a)" +PythonCommandNumpyStd = "np.std(a)" +PythonCommandNumpySum = "np.sum(a)" +PythonCommandNumpyTrace = "np.trace(a)" +PythonCommandNumpyTrapz = "np.trapz(y)" +PythonCommandNumpyWhere = "np.where(c, x, y)" +PythonCommandNumpyVectorize = "np.vectorize(f)" +PythonCommandNumpyAcos = "np.acos(a)" +PythonCommandNumpyAcosh = "np.acosh(a)" +PythonCommandNumpyArctan2 = "np.arctan2(a)" +PythonCommandNumpyAround = "np.around(a)" +PythonCommandNumpyAsin = "np.asin(a)" +PythonCommandNumpyAsinh = "np.asinh(a)" +PythonCommandNumpyAtan = "np.atan(a)" +PythonCommandNumpyAtanh = "np.atanh(a)" +PythonCommandNumpyCeil = "np.ceil(a)" +PythonCommandNumpyCos = "np.cos(a)" +PythonCommandNumpyCosh = "np.cosh(a)" +PythonCommandNumpyDegrees = "np.degrees(a)" +PythonCommandNumpyExp = "np.exp(a)" +PythonCommandNumpyExpm1 = "np.expm1(a)" +PythonCommandNumpyFloor = "np.floor(a)" +PythonCommandNumpyLog = "np.log(a)" +PythonCommandNumpyLog10 = "np.log10(a)" +PythonCommandNumpyLog2 = "np.log2(a)" +PythonCommandNumpyRadians = "np.radians(a)" +PythonCommandNumpySin = "np.sin(a)" +PythonCommandNumpySinh = "np.sinh(a)" +PythonCommandNumpySqrt = "np.sqrt(a)" +PythonCommandNumpyTan = "np.tan(a)" +PythonCommandNumpyTanh = "np.tanh(a)" +PythonCommandNumpyBool = "np.bool" +PythonCommandNumpyFloat = "np.float" +PythonCommandNumpyUint8 = "np.uint8" +PythonCommandNumpyInt8 = "np.int8" +PythonCommandNumpyUint16 = "np.uint16" +PythonCommandNumpyInt16 = "np.int16" +PythonCommandNumpyNan = "np.nan" +PythonCommandNumpyInf = "np.inf" +PythonCommandNumpyE = "np.e" +PythonCommandNumpyPi = "np.pi" +PythonCommandNumpyFft = "np.fft.fft(a)" +PythonCommandNumpyIfft = "np.fft.ifft(a)" +PythonCommandNumpyDet = "np.linalg.det(a)" +PythonCommandNumpyEig = "np.linalg.eig(a)" +PythonCommandNumpyCholesky = "np.linalg.cholesky(a)" +PythonCommandNumpyInv = "np.linalg.inv(a)" +PythonCommandNumpyNorm = "np.linalg.norm(a)" +PythonCommandNumpyFunction = "np.function" +PythonCommandNumpyFunctionWithoutArg = "np.\x11" +PythonCommandNumpyFftFunction = "np.fft.function" +PythonCommandNumpyFftFunctionWithoutArg = "np.fft.\x11" +PythonCommandNumpyLinalgFunction = "np.linalg.function" +PythonCommandNumpyLinalgFunctionWithoutArg = "np.linalg.\x11" PythonCommandOct = "oct(x)" PythonCommandPhase = "phase(z)" PythonCommandPlot = "plot(x,y,color)" diff --git a/apps/code/python_toolbox.cpp b/apps/code/python_toolbox.cpp index 3a17fe503..7d4c5384f 100644 --- a/apps/code/python_toolbox.cpp +++ b/apps/code/python_toolbox.cpp @@ -135,6 +135,127 @@ const ToolboxMessageTree MatplotlibPyplotModuleChildren[] = { ToolboxMessageTree::Leaf(I18n::Message::PythonCommandColorGray, I18n::Message::PythonColorGray, false) }; + const ToolboxMessageTree NumpyNdarrayModuleChildren[] = { + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyArray, I18n::Message::PythonNumpyArray), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyArange, I18n::Message::PythonNumpyArange), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyConcatenate, I18n::Message::PythonNumpyConcatenate), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyDiag, I18n::Message::PythonNumpyDiag), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyZeros, I18n::Message::PythonNumpyZeros), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyOnes, I18n::Message::PythonNumpyOnes), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyEmpty, I18n::Message::PythonNumpyEmpty), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyEye, I18n::Message::PythonNumpyEye), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyFull, I18n::Message::PythonNumpyFull), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyLinspace, I18n::Message::PythonNumpyLinspace), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyLogspace, I18n::Message::PythonNumpyLogspace), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyCopy, I18n::Message::PythonNumpyCopy, false, I18n::Message::PythonCommandNumpyCopyWithoutArg), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyDtype, I18n::Message::PythonNumpyDtype, false, I18n::Message::PythonCommandNumpyDtypeWithoutArg), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyFlat, I18n::Message::PythonNumpyFlat, false, I18n::Message::PythonCommandNumpyFlatWithoutArg), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyFlatten, I18n::Message::PythonNumpyFlatten, false, I18n::Message::PythonCommandNumpyFlattenWithoutArg), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyShape, I18n::Message::PythonNumpyShape, false, I18n::Message::PythonCommandNumpyShapeWithoutArg), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyReshape, I18n::Message::PythonNumpyReshape, false, I18n::Message::PythonCommandNumpyReshapeWithoutArg), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpySize, I18n::Message::PythonNumpySize, false, I18n::Message::PythonCommandNumpySizeWithoutArg), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyTranspose, I18n::Message::PythonNumpyTranspose, false, I18n::Message::PythonCommandNumpyTransposeWithoutArg), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpySort, I18n::Message::PythonNumpySortWithArguments, false, I18n::Message::PythonCommandNumpySortWithoutArg), +}; + +const ToolboxMessageTree NumpyFunctionsModuleChildren[] = { + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyNdinfo, I18n::Message::PythonNumpyNdinfo), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyAll, I18n::Message::PythonNumpyAll), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyAny, I18n::Message::PythonNumpyAny), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyArgmax, I18n::Message::PythonNumpyArgmax), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyArgmin, I18n::Message::PythonNumpyArgmin), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyArgsort, I18n::Message::PythonNumpyArgsort), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyClip, I18n::Message::PythonNumpyClip), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyConvolve, I18n::Message::PythonNumpyConvolve), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyDiff, I18n::Message::PythonNumpyDiff), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyInterp, I18n::Message::PythonNumpyInterp), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyDot, I18n::Message::PythonNumpyDot), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyCross, I18n::Message::PythonNumpyCross), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyEqual, I18n::Message::PythonNumpyEqual), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyNot_equal, I18n::Message::PythonNumpyNot_equal), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyFlip, I18n::Message::PythonNumpyFlip), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyIsfinite, I18n::Message::PythonNumpyIsfinite), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyIsinf, I18n::Message::PythonNumpyIsinf), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyMean, I18n::Message::PythonNumpyMean), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyMin, I18n::Message::PythonNumpyMin), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyMax, I18n::Message::PythonNumpyMax), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyMedian, I18n::Message::PythonNumpyMedian), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyMinimum, I18n::Message::PythonNumpyMinimum), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyMaximum, I18n::Message::PythonNumpyMaximum), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyPolyfit, I18n::Message::PythonNumpyPolyfit), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyPolyval, I18n::Message::PythonNumpyPolyval), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyRoll, I18n::Message::PythonNumpyRoll), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpySortWithArguments, I18n::Message::PythonNumpySort), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyStd, I18n::Message::PythonNumpyStd), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpySum, I18n::Message::PythonNumpySum), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyTrace, I18n::Message::PythonNumpyTrace), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyTrapz, I18n::Message::PythonNumpyTrapz), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyWhere, I18n::Message::PythonNumpyWhere), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyVectorize, I18n::Message::PythonNumpyVectorize), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyAcos, I18n::Message::PythonNumpyAcos), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyAcosh, I18n::Message::PythonNumpyAcosh), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyArctan2, I18n::Message::PythonNumpyArctan2), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyAround, I18n::Message::PythonNumpyAround), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyAsin, I18n::Message::PythonNumpyAsin), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyAsinh, I18n::Message::PythonNumpyAsinh), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyAtan, I18n::Message::PythonNumpyAtan), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyAtanh, I18n::Message::PythonNumpyAtanh), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyCeil, I18n::Message::PythonNumpyCeil), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyCos, I18n::Message::PythonNumpyCos), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyCosh, I18n::Message::PythonNumpyCosh), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyDegrees, I18n::Message::PythonNumpyDegrees), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyExp, I18n::Message::PythonNumpyExp), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyExpm1, I18n::Message::PythonNumpyExpm1), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyFloor, I18n::Message::PythonNumpyFloor), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyLog, I18n::Message::PythonNumpyLog), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyLog10, I18n::Message::PythonNumpyLog10), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyLog2, I18n::Message::PythonNumpyLog2), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyRadians, I18n::Message::PythonNumpyRadians), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpySin, I18n::Message::PythonNumpySin), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpySinh, I18n::Message::PythonNumpySinh), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpySqrt, I18n::Message::PythonNumpySqrt), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyTan, I18n::Message::PythonNumpyTan), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyTanh, I18n::Message::PythonNumpyTanh), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyBool, I18n::Message::PythonNumpyBool), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyFloat, I18n::Message::PythonNumpyFloat), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyUint8, I18n::Message::PythonNumpyUint8), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyInt8, I18n::Message::PythonNumpyInt8), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyUint16, I18n::Message::PythonNumpyUint16), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyInt16, I18n::Message::PythonNumpyInt16), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyNan, I18n::Message::PythonNumpyNan), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyInf, I18n::Message::PythonNumpyInf), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyE, I18n::Message::PythonNumpyE), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyPi, I18n::Message::PythonNumpyPi) +}; + +const ToolboxMessageTree NumpyFftModuleChildren[] = { + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyFftFunction, I18n::Message::PythonTurtleFunction, false, I18n::Message::PythonCommandNumpyFftFunctionWithoutArg), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyFft, I18n::Message::PythonNumpyFft), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyIfft, I18n::Message::PythonNumpyIfft) +}; + +const ToolboxMessageTree NumpyLinalgModuleChildren[] = { + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyLinalgFunction, I18n::Message::PythonTurtleFunction, false, I18n::Message::PythonCommandNumpyLinalgFunctionWithoutArg), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyDet, I18n::Message::PythonNumpyDet), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyEig, I18n::Message::PythonNumpyEig), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyCholesky, I18n::Message::PythonNumpyCholesky), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyInv, I18n::Message::PythonNumpyInv), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyNorm, I18n::Message::PythonNumpyNorm) +}; + +const ToolboxMessageTree NumpyModuleChildren[] = { + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandImportFromNumpy, I18n::Message::PythonImportTurtle, false), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyFunction, I18n::Message::PythonTurtleFunction, false, I18n::Message::PythonCommandNumpyPythonCommandNumpyFunctionWithoutArgFunction), + ToolboxMessageTree::Node(I18n::Message::NumpyNdarray, NumpyNdarrayModuleChildren), + ToolboxMessageTree::Node(I18n::Message::Functions, NumpyFunctionsModuleChildren), + ToolboxMessageTree::Node(I18n::Message::NumpyFftModule, NumpyFftModuleChildren), + ToolboxMessageTree::Node(I18n::Message::NumpyLinalgModule, NumpyLinalgModuleChildren) +}; + +const ToolboxMessageTree UlabModuleChildren[] = { + ToolboxMessageTree::Node(I18n::Message::NumpyModule, NumpyModuleChildren) +}; + const ToolboxMessageTree TurtleModuleChildren[] = { ToolboxMessageTree::Leaf(I18n::Message::PythonCommandImportTurtle, I18n::Message::PythonImportTurtle, false), ToolboxMessageTree::Leaf(I18n::Message::PythonCommandImportFromTurtle, I18n::Message::PythonImportTurtle, false), @@ -231,6 +352,7 @@ const ToolboxMessageTree modulesChildren[] = { ToolboxMessageTree::Node(I18n::Message::MathModule, MathModuleChildren), ToolboxMessageTree::Node(I18n::Message::CmathModule, CMathModuleChildren), ToolboxMessageTree::Node(I18n::Message::MatplotlibPyplotModule, MatplotlibPyplotModuleChildren), + ToolboxMessageTree::Node(I18n::Message::UlabModule, UlabModuleChildren), ToolboxMessageTree::Node(I18n::Message::TurtleModule, TurtleModuleChildren), ToolboxMessageTree::Node(I18n::Message::RandomModule, RandomModuleChildren), ToolboxMessageTree::Node(I18n::Message::KandinskyModule, KandinskyModuleChildren), diff --git a/apps/code/toolbox.universal.i18n b/apps/code/toolbox.universal.i18n index 4dd5345de..df6a9171d 100644 --- a/apps/code/toolbox.universal.i18n +++ b/apps/code/toolbox.universal.i18n @@ -3,9 +3,14 @@ IonModule = "ion" KandinskyModule = "kandinsky" MathModule = "math" MatplotlibPyplotModule = "matplotlib.pyplot" +NumpyModule = "numpy" +NumpyFftModule = "fft" +NumpyLinalgModule = "linalg" +NumpyNdarray = "ndarray" OsModule = "os" TimeModule = "time" TurtleModule = "turtle" +UlabModule = "ulab" ForLoopMenu = "For" IfStatementMenu = "If" WhileLoopMenu = "While" diff --git a/escher/include/escher/nested_menu_controller.h b/escher/include/escher/nested_menu_controller.h index 44ac41eca..ca671a1b7 100644 --- a/escher/include/escher/nested_menu_controller.h +++ b/escher/include/escher/nested_menu_controller.h @@ -41,7 +41,7 @@ protected: int depth() const; void resetStack(); private: - constexpr static int k_maxModelTreeDepth = 3; + constexpr static int k_maxModelTreeDepth = 4; State m_statesStack[k_maxModelTreeDepth]; }; From a15c682e3ee6d83465dd73a8cd34a67da9cefd8d Mon Sep 17 00:00:00 2001 From: Laury Date: Sat, 4 Sep 2021 21:23:23 +0200 Subject: [PATCH 42/48] [code/ulab] Replaced translations by official documentation --- apps/code/catalog.de.i18n | 94 -------------- apps/code/catalog.en.i18n | 94 -------------- apps/code/catalog.es.i18n | 94 -------------- apps/code/catalog.fr.i18n | 94 -------------- apps/code/catalog.hu.i18n | 94 -------------- apps/code/catalog.it.i18n | 94 -------------- apps/code/catalog.nl.i18n | 94 -------------- apps/code/catalog.pt.i18n | 94 -------------- apps/code/python_toolbox.cpp | 204 ++++++++++++++++--------------- apps/code/toolbox.de.i18n | 1 + apps/code/toolbox.en.i18n | 1 + apps/code/toolbox.es.i18n | 1 + apps/code/toolbox.fr.i18n | 1 + apps/code/toolbox.hu.i18n | 1 + apps/code/toolbox.it.i18n | 1 + apps/code/toolbox.nl.i18n | 1 + apps/code/toolbox.pt.i18n | 1 + apps/code/toolbox.universal.i18n | 1 + 18 files changed, 113 insertions(+), 852 deletions(-) diff --git a/apps/code/catalog.de.i18n b/apps/code/catalog.de.i18n index 2c942a56e..da6c16f99 100644 --- a/apps/code/catalog.de.i18n +++ b/apps/code/catalog.de.i18n @@ -108,100 +108,6 @@ PythonMonotonic = "Wert einer monotonen Uhr" PythonNumpyFunction = "numpy Modul-Präfix" PythonNumpyFftFunction = "numpy.fft Modul-Präfix" PythonNumpyLinalgFunction = "numpy.linalg Modul-Präfix" -PythonNumpyArray = "Konvertieren Sie ein Array in ndarray" -PythonNumpyArange = "Erstellen Sie eine Tabelle aus dem Bereich (i)" -PythonNumpyConcatenate = "Verketten Sie a und b" -PythonNumpyDiag = "Extrahiere oder konstruiere ein diagonales Array" -PythonNumpyZeros = "S-förmiges Array gefüllt mit 0" -PythonNumpyOnes = "S-förmiges Array gefüllt mit 1" -PythonNumpyEmpty = "Nicht initialisiertes Array der Form s" -PythonNumpyEye = "Tabelle mit Einsen auf der Diagonale und Nullen an anderer Stelle" -PythonNumpyFull = "S-förmiges Array gefüllt mit v" -PythonNumpyLinspace = "Zahlen, die über ein bestimmtes Intervall verteilt sind" -PythonNumpyLogspace = "Zahlen mit logarithmischem Abstand" -PythonNumpyCopy = "Kopie der Tabelle" -PythonNumpyDtype = "Tischtyp" -PythonNumpyFlat = "Flat-Array-Iterator" -PythonNumpyFlatten = "Abgeflachte Version der Tabelle" -PythonNumpyShape = "Holen Sie sich die Form des Arrays" -PythonNumpyReshape = "Array-Form durch s ersetzen" -PythonNumpySize = "Anzahl der Elemente im Array" -PythonNumpyTranspose = "Transponierte Version der Tabelle" -PythonNumpySortWithArguments = "Sortierte Version der Tabelle" -PythonNumpyNdinfo = "Informationen zu a . drucken" -PythonNumpyAll = "Testen Sie, ob alle Elemente von a trye sind" -PythonNumpyAny = "Teste, ob ein Element von a wahr ist" -PythonNumpyArgmax = "Index des Maximalwertes von a" -PythonNumpyArgmin = "Tiefgestellter Wert des Mindestwertes von a" -PythonNumpyArgsort = "Hinweise, die ein Array sortieren würden" -PythonNumpyClip = "Werte in einem Array ausschneiden" -PythonNumpyConvolve = "Diskrete lineare Faltung von a und b" -PythonNumpyDiff = "Abgeleitet von a" -PythonNumpyInterp = "Linear interpolierte Werte von a" -PythonNumpyDot = "Punktprodukt von a und b" -PythonNumpyCross = "Kreuzprodukt von a und b" -PythonNumpyEqual = "A == Element für Element" -PythonNumpyNot_equal = "A! = Element für Element" -PythonNumpyFlip = "Turnaround-Tabelle" -PythonNumpyIsfinite = "Testen Sie die Endlichkeit Element für Element" -PythonNumpyIsinf = "Teste die Unendlichkeit Element für Element" -PythonNumpyMean = "Durchschnitt d" -PythonNumpyMin = "Maximalwert von a" -PythonNumpyMax = "Mindestwert von a" -PythonNumpyMedian = "Medianwert von a" -PythonNumpyMinimum = "Minimale Array-Elemente pro Element" -PythonNumpyMaximum = "Maximum pro Element von Array-Elementen" -PythonNumpyPolyfit = "Polynomanpassung der kleinsten Quadrate" -PythonNumpyPolyval = "Bewerte ein Polynom bei bestimmten Werten" -PythonNumpyRoll = "Verschiebe den Inhalt von a um n" -PythonNumpySort = "Sortieren nach" -PythonNumpyStd = "Berechnen Sie die Standardabweichung von a" -PythonNumpySum = "Berechnen Sie die Summe von a" -PythonNumpyTrace = "Berechnen Sie die Summe der diagonalen Elemente von a" -PythonNumpyTrapz = "Integrieren Sie mit dem zusammengesetzten Trapezlineal" -PythonNumpyWhere = "Gibt Elemente aus x oder y gemäß c . zurück" -PythonNumpyVectorize = "Vektorisieren Sie die generische Python-Funktion f" -PythonNumpyAcos = "Wenden Sie acos Artikel für Artikel an" -PythonNumpyAcosh = "Wenden Sie acosh Artikel für Artikel an" -PythonNumpyArctan2 = "arctan2 Element für Element anwenden" -PythonNumpyAround = "Um das Element herum auftragen" -PythonNumpyAsin = "Element für Element anwenden" -PythonNumpyAsinh = "Wenden Sie asinh Element für Element an" -PythonNumpyAtan = "Wenden Sie ein Element für Element an" -PythonNumpyAtanh = "Wenden Sie atanh Element für Element an" -PythonNumpyCeil = "Bringen Sie die Decke nach Element an" -PythonNumpyCos = "Wenden Sie cos Element für Element an" -PythonNumpyCosh = "Wenden Sie cosh Element für Element an" -PythonNumpyDegrees = "Grade Element für Element anwenden" -PythonNumpyExp = "Exp pro Artikel anwenden" -PythonNumpyExpm1 = "Wenden Sie expm1 Element für Element an" -PythonNumpyFloor = "Boden nach Element auftragen" -PythonNumpyLog = "Tagebuch nach Artikel anwenden" -PythonNumpyLog10 = "Wenden Sie log10 Element für Element an" -PythonNumpyLog2 = "Wenden Sie log2 Element für Element an" -PythonNumpyRadians = "Wenden Sie Radiant pro Element an" -PythonNumpySin = "Wende Sünde nach Element an" -PythonNumpySinh = "Wenden Sie sinh Element für Element an" -PythonNumpySqrt = "Wenden Sie sqrt Element für Element an" -PythonNumpyTan = "Trage die Bräune nach Element auf" -PythonNumpyTanh = "Tanh pro Artikel auftragen" -PythonNumpyBool = "Bool Typ von numpy" -PythonNumpyFloat = "Float-Typ von numpy" -PythonNumpyUint8 = "Geben Sie uint8 von numpy . ein" -PythonNumpyInt8 = "Geben Sie int8 von numpy . ein" -PythonNumpyUint16 = "Geben Sie uint16 von numpy ein" -PythonNumpyInt16 = "Geben Sie int16 von numpy . ein" -PythonNumpyNan = "Nan-Darstellung von numpy" -PythonNumpyInf = "Inf-Darstellung von numpy" -PythonNumpyE = "2.718281828459045" -PythonNumpyPi = "3.141592653589793" -PythonNumpyFft = "Eindimensionale diskrete Fourier-Transformation" -PythonNumpyIfft = "Eindimensionale inverse diskrete Fourier-Transformation" -PythonNumpyDet = "Determinante von a" -PythonNumpyEig = "Eigenwerte und rechte Eigenvektoren von a" -PythonNumpyCholesky = "Cholesky-Zerlegung" -PythonNumpyInv = "Inverse Matrix a" -PythonNumpyNorm = "Matrix- oder Vektorstandard" PythonOct = "Ganzzahl in Oktal umwandeln" PythonPhase = "Phase von z" PythonPlot = "Plotten von y gegen x als Linien" diff --git a/apps/code/catalog.en.i18n b/apps/code/catalog.en.i18n index 09de24ccb..363dd820e 100644 --- a/apps/code/catalog.en.i18n +++ b/apps/code/catalog.en.i18n @@ -102,100 +102,6 @@ PythonMonotonic = "Value of a monotonic clock" PythonNumpyFunction = "numpy module prefix" PythonNumpyFftFunction = "numpy.fft module prefix" PythonNumpyLinalgFunction = "numpy.linalg module prefix" -PythonNumpyArray = "Convert an array to ndarray" -PythonNumpyArange = "Make a table from the range (i)" -PythonNumpyConcatenate = "Concatenate a and b" -PythonNumpyDiag = "Extract or construct a diagonal array" -PythonNumpyZeros = "S shape array filled with 0" -PythonNumpyOnes = "S shape array filled with 1" -PythonNumpyEmpty = "Uninitialized array of form s" -PythonNumpyEye = "Table with 1s on the diagonal and 0s elsewhere" -PythonNumpyFull = "S shape array filled with v" -PythonNumpyLinspace = "Numbers spaced over a specified interval" -PythonNumpyLogspace = "Numbers spaced on a logarithmic scale" -PythonNumpyCopy = "Copy of table" -PythonNumpyDtype = "Table type" -PythonNumpyFlat = "Flat array iterator" -PythonNumpyFlatten = "Flattened version of the table" -PythonNumpyShape = "Get the shape of the array" -PythonNumpyReshape = "Replace array shape with s" -PythonNumpySize = "Number of elements in the array" -PythonNumpyTranspose = "Transposed version of the table" -PythonNumpySortWithArguments = "Sorted version of the table" -PythonNumpyNdinfo = "Print information about a" -PythonNumpyAll = "Test if all elements of a are trye" -PythonNumpyAny = "Test if an element of a is true" -PythonNumpyArgmax = "Index of the maximum value of a" -PythonNumpyArgmin = "Subscript of the minimum value of a" -PythonNumpyArgsort = "Clues that would sort an array" -PythonNumpyClip = "Cut values in an array" -PythonNumpyConvolve = "Discrete linear convolution of a and b" -PythonNumpyDiff = "Derived from a" -PythonNumpyInterp = "Linearly interpolated values of a" -PythonNumpyDot = "Dot product of a and b" -PythonNumpyCross = "Cross product of a and b" -PythonNumpyEqual = "A == a element by element" -PythonNumpyNot_equal = "A! = A element by element" -PythonNumpyFlip = "Turnaround table" -PythonNumpyIsfinite = "Test the finiteness element by element" -PythonNumpyIsinf = "Test the infinity element by element" -PythonNumpyMean = "Average d" -PythonNumpyMin = "Maximum value of a" -PythonNumpyMax = "Minimum value of a" -PythonNumpyMedian = "Median value of a" -PythonNumpyMinimum = "Minimum array elements per element" -PythonNumpyMaximum = "Maximum per element of array elements" -PythonNumpyPolyfit = "Least squares polynomial fit" -PythonNumpyPolyval = "Evaluate a polynomial at specific values" -PythonNumpyRoll = "Shift the content of a by n" -PythonNumpySort = "Sort to" -PythonNumpyStd = "Calculate the standard deviation of a" -PythonNumpySum = "Calculate the sum of a" -PythonNumpyTrace = "Calculate the sum of the diagonal elements of a" -PythonNumpyTrapz = "Integrate using the composite trapezoidal ruler" -PythonNumpyWhere = "Returns elements chosen from x or y according to c" -PythonNumpyVectorize = "Vectorize the generic python function f" -PythonNumpyAcos = "Apply acos item by item" -PythonNumpyAcosh = "Apply acosh item by item" -PythonNumpyArctan2 = "Apply arctan2 element by element" -PythonNumpyAround = "Apply around the element" -PythonNumpyAsin = "Apply asin element by element" -PythonNumpyAsinh = "Apply asinh element by element" -PythonNumpyAtan = "Apply one item by item" -PythonNumpyAtanh = "Apply atanh element by element" -PythonNumpyCeil = "Apply the ceiling by element" -PythonNumpyCos = "Apply cos element by element" -PythonNumpyCosh = "Apply cosh element by element" -PythonNumpyDegrees = "Apply degrees element by element" -PythonNumpyExp = "Apply exp per item" -PythonNumpyExpm1 = "Apply expm1 element by element" -PythonNumpyFloor = "Apply soil by element" -PythonNumpyLog = "Apply journal by item" -PythonNumpyLog10 = "Apply log10 element by element" -PythonNumpyLog2 = "Apply log2 element by element" -PythonNumpyRadians = "Apply radians per element" -PythonNumpySin = "Apply sin by element" -PythonNumpySinh = "Apply sinh element by element" -PythonNumpySqrt = "Apply sqrt element by element" -PythonNumpyTan = "Apply the tan by element" -PythonNumpyTanh = "Apply tanh per item" -PythonNumpyBool = "Bool type of numpy" -PythonNumpyFloat = "Float type of numpy" -PythonNumpyUint8 = "Type uint8 of numpy" -PythonNumpyInt8 = "Type int8 of numpy" -PythonNumpyUint16 = "Type uint16 from numpy" -PythonNumpyInt16 = "Type int16 of numpy" -PythonNumpyNan = "Nan representation of numpy" -PythonNumpyInf = "Inf representation of numpy" -PythonNumpyE = "2.718281828459045" -PythonNumpyPi = "3.141592653589793" -PythonNumpyFft = "One-dimensional discrete fourier transform" -PythonNumpyIfft = "One-dimensional inverse discrete fourier transform" -PythonNumpyDet = "Determinant of a" -PythonNumpyEig = "Eigenvalues and right eigenvectors of a" -PythonNumpyCholesky = "Cholesky decomposition" -PythonNumpyInv = "Inverse matrix a" -PythonNumpyNorm = "Matrix or vector standard" PythonOct = "Convert integer to octal" PythonPhase = "Phase of z" PythonPlot = "Plot y versus x as lines" diff --git a/apps/code/catalog.es.i18n b/apps/code/catalog.es.i18n index 48bff282b..d39456839 100644 --- a/apps/code/catalog.es.i18n +++ b/apps/code/catalog.es.i18n @@ -102,100 +102,6 @@ PythonMonotonic = "Value of a monotonic clock" PythonNumpyFunction = "numpy module prefix" PythonNumpyFftFunction = "numpy.fft module prefix" PythonNumpyLinalgFunction = "numpy.linalg module prefix" -PythonNumpyArray = "Convertir una matriz a ndarray" -PythonNumpyArange = "Haz una tabla de la gama (i)" -PythonNumpyConcatenate = "Concatenar ayb" -PythonNumpyDiag = "Extraer o construir una matriz diagonal" -PythonNumpyZeros = "Matriz en forma de S rellena con 0" -PythonNumpyOnes = "Matriz en forma de S llena con 1" -PythonNumpyEmpty = "Matriz no inicializada de formulario s" -PythonNumpyEye = "Tabla con 1 en la diagonal y 0 en el resto" -PythonNumpyFull = "Matriz en forma de S rellena con v" -PythonNumpyLinspace = "Números espaciados en un intervalo específico" -PythonNumpyLogspace = "Números espaciados en una escala logarítmica" -PythonNumpyCopy = "Copia de la tabla" -PythonNumpyDtype = "Tipo de mesa" -PythonNumpyFlat = "Iterador de matriz plana" -PythonNumpyFlatten = "Versión aplanada de la mesa." -PythonNumpyShape = "Obtén la forma de la matriz" -PythonNumpyReshape = "Reemplazar la forma de la matriz con s" -PythonNumpySize = "Número de elementos en la matriz" -PythonNumpyTranspose = "Versión transpuesta de la tabla" -PythonNumpySortWithArguments = "Versión ordenada de la tabla" -PythonNumpyNdinfo = "Imprimir información sobre un" -PythonNumpyAll = "Prueba si todos los elementos de a son probables" -PythonNumpyAny = "Prueba si un elemento de a es verdadero" -PythonNumpyArgmax = "Índice del valor máximo de un" -PythonNumpyArgmin = "Subíndice del valor mínimo de un" -PythonNumpyArgsort = "Pistas que ordenarían una matriz" -PythonNumpyClip = "Cortar valores en una matriz" -PythonNumpyConvolve = "Convolución lineal discreta de ayb" -PythonNumpyDiff = "Derivado de un" -PythonNumpyInterp = "Valores interpolados linealmente de a" -PythonNumpyDot = "Producto escalar de ayb" -PythonNumpyCross = "Producto cruzado de ayb" -PythonNumpyEqual = "A == un elemento por elemento" -PythonNumpyNot_equal = "A! = Un elemento por elemento" -PythonNumpyFlip = "Tabla de cambio" -PythonNumpyIsfinite = "Prueba la finitud elemento por elemento" -PythonNumpyIsinf = "Prueba el infinito elemento por elemento" -PythonNumpyMean = "Promedio d" -PythonNumpyMin = "Valor máximo de un" -PythonNumpyMax = "Valor mínimo de un" -PythonNumpyMedian = "Valor mediano de a" -PythonNumpyMinimum = "Elementos de matriz mínimos por elemento" -PythonNumpyMaximum = "Máximo por elemento de elementos de matriz" -PythonNumpyPolyfit = "Ajuste de polinomio de mínimos cuadrados" -PythonNumpyPolyval = "Evaluar un polinomio en valores específicos" -PythonNumpyRoll = "Cambiar el contenido de a por n" -PythonNumpySort = "Ordenar por" -PythonNumpyStd = "Calcule la desviación estándar de un" -PythonNumpySum = "Calcule la suma de a" -PythonNumpyTrace = "Calcule la suma de los elementos diagonales de un" -PythonNumpyTrapz = "Integrar usando la regla trapezoidal compuesta" -PythonNumpyWhere = "Devuelve elementos elegidos de xoy según c" -PythonNumpyVectorize = "Vectorizar la función genérica de python f" -PythonNumpyAcos = "Aplicar acos artículo por artículo" -PythonNumpyAcosh = "Aplicar un elemento por elemento" -PythonNumpyArctan2 = "Aplicar arctan2 elemento por elemento" -PythonNumpyAround = "Aplicar alrededor del elemento" -PythonNumpyAsin = "Aplicar asin elemento por elemento" -PythonNumpyAsinh = "Aplicar asinh elemento por elemento" -PythonNumpyAtan = "Aplicar un artículo por artículo" -PythonNumpyAtanh = "Aplicar atanh elemento por elemento" -PythonNumpyCeil = "Aplicar el techo por elemento" -PythonNumpyCos = "Aplicar cos elemento por elemento" -PythonNumpyCosh = "Aplicar cosh elemento por elemento" -PythonNumpyDegrees = "Aplicar grados elemento por elemento" -PythonNumpyExp = "Aplicar exp por artículo" -PythonNumpyExpm1 = "Aplicar expm1 elemento por elemento" -PythonNumpyFloor = "Aplicar suelo por elemento" -PythonNumpyLog = "Aplicar diario por artículo" -PythonNumpyLog10 = "Aplicar log10 elemento por elemento" -PythonNumpyLog2 = "Aplicar log2 elemento por elemento" -PythonNumpyRadians = "Aplicar radianes por elemento" -PythonNumpySin = "Aplicar el pecado por elemento" -PythonNumpySinh = "Aplicar sinh elemento por elemento" -PythonNumpySqrt = "Aplicar elemento sqrt por elemento" -PythonNumpyTan = "Aplicar el bronceado por elemento" -PythonNumpyTanh = "Aplicar tanh por artículo" -PythonNumpyBool = "Bool tipo de numpy" -PythonNumpyFloat = "Flotador tipo de numpy" -PythonNumpyUint8 = "Escriba uint8 de numpy" -PythonNumpyInt8 = "Escriba int8 de numpy" -PythonNumpyUint16 = "Escriba uint16 desde numpy" -PythonNumpyInt16 = "Escriba int16 de numpy" -PythonNumpyNan = "Nan representación de numpy" -PythonNumpyInf = "Inf representación de numpy" -PythonNumpyE = "2.718281828459045" -PythonNumpyPi = "3.141592653589793" -PythonNumpyFft = "Transformada de Fourier discreta unidimensional" -PythonNumpyIfft = "Transformada de Fourier discreta inversa unidimensional" -PythonNumpyDet = "Determinante de un" -PythonNumpyEig = "Autovalores y autovectores derechos de un" -PythonNumpyCholesky = "Descomposición de Cholesky" -PythonNumpyInv = "Matriz inversa a" -PythonNumpyNorm = "Matriz o estándar vectorial" PythonOct = "Convert integer to octal" PythonPhase = "Phase of z" PythonPlot = "Plot y versus x as lines" diff --git a/apps/code/catalog.fr.i18n b/apps/code/catalog.fr.i18n index 5846f7c4a..d8d9daced 100644 --- a/apps/code/catalog.fr.i18n +++ b/apps/code/catalog.fr.i18n @@ -102,100 +102,6 @@ PythonMonotonic = "Renvoie la valeur de l'horloge" PythonNumpyFunction = "Préfixe fonction du module numpy" PythonNumpyFftFunction = "Préfixe fonction du module numpy.fft" PythonNumpyLinalgFunction = "Préfixe fonction du module numpy.linalg" -PythonNumpyArray = "Convertir un tableau en ndarray" -PythonNumpyArange = "Faire un tableau à partir de la plage (i)" -PythonNumpyConcatenate = "Concaténer a et b" -PythonNumpyDiag = "Extraire ou construire un tableau diagonal" -PythonNumpyZeros = "Tableau de forme s rempli de 0" -PythonNumpyOnes = "Tableau de forme s rempli de 1" -PythonNumpyEmpty = "Tableau uninitialisé de forme s" -PythonNumpyEye = "Tableau avec des 1 sur la diagonale et des 0 ailleurs" -PythonNumpyFull = "Tableau de forme s rempli de v" -PythonNumpyLinspace = "Nombres espacés sur un intervalle spécifié" -PythonNumpyLogspace = "Nombres espacés sur une échelle logarithmique" -PythonNumpyCopy = "Copie du tableau" -PythonNumpyDtype = "Dtype du tableau" -PythonNumpyFlat = "Itérateur plat du tableau" -PythonNumpyFlatten = "Version aplatie du tableau" -PythonNumpyShape = "Obtenir la forme du tableau" -PythonNumpyReshape = "Remplacer la forme du tableau par s" -PythonNumpySize = "Nombre d'éléments dans le tableau" -PythonNumpyTranspose = "Version transposée du tableau" -PythonNumpySortWithArguments = "Version triée du tableau" -PythonNumpyNdinfo = "Imprimer des informations sur un" -PythonNumpyAll = "Tester si tous les éléments de a sont trye" -PythonNumpyAny = "Tester si un élément de a est vrai" -PythonNumpyArgmax = "Indice de la valeur maximale de a" -PythonNumpyArgmin = "Indice de la valeur minimale de a" -PythonNumpyArgsort = "Indices qui trieraient un tableau" -PythonNumpyClip = "Couper les valeurs dans un tableau" -PythonNumpyConvolve = "Convolution linéaire discrète de a et b" -PythonNumpyDiff = "Dérivée du a" -PythonNumpyInterp = "Valeurs interpolées linéairement de a" -PythonNumpyDot = "Produit scalaire de a et b" -PythonNumpyCross = "Produit vectoriel de a et b" -PythonNumpyEqual = "a == a élément par élément" -PythonNumpyNot_equal = "a != a élément par élément" -PythonNumpyFlip = "Tableau de retournement" -PythonNumpyIsfinite = "Testez la finitude élément par élément" -PythonNumpyIsinf = "Testez l'infinité élément par élément" -PythonNumpyMean = "Moyenne d" -PythonNumpyMin = "Valeur maximale de a" -PythonNumpyMax = "Valeur minimale de a" -PythonNumpyMedian = "Valeur médiane de a" -PythonNumpyMinimum = "Minimum d'éléments de tableau par élément" -PythonNumpyMaximum = "Maximum par élément d'éléments de tableau" -PythonNumpyPolyfit = "Ajustement polynomial des moindres carrés" -PythonNumpyPolyval = "Évaluer un polynôme à des valeurs spécifiques" -PythonNumpyRoll = "Décaler le contenu de a par n" -PythonNumpySort = "Trier a" -PythonNumpyStd = "Calculer l'écart type de a" -PythonNumpySum = "Calculer la somme de a" -PythonNumpyTrace = "Calculer la somme des éléments diagonaux de a" -PythonNumpyTrapz = "Intégrer à l'aide de la règle trapézoïdale composite" -PythonNumpyWhere = "Renvoie des éléments choisis parmi x ou y selon c" -PythonNumpyVectorize = "Vectoriser la fonction python générique f" -PythonNumpyAcos = "Appliquer acos élément par élément" -PythonNumpyAcosh = "Appliquer acosh élément par élément" -PythonNumpyArctan2 = "Appliquer arctan2 élément par élément" -PythonNumpyAround = "Appliquer autour de l'élément" -PythonNumpyAsin = "Appliquer asin élément par élément" -PythonNumpyAsinh = "Appliquer asinh élément par élément" -PythonNumpyAtan = "Appliquer un élément par élément" -PythonNumpyAtanh = "Appliquer atanh élément par élément" -PythonNumpyCeil = "Appliquer le plafond par élément" -PythonNumpyCos = "Appliquer cos élément par élément" -PythonNumpyCosh = "Appliquer cosh élément par élément" -PythonNumpyDegrees = "Appliquer des degrés élément par élément" -PythonNumpyExp = "Appliquer exp par élément" -PythonNumpyExpm1 = "Appliquer expm1 élément par élément" -PythonNumpyFloor = "Appliquer le sol par élément" -PythonNumpyLog = "Appliquer le journal par élément" -PythonNumpyLog10 = "Appliquer log10 élément par élément" -PythonNumpyLog2 = "Appliquer log2 élément par élément" -PythonNumpyRadians = "Appliquer des radians par élément" -PythonNumpySin = "Appliquer le péché par élément" -PythonNumpySinh = "Appliquer sinh élément par élément" -PythonNumpySqrt = "Appliquer sqrt élément par élément" -PythonNumpyTan = "Appliquer le bronzage par élément" -PythonNumpyTanh = "Appliquer tanh par élément" -PythonNumpyBool = "Type bool de numpy" -PythonNumpyFloat = "Type float de numpy" -PythonNumpyUint8 = "Tapez uint8 de numpy" -PythonNumpyInt8 = "Tapez int8 de numpy" -PythonNumpyUint16 = "Tapez uint16 de numpy" -PythonNumpyInt16 = "Tapez int16 de numpy" -PythonNumpyNan = "Nan représentation de numpy" -PythonNumpyInf = "Inf représentation de numpy" -PythonNumpyE = "2.718281828459045" -PythonNumpyPi = "3.141592653589793" -PythonNumpyFft = "Transformée de Fourier discrète à une dimension" -PythonNumpyIfft = "Transformée de Fourier discrète inverse unidimensionnelle" -PythonNumpyDet = "Déterminant de a" -PythonNumpyEig = "Valeurs propres et vecteurs propres droits de a" -PythonNumpyCholesky = "Décomposition de Cholesky" -PythonNumpyInv = "Matrice inverse a" -PythonNumpyNorm = "Norme matricielle ou vectorielle" PythonOct = "Conversion en octal" PythonPhase = "Argument de z" PythonPlot = "Trace y en fonction de x" diff --git a/apps/code/catalog.hu.i18n b/apps/code/catalog.hu.i18n index f5365e633..b2011816b 100644 --- a/apps/code/catalog.hu.i18n +++ b/apps/code/catalog.hu.i18n @@ -102,100 +102,6 @@ PythonMonotonic = "Az óra értékét adja vissza" PythonNumpyFunction = "numpy elötag" PythonNumpyFftFunction = "numpy.fft elötag" PythonNumpyLinalgFunction = "numpy.linalg elötag" -PythonNumpyArray = "Egy tömb konvertálása ndarray -re" -PythonNumpyArange = "Készítsen táblázatot az (i) tartományból" -PythonNumpyConcatenate = "Összekapcsolás a és b" -PythonNumpyDiag = "Bontson ki vagy készítsen átlós tömböt" -PythonNumpyZeros = "S alakú tömb 0 -val kitöltve" -PythonNumpyOnes = "S alakú tömb 1-el" -PythonNumpyEmpty = "Az űrlap inicializálatlan tömbje" -PythonNumpyEye = "Asztal 1 -es átlóval és 0 -val máshol" -PythonNumpyFull = "S alakú tömb tele v" -PythonNumpyLinspace = "Számok meghatározott intervallumon belül" -PythonNumpyLogspace = "A számok logaritmikus skálán helyezkednek el" -PythonNumpyCopy = "A táblázat másolata" -PythonNumpyDtype = "Táblázat típusa" -PythonNumpyFlat = "Lapos tömb iterátor" -PythonNumpyFlatten = "Az asztal lapított változata" -PythonNumpyShape = "Szerezd meg a tömb alakját" -PythonNumpyReshape = "Cserélje le a tömb alakját az s -vel" -PythonNumpySize = "A tömb elemeinek száma" -PythonNumpyTranspose = "A táblázat átültetett változata" -PythonNumpySortWithArguments = "A táblázat rendezett változata" -PythonNumpyNdinfo = "Információk nyomtatása a" -PythonNumpyAll = "Ellenőrizze, hogy egy elem minden eleme trye" -PythonNumpyAny = "Ellenőrizze, hogy az a eleme igaz -e" -PythonNumpyArgmax = "A maximális érték indexe a" -PythonNumpyArgmin = "A minimális értékének a indexe" -PythonNumpyArgsort = "Nyomok, amelyek rendeznek egy tömböt" -PythonNumpyClip = "Vágja le az értékeket egy tömbben" -PythonNumpyConvolve = "A és b diszkrét lineáris konvolúciója" -PythonNumpyDiff = "Származik a" -PythonNumpyInterp = "A lineárisan interpolált értékei" -PythonNumpyDot = "Az a és b pontszerű szorzata" -PythonNumpyCross = "Az a és b keresztterméke" -PythonNumpyEqual = "A == elemenként" -PythonNumpyNot_equal = "A! = Elemenként" -PythonNumpyFlip = "Fordulóasztal" -PythonNumpyIsfinite = "Tesztelje a végességet elemenként" -PythonNumpyIsinf = "Tesztelje a végtelen elemet elemenként" -PythonNumpyMean = "Átlagos d" -PythonNumpyMin = "Maximális értéke a" -PythonNumpyMax = "Minimális értéke a" -PythonNumpyMedian = "Medián értéke a" -PythonNumpyMinimum = "Minimális tömb elemek elemenként" -PythonNumpyMaximum = "Maximum tömb elemenként" -PythonNumpyPolyfit = "Legkevesebb négyzet polinom illeszkedés" -PythonNumpyPolyval = "Polinom értékelése meghatározott értékeken" -PythonNumpyRoll = "Az a tartalmának eltolása n -vel" -PythonNumpySort = "Rendezés ide" -PythonNumpyStd = "Számítsa ki a szórását a" -PythonNumpySum = "Számítsa ki a összegét" -PythonNumpyTrace = "Számítsa ki az a átlós elemeinek összegét!" -PythonNumpyTrapz = "Integrálja az összetett trapéz vonalzó használatával" -PythonNumpyWhere = "A c szerint x vagy y közül választott elemeket adja vissza" -PythonNumpyVectorize = "Vektorizálja az általános python függvényt f" -PythonNumpyAcos = "Alkalmazza az acos -t elemenként" -PythonNumpyAcosh = "Alkalmazza az elemeket elemenként" -PythonNumpyArctan2 = "Alkalmazza az arctan2 elemet elemenként" -PythonNumpyAround = "Alkalmazza az elem körül" -PythonNumpyAsin = "Alkalmazza az asszonyt elemenként" -PythonNumpyAsinh = "Alkalmazza az elemet elemenként" -PythonNumpyAtan = "Alkalmazzon egy elemet elemenként" -PythonNumpyAtanh = "Alkalmazza az atanh elemenként" -PythonNumpyCeil = "Alkalmazza a mennyezetet elemenként" -PythonNumpyCos = "Alkalmazza a cos elemet elemenként" -PythonNumpyCosh = "Alkalmazza a cosh elemet elemenként" -PythonNumpyDegrees = "Alkalmazza a fokokat elemenként" -PythonNumpyExp = "Alkalmazza az exp -ot elemenként" -PythonNumpyExpm1 = "Alkalmazza az expm1 elemet elemenként" -PythonNumpyFloor = "A talajt elemenként vigye fel" -PythonNumpyLog = "Napló alkalmazása tétel szerint" -PythonNumpyLog10 = "Alkalmazza a log10 elemet elemenként" -PythonNumpyLog2 = "Alkalmazza a log2 elemet elemenként" -PythonNumpyRadians = "Alkalmazzon radiánt elemenként" -PythonNumpySin = "Alkalmazza a bűnt elemenként" -PythonNumpySinh = "Alkalmazza a sinh elemet elemenként" -PythonNumpySqrt = "Alkalmazza az sqrt elemet elemenként" -PythonNumpyTan = "Vigye fel a barnulást elemenként" -PythonNumpyTanh = "Alkalmazzon tannt elemenként" -PythonNumpyBool = "Bull típusú numpy" -PythonNumpyFloat = "Lebegő típusú számológép" -PythonNumpyUint8 = "Írja be az uint8 számot" -PythonNumpyInt8 = "Írja be a numpy int8 típusát" -PythonNumpyUint16 = "Írja be az uint16 parancsot a numpy -ból" -PythonNumpyInt16 = "Írja be a numpy int16 típusát" -PythonNumpyNan = "A numpy nanos ábrázolása" -PythonNumpyInf = "A numpy inf ábrázolása" -PythonNumpyE = "2.718281828459045" -PythonNumpyPi = "3.141592653589793" -PythonNumpyFft = "Egydimenziós diszkrét Fourier-transzformáció" -PythonNumpyIfft = "Egydimenziós inverz diszkrét Fourier-transzformáció" -PythonNumpyDet = "Meghatározó a" -PythonNumpyEig = "Sajátértékek és jobb sajátvektorok a" -PythonNumpyCholesky = "Cholesky bomlás" -PythonNumpyInv = "Fordított mátrix a" -PythonNumpyNorm = "Mátrix vagy vektor standard" PythonOct = "Decimális szám konvertálása octális számra" PythonPhase = "z fázisa" PythonPlot = "y-t jelöli x függvényében" diff --git a/apps/code/catalog.it.i18n b/apps/code/catalog.it.i18n index 9402ad69a..8627e679c 100644 --- a/apps/code/catalog.it.i18n +++ b/apps/code/catalog.it.i18n @@ -108,100 +108,6 @@ PythonMonotonic = "Restituisce il valore dell'orologio" PythonNumpyFunction = "Prefisso modulo numpy" PythonNumpyFftFunction = "Prefisso modulo numpy.fft" PythonNumpyLinalgFunction = "Prefisso modulo numpy.linalg" -PythonNumpyArray = "Converti un array in ndarray" -PythonNumpyArange = "Crea una tabella dall'intervallo (i)" -PythonNumpyConcatenate = "Concatena a e b" -PythonNumpyDiag = "Estrai o costruisci un array diagonale" -PythonNumpyZeros = "Matrice a forma di S riempita con 0" -PythonNumpyOnes = "Array a forma di S riempito con 1" -PythonNumpyEmpty = "Matrice non inizializzata della forma s" -PythonNumpyEye = "Tabella con 1 in diagonale e 0 altrove" -PythonNumpyFull = "Matrice a forma di S riempita con v" -PythonNumpyLinspace = "Numeri spaziati su un intervallo specificato" -PythonNumpyLogspace = "Numeri spaziati su una scala logaritmica" -PythonNumpyCopy = "Copia della tabella" -PythonNumpyDtype = "Tipo di tabella" -PythonNumpyFlat = "Iteratore flat array" -PythonNumpyFlatten = "Versione appiattita del tavolo" -PythonNumpyShape = "Ottieni la forma dell'array" -PythonNumpyReshape = "Sostituisci la forma dell'array con s" -PythonNumpySize = "Numero di elementi nell'array" -PythonNumpyTranspose = "Versione trasposta della tabella" -PythonNumpySortWithArguments = "Versione ordinata della tabella" -PythonNumpyNdinfo = "Stampa informazioni su a" -PythonNumpyAll = "Verifica se tutti gli elementi di a sono provati" -PythonNumpyAny = "Verifica se un elemento di a è vero" -PythonNumpyArgmax = "Indice del valore massimo di a" -PythonNumpyArgmin = "Pedice del valore minimo di a" -PythonNumpyArgsort = "Indizi che ordinerebbero un array" -PythonNumpyClip = "Taglia i valori in un array" -PythonNumpyConvolve = "Convoluzione lineare discreta di a e b" -PythonNumpyDiff = "Derivato da a" -PythonNumpyInterp = "Valori interpolati linearmente di a" -PythonNumpyDot = "Prodotto scalare di a e b" -PythonNumpyCross = "Prodotto incrociato di a e b" -PythonNumpyEqual = "A == un elemento per elemento" -PythonNumpyNot_equal = "A! = Un elemento per elemento" -PythonNumpyFlip = "Tavolo di turnaround" -PythonNumpyIsfinite = "Testa la finitezza elemento per elemento" -PythonNumpyIsinf = "Prova l'infinito elemento per elemento" -PythonNumpyMean = "d . medio" -PythonNumpyMin = "Valore massimo di a" -PythonNumpyMax = "Valore minimo di a" -PythonNumpyMedian = "Valore medio di a" -PythonNumpyMinimum = "Elementi minimi dell'array per elemento" -PythonNumpyMaximum = "Massimo per elemento di elementi dell'array" -PythonNumpyPolyfit = "Approssimazione polinomiale ai minimi quadrati" -PythonNumpyPolyval = "Valuta un polinomio a valori specifici" -PythonNumpyRoll = "Sposta il contenuto di a di n" -PythonNumpySort = "Ordina per" -PythonNumpyStd = "Calcola la deviazione standard di a" -PythonNumpySum = "Calcola la somma di a" -PythonNumpyTrace = "Calcola la somma degli elementi diagonali di a" -PythonNumpyTrapz = "Integrare utilizzando il righello trapezoidale composito" -PythonNumpyWhere = "Restituisce elementi scelti da x o y secondo c" -PythonNumpyVectorize = "Vettorizza la funzione Python generica f" -PythonNumpyAcos = "Applica acos articolo per articolo" -PythonNumpyAcosh = "Applica acosh articolo per articolo" -PythonNumpyArctan2 = "Applica arctan2 elemento per elemento" -PythonNumpyAround = "Applicare intorno all'elemento" -PythonNumpyAsin = "Applica asin elemento per elemento" -PythonNumpyAsinh = "Applica asinh elemento per elemento" -PythonNumpyAtan = "Applicare un articolo per articolo" -PythonNumpyAtanh = "Applicare atanh elemento per elemento" -PythonNumpyCeil = "Applicare il soffitto per elemento" -PythonNumpyCos = "Applica cos elemento per elemento" -PythonNumpyCosh = "Applicare cosh elemento per elemento" -PythonNumpyDegrees = "Applica gradi elemento per elemento" -PythonNumpyExp = "Applica esperienza per articolo" -PythonNumpyExpm1 = "Applica expm1 elemento per elemento" -PythonNumpyFloor = "Applicare terreno per elemento" -PythonNumpyLog = "Applica giornale per articolo" -PythonNumpyLog10 = "Applica log10 elemento per elemento" -PythonNumpyLog2 = "Applica log2 elemento per elemento" -PythonNumpyRadians = "Applica radianti per elemento" -PythonNumpySin = "Applica sin per elemento" -PythonNumpySinh = "Applica sinh elemento per elemento" -PythonNumpySqrt = "Applica sqrt elemento per elemento" -PythonNumpyTan = "Applicare l'abbronzatura per elemento" -PythonNumpyTanh = "Applicare tanh per articolo" -PythonNumpyBool = "Tipo bool di numpy" -PythonNumpyFloat = "Tipo galleggiante di numpy" -PythonNumpyUint8 = "Digita uint8 di numpy" -PythonNumpyInt8 = "Digita int8 di numpy" -PythonNumpyUint16 = "Digita uint16 da numpy" -PythonNumpyInt16 = "Digita int16 di numpy" -PythonNumpyNan = "Nan rappresentazione di numpy" -PythonNumpyInf = "Inf rappresentazione di numpy" -PythonNumpyE = "2.718281828459045" -PythonNumpyPi = "3.141592653589933" -PythonNumpyFft = "Trasformata di Fourier discreta unidimensionale" -PythonNumpyIfft = "Trasformata di Fourier discreta inversa unidimensionale" -PythonNumpyDet = "Determinante di a" -PythonNumpyEig = "Autovalori e autovettori giusti di a" -PythonNumpyCholesky = "Decomposizione Cholesky" -PythonNumpyInv = "matrice inversa a" -PythonNumpyNorm = "Matrice o standard vettoriale" PythonOct = "Conversione in ottale" PythonPhase = "Argomento di z" PythonPlot = "Disegna y in f. di x come linee" diff --git a/apps/code/catalog.nl.i18n b/apps/code/catalog.nl.i18n index fa54b6bc1..f46b4606f 100644 --- a/apps/code/catalog.nl.i18n +++ b/apps/code/catalog.nl.i18n @@ -108,100 +108,6 @@ PythonMonotonic = "Waarde van een monotone klok" PythonNumpyFunction = "numpy module prefix" PythonNumpyFftFunction = "numpy.fft module prefix" PythonNumpyLinalgFunction = "numpy.linalg module prefix" -PythonNumpyArray = "Converteer een array naar ndarray" -PythonNumpyArange = "Maak een tabel uit de reeks (i)" -PythonNumpyConcatenate = "Samenvoegen a en b" -PythonNumpyDiag = "Een diagonale array extraheren of construeren" -PythonNumpyZeros = "S-vormarray gevuld met 0" -PythonNumpyOnes = "S-vormige array gevuld met 1" -PythonNumpyEmpty = "Niet-geïnitialiseerde matrix van vorm s" -PythonNumpyEye = "Tabel met enen op de diagonaal en nullen elders" -PythonNumpyFull = "S-vormarray gevuld met v" -PythonNumpyLinspace = "Getallen verdeeld over een opgegeven interval" -PythonNumpyLogspace = "Getallen op een logaritmische schaal verdeeld" -PythonNumpyCopy = "Kopie van tabel" -PythonNumpyDtype = "Tafeltype:" -PythonNumpyFlat = "Flat array iterator" -PythonNumpyFlatten = "Afgeplatte versie van de tafel" -PythonNumpyShape = "De vorm van de array verkrijgen" -PythonNumpyReshape = "Vervang matrixvorm door s" -PythonNumpySize = "Aantal elementen in de array" -PythonNumpyTranspose = "Getransponeerde versie van de tabel" -PythonNumpySortWithArguments = "Gesorteerde versie van de tafel" -PythonNumpyNdinfo = "Informatie afdrukken over a" -PythonNumpyAll = "Test of alle elementen van a trye zijn" -PythonNumpyAny = "Test of een element van a waar is" -PythonNumpyArgmax = "Index van de maximale waarde van a" -PythonNumpyArgmin = "Subscript van de minimumwaarde van a" -PythonNumpyArgsort = "Aanwijzingen die een array zouden sorteren" -PythonNumpyClip = "Knip waarden in een array" -PythonNumpyConvolve = "Discrete lineaire convolutie van a en b" -PythonNumpyDiff = "Afgeleid van a" -PythonNumpyInterp = "Lineair geïnterpoleerde waarden van a" -PythonNumpyDot = "Puntproduct van a en b" -PythonNumpyCross = "Kruisproduct van a en b" -PythonNumpyEqual = "A == een element voor element" -PythonNumpyNot_equal = "A! = Een element voor element" -PythonNumpyFlip = "Omslagtabel" -PythonNumpyIsfinite = "Test de eindigheid element voor element" -PythonNumpyIsinf = "Test het oneindige element voor element" -PythonNumpyMean = "gemiddelde d" -PythonNumpyMin = "Maximale waarde van a" -PythonNumpyMax = "Minimale waarde van a" -PythonNumpyMedian = "Mediane waarde van a" -PythonNumpyMinimum = "Minimum array-elementen per element" -PythonNumpyMaximum = "Maximum per element van array-elementen" -PythonNumpyPolyfit = "Kleinste kwadraten polynoom fit" -PythonNumpyPolyval = "Een polynoom evalueren op specifieke waarden" -PythonNumpyRoll = "Verschuif de inhoud van a met n" -PythonNumpySort = "Sorteren op" -PythonNumpyStd = "Bereken de standaarddeviatie van a" -PythonNumpySum = "Bereken de som van a" -PythonNumpyTrace = "Bereken de som van de diagonale elementen van a" -PythonNumpyTrapz = "Integreer met behulp van de samengestelde trapeziumvormige liniaal" -PythonNumpyWhere = "Retourneert elementen gekozen uit x of y volgens c" -PythonNumpyVectorize = "Vectoriseer de generieke python-functie f" -PythonNumpyAcos = "Acos item per item toepassen" -PythonNumpyAcosh = "Acosh item voor item toepassen" -PythonNumpyArctan2 = "Arctan2 element voor element toepassen" -PythonNumpyAround = "Toepassen rond het element" -PythonNumpyAsin = "Asin element voor element toepassen" -PythonNumpyAsinh = "Asinh element voor element toepassen" -PythonNumpyAtan = "Eén item per item toepassen" -PythonNumpyAtanh = "Atanh element voor element toepassen" -PythonNumpyCeil = "Breng het plafond per element aan" -PythonNumpyCos = "Pas co element voor element toe" -PythonNumpyCosh = "Cosh element voor element toepassen" -PythonNumpyDegrees = "Graden element voor element toepassen" -PythonNumpyExp = "exp per item toepassen" -PythonNumpyExpm1 = "expm1 element voor element toepassen" -PythonNumpyFloor = "Grond per element aanbrengen" -PythonNumpyLog = "Journaal per item toepassen" -PythonNumpyLog10 = "Pas log10 element voor element toe" -PythonNumpyLog2 = "Log2 element voor element toepassen" -PythonNumpyRadians = "Pas radialen toe per element" -PythonNumpySin = "Zonde per element toepassen" -PythonNumpySinh = "Sinh element voor element toepassen" -PythonNumpySqrt = "Sqrt element voor element toepassen" -PythonNumpyTan = "Breng de kleur aan per element" -PythonNumpyTanh = "Tanh toepassen per item" -PythonNumpyBool = "Bool type numpy" -PythonNumpyFloat = "Float type numpy" -PythonNumpyUint8 = "Typ uint8 van numpy" -PythonNumpyInt8 = "Typ int8 van numpy" -PythonNumpyUint16 = "Typ uint16 van numpy" -PythonNumpyInt16 = "Typ int16 van numpy" -PythonNumpyNan = "Nan vertegenwoordiging van numpy" -PythonNumpyInf = "Inf representatie van numpy" -PythonNumpyE = "2.718281828459045" -PythonNumpyPi = "3.141592653589793" -PythonNumpyFft = "Eendimensionale discrete fouriertransformatie" -PythonNumpyIfft = "Eendimensionale inverse discrete fouriertransformatie" -PythonNumpyDet = "Determinant van a" -PythonNumpyEig = "Eigenwaarden en rechter eigenvectoren van a" -PythonNumpyCholesky = "Cholesky-decompositie" -PythonNumpyInv = "Inverse matrix a" -PythonNumpyNorm = "Matrix- of vectorstandaard" PythonOct = "Integer omzetten naar octaal" PythonPhase = "Fase van z in radialen" PythonPlot = "Plot y versus x als lijnen" diff --git a/apps/code/catalog.pt.i18n b/apps/code/catalog.pt.i18n index be87d2285..513297365 100644 --- a/apps/code/catalog.pt.i18n +++ b/apps/code/catalog.pt.i18n @@ -102,100 +102,6 @@ PythonMonotonic = "Devolve o valor do relógio" PythonNumpyFunction = "Prefixo do módulo numpy" PythonNumpyFftFunction = "Prefixo do módulo numpy.fft" PythonNumpyLinalgFunction = "Prefixo do módulo numpy.linalg" -PythonNumpyArray = "Converter uma matriz em ndarray" -PythonNumpyArange = "Faça uma mesa do intervalo (i)" -PythonNumpyConcatenate = "Concatenar a e b" -PythonNumpyDiag = "Extraia ou construa uma matriz diagonal" -PythonNumpyZeros = "Matriz de forma S preenchida com 0" -PythonNumpyOnes = "Matriz em forma de S preenchida com 1" -PythonNumpyEmpty = "Matriz não inicializada de formulários" -PythonNumpyEye = "Tabela com 1s na diagonal e 0s em outros lugares" -PythonNumpyFull = "Matriz de forma S preenchida com v" -PythonNumpyLinspace = "Números espaçados em um intervalo especificado" -PythonNumpyLogspace = "Números espaçados em escala logarítmica" -PythonNumpyCopy = "Cópia da tabela" -PythonNumpyDtype = "Tipo de mesa" -PythonNumpyFlat = "Iterador de matriz plana" -PythonNumpyFlatten = "Versão achatada da mesa" -PythonNumpyShape = "Obtenha a forma da matriz" -PythonNumpyReshape = "Substitua a forma da matriz por s" -PythonNumpySize = "Número de elementos na matriz" -PythonNumpyTranspose = "Versão transposta da tabela" -PythonNumpySortWithArguments = "Versão ordenada da tabela" -PythonNumpyNdinfo = "Imprimir informações sobre um" -PythonNumpyAll = "Teste se todos os elementos de um são trye" -PythonNumpyAny = "Teste se um elemento de a é verdadeiro" -PythonNumpyArgmax = "Índice do valor máximo de um" -PythonNumpyArgmin = "Subscrito do valor mínimo de um" -PythonNumpyArgsort = "Pistas que classificariam um array" -PythonNumpyClip = "Corte os valores em uma matriz" -PythonNumpyConvolve = "Convolução linear discreta de a e b" -PythonNumpyDiff = "Derivado de um" -PythonNumpyInterp = "Valores linearmente interpolados de um" -PythonNumpyDot = "Produto escalar de a e b" -PythonNumpyCross = "Produto cruzado de a e b" -PythonNumpyEqual = "A == um elemento por elemento" -PythonNumpyNot_equal = "A! = Um elemento por elemento" -PythonNumpyFlip = "Mesa giratória" -PythonNumpyIsfinite = "Teste a finitude elemento por elemento" -PythonNumpyIsinf = "Teste o infinito elemento por elemento" -PythonNumpyMean = "D médio" -PythonNumpyMin = "Valor máximo de a" -PythonNumpyMax = "Valor mínimo de a" -PythonNumpyMedian = "Valor mediano de a" -PythonNumpyMinimum = "Elementos mínimos da matriz por elemento" -PythonNumpyMaximum = "Máximo por elemento de elementos da matriz" -PythonNumpyPolyfit = "Ajuste polinomial de mínimos quadrados" -PythonNumpyPolyval = "Avalie um polinômio em valores específicos" -PythonNumpyRoll = "Mudar o conteúdo de a por n" -PythonNumpySort = "Classificar para" -PythonNumpyStd = "Calcule o desvio padrão de um" -PythonNumpySum = "Calcule a soma de um" -PythonNumpyTrace = "Calcule a soma dos elementos diagonais de um" -PythonNumpyTrapz = "Integre usando a régua trapezoidal composta" -PythonNumpyWhere = "Retorna elementos escolhidos de x ou y de acordo com c" -PythonNumpyVectorize = "Vectorize a função python genérica f" -PythonNumpyAcos = "Aplicar acos item por item" -PythonNumpyAcosh = "Aplicar acosh item por item" -PythonNumpyArctan2 = "Aplicar arctan2 elemento por elemento" -PythonNumpyAround = "Aplicar ao redor do elemento" -PythonNumpyAsin = "Aplicar asin elemento a elemento" -PythonNumpyAsinh = "Aplicar asinh elemento por elemento" -PythonNumpyAtan = "Aplicar um item por item" -PythonNumpyAtanh = "Aplicar atanh elemento por elemento" -PythonNumpyCeil = "Aplicar o teto por elemento" -PythonNumpyCos = "Aplicar cos elemento por elemento" -PythonNumpyCosh = "Aplicar cosh elemento por elemento" -PythonNumpyDegrees = "Aplicar graus elemento a elemento" -PythonNumpyExp = "Aplicar exp por item" -PythonNumpyExpm1 = "Aplicar expm1 elemento a elemento" -PythonNumpyFloor = "Aplicar solo por elemento" -PythonNumpyLog = "Aplicar diário por item" -PythonNumpyLog10 = "Aplicar log10 elemento a elemento" -PythonNumpyLog2 = "Aplicar log2 elemento por elemento" -PythonNumpyRadians = "Aplicar radianos por elemento" -PythonNumpySin = "Aplicar pecado por elemento" -PythonNumpySinh = "Aplicar sinh elemento a elemento" -PythonNumpySqrt = "Aplicar sqrt elemento a elemento" -PythonNumpyTan = "Aplicar o bronzeado por elemento" -PythonNumpyTanh = "Aplicar tanh por item" -PythonNumpyBool = "Tipo Bool de entorpecido" -PythonNumpyFloat = "Tipo flutuante de entorpecimento" -PythonNumpyUint8 = "Digite uint8 de numpy" -PythonNumpyInt8 = "Digite int8 de numpy" -PythonNumpyUint16 = "Digite uint16 de numpy" -PythonNumpyInt16 = "Digite int16 de numpy" -PythonNumpyNan = "Representação Nan de numpy" -PythonNumpyInf = "Representação de inf de numpy" -PythonNumpyE = "2.718281828459045" -PythonNumpyPi = "3,141592653589793" -PythonNumpyFft = "Transformada discreta de Fourier unidimensional" -PythonNumpyIfft = "Transformada de Fourier discreta inversa unidimensional" -PythonNumpyDet = "Determinante de um" -PythonNumpyEig = "Valores próprios e vetores próprios direitos de um" -PythonNumpyCholesky = "Decomposição de Cholesky" -PythonNumpyInv = "Matriz inversa a" -PythonNumpyNorm = "Matriz ou padrão vetorial" PythonOct = "Converter número inteiro em octal" PythonPhase = "Argumento de z" PythonPlot = "Desenhar y em função de x" diff --git a/apps/code/python_toolbox.cpp b/apps/code/python_toolbox.cpp index 7d4c5384f..2a26f98a7 100644 --- a/apps/code/python_toolbox.cpp +++ b/apps/code/python_toolbox.cpp @@ -136,116 +136,116 @@ const ToolboxMessageTree MatplotlibPyplotModuleChildren[] = { }; const ToolboxMessageTree NumpyNdarrayModuleChildren[] = { - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyArray, I18n::Message::PythonNumpyArray), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyArange, I18n::Message::PythonNumpyArange), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyConcatenate, I18n::Message::PythonNumpyConcatenate), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyDiag, I18n::Message::PythonNumpyDiag), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyZeros, I18n::Message::PythonNumpyZeros), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyOnes, I18n::Message::PythonNumpyOnes), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyEmpty, I18n::Message::PythonNumpyEmpty), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyEye, I18n::Message::PythonNumpyEye), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyFull, I18n::Message::PythonNumpyFull), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyLinspace, I18n::Message::PythonNumpyLinspace), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyLogspace, I18n::Message::PythonNumpyLogspace), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyCopy, I18n::Message::PythonNumpyCopy, false, I18n::Message::PythonCommandNumpyCopyWithoutArg), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyDtype, I18n::Message::PythonNumpyDtype, false, I18n::Message::PythonCommandNumpyDtypeWithoutArg), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyFlat, I18n::Message::PythonNumpyFlat, false, I18n::Message::PythonCommandNumpyFlatWithoutArg), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyFlatten, I18n::Message::PythonNumpyFlatten, false, I18n::Message::PythonCommandNumpyFlattenWithoutArg), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyShape, I18n::Message::PythonNumpyShape, false, I18n::Message::PythonCommandNumpyShapeWithoutArg), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyReshape, I18n::Message::PythonNumpyReshape, false, I18n::Message::PythonCommandNumpyReshapeWithoutArg), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpySize, I18n::Message::PythonNumpySize, false, I18n::Message::PythonCommandNumpySizeWithoutArg), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyTranspose, I18n::Message::PythonNumpyTranspose, false, I18n::Message::PythonCommandNumpyTransposeWithoutArg), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpySort, I18n::Message::PythonNumpySortWithArguments, false, I18n::Message::PythonCommandNumpySortWithoutArg), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyArray), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyArange), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyConcatenate), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyDiag), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyZeros), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyOnes), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyEmpty), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyEye), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyFull), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyLinspace), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyLogspace), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyCopy), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyDtype), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyFlat), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyFlatten), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyShape), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyReshape), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpySize), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyTranspose), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpySort), }; const ToolboxMessageTree NumpyFunctionsModuleChildren[] = { - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyNdinfo, I18n::Message::PythonNumpyNdinfo), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyAll, I18n::Message::PythonNumpyAll), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyAny, I18n::Message::PythonNumpyAny), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyArgmax, I18n::Message::PythonNumpyArgmax), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyArgmin, I18n::Message::PythonNumpyArgmin), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyArgsort, I18n::Message::PythonNumpyArgsort), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyClip, I18n::Message::PythonNumpyClip), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyConvolve, I18n::Message::PythonNumpyConvolve), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyDiff, I18n::Message::PythonNumpyDiff), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyInterp, I18n::Message::PythonNumpyInterp), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyDot, I18n::Message::PythonNumpyDot), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyCross, I18n::Message::PythonNumpyCross), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyEqual, I18n::Message::PythonNumpyEqual), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyNot_equal, I18n::Message::PythonNumpyNot_equal), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyFlip, I18n::Message::PythonNumpyFlip), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyIsfinite, I18n::Message::PythonNumpyIsfinite), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyIsinf, I18n::Message::PythonNumpyIsinf), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyMean, I18n::Message::PythonNumpyMean), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyMin, I18n::Message::PythonNumpyMin), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyMax, I18n::Message::PythonNumpyMax), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyMedian, I18n::Message::PythonNumpyMedian), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyMinimum, I18n::Message::PythonNumpyMinimum), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyMaximum, I18n::Message::PythonNumpyMaximum), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyPolyfit, I18n::Message::PythonNumpyPolyfit), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyPolyval, I18n::Message::PythonNumpyPolyval), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyRoll, I18n::Message::PythonNumpyRoll), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpySortWithArguments, I18n::Message::PythonNumpySort), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyStd, I18n::Message::PythonNumpyStd), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpySum, I18n::Message::PythonNumpySum), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyTrace, I18n::Message::PythonNumpyTrace), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyTrapz, I18n::Message::PythonNumpyTrapz), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyWhere, I18n::Message::PythonNumpyWhere), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyVectorize, I18n::Message::PythonNumpyVectorize), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyAcos, I18n::Message::PythonNumpyAcos), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyAcosh, I18n::Message::PythonNumpyAcosh), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyArctan2, I18n::Message::PythonNumpyArctan2), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyAround, I18n::Message::PythonNumpyAround), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyAsin, I18n::Message::PythonNumpyAsin), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyAsinh, I18n::Message::PythonNumpyAsinh), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyAtan, I18n::Message::PythonNumpyAtan), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyAtanh, I18n::Message::PythonNumpyAtanh), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyCeil, I18n::Message::PythonNumpyCeil), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyCos, I18n::Message::PythonNumpyCos), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyCosh, I18n::Message::PythonNumpyCosh), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyDegrees, I18n::Message::PythonNumpyDegrees), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyExp, I18n::Message::PythonNumpyExp), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyExpm1, I18n::Message::PythonNumpyExpm1), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyFloor, I18n::Message::PythonNumpyFloor), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyLog, I18n::Message::PythonNumpyLog), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyLog10, I18n::Message::PythonNumpyLog10), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyLog2, I18n::Message::PythonNumpyLog2), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyRadians, I18n::Message::PythonNumpyRadians), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpySin, I18n::Message::PythonNumpySin), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpySinh, I18n::Message::PythonNumpySinh), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpySqrt, I18n::Message::PythonNumpySqrt), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyTan, I18n::Message::PythonNumpyTan), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyTanh, I18n::Message::PythonNumpyTanh), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyBool, I18n::Message::PythonNumpyBool), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyFloat, I18n::Message::PythonNumpyFloat), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyUint8, I18n::Message::PythonNumpyUint8), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyInt8, I18n::Message::PythonNumpyInt8), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyUint16, I18n::Message::PythonNumpyUint16), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyInt16, I18n::Message::PythonNumpyInt16), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyNan, I18n::Message::PythonNumpyNan), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyInf, I18n::Message::PythonNumpyInf), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyE, I18n::Message::PythonNumpyE), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyPi, I18n::Message::PythonNumpyPi) + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyNdinfo), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyAll), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyAny), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyArgmax), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyArgmin), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyArgsort), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyClip), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyConvolve), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyDiff), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyInterp), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyDot), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyCross), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyEqual), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyNot_equal), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyFlip), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyIsfinite), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyIsinf), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyMean), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyMin), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyMax), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyMedian), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyMinimum), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyMaximum), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyPolyfit), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyPolyval), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyRoll), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpySortWithArguments), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyStd), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpySum), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyTrace), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyTrapz), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyWhere), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyVectorize), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyAcos), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyAcosh), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyArctan2), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyAround), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyAsin), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyAsinh), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyAtan), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyAtanh), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyCeil), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyCos), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyCosh), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyDegrees), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyExp), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyExpm1), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyFloor), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyLog), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyLog10), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyLog2), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyRadians), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpySin), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpySinh), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpySqrt), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyTan), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyTanh), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyBool), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyFloat), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyUint8), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyInt8), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyUint16), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyInt16), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyNan), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyInf), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyE), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyPi) }; const ToolboxMessageTree NumpyFftModuleChildren[] = { - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyFftFunction, I18n::Message::PythonTurtleFunction, false, I18n::Message::PythonCommandNumpyFftFunctionWithoutArg), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyFft, I18n::Message::PythonNumpyFft), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyIfft, I18n::Message::PythonNumpyIfft) + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyFftFunction, I18n::Message::PythonNumpyFftFunction, false, I18n::Message::PythonCommandNumpyFftFunctionWithoutArg), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyFft), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyIfft) }; const ToolboxMessageTree NumpyLinalgModuleChildren[] = { - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyLinalgFunction, I18n::Message::PythonTurtleFunction, false, I18n::Message::PythonCommandNumpyLinalgFunctionWithoutArg), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyDet, I18n::Message::PythonNumpyDet), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyEig, I18n::Message::PythonNumpyEig), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyCholesky, I18n::Message::PythonNumpyCholesky), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyInv, I18n::Message::PythonNumpyInv), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyNorm, I18n::Message::PythonNumpyNorm) + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyLinalgFunction, I18n::Message::PythonNumpyLinalgFunction, false, I18n::Message::PythonCommandNumpyLinalgFunctionWithoutArg), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyDet), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyEig), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyCholesky), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyInv), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyNorm) }; const ToolboxMessageTree NumpyModuleChildren[] = { - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandImportFromNumpy, I18n::Message::PythonImportTurtle, false), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyFunction, I18n::Message::PythonTurtleFunction, false, I18n::Message::PythonCommandNumpyPythonCommandNumpyFunctionWithoutArgFunction), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandImportFromNumpy, I18n::Message::PythonImportNumpy, false), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyFunction, I18n::Message::PythonNumpyFunction, false, I18n::Message::PythonCommandNumpyFunctionWithoutArg), ToolboxMessageTree::Node(I18n::Message::NumpyNdarray, NumpyNdarrayModuleChildren), ToolboxMessageTree::Node(I18n::Message::Functions, NumpyFunctionsModuleChildren), ToolboxMessageTree::Node(I18n::Message::NumpyFftModule, NumpyFftModuleChildren), @@ -253,7 +253,8 @@ const ToolboxMessageTree NumpyModuleChildren[] = { }; const ToolboxMessageTree UlabModuleChildren[] = { - ToolboxMessageTree::Node(I18n::Message::NumpyModule, NumpyModuleChildren) + ToolboxMessageTree::Node(I18n::Message::NumpyModule, NumpyModuleChildren), + ToolboxMessageTree::Leaf(I18n::Message::UlabDocumentation, I18n::Message::UlabDocumentationLink) }; const ToolboxMessageTree TurtleModuleChildren[] = { @@ -622,8 +623,11 @@ KDCoordinate PythonToolbox::rowHeight(int j) { } bool PythonToolbox::selectLeaf(int selectedRow) { - m_selectableTableView.deselectTable(); ToolboxMessageTree * node = (ToolboxMessageTree *)m_messageTreeModel->childAtIndex(selectedRow); + if(node->text() == I18n::Message::UlabDocumentationLink){ + return true; + } + m_selectableTableView.deselectTable(); if(node->insertedText() == I18n::Message::IonSelector){ m_ionKeys.setSender(sender()); Container::activeApp()->displayModalViewController(static_cast(&m_ionKeys), 0.f, 0.f, Metric::PopUpTopMargin, Metric::PopUpLeftMargin, 0, Metric::PopUpRightMargin); diff --git a/apps/code/toolbox.de.i18n b/apps/code/toolbox.de.i18n index 34840329b..82dd2b142 100644 --- a/apps/code/toolbox.de.i18n +++ b/apps/code/toolbox.de.i18n @@ -4,3 +4,4 @@ Modules = "Module" LoopsAndTests = "Schleifen und Tests" Files = "Dateien" Exceptions = "Ausnahmen" +UlabDocumentation = "Dokumentation" diff --git a/apps/code/toolbox.en.i18n b/apps/code/toolbox.en.i18n index 81e60c7da..59eadf403 100644 --- a/apps/code/toolbox.en.i18n +++ b/apps/code/toolbox.en.i18n @@ -4,3 +4,4 @@ Modules = "Modules" LoopsAndTests = "Loops and tests" Files = "Files" Exceptions = "Exceptions" +UlabDocumentation = "Documentation" diff --git a/apps/code/toolbox.es.i18n b/apps/code/toolbox.es.i18n index 81e60c7da..905e28a8e 100644 --- a/apps/code/toolbox.es.i18n +++ b/apps/code/toolbox.es.i18n @@ -4,3 +4,4 @@ Modules = "Modules" LoopsAndTests = "Loops and tests" Files = "Files" Exceptions = "Exceptions" +UlabDocumentation = "Documentación" diff --git a/apps/code/toolbox.fr.i18n b/apps/code/toolbox.fr.i18n index 724abb7a5..077e4550a 100644 --- a/apps/code/toolbox.fr.i18n +++ b/apps/code/toolbox.fr.i18n @@ -4,3 +4,4 @@ Modules = "Modules" LoopsAndTests = "Boucles et tests" Files = "Fichiers" Exceptions = "Exceptions" +UlabDocumentation = "Documentation" diff --git a/apps/code/toolbox.hu.i18n b/apps/code/toolbox.hu.i18n index 890151220..fbdff6f74 100644 --- a/apps/code/toolbox.hu.i18n +++ b/apps/code/toolbox.hu.i18n @@ -4,3 +4,4 @@ Modules = "Modulok" LoopsAndTests = "Hurkok és tesztek" Files = "Fájlok" Exceptions = "Kivételek" +UlabDocumentation = "Dokumentáció" diff --git a/apps/code/toolbox.it.i18n b/apps/code/toolbox.it.i18n index d7b219d87..7069ccb4e 100644 --- a/apps/code/toolbox.it.i18n +++ b/apps/code/toolbox.it.i18n @@ -4,3 +4,4 @@ Modules = "Moduli" LoopsAndTests = "Cicli e test" Files = "Files" Exceptions = "Exceptions" +UlabDocumentation = "Documentazione" diff --git a/apps/code/toolbox.nl.i18n b/apps/code/toolbox.nl.i18n index 849bd76a6..4df9ed1a7 100644 --- a/apps/code/toolbox.nl.i18n +++ b/apps/code/toolbox.nl.i18n @@ -4,3 +4,4 @@ Modules = "Modules" LoopsAndTests = "Herhalingen en testen" Files = "Files" Exceptions = "Exceptions" +UlabDocumentation = "Documentatie" diff --git a/apps/code/toolbox.pt.i18n b/apps/code/toolbox.pt.i18n index f7cfad07b..4ea2d75fd 100644 --- a/apps/code/toolbox.pt.i18n +++ b/apps/code/toolbox.pt.i18n @@ -4,3 +4,4 @@ Modules = "Módulos" LoopsAndTests = "Laços e testes" Files = "Files" Exceptions = "Exceptions" +UlabDocumentation = "Documentação" diff --git a/apps/code/toolbox.universal.i18n b/apps/code/toolbox.universal.i18n index df6a9171d..5a307394c 100644 --- a/apps/code/toolbox.universal.i18n +++ b/apps/code/toolbox.universal.i18n @@ -64,3 +64,4 @@ PythonCommandReturn = "return " RandomModule = "random" IonSelector = "Key selector" PressAKey = "Press a key" +UlabDocumentationLink = "micropython-ulab.readthedocs.io" From 9eb81a6daf6e57ae35f2a5b632dd2302336b1bda Mon Sep 17 00:00:00 2001 From: "Yaya.Cout" Date: Sun, 5 Sep 2021 11:49:42 +0200 Subject: [PATCH 43/48] Fix OmegaVersion in settings --- apps/settings/main_controller.cpp | 2 +- apps/settings/sub_menu/about_controller.cpp | 8 ++++---- ion/include/ion.h | 2 +- ion/src/shared/platform_info.cpp | 12 ++++++------ 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/apps/settings/main_controller.cpp b/apps/settings/main_controller.cpp index 6b65f15d3..1e19235fc 100644 --- a/apps/settings/main_controller.cpp +++ b/apps/settings/main_controller.cpp @@ -20,7 +20,7 @@ constexpr SettingsMessageTree s_modelMathOptionsChildren[6] = {SettingsMessageTr constexpr SettingsMessageTree s_modelFontChildren[2] = {SettingsMessageTree(I18n::Message::LargeFont), SettingsMessageTree(I18n::Message::SmallFont)}; constexpr SettingsMessageTree s_accessibilityChildren[6] = {SettingsMessageTree(I18n::Message::AccessibilityInvertColors), SettingsMessageTree(I18n::Message::AccessibilityMagnify),SettingsMessageTree(I18n::Message::AccessibilityGamma),SettingsMessageTree(I18n::Message::AccessibilityGammaRed),SettingsMessageTree(I18n::Message::AccessibilityGammaGreen),SettingsMessageTree(I18n::Message::AccessibilityGammaBlue)}; constexpr SettingsMessageTree s_contributorsChildren[23] = {SettingsMessageTree(I18n::Message::Developers), SettingsMessageTree(I18n::Message::QuentinGuidee), SettingsMessageTree(I18n::Message::JoachimLeFournis), SettingsMessageTree(I18n::Message::MaximeFriess), SettingsMessageTree(I18n::Message::JeanBaptisteBoric), SettingsMessageTree(I18n::Message::SandraSimmons), SettingsMessageTree(I18n::Message::David), SettingsMessageTree(I18n::Message::DamienNicolet), SettingsMessageTree(I18n::Message::EvannDreumont), SettingsMessageTree(I18n::Message::SzaboLevente), SettingsMessageTree(I18n::Message::VenceslasDuet), SettingsMessageTree(I18n::Message::CharlotteThomas), SettingsMessageTree(I18n::Message::AntoninLoubiere), SettingsMessageTree(I18n::Message::CyprienMejat), SettingsMessageTree(I18n::Message::BetaTesters), SettingsMessageTree(I18n::Message::TimeoArnouts), SettingsMessageTree(I18n::Message::JulieC), SettingsMessageTree(I18n::Message::LelahelHideux), SettingsMessageTree(I18n::Message::Madil), SettingsMessageTree(I18n::Message::HilaireLeRoux), SettingsMessageTree(I18n::Message::HectorNussbaumer), SettingsMessageTree(I18n::Message::RaphaelDyda), SettingsMessageTree(I18n::Message::ThibautC)}; -constexpr SettingsMessageTree s_modelAboutChildren[8] = {SettingsMessageTree(I18n::Message::Username), SettingsMessageTree(I18n::Message::SoftwareVersion), SettingsMessageTree(I18n::Message::OmegaVersion), SettingsMessageTree(I18n::Message::MicroPythonVersion), SettingsMessageTree(I18n::Message::MemUse), SettingsMessageTree(I18n::Message::SerialNumber), SettingsMessageTree(I18n::Message::FccId), SettingsMessageTree(I18n::Message::Contributors, s_contributorsChildren)}; +constexpr SettingsMessageTree s_modelAboutChildren[8] = {SettingsMessageTree(I18n::Message::Username), SettingsMessageTree(I18n::Message::SoftwareVersion), SettingsMessageTree(I18n::Message::UpsilonVersion), SettingsMessageTree(I18n::Message::MicroPythonVersion), SettingsMessageTree(I18n::Message::MemUse), SettingsMessageTree(I18n::Message::SerialNumber), SettingsMessageTree(I18n::Message::FccId), SettingsMessageTree(I18n::Message::Contributors, s_contributorsChildren)}; MainController::MainController(Responder * parentResponder, InputEventHandlerDelegate * inputEventHandlerDelegate) : ViewController(parentResponder), diff --git a/apps/settings/sub_menu/about_controller.cpp b/apps/settings/sub_menu/about_controller.cpp index 220a2c396..40900e67f 100644 --- a/apps/settings/sub_menu/about_controller.cpp +++ b/apps/settings/sub_menu/about_controller.cpp @@ -59,13 +59,13 @@ bool AboutController::handleEvent(Ion::Events::Event event) { } return true; } - if (childLabel == I18n::Message::OmegaVersion) { + if (childLabel == I18n::Message::UpsilonVersion) { MessageTableCellWithBuffer * myCell = (MessageTableCellWithBuffer *)m_selectableTableView.selectedCell(); - if (strcmp(myCell->accessoryText(), Ion::omegaVersion()) == 0) { + if (strcmp(myCell->accessoryText(), Ion::UpsilonVersion()) == 0) { myCell->setAccessoryText(MP_STRINGIFY(OMEGA_STATE)); //Change for public/dev return true; } - myCell->setAccessoryText(Ion::omegaVersion()); + myCell->setAccessoryText(Ion::UpsilonVersion()); return true; } if (childLabel == I18n::Message::MemUse) { @@ -162,7 +162,7 @@ void AboutController::willDisplayCellForIndex(HighlightCell * cell, int index) { static const char * messages[] = { (const char*) Ion::username(), Ion::softwareVersion(), - Ion::omegaVersion(), + Ion::UpsilonVersion(), mpVersion, "", Ion::serialNumber(), diff --git a/ion/include/ion.h b/ion/include/ion.h index 854c3c539..06d9da2e7 100644 --- a/ion/include/ion.h +++ b/ion/include/ion.h @@ -35,7 +35,7 @@ namespace Ion { const char * serialNumber(); const volatile char * username(); const char * softwareVersion(); -const char * omegaVersion(); +const char * UpsilonVersion(); const char * patchLevel(); const char * fccId(); const char * pcbVersion(); diff --git a/ion/src/shared/platform_info.cpp b/ion/src/shared/platform_info.cpp index cdf676a0c..1950cf09e 100644 --- a/ion/src/shared/platform_info.cpp +++ b/ion/src/shared/platform_info.cpp @@ -32,7 +32,7 @@ public: m_storageSize(Ion::Storage::k_storageSize), m_footer(Magic), m_ohm_header(OmegaMagic), - m_omegaVersion{OMEGA_VERSION}, + m_UpsilonVersion{OMEGA_VERSION}, #ifdef OMEGA_USERNAME m_username{OMEGA_USERNAME}, #else @@ -48,14 +48,14 @@ public: assert(m_ohm_footer == OmegaMagic); return m_version; } - const char * omegaVersion() const { + const char * UpsilonVersion() const { assert(m_storageAddress != nullptr); assert(m_storageSize != 0); assert(m_header == Magic); assert(m_footer == Magic); assert(m_ohm_header == OmegaMagic); assert(m_ohm_footer == OmegaMagic); - return m_omegaVersion; + return m_UpsilonVersion; } const volatile char * username() const volatile { assert(m_storageAddress != nullptr); @@ -85,7 +85,7 @@ private: size_t m_storageSize; uint32_t m_footer; uint32_t m_ohm_header; - const char m_omegaVersion[16]; + const char m_UpsilonVersion[16]; const volatile char m_username[16]; uint32_t m_ohm_footer; }; @@ -96,8 +96,8 @@ const char * Ion::softwareVersion() { return platform_infos.version(); } -const char * Ion::omegaVersion() { - return platform_infos.omegaVersion(); +const char * Ion::UpsilonVersion() { + return platform_infos.UpsilonVersion(); } const volatile char * Ion::username() { From 0f4502ebce4e6c23456a55b8719ca27d4d8ef248 Mon Sep 17 00:00:00 2001 From: Laury Date: Sun, 5 Sep 2021 16:27:44 +0200 Subject: [PATCH 44/48] [code/ulab] Added scipy --- apps/code/catalog.de.i18n | 6 +++++ apps/code/catalog.en.i18n | 6 +++++ apps/code/catalog.es.i18n | 6 +++++ apps/code/catalog.fr.i18n | 6 +++++ apps/code/catalog.hu.i18n | 6 +++++ apps/code/catalog.it.i18n | 6 +++++ apps/code/catalog.nl.i18n | 6 +++++ apps/code/catalog.pt.i18n | 6 +++++ apps/code/catalog.universal.i18n | 22 ++++++++++++++++++ apps/code/python_toolbox.cpp | 38 ++++++++++++++++++++++++++++++++ apps/code/toolbox.universal.i18n | 5 +++++ 11 files changed, 113 insertions(+) diff --git a/apps/code/catalog.de.i18n b/apps/code/catalog.de.i18n index da6c16f99..d168f9c13 100644 --- a/apps/code/catalog.de.i18n +++ b/apps/code/catalog.de.i18n @@ -75,6 +75,7 @@ PythonImportRandom = "Random-Modul importieren" PythonImportMath = "Math-Modul importieren" PythonImportMatplotlibPyplot = "Matplotlib.pyplot-Modul importieren" PythonImportNumpy = "Ulab.numpy-Modul importieren" +PythonImportScipy = "Ulab.scipy-Modul importieren" PythonImportOs = "OS-Modul importieren" PythonOsUname = "Informationen über das System holen" PythonOsGetlogin = "Benutzernamen holen" @@ -108,6 +109,11 @@ PythonMonotonic = "Wert einer monotonen Uhr" PythonNumpyFunction = "numpy Modul-Präfix" PythonNumpyFftFunction = "numpy.fft Modul-Präfix" PythonNumpyLinalgFunction = "numpy.linalg Modul-Präfix" +PythonScipyFunction = "scipy Modul-Präfix" +PythonScipyLinalgFunction = "scipy.linalg Modul-Präfix" +PythonScipyOptimizeFunction = "scipy.optimize Modul-Präfix" +PythonScipySignalFunction = "scipy.signal Modul-Präfix" +PythonScipySpecialFunction = "scipy.special Modul-Präfix" PythonOct = "Ganzzahl in Oktal umwandeln" PythonPhase = "Phase von z" PythonPlot = "Plotten von y gegen x als Linien" diff --git a/apps/code/catalog.en.i18n b/apps/code/catalog.en.i18n index 363dd820e..3b5a707bf 100644 --- a/apps/code/catalog.en.i18n +++ b/apps/code/catalog.en.i18n @@ -75,6 +75,7 @@ PythonImportRandom = "Import random module" PythonImportMath = "Import math module" PythonImportMatplotlibPyplot = "Import matplotlib.pyplot module" PythonImportNumpy = "Import ulab.numpy module" +PythonImportScipy = "Import ulab.scipy module" PythonImportTime = "Import time module" PythonImportTurtle = "Import turtle module" PythonIndex = "Index of the first x occurrence" @@ -102,6 +103,11 @@ PythonMonotonic = "Value of a monotonic clock" PythonNumpyFunction = "numpy module prefix" PythonNumpyFftFunction = "numpy.fft module prefix" PythonNumpyLinalgFunction = "numpy.linalg module prefix" +PythonScipyFunction = "scipy module prefix" +PythonScipyLinalgFunction = "scipy.linalg module prefix" +PythonScipyOptimizeFunction = "scipy.optimize module prefix" +PythonScipySignalFunction = "scipy.signal module prefix" +PythonScipySpecialFunction = "scipy.special module prefix" PythonOct = "Convert integer to octal" PythonPhase = "Phase of z" PythonPlot = "Plot y versus x as lines" diff --git a/apps/code/catalog.es.i18n b/apps/code/catalog.es.i18n index d39456839..09ad6235d 100644 --- a/apps/code/catalog.es.i18n +++ b/apps/code/catalog.es.i18n @@ -75,6 +75,7 @@ PythonImportRandom = "Import random module" PythonImportMath = "Import math module" PythonImportMatplotlibPyplot = "Import matplotlib.pyplot module" PythonImportNumpy = "Import ulab.numpy module" +PythonImportScipy = "Import ulab.scipy module" PythonImportTime = "Import time module" PythonImportTurtle = "Import turtle module" PythonIndex = "Index of the first x occurrence" @@ -102,6 +103,11 @@ PythonMonotonic = "Value of a monotonic clock" PythonNumpyFunction = "numpy module prefix" PythonNumpyFftFunction = "numpy.fft module prefix" PythonNumpyLinalgFunction = "numpy.linalg module prefix" +PythonScipyFunction = "scipy module prefix" +PythonScipyLinalgFunction = "scipy.linalg module prefix" +PythonScipyOptimizeFunction = "scipy.optimize module prefix" +PythonScipySignalFunction = "scipy.signal module prefix" +PythonScipySpecialFunction = "scipy.special module prefix" PythonOct = "Convert integer to octal" PythonPhase = "Phase of z" PythonPlot = "Plot y versus x as lines" diff --git a/apps/code/catalog.fr.i18n b/apps/code/catalog.fr.i18n index d8d9daced..af0676142 100644 --- a/apps/code/catalog.fr.i18n +++ b/apps/code/catalog.fr.i18n @@ -75,6 +75,7 @@ PythonImportRandom = "Importation du module random" PythonImportMath = "Importation du module math" PythonImportMatplotlibPyplot = "Importation de matplotlib.pyplot" PythonImportNumpy = "Importation de ulab.numpy" +PythonImportScipy = "Importation de ulab.scipy" PythonImportTurtle = "Importation du module turtle" PythonImportTime = "Importation du module time" PythonIndex = "Indice première occurrence de x" @@ -102,6 +103,11 @@ PythonMonotonic = "Renvoie la valeur de l'horloge" PythonNumpyFunction = "Préfixe fonction du module numpy" PythonNumpyFftFunction = "Préfixe fonction du module numpy.fft" PythonNumpyLinalgFunction = "Préfixe fonction du module numpy.linalg" +PythonScipyFunction = "Préfixe fonction du module scipy" +PythonScipyLinalgFunction = "Préfixe fonction du module scipy.linalg" +PythonScipyOptimizeFunction = "Préfixe fonction du module scipy.optimize" +PythonScipySignalFunction = "Préfixe fonction du module scipy.signal" +PythonScipySpecialFunction = "Préfixe fonction du module scipy.special" PythonOct = "Conversion en octal" PythonPhase = "Argument de z" PythonPlot = "Trace y en fonction de x" diff --git a/apps/code/catalog.hu.i18n b/apps/code/catalog.hu.i18n index b2011816b..b64a41192 100644 --- a/apps/code/catalog.hu.i18n +++ b/apps/code/catalog.hu.i18n @@ -75,6 +75,7 @@ PythonImportRandom = "Véletlenszerü modul importálása" PythonImportMath = "math modul importálása" PythonImportMatplotlibPyplot = "matplotlib.pyplot modul importálása" PythonImportNumpy = "ulab.numpy modul importálása" +PythonImportScipy = "ulab.scipy modul importálása" PythonImportTurtle = "turtle modul importálása" PythonImportTime = "time modul importálása" PythonIndex = "Az elsö x esemény indexe" @@ -102,6 +103,11 @@ PythonMonotonic = "Az óra értékét adja vissza" PythonNumpyFunction = "numpy elötag" PythonNumpyFftFunction = "numpy.fft elötag" PythonNumpyLinalgFunction = "numpy.linalg elötag" +PythonScipyFunction = "scipy elötag" +PythonScipyLinalgFunction = "scipy.linalg elötag" +PythonScipyOptimizeFunction = "scipy.optimize elötag" +PythonScipySignalFunction = "scipy.signal elötag" +PythonScipySpecialFunction = "scipy.special elötag" PythonOct = "Decimális szám konvertálása octális számra" PythonPhase = "z fázisa" PythonPlot = "y-t jelöli x függvényében" diff --git a/apps/code/catalog.it.i18n b/apps/code/catalog.it.i18n index 8627e679c..2a61d0a5b 100644 --- a/apps/code/catalog.it.i18n +++ b/apps/code/catalog.it.i18n @@ -75,6 +75,7 @@ PythonImportRandom = "Importa modulo random" PythonImportMath = "Importa modulo math" PythonImportMatplotlibPyplot = "Importa modulo matplotlib.pyplot" PythonImportNumpy = "Importa modulo ulab.numpy" +PythonImportScipy = "Importa modulo ulab.scipy" PythonImportTurtle = "Importa del modulo turtle" PythonImportTime = "Importa del modulo time" PythonImportOs = "Importa modulo os" @@ -108,6 +109,11 @@ PythonMonotonic = "Restituisce il valore dell'orologio" PythonNumpyFunction = "Prefisso modulo numpy" PythonNumpyFftFunction = "Prefisso modulo numpy.fft" PythonNumpyLinalgFunction = "Prefisso modulo numpy.linalg" +PythonScipyFunction = "Prefisso modulo scipy" +PythonScipyLinalgFunction = "Prefisso modulo scipy.linalg" +PythonScipyOptimizeFunction = "Prefisso modulo scipy.optimize" +PythonScipySignalFunction = "Prefisso modulo scipy.signal" +PythonScipySpecialFunction = "Prefisso modulo scipy.special" PythonOct = "Conversione in ottale" PythonPhase = "Argomento di z" PythonPlot = "Disegna y in f. di x come linee" diff --git a/apps/code/catalog.nl.i18n b/apps/code/catalog.nl.i18n index f46b4606f..1d8349e24 100644 --- a/apps/code/catalog.nl.i18n +++ b/apps/code/catalog.nl.i18n @@ -75,6 +75,7 @@ PythonImportRandom = "Importeer random module" PythonImportMath = "Importeer math module" PythonImportMatplotlibPyplot = "Importeer matplotlib.pyplot module" PythonImportNumpy = "Importeer ulab.numpy module" +PythonImportScipy = "Importeer ulab.scipy module" PythonImportTime = "Importeer time module" PythonImportOs = "Importeer os module" PythonOsUname = " Krijg systeeminfo" @@ -108,6 +109,11 @@ PythonMonotonic = "Waarde van een monotone klok" PythonNumpyFunction = "numpy module prefix" PythonNumpyFftFunction = "numpy.fft module prefix" PythonNumpyLinalgFunction = "numpy.linalg module prefix" +PythonScipyFunction = "scipy module prefix" +PythonScipyLinalgFunction = "scipy.linalg module prefix" +PythonScipyOptimizeFunction = "scipy.optimize module prefix" +PythonScipySignalFunction = "scipy.signal module prefix" +PythonScipySpecialFunction = "scipy.special module prefix" PythonOct = "Integer omzetten naar octaal" PythonPhase = "Fase van z in radialen" PythonPlot = "Plot y versus x als lijnen" diff --git a/apps/code/catalog.pt.i18n b/apps/code/catalog.pt.i18n index 513297365..394728507 100644 --- a/apps/code/catalog.pt.i18n +++ b/apps/code/catalog.pt.i18n @@ -75,6 +75,7 @@ PythonImportRandom = "Importar módulo random" PythonImportMath = "Importar módulo math" PythonImportMatplotlibPyplot = "Importar módulo matplotlib.pyplot" PythonImportNumpy = "Importar módulo ulab.numpy" +PythonImportScipy = "Importar módulo ulab.scipy" PythonImportTime = "Importar módulo time" PythonImportTurtle = "Importar módulo turtle" PythonIndex = "Índice da primeira ocorrência de x" @@ -102,6 +103,11 @@ PythonMonotonic = "Devolve o valor do relógio" PythonNumpyFunction = "Prefixo do módulo numpy" PythonNumpyFftFunction = "Prefixo do módulo numpy.fft" PythonNumpyLinalgFunction = "Prefixo do módulo numpy.linalg" +PythonScipyFunction = "Prefixo do módulo scipy" +PythonScipyLinalgFunction = "Prefixo do módulo scipy.linalg" +PythonScipyOptimizeFunction = "Prefixo do módulo scipy.optimize" +PythonScipySignalFunction = "Prefixo do módulo scipy.signal" +PythonScipySpecialFunction = "Prefixo do módulo scipy.special" PythonOct = "Converter número inteiro em octal" PythonPhase = "Argumento de z" PythonPlot = "Desenhar y em função de x" diff --git a/apps/code/catalog.universal.i18n b/apps/code/catalog.universal.i18n index 106d57dbb..510fe65a0 100644 --- a/apps/code/catalog.universal.i18n +++ b/apps/code/catalog.universal.i18n @@ -91,6 +91,7 @@ PythonCommandImportKandinsky = "import kandinsky" PythonCommandImportMath = "import math" PythonCommandImportMatplotlibPyplot = "import matplotlib.pyplot" PythonCommandImportFromNumpy = "from ulab import numpy as np" +PythonCommandImportFromScipy = "from ulab import scipy as spy" PythonCommandImportRandom = "import random" PythonCommandImportOs = "import os" PythonCommandImportFromOs = "from os import *" @@ -306,6 +307,27 @@ PythonCommandReverseWithoutArg = ".reverse()" PythonCommandRound = "round(x,n)" PythonCommandScatter = "scatter(x,y)" PythonCommandSeed = "seed(x)" +PythonCommandScipyFunction = "spy.function" +PythonCommandScipyFunctionWithoutArg = "spy.\x11" +PythonCommandScipyLinalgFunction = "spy.linalg.function" +PythonCommandScipyLinalgFunctionWithoutArg = "spy.linalg.\x11" +PythonCommandScipyOptimizeFunction = "spy.optimize.function" +PythonCommandScipyOptimizeFunctionWithoutArg = "spy.optimize.\x11" +PythonCommandScipySignalFunction = "spy.signal.function" +PythonCommandScipySignalFunctionWithoutArg = "spy.signal.\x11" +PythonCommandScipySpecialFunction = "spy.special.function" +PythonCommandScipySpecialFunctionWithoutArg = "spy.special.\x11" +PythonCommandScipyLinalgChoSolve = "spy.linalg.cho_solve(a, b)" +PythonCommandScipyLinalgSolveTriangular = "spy.linalg.solve_triangular(a, b)" +PythonCommandScipyOptimizeBisect = "spy.optimize.bisect(f, a, b)" +PythonCommandScipyOptimizeFmin = "spy.optimize.fmin(f, x0)" +PythonCommandScipyOptimizeNewton = "spy.optimize.newton(f, x0)" +PythonCommandScipySignalSosfilt = "spy.signal.sosfilt(sos, x)" +PythonCommandScipySignalSpectrogram = "spy.signal.spectrogram(y)" +PythonCommandScipySpecialErf = "spy.erf(a)" +PythonCommandScipySpecialErfc = "spy.erfc(a)" +PythonCommandScipySpecialGamma = "spy.gamma(a)" +PythonCommandScipySpecialGammaln = "spy.gammaln(a)" PythonCommandSetPixel = "set_pixel(x,y,color)" PythonCommandShow = "show()" PythonCommandSin = "sin(x)" diff --git a/apps/code/python_toolbox.cpp b/apps/code/python_toolbox.cpp index 2a26f98a7..ef64e1b4d 100644 --- a/apps/code/python_toolbox.cpp +++ b/apps/code/python_toolbox.cpp @@ -252,8 +252,46 @@ const ToolboxMessageTree NumpyModuleChildren[] = { ToolboxMessageTree::Node(I18n::Message::NumpyLinalgModule, NumpyLinalgModuleChildren) }; +const ToolboxMessageTree ScipyLinalgModuleChildren[] = { + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandScipyLinalgFunction, I18n::Message::PythonScipyLinalgFunction, false, I18n::Message::PythonCommandScipyLinalgFunctionWithoutArg), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandScipyLinalgChoSolve), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandScipyLinalgSolveTriangular) +}; + +const ToolboxMessageTree ScipyOptimizeModuleChildren[] = { + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandScipyOptimizeFunction, I18n::Message::PythonScipyOptimizeFunction, false, I18n::Message::PythonCommandScipyOptimizeFunctionWithoutArg), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandScipyOptimizeBisect), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandScipyOptimizeFmin), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandScipyOptimizeNewton) +}; + +const ToolboxMessageTree ScipySignalModuleChildren[] = { + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandScipySignalFunction, I18n::Message::PythonScipySignalFunction, false, I18n::Message::PythonCommandScipySignalFunctionWithoutArg), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandScipySignalSosfilt), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandScipySignalSpectrogram) +}; + +const ToolboxMessageTree ScipySpecialModuleChildren[] = { + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandScipySpecialFunction, I18n::Message::PythonScipySpecialFunction, false, I18n::Message::PythonCommandScipySpecialFunctionWithoutArg), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandScipySpecialErf), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandScipySpecialErfc), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandScipySpecialGamma), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandScipySpecialGammaln), +}; + +const ToolboxMessageTree ScipyModuleChildren[] = { + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandImportFromScipy, I18n::Message::PythonImportScipy, false), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandScipyFunction, I18n::Message::PythonScipyFunction, false, I18n::Message::PythonCommandScipyFunctionWithoutArg), + ToolboxMessageTree::Node(I18n::Message::ScipyLinalgModule, ScipyLinalgModuleChildren), + ToolboxMessageTree::Node(I18n::Message::ScipyOptimizeModule, ScipyOptimizeModuleChildren), + ToolboxMessageTree::Node(I18n::Message::ScipySignalModule, ScipySignalModuleChildren), + ToolboxMessageTree::Node(I18n::Message::ScipySpecialModule, ScipySpecialModuleChildren), + +}; + const ToolboxMessageTree UlabModuleChildren[] = { ToolboxMessageTree::Node(I18n::Message::NumpyModule, NumpyModuleChildren), + ToolboxMessageTree::Node(I18n::Message::ScipyModule, ScipyModuleChildren), ToolboxMessageTree::Leaf(I18n::Message::UlabDocumentation, I18n::Message::UlabDocumentationLink) }; diff --git a/apps/code/toolbox.universal.i18n b/apps/code/toolbox.universal.i18n index 5a307394c..99810151a 100644 --- a/apps/code/toolbox.universal.i18n +++ b/apps/code/toolbox.universal.i18n @@ -6,6 +6,11 @@ MatplotlibPyplotModule = "matplotlib.pyplot" NumpyModule = "numpy" NumpyFftModule = "fft" NumpyLinalgModule = "linalg" +ScipyModule = "scipy" +ScipyLinalgModule = "linalg" +ScipyOptimizeModule = "optimize" +ScipySignalModule = "signal" +ScipySpecialModule = "special" NumpyNdarray = "ndarray" OsModule = "os" TimeModule = "time" From 98b665ac4b0604355cd345a3fb45d3b3de0bae63 Mon Sep 17 00:00:00 2001 From: Laury Date: Sun, 5 Sep 2021 16:57:09 +0200 Subject: [PATCH 45/48] [code/ulab] Disabled scipy in toolbox for n0100 + added 2 forgotten numpy functions --- apps/code/catalog.universal.i18n | 2 ++ apps/code/python_toolbox.cpp | 10 ++++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/apps/code/catalog.universal.i18n b/apps/code/catalog.universal.i18n index 510fe65a0..ad07c7ba9 100644 --- a/apps/code/catalog.universal.i18n +++ b/apps/code/catalog.universal.i18n @@ -201,6 +201,8 @@ PythonCommandNumpyTranspose = "ndarray.transpose()" PythonCommandNumpyTransposeWithoutArg = ".transpose()" PythonCommandNumpySort = "ndarray.sort()" PythonCommandNumpySortWithoutArg = ".sort()" +PythonCommandNumpySetPrintOptions = "np.set_printoptions()" +PythonCommandNumpyGetPrintOptions = "np.get_printoptions()" PythonCommandNumpyNdinfo = "np.ndinfo(a)" PythonCommandNumpyAll = "np.all(a)" PythonCommandNumpyAny = "np.any(a)" diff --git a/apps/code/python_toolbox.cpp b/apps/code/python_toolbox.cpp index ef64e1b4d..2dc5a6e81 100644 --- a/apps/code/python_toolbox.cpp +++ b/apps/code/python_toolbox.cpp @@ -225,7 +225,9 @@ const ToolboxMessageTree NumpyFunctionsModuleChildren[] = { ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyNan), ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyInf), ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyE), - ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyPi) + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyPi), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpySetPrintOptions), + ToolboxMessageTree::Leaf(I18n::Message::PythonCommandNumpyGetPrintOptions) }; const ToolboxMessageTree NumpyFftModuleChildren[] = { @@ -252,6 +254,7 @@ const ToolboxMessageTree NumpyModuleChildren[] = { ToolboxMessageTree::Node(I18n::Message::NumpyLinalgModule, NumpyLinalgModuleChildren) }; +#if !defined(DEVICE_N0100) const ToolboxMessageTree ScipyLinalgModuleChildren[] = { ToolboxMessageTree::Leaf(I18n::Message::PythonCommandScipyLinalgFunction, I18n::Message::PythonScipyLinalgFunction, false, I18n::Message::PythonCommandScipyLinalgFunctionWithoutArg), ToolboxMessageTree::Leaf(I18n::Message::PythonCommandScipyLinalgChoSolve), @@ -286,12 +289,15 @@ const ToolboxMessageTree ScipyModuleChildren[] = { ToolboxMessageTree::Node(I18n::Message::ScipyOptimizeModule, ScipyOptimizeModuleChildren), ToolboxMessageTree::Node(I18n::Message::ScipySignalModule, ScipySignalModuleChildren), ToolboxMessageTree::Node(I18n::Message::ScipySpecialModule, ScipySpecialModuleChildren), - }; +#endif + const ToolboxMessageTree UlabModuleChildren[] = { ToolboxMessageTree::Node(I18n::Message::NumpyModule, NumpyModuleChildren), +#if !defined(DEVICE_N0100) ToolboxMessageTree::Node(I18n::Message::ScipyModule, ScipyModuleChildren), +#endif ToolboxMessageTree::Leaf(I18n::Message::UlabDocumentation, I18n::Message::UlabDocumentationLink) }; From 74c500df015161c475c10dff40a13a692b785fa8 Mon Sep 17 00:00:00 2001 From: Laury Date: Sun, 5 Sep 2021 16:57:50 +0200 Subject: [PATCH 46/48] [code/ulab] removed unnecessary module utils --- python/port/mod/ulab/ulab.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/port/mod/ulab/ulab.h b/python/port/mod/ulab/ulab.h index 647672d8b..032768542 100644 --- a/python/port/mod/ulab/ulab.h +++ b/python/port/mod/ulab/ulab.h @@ -270,7 +270,7 @@ // frombuffer adds 600 bytes to the firmware #ifndef ULAB_NUMPY_HAS_FROMBUFFER -#define ULAB_NUMPY_HAS_FROMBUFFER (1) +#define ULAB_NUMPY_HAS_FROMBUFFER (0) #endif // functions that create an array @@ -644,7 +644,7 @@ #endif #ifndef ULAB_HAS_UTILS_MODULE -#define ULAB_HAS_UTILS_MODULE (1) +#define ULAB_HAS_UTILS_MODULE (0) #endif #ifndef ULAB_UTILS_HAS_FROM_INT16_BUFFER From 19ee32986fd63eafa8ddc66544a95970577deb3a Mon Sep 17 00:00:00 2001 From: Laury Date: Sun, 5 Sep 2021 21:10:56 +0200 Subject: [PATCH 47/48] [reader] Make improve coding style and added empty view message --- apps/home/apps_layout.csv | 4 +- apps/reader/Makefile | 2 +- apps/reader/app.cpp | 5 +- apps/reader/app.h | 3 +- apps/reader/base.de.i18n | 6 +- apps/reader/base.en.i18n | 2 +- apps/reader/base.es.i18n | 6 +- apps/reader/base.fr.i18n | 6 +- apps/reader/base.hu.i18n | 6 +- apps/reader/base.it.i18n | 6 +- apps/reader/base.nl.i18n | 6 +- apps/reader/base.pt.i18n | 6 +- apps/reader/list_book_controller.cpp | 138 +++++----- apps/reader/list_book_controller.h | 46 ++-- apps/reader/read_book_controller.cpp | 67 ++--- apps/reader/read_book_controller.h | 2 +- apps/reader/utility.cpp | 149 +++++------ apps/reader/utility.h | 4 +- apps/reader/word_wrap_view.cpp | 247 ++++++++---------- apps/reader/word_wrap_view.h | 22 +- .../src/alternate_empty_view_controller.cpp | 3 +- 21 files changed, 349 insertions(+), 387 deletions(-) diff --git a/apps/home/apps_layout.csv b/apps/home/apps_layout.csv index 77e47e4b8..b17d33f7d 100644 --- a/apps/home/apps_layout.csv +++ b/apps/home/apps_layout.csv @@ -1,2 +1,2 @@ -Default,calculation,rpn,graph,code,statistics,probability,solver,atomic,sequence,regression,settings -HidePython,calculation,rpn,graph,code,statistics,probability,solver,atomic,sequence,regression,settings +Default,calculation,rpn,graph,code,statistics,probability,solver,atomic,sequence,regression,reader,settings +HidePython,calculation,rpn,graph,code,statistics,probability,solver,atomic,sequence,regression,reader,settings diff --git a/apps/reader/Makefile b/apps/reader/Makefile index 71adec9e0..f786e1510 100644 --- a/apps/reader/Makefile +++ b/apps/reader/Makefile @@ -1,4 +1,4 @@ -apps += reader::App +apps += Reader::App app_headers += apps/reader/app.h app_sreader_src = $(addprefix apps/reader/,\ diff --git a/apps/reader/app.cpp b/apps/reader/app.cpp index 18ae33da0..5d9b8b156 100644 --- a/apps/reader/app.cpp +++ b/apps/reader/app.cpp @@ -4,7 +4,7 @@ #include "apps/i18n.h" -namespace reader { +namespace Reader { I18n::Message App::Descriptor::name() { return I18n::Message::ReaderApp; @@ -32,7 +32,8 @@ App::Descriptor * App::Snapshot::descriptor() { App::App(Snapshot * snapshot) : ::App(snapshot, &m_stackViewController), m_listBookController(&m_stackViewController), - m_stackViewController(nullptr, &m_listBookController) + m_alternateEmptyViewController(&m_stackViewController, &m_listBookController, &m_listBookController), + m_stackViewController(&m_modalViewController, &m_alternateEmptyViewController) { } diff --git a/apps/reader/app.h b/apps/reader/app.h index 1204862c4..8de726e9f 100644 --- a/apps/reader/app.h +++ b/apps/reader/app.h @@ -4,7 +4,7 @@ #include #include "list_book_controller.h" -namespace reader { +namespace Reader { class App : public ::App { public: @@ -22,6 +22,7 @@ public: private: App(Snapshot * snapshot); ListBookController m_listBookController; + AlternateEmptyViewController m_alternateEmptyViewController; StackViewController m_stackViewController; }; diff --git a/apps/reader/base.de.i18n b/apps/reader/base.de.i18n index 68a64986d..3dcf262d7 100644 --- a/apps/reader/base.de.i18n +++ b/apps/reader/base.de.i18n @@ -1,3 +1,3 @@ -ReaderApp = "Reader" -ReaderAppCapital = "READER" -NoFileToDisplay = "Keine Dateien zum Anzeigen!" \ No newline at end of file +ReaderApp = "Leser" +ReaderAppCapital = "LESER" +NoFileToDisplay = "Keine Dateien zum Anzeigen" \ No newline at end of file diff --git a/apps/reader/base.en.i18n b/apps/reader/base.en.i18n index 0101ebe2a..0b0a2de2a 100644 --- a/apps/reader/base.en.i18n +++ b/apps/reader/base.en.i18n @@ -1,3 +1,3 @@ ReaderApp = "Reader" ReaderAppCapital = "READER" -NoFileToDisplay = "No file to display!" \ No newline at end of file +NoFileToDisplay = "No file to display" \ No newline at end of file diff --git a/apps/reader/base.es.i18n b/apps/reader/base.es.i18n index 51be1eaf4..bcbe8f0cd 100644 --- a/apps/reader/base.es.i18n +++ b/apps/reader/base.es.i18n @@ -1,3 +1,3 @@ -ReaderApp = "Reader" -ReaderAppCapital = "READER" -NoFileToDisplay ="No hay archivos para mostrar!" \ No newline at end of file +ReaderApp = "Lector" +ReaderAppCapital = "LECTOR" +NoFileToDisplay ="No hay archivos para mostrar" \ No newline at end of file diff --git a/apps/reader/base.fr.i18n b/apps/reader/base.fr.i18n index 220c36fa1..f8b97e036 100644 --- a/apps/reader/base.fr.i18n +++ b/apps/reader/base.fr.i18n @@ -1,3 +1,3 @@ -ReaderApp = "Reader" -ReaderAppCapital = "READER" -NoFileToDisplay = "Aucun fichier à afficher!" \ No newline at end of file +ReaderApp = "Liseuse" +ReaderAppCapital = "LISEUSE" +NoFileToDisplay = "Aucun fichier à afficher" \ No newline at end of file diff --git a/apps/reader/base.hu.i18n b/apps/reader/base.hu.i18n index 264d6e878..09357ced7 100644 --- a/apps/reader/base.hu.i18n +++ b/apps/reader/base.hu.i18n @@ -1,3 +1,3 @@ -ReaderApp = "Reader" -ReaderAppCapital = "READER" -NoFileToDisplay ="Nincs megjeleníthető fájl" \ No newline at end of file +ReaderApp = "Olvasó" +ReaderAppCapital = "OLVASÓ" +NoFileToDisplay = "Nincs megjeleníthető fájl" \ No newline at end of file diff --git a/apps/reader/base.it.i18n b/apps/reader/base.it.i18n index 4464ff077..c553356d9 100644 --- a/apps/reader/base.it.i18n +++ b/apps/reader/base.it.i18n @@ -1,3 +1,3 @@ -ReaderApp = "Reader" -ReaderAppCapital = "READER" -NoFileToDisplay ="essun file da visualizzare" \ No newline at end of file +ReaderApp = "Lettore" +ReaderAppCapital = "LETTORE" +NoFileToDisplay = "essun file da visualizzare" \ No newline at end of file diff --git a/apps/reader/base.nl.i18n b/apps/reader/base.nl.i18n index 55693de3e..576752673 100644 --- a/apps/reader/base.nl.i18n +++ b/apps/reader/base.nl.i18n @@ -1,3 +1,3 @@ -ReaderApp = "Reader" -ReaderAppCapital = "READER" -NoFileToDisplay ="Geen bestanden om weer te geven" \ No newline at end of file +ReaderApp = "Lezer" +ReaderAppCapital = "LEZER" +NoFileToDisplay = "Geen bestanden om weer te geven" \ No newline at end of file diff --git a/apps/reader/base.pt.i18n b/apps/reader/base.pt.i18n index 886d4930c..2aa88319b 100644 --- a/apps/reader/base.pt.i18n +++ b/apps/reader/base.pt.i18n @@ -1,3 +1,3 @@ -ReaderApp = "Reader" -ReaderAppCapital = "READER" -NoFileToDisplay ="Nenhum arquivo para exibir" \ No newline at end of file +ReaderApp = "Leitor" +ReaderAppCapital = "LEITOR" +NoFileToDisplay = "Nenhum arquivo para exibir" \ No newline at end of file diff --git a/apps/reader/list_book_controller.cpp b/apps/reader/list_book_controller.cpp index d2635129c..814f60d5f 100644 --- a/apps/reader/list_book_controller.cpp +++ b/apps/reader/list_book_controller.cpp @@ -1,102 +1,100 @@ #include "list_book_controller.h" #include "utility.h" +#include #include "apps/i18n.h" -namespace reader +namespace Reader { -View* ListBookController::view() -{ - return &m_tableView; +View* ListBookController::view() { + return &m_tableView; } ListBookController::ListBookController(Responder * parentResponder): - ViewController(parentResponder), - m_tableView(this, this, this), - m_readBookController(this) + ViewController(parentResponder), + m_tableView(this, this, this), + m_readBookController(this) { - m_nbFiles = filesWithExtension(".txt", m_files, NB_FILES); - cleanRemovedBookRecord(); + m_filesNumber = filesWithExtension(".txt", m_files, k_maxFilesNumber); + cleanRemovedBookRecord(); } -int ListBookController::numberOfRows() const -{ - return m_nbFiles; +int ListBookController::numberOfRows() const { + return m_filesNumber; } -KDCoordinate ListBookController::cellHeight() -{ - return 50; +KDCoordinate ListBookController::cellHeight() { + return Metric::StoreRowHeight; } -HighlightCell * ListBookController::reusableCell(int index) -{ - return &m_cells[index]; +HighlightCell * ListBookController::reusableCell(int index) { + return &m_cells[index]; } - -int ListBookController::reusableCellCount() const -{ - return NB_CELLS; + +int ListBookController::reusableCellCount() const { + return k_cellsNumber; } -void ListBookController::willDisplayCellForIndex(HighlightCell * cell, int index) -{ - MessageTableCell* myTextCell = static_cast(cell); - MessageTextView* textView = static_cast(myTextCell->labelView()); - textView->setText(m_files[index].name); - myTextCell->setMessageFont(KDFont::LargeFont); +void ListBookController::willDisplayCellForIndex(HighlightCell * cell, int index) { + MessageTableCell* myTextCell = static_cast(cell); + MessageTextView* textView = static_cast(myTextCell->labelView()); + textView->setText(m_files[index].name); + myTextCell->setMessageFont(KDFont::LargeFont); //TODO set cell font at building ? } -void ListBookController::didBecomeFirstResponder() -{ - if (selectedRow() < 0) { - selectCellAtLocation(0, 0); - } - Container::activeApp()->setFirstResponder(&m_tableView); - if(m_nbFiles == 0) +void ListBookController::didBecomeFirstResponder() { + if (selectedRow() < 0) { + selectCellAtLocation(0, 0); + } + Container::activeApp()->setFirstResponder(&m_tableView); +} + +bool ListBookController::handleEvent(Ion::Events::Event event) { + if (event == Ion::Events::OK || event == Ion::Events::EXE || event == Ion::Events::Right) + { + m_readBookController.setBook(m_files[selectedRow()]); + static_cast(parentResponder())->push(&m_readBookController); + Container::activeApp()->setFirstResponder(&m_readBookController); + return true; + } + + return false; +} + + +bool ListBookController::hasBook(const char* filename) const { + for(int i=0;idisplayWarning(I18n::Message::NoFileToDisplay); + return true; } + } + return false; } -bool ListBookController::handleEvent(Ion::Events::Event event) -{ - if (event == Ion::Events::OK || event == Ion::Events::EXE || event == Ion::Events::Right) - { - - m_readBookController.setBook(m_files[selectedRow()]); - static_cast(parentResponder())->push(&m_readBookController); - Container::activeApp()->setFirstResponder(&m_readBookController); - return true; - } - - return false; -} - - -bool ListBookController::hasBook(const char* filename) const -{ - for(int i=0;inumberOfRecordsWithExtension("txt"); for(int i=0; irecordWithExtensionAtIndex("txt", i); - if(!hasBook(r.fullName())) - { - r.destroy(); - } + Ion::Storage::Record r = Ion::Storage::sharedStorage()->recordWithExtensionAtIndex("txt", i); + if(!hasBook(r.fullName())) + { + r.destroy(); + } } } +bool ListBookController::isEmpty() const { + return m_filesNumber == 0; +} + +I18n::Message ListBookController::emptyMessage() { + return I18n::Message::NoFileToDisplay; +} + +Responder * ListBookController::defaultController() { + return parentResponder(); +} + } \ No newline at end of file diff --git a/apps/reader/list_book_controller.h b/apps/reader/list_book_controller.h index 82eecf207..ddbd3f553 100644 --- a/apps/reader/list_book_controller.h +++ b/apps/reader/list_book_controller.h @@ -6,35 +6,35 @@ #include "read_book_controller.h" -namespace reader +namespace Reader { -class ListBookController : public ViewController, public SimpleListViewDataSource, public SelectableTableViewDataSource +class ListBookController : public ViewController, public SimpleListViewDataSource, public SelectableTableViewDataSource, public AlternateEmptyViewDefaultDelegate { public: - ListBookController(Responder * parentResponder); - View* view() override; + ListBookController(Responder * parentResponder); + View* view() override; - int numberOfRows() const override; - KDCoordinate cellHeight() override; - HighlightCell * reusableCell(int index) override; - int reusableCellCount() const override; - void willDisplayCellForIndex(HighlightCell * cell, int index) override; - void didBecomeFirstResponder() override; - bool handleEvent(Ion::Events::Event event) override; - bool hasBook(const char* filename) const; - void cleanRemovedBookRecord(); + int numberOfRows() const override; + KDCoordinate cellHeight() override; + HighlightCell * reusableCell(int index) override; + int reusableCellCount() const override; + void willDisplayCellForIndex(HighlightCell * cell, int index) override; + void didBecomeFirstResponder() override; + bool handleEvent(Ion::Events::Event event) override; + bool hasBook(const char* filename) const; + void cleanRemovedBookRecord(); + bool isEmpty() const override; + I18n::Message emptyMessage() override; + Responder * defaultController() override; private: - SelectableTableView m_tableView; - - static const int NB_FILES = 20; - External::Archive::File m_files[NB_FILES]; - int m_nbFiles = 0; - - static const int NB_CELLS = 6; - MessageTableCell m_cells[NB_CELLS]; - - ReadBookController m_readBookController; + SelectableTableView m_tableView; + static const int k_maxFilesNumber = 20; + External::Archive::File m_files[k_maxFilesNumber]; + int m_filesNumber = 0; + static const int k_cellsNumber = 6; + MessageTableCellWithChevron m_cells[k_cellsNumber]; + ReadBookController m_readBookController; }; } diff --git a/apps/reader/read_book_controller.cpp b/apps/reader/read_book_controller.cpp index 7bdd9380e..144385918 100644 --- a/apps/reader/read_book_controller.cpp +++ b/apps/reader/read_book_controller.cpp @@ -1,6 +1,6 @@ #include "read_book_controller.h" -namespace reader +namespace Reader { ReadBookController::ReadBookController(Responder * parentResponder) : @@ -8,60 +8,49 @@ ReadBookController::ReadBookController(Responder * parentResponder) : { } -View * ReadBookController::view() -{ +View * ReadBookController::view() { return &m_readerView; } -void ReadBookController::setBook(const External::Archive::File& file) -{ - m_file = &file; - loadPosition(); - m_readerView.setText(reinterpret_cast(file.data), file.dataLength); +void ReadBookController::setBook(const External::Archive::File& file) { + m_file = &file; + loadPosition(); + m_readerView.setText(reinterpret_cast(file.data), file.dataLength); } -bool ReadBookController::handleEvent(Ion::Events::Event event) -{ - if(event == Ion::Events::Down) - { - m_readerView.nextPage(); - return true; - } - if(event == Ion::Events::Up) - { - m_readerView.previousPage(); - return true; - } - if(event == Ion::Events::Back || event == Ion::Events::Home) - { - savePosition(); - } - return false; +bool ReadBookController::handleEvent(Ion::Events::Event event) { + if(event == Ion::Events::Down) { + m_readerView.nextPage(); + return true; + } + if(event == Ion::Events::Up) { + m_readerView.previousPage(); + return true; + } + if(event == Ion::Events::Back || event == Ion::Events::Home) { + savePosition(); + } + return false; } -void ReadBookController::savePosition() const -{ +void ReadBookController::savePosition() const { int pageOffset = m_readerView.getPageOffset(); Ion::Storage::Record::ErrorStatus status = Ion::Storage::sharedStorage()->createRecordWithFullName(m_file->name, &pageOffset, sizeof(pageOffset)); - if(Ion::Storage::Record::ErrorStatus::NameTaken == status) - { - Ion::Storage::Record::Data data; - data.buffer = &pageOffset; - data.size = sizeof(pageOffset); - status = Ion::Storage::sharedStorage()->recordNamed(m_file->name).setValue(data); + if(Ion::Storage::Record::ErrorStatus::NameTaken == status) { + Ion::Storage::Record::Data data; + data.buffer = &pageOffset; + data.size = sizeof(pageOffset); + status = Ion::Storage::sharedStorage()->recordNamed(m_file->name).setValue(data); } } -void ReadBookController::loadPosition() -{ +void ReadBookController::loadPosition() { Ion::Storage::Record r = Ion::Storage::sharedStorage()->recordNamed(m_file->name); - if(Ion::Storage::sharedStorage()->hasRecord(r)) - { + if(Ion::Storage::sharedStorage()->hasRecord(r)) { int pageOffset = *(static_cast(r.value().buffer)); m_readerView.setPageOffset(pageOffset); } - else - { + else { m_readerView.setPageOffset(0); } } diff --git a/apps/reader/read_book_controller.h b/apps/reader/read_book_controller.h index c4349be4e..77d4f57f0 100644 --- a/apps/reader/read_book_controller.h +++ b/apps/reader/read_book_controller.h @@ -5,7 +5,7 @@ #include "apps/external/archive.h" #include "word_wrap_view.h" -namespace reader { +namespace Reader { class ReadBookController : public ViewController { public: diff --git a/apps/reader/utility.cpp b/apps/reader/utility.cpp index 2f82b61f3..123fe3920 100644 --- a/apps/reader/utility.cpp +++ b/apps/reader/utility.cpp @@ -8,109 +8,100 @@ #include #endif -namespace reader +namespace Reader { -bool stringEndsWith(const char* str, const char* pattern) -{ - int strLength = strlen(str); - int patternLength = strlen(pattern); - if (patternLength > strLength) - return false; - - const char* strIter = str + strlen(str); - const char* patternIter = pattern + strlen(pattern); - - while(*strIter == *patternIter) - { - if(patternIter == pattern) - return true; - strIter--; - patternIter--; - } +bool stringEndsWith(const char* str, const char* pattern) { + int strLength = strlen(str); + int patternLength = strlen(pattern); + if (patternLength > strLength) { return false; + } + + const char* strIter = str + strlen(str); + const char* patternIter = pattern + strlen(pattern); + + while(*strIter == *patternIter) { + if(patternIter == pattern) { + return true; + } + strIter--; + patternIter--; + } + return false; } -void stringNCopy(char* dest, int max, const char* src, int len) -{ - while(len>0 && max >1 && *src) - { - *dest = *src; - dest++; - src++; - len--; - max--; - } - *dest=0; +void stringNCopy(char* dest, int max, const char* src, int len) { + while(len>0 && max >1 && *src) + { + *dest = *src; + dest++; + src++; + len--; + max--; + } + *dest=0; } #ifdef DEVICE -int filesWithExtension(const char* extension, External::Archive::File* files, int filesSize) -{ - size_t nbTotalFiles = External::Archive::numberOfFiles(); - int nbFiles = 0; - for(size_t i=0; i < nbTotalFiles; ++i) +int filesWithExtension(const char* extension, External::Archive::File* files, int filesSize) { + size_t nbTotalFiles = External::Archive::numberOfFiles(); + int nbFiles = 0; + for(size_t i=0; i < nbTotalFiles; ++i) + { + External::Archive::File file; + External::Archive::fileAtIndex(i, file); + if(stringEndsWith(file.name, ".txt")) { - External::Archive::File file; - External::Archive::fileAtIndex(i, file); - if(stringEndsWith(file.name, ".txt")) - { - files[nbFiles] = file; - nbFiles++; - if(nbFiles == filesSize) - break; - } + files[nbFiles] = file; + nbFiles++; + if(nbFiles == filesSize) + break; } - return nbFiles; + } + return nbFiles; } #else -static void fillFileData(External::Archive::File& file) -{ - file.data = nullptr; - file.dataLength = 0; +static void fillFileData(External::Archive::File& file) { + file.data = nullptr; + file.dataLength = 0; - struct stat info; - if (stat(file.name, &info) != 0) - { - return; - } - - unsigned char* content = new unsigned char[info.st_size]; - if (content == NULL) - { - return ; - } - FILE *fp = fopen(file.name, "rb"); - if (fp == NULL) - { - return ; - } - - fread(content, info.st_size, 1, fp); - fclose(fp); - file.data = content; - file.dataLength = info.st_size; + struct stat info; + if (stat(file.name, &info) != 0) { + return; + } + + unsigned char* content = new unsigned char[info.st_size]; + if (content == NULL) { + return; + } + FILE *fp = fopen(file.name, "rb"); + if (fp == NULL) { + return ; + } + + fread(content, info.st_size, 1, fp); + fclose(fp); + file.data = content; + file.dataLength = info.st_size; } -int filesWithExtension(const char* extension, External::Archive::File* files, int filesSize) -{ +int filesWithExtension(const char* extension, External::Archive::File* files, int filesSize) { dirent *file; DIR *d = opendir("."); int nb = 0; - if (d) - { - while ((file = readdir(d)) != NULL) - { - if(stringEndsWith(file->d_name, extension)) - { + if (d) { + while ((file = readdir(d)) != NULL) { + if(stringEndsWith(file->d_name, extension)) { files[nb].name = strdup(file->d_name);//will probably leak fillFileData(files[nb]); nb++; - if(nb == filesSize) - break; + if(nb == filesSize) { + break; + } } } closedir(d); diff --git a/apps/reader/utility.h b/apps/reader/utility.h index 6893ab95c..36e1d1b4b 100644 --- a/apps/reader/utility.h +++ b/apps/reader/utility.h @@ -3,11 +3,11 @@ #include -namespace reader +namespace Reader { bool stringEndsWith(const char* str, const char* end); -int filesWithExtension(const char* extension, External::Archive::File* files, int filesSize) ; +int filesWithExtension(const char* extension, External::Archive::File* files, int filesSize); void stringNCopy(char* dest, int max, const char* src, int len); } diff --git a/apps/reader/word_wrap_view.cpp b/apps/reader/word_wrap_view.cpp index 9e4cccd62..c022658cc 100644 --- a/apps/reader/word_wrap_view.cpp +++ b/apps/reader/word_wrap_view.cpp @@ -2,155 +2,138 @@ #include "utility.h" -namespace reader +namespace Reader { -void WordWrapTextView::nextPage() -{ - if(m_nextPageOffset >= m_length) - return; - m_pageOffset = m_nextPageOffset; - markRectAsDirty(bounds()); +void WordWrapTextView::nextPage() { + if(m_nextPageOffset >= m_length) { + return; + } + m_pageOffset = m_nextPageOffset; + markRectAsDirty(bounds()); } -void WordWrapTextView::setText(const char* text, int length) -{ - PointerTextView::setText(text); - m_length = length; +void WordWrapTextView::setText(const char* text, int length) { + PointerTextView::setText(text); + m_length = length; } -void WordWrapTextView::previousPage() -{ - if(m_pageOffset <= 0) - return; +void WordWrapTextView::previousPage() { + if(m_pageOffset <= 0) { + return; + } + const int spaceWidth = m_font->stringSize(" ").width(); - const int spaceWidth = m_font->stringSize(" ").width(); + const char * endOfWord = text() + m_pageOffset; + const char * startOfWord = UTF8Helper::BeginningOfWord(text(), endOfWord); + + KDPoint textPosition(m_frame.width() - margin, m_frame.height() - margin); - const char * endOfWord = text() + m_pageOffset; - const char * startOfWord = UTF8Helper::BeginningOfWord(text(), endOfWord); + while(startOfWord>=text()) { + endOfWord = UTF8Helper::EndOfWord(startOfWord); + KDSize textSize = m_font->stringSizeUntil(startOfWord, endOfWord); + KDPoint previousTextPosition = KDPoint(textPosition.x()-textSize.width(), textPosition.y()); - KDPoint textPosition(m_frame.width() - margin, m_frame.height() - margin); + if(previousTextPosition.x() < margin) { + textPosition = KDPoint(m_frame.width() - margin, textPosition.y() - textSize.height()); + previousTextPosition = KDPoint(textPosition.x() - textSize.width(), textPosition.y()); + } + if(textPosition.y() - textSize.height() < margin) { + break; + } - while(startOfWord>=text()) - { - endOfWord = UTF8Helper::EndOfWord(startOfWord); - KDSize textSize = m_font->stringSizeUntil(startOfWord, endOfWord); - KDPoint previousTextPosition = KDPoint(textPosition.x()-textSize.width(), textPosition.y()); - - if(previousTextPosition.x() < margin) - { - textPosition = KDPoint(m_frame.width() - margin, textPosition.y() - textSize.height()); - previousTextPosition = KDPoint(textPosition.x() - textSize.width(), textPosition.y()); - } - if(textPosition.y() - textSize.height() < margin) - { - break; - } - - - --startOfWord; - while(startOfWord >= text() && *startOfWord == ' ') - { - previousTextPosition = KDPoint(previousTextPosition.x() - spaceWidth, previousTextPosition.y()); - --startOfWord; - } - if(previousTextPosition.x() < margin) - { - previousTextPosition = KDPoint(m_frame.width() - margin, previousTextPosition.y() - textSize.height()); - } - - while(startOfWord >= text() && *startOfWord == '\n') - { - previousTextPosition = KDPoint(m_frame.width() - margin, previousTextPosition.y() - textSize.height()); - --startOfWord; - } - - if(previousTextPosition.y() - textSize.height() < margin) - { - break; - } - - textPosition = previousTextPosition; - endOfWord = startOfWord; - startOfWord = UTF8Helper::BeginningOfWord(text(), endOfWord); + --startOfWord; + while(startOfWord >= text() && *startOfWord == ' ') { + previousTextPosition = KDPoint(previousTextPosition.x() - spaceWidth, previousTextPosition.y()); + --startOfWord; } - if(startOfWord == text()) - m_pageOffset = 0; - else - m_pageOffset = UTF8Helper::EndOfWord(startOfWord) - text() + 1; - markRectAsDirty(bounds()); -} - -void WordWrapTextView::drawRect(KDContext * ctx, KDRect rect) const -{ - ctx->fillRect(KDRect(0, 0, bounds().width(), bounds().height()), m_backgroundColor); - - const char * endOfFile = text() + m_length; - const char * startOfWord = text() + m_pageOffset; - const char * endOfWord = UTF8Helper::EndOfWord(startOfWord); - KDPoint textPosition(margin, margin); - - const int wordMaxLength = 128; - char word[wordMaxLength]; - - const int spaceWidth = m_font->stringSize(" ").width(); - - while(startOfWord < endOfFile) - { - - KDSize textSize = m_font->stringSizeUntil(startOfWord, endOfWord); - KDPoint nextTextPosition = KDPoint(textPosition.x()+textSize.width(), textPosition.y()); - - if(nextTextPosition.x() > m_frame.width() - margin) - { - textPosition = KDPoint(margin, textPosition.y() + textSize.height()); - nextTextPosition = KDPoint(margin + textSize.width(), textPosition.y()); - } - if(textPosition.y() + textSize.height() > m_frame.height() - margin) - { - break; - } - - stringNCopy(word, wordMaxLength, startOfWord, endOfWord-startOfWord); - ctx->drawString(word, textPosition, m_font, m_textColor, m_backgroundColor); - - while(*endOfWord == ' ') - { - nextTextPosition = KDPoint(nextTextPosition.x() + spaceWidth, nextTextPosition.y()); - ++endOfWord; - } - if(nextTextPosition.x() > m_frame.width() - margin) - { - nextTextPosition = KDPoint(margin, nextTextPosition.y() + textSize.height()); - } - - while(*endOfWord == '\n') - { - nextTextPosition = KDPoint(margin, nextTextPosition.y() + textSize.height()); - ++endOfWord; - } - - if(nextTextPosition.y() + textSize.height() > m_frame.height() - margin) - { - break; - } - - textPosition = nextTextPosition; - startOfWord = endOfWord; - endOfWord = UTF8Helper::EndOfWord(startOfWord); + if(previousTextPosition.x() < margin) { + previousTextPosition = KDPoint(m_frame.width() - margin, previousTextPosition.y() - textSize.height()); } - m_nextPageOffset = startOfWord - text(); + + + while(startOfWord >= text() && *startOfWord == '\n') { + previousTextPosition = KDPoint(m_frame.width() - margin, previousTextPosition.y() - textSize.height()); + --startOfWord; + } + + if(previousTextPosition.y() - textSize.height() < margin) { + break; + } + + textPosition = previousTextPosition; + endOfWord = startOfWord; + startOfWord = UTF8Helper::BeginningOfWord(text(), endOfWord); + } + if(startOfWord == text()) { + m_pageOffset = 0; + } + else { + m_pageOffset = UTF8Helper::EndOfWord(startOfWord) - text() + 1; + } + markRectAsDirty(bounds()); } -int WordWrapTextView::getPageOffset() const -{ - return m_pageOffset; +void WordWrapTextView::drawRect(KDContext * ctx, KDRect rect) const { + ctx->fillRect(KDRect(0, 0, bounds().width(), bounds().height()), m_backgroundColor); + + const char * endOfFile = text() + m_length; + const char * startOfWord = text() + m_pageOffset; + const char * endOfWord = UTF8Helper::EndOfWord(startOfWord); + KDPoint textPosition(margin, margin); + + const int wordMaxLength = 128; + char word[wordMaxLength]; + + const int spaceWidth = m_font->stringSize(" ").width(); + + while(startOfWord < endOfFile) { + + KDSize textSize = m_font->stringSizeUntil(startOfWord, endOfWord); + KDPoint nextTextPosition = KDPoint(textPosition.x()+textSize.width(), textPosition.y()); + + if(nextTextPosition.x() > m_frame.width() - margin) { + textPosition = KDPoint(margin, textPosition.y() + textSize.height()); + nextTextPosition = KDPoint(margin + textSize.width(), textPosition.y()); + } + if(textPosition.y() + textSize.height() > m_frame.height() - margin) { + break; + } + + stringNCopy(word, wordMaxLength, startOfWord, endOfWord-startOfWord); + ctx->drawString(word, textPosition, m_font, m_textColor, m_backgroundColor); + + while(*endOfWord == ' ') { + nextTextPosition = KDPoint(nextTextPosition.x() + spaceWidth, nextTextPosition.y()); + ++endOfWord; + } + if(nextTextPosition.x() > m_frame.width() - margin) { + nextTextPosition = KDPoint(margin, nextTextPosition.y() + textSize.height()); + } + + while(*endOfWord == '\n') { + nextTextPosition = KDPoint(margin, nextTextPosition.y() + textSize.height()); + ++endOfWord; + } + + if(nextTextPosition.y() + textSize.height() > m_frame.height() - margin) { + break; + } + + textPosition = nextTextPosition; + startOfWord = endOfWord; + endOfWord = UTF8Helper::EndOfWord(startOfWord); + } + m_nextPageOffset = startOfWord - text(); } -void WordWrapTextView::setPageOffset(int o) -{ - m_pageOffset = o; +int WordWrapTextView::getPageOffset() const { + return m_pageOffset; +} + +void WordWrapTextView::setPageOffset(int o) { + m_pageOffset = o; } } \ No newline at end of file diff --git a/apps/reader/word_wrap_view.h b/apps/reader/word_wrap_view.h index 4075ff4e2..0fc57e295 100644 --- a/apps/reader/word_wrap_view.h +++ b/apps/reader/word_wrap_view.h @@ -3,23 +3,23 @@ #include -namespace reader +namespace Reader { class WordWrapTextView : public PointerTextView { public: - void drawRect(KDContext * ctx, KDRect rect) const override; - void setText(const char*, int length); - void nextPage(); - void previousPage(); - int getPageOffset() const; - void setPageOffset(int o); + void drawRect(KDContext * ctx, KDRect rect) const override; + void setText(const char*, int length); + void nextPage(); + void previousPage(); + int getPageOffset() const; + void setPageOffset(int o); protected: - int m_pageOffset = 0; - mutable int m_nextPageOffset = 0; - int m_length = 0; + int m_pageOffset = 0; + mutable int m_nextPageOffset = 0; + int m_length = 0; - static const int margin = 10; + static const int margin = 10; }; } diff --git a/escher/src/alternate_empty_view_controller.cpp b/escher/src/alternate_empty_view_controller.cpp index be61f67f2..2d64ae2d7 100644 --- a/escher/src/alternate_empty_view_controller.cpp +++ b/escher/src/alternate_empty_view_controller.cpp @@ -57,8 +57,7 @@ const char * AlternateEmptyViewController::title() { bool AlternateEmptyViewController::handleEvent(Ion::Events::Event event) { if (m_contentView.alternateEmptyViewDelegate()->isEmpty()) { if (event != Ion::Events::Home && event != Ion::Events::OnOff) { - m_contentView.alternateEmptyViewDelegate()->defaultController()->handleEvent(Ion::Events::Back); - return true; + return m_contentView.alternateEmptyViewDelegate()->defaultController()->handleEvent(Ion::Events::Back); } return false; } From 367f64dac8fc958bbe4fec135463b74e3d416046 Mon Sep 17 00:00:00 2001 From: Laury Date: Wed, 8 Sep 2021 21:42:39 +0200 Subject: [PATCH 48/48] [reader] Fixed a bug when going backwards --- apps/reader/word_wrap_view.cpp | 128 ++++++++++++++++++--------------- apps/reader/word_wrap_view.h | 3 +- 2 files changed, 70 insertions(+), 61 deletions(-) diff --git a/apps/reader/word_wrap_view.cpp b/apps/reader/word_wrap_view.cpp index c022658cc..1ee80f82d 100644 --- a/apps/reader/word_wrap_view.cpp +++ b/apps/reader/word_wrap_view.cpp @@ -1,3 +1,4 @@ + #include "word_wrap_view.h" #include "utility.h" @@ -22,51 +23,55 @@ void WordWrapTextView::previousPage() { if(m_pageOffset <= 0) { return; } - const int spaceWidth = m_font->stringSize(" ").width(); - const char * endOfWord = text() + m_pageOffset; + const int charWidth = m_font->glyphSize().width(); + const int charHeight = m_font->glyphSize().height(); + + const char * endOfWord = text() + m_pageOffset - 1; const char * startOfWord = UTF8Helper::BeginningOfWord(text(), endOfWord); - - KDPoint textPosition(m_frame.width() - margin, m_frame.height() - margin); + + KDPoint textEndPosition(m_frame.width() - k_margin, m_frame.height() - k_margin); while(startOfWord>=text()) { - endOfWord = UTF8Helper::EndOfWord(startOfWord); - KDSize textSize = m_font->stringSizeUntil(startOfWord, endOfWord); - KDPoint previousTextPosition = KDPoint(textPosition.x()-textSize.width(), textPosition.y()); - - if(previousTextPosition.x() < margin) { - textPosition = KDPoint(m_frame.width() - margin, textPosition.y() - textSize.height()); - previousTextPosition = KDPoint(textPosition.x() - textSize.width(), textPosition.y()); - } - if(textPosition.y() - textSize.height() < margin) { - break; - } - - - --startOfWord; - while(startOfWord >= text() && *startOfWord == ' ') { - previousTextPosition = KDPoint(previousTextPosition.x() - spaceWidth, previousTextPosition.y()); - --startOfWord; - } - if(previousTextPosition.x() < margin) { - previousTextPosition = KDPoint(m_frame.width() - margin, previousTextPosition.y() - textSize.height()); - } - - - while(startOfWord >= text() && *startOfWord == '\n') { - previousTextPosition = KDPoint(m_frame.width() - margin, previousTextPosition.y() - textSize.height()); - --startOfWord; - } - - if(previousTextPosition.y() - textSize.height() < margin) { - break; - } - - textPosition = previousTextPosition; - endOfWord = startOfWord; startOfWord = UTF8Helper::BeginningOfWord(text(), endOfWord); + endOfWord = UTF8Helper::EndOfWord(startOfWord); + KDSize textSize = m_font->stringSizeUntil(startOfWord, endOfWord); + KDPoint textStartPosition = KDPoint(textEndPosition.x()-textSize.width(), textEndPosition.y()); + + if(textStartPosition.x() < k_margin) { + textEndPosition = KDPoint(m_frame.width() - k_margin, textEndPosition.y() - charHeight); + textStartPosition = KDPoint(textEndPosition.x() - textSize.width(), textEndPosition.y()); + } + if(textEndPosition.y() - textSize.height() < k_margin) { + break; + } + + --startOfWord; + while(startOfWord >= text() && (*startOfWord == ' ' || *startOfWord == '\n')) { + if(*startOfWord == ' ') { + textStartPosition = KDPoint(textStartPosition.x() - charWidth, textStartPosition.y()); + } + else { + textStartPosition = KDPoint(m_frame.width() - k_margin, textStartPosition.y() - charHeight); + } + --startOfWord; + } + + if(textStartPosition.y() < k_margin) { // If out of page, quit + break; + } + + if(textStartPosition.y() != textEndPosition.y()) { // If line changed, x is at start of line + textStartPosition = KDPoint(m_frame.width() - k_margin, textStartPosition.y()); + } + if(textStartPosition.x() < k_margin) { // Go to line if left overflow + textStartPosition = KDPoint(m_frame.width() - k_margin, textStartPosition.y() - charHeight); + } + + textEndPosition = textStartPosition; + endOfWord = startOfWord; } - if(startOfWord == text()) { + if(startOfWord + 1 == text()) { m_pageOffset = 0; } else { @@ -81,52 +86,57 @@ void WordWrapTextView::drawRect(KDContext * ctx, KDRect rect) const { const char * endOfFile = text() + m_length; const char * startOfWord = text() + m_pageOffset; const char * endOfWord = UTF8Helper::EndOfWord(startOfWord); - KDPoint textPosition(margin, margin); + KDPoint textPosition(k_margin, k_margin); const int wordMaxLength = 128; char word[wordMaxLength]; - const int spaceWidth = m_font->stringSize(" ").width(); + const int charWidth = m_font->glyphSize().width(); + const int charHeight = m_font->glyphSize().height(); while(startOfWord < endOfFile) { - KDSize textSize = m_font->stringSizeUntil(startOfWord, endOfWord); KDPoint nextTextPosition = KDPoint(textPosition.x()+textSize.width(), textPosition.y()); - if(nextTextPosition.x() > m_frame.width() - margin) { - textPosition = KDPoint(margin, textPosition.y() + textSize.height()); - nextTextPosition = KDPoint(margin + textSize.width(), textPosition.y()); + if(nextTextPosition.x() > m_frame.width() - k_margin) { // Right overflow + textPosition = KDPoint(k_margin, textPosition.y() + textSize.height()); + nextTextPosition = KDPoint(k_margin + textSize.width(), textPosition.y()); } - if(textPosition.y() + textSize.height() > m_frame.height() - margin) { + + if(textPosition.y() + textSize.height() > m_frame.height() - k_margin) { // Bottom overflow break; } stringNCopy(word, wordMaxLength, startOfWord, endOfWord-startOfWord); ctx->drawString(word, textPosition, m_font, m_textColor, m_backgroundColor); - while(*endOfWord == ' ') { - nextTextPosition = KDPoint(nextTextPosition.x() + spaceWidth, nextTextPosition.y()); + while(*endOfWord == ' ' || *endOfWord == '\n') { + if(*endOfWord == ' ') { + nextTextPosition = KDPoint(nextTextPosition.x() + charWidth, nextTextPosition.y()); + } + else { + nextTextPosition = KDPoint(k_margin, nextTextPosition.y() + charHeight); + } ++endOfWord; } - if(nextTextPosition.x() > m_frame.width() - margin) { - nextTextPosition = KDPoint(margin, nextTextPosition.y() + textSize.height()); - } - while(*endOfWord == '\n') { - nextTextPosition = KDPoint(margin, nextTextPosition.y() + textSize.height()); - ++endOfWord; - } - - if(nextTextPosition.y() + textSize.height() > m_frame.height() - margin) { + if(nextTextPosition.y() + textSize.height() > m_frame.height() - k_margin) { // If out of page, quit break; } + if(nextTextPosition.y() != textPosition.y()) { // If line changed, x is at start of line + nextTextPosition = KDPoint(k_margin, nextTextPosition.y()); + } + if(nextTextPosition.x() > m_frame.width() - k_margin) { // Go to line if right overflow + nextTextPosition = KDPoint(k_margin, nextTextPosition.y() + textSize.height()); + } textPosition = nextTextPosition; startOfWord = endOfWord; endOfWord = UTF8Helper::EndOfWord(startOfWord); } - m_nextPageOffset = startOfWord - text(); -} + + m_nextPageOffset = endOfWord - text(); +}; int WordWrapTextView::getPageOffset() const { return m_pageOffset; diff --git a/apps/reader/word_wrap_view.h b/apps/reader/word_wrap_view.h index 0fc57e295..0bce2f67e 100644 --- a/apps/reader/word_wrap_view.h +++ b/apps/reader/word_wrap_view.h @@ -18,8 +18,7 @@ protected: int m_pageOffset = 0; mutable int m_nextPageOffset = 0; int m_length = 0; - - static const int margin = 10; + static const int k_margin = 10; }; }