From c18a8405995fae5f2620c598a60c8b5979658784 Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Thu, 9 Jan 2025 00:37:05 +0100 Subject: [PATCH 01/80] cnn: Build both int8 and fp32 variants --- Makefile | 11 +-- src/tinymaix_cnn/Makefile | 8 ++- src/tinymaix_cnn/fp32/tm_port.h | 97 +++++++++++++++++++++++++++ src/tinymaix_cnn/{ => int8}/tm_port.h | 0 4 files changed, 110 insertions(+), 6 deletions(-) create mode 100644 src/tinymaix_cnn/fp32/tm_port.h rename src/tinymaix_cnn/{ => int8}/tm_port.h (100%) diff --git a/Makefile b/Makefile index 0a4606d..ec5dcf7 100644 --- a/Makefile +++ b/Makefile @@ -22,8 +22,11 @@ $(MODULES_PATH)/emlearn_iir.mpy: $(MODULES_PATH)/emlearn_fft.mpy: make -C src/emlearn_fft/ ARCH=$(ARCH) MPY_DIR=$(MPY_DIR_ABS) V=1 clean dist -$(MODULES_PATH)/emlearn_cnn.mpy: - make -C src/tinymaix_cnn/ ARCH=$(ARCH) MPY_DIR=$(MPY_DIR_ABS) V=1 clean dist +$(MODULES_PATH)/emlearn_cnn_int8.mpy: + make -C src/tinymaix_cnn/ ARCH=$(ARCH) MPY_DIR=$(MPY_DIR_ABS) V=1 CONFIG=int8 clean dist + +$(MODULES_PATH)/emlearn_cnn_fp32.mpy: + make -C src/tinymaix_cnn/ ARCH=$(ARCH) MPY_DIR=$(MPY_DIR_ABS) V=1 CONFIG=fp32 clean dist $(MODULES_PATH)/emlearn_kmeans.mpy: make -C src/emlearn_kmeans/ ARCH=$(ARCH) MPY_DIR=$(MPY_DIR_ABS) V=1 clean dist @@ -46,7 +49,7 @@ emlearn_iir.results: $(MODULES_PATH)/emlearn_iir.mpy emlearn_fft.results: $(MODULES_PATH)/emlearn_fft.mpy MICROPYPATH=$(MODULES_PATH) $(MICROPYTHON_BIN) tests/test_fft.py -emlearn_cnn.results: $(MODULES_PATH)/emlearn_cnn.mpy +emlearn_cnn.results: $(MODULES_PATH)/emlearn_cnn_int8.mpy $(MODULES_PATH)/emlearn_cnn_fp32.mpy MICROPYPATH=$(MODULES_PATH) $(MICROPYTHON_BIN) tests/test_cnn.py emlearn_kmeans.results: $(MODULES_PATH)/emlearn_kmeans.mpy @@ -76,6 +79,6 @@ release: check: emlearn_trees.results emlearn_neighbors.results emlearn_iir.results emlearn_iir_q15.results emlearn_fft.results emlearn_kmeans.results emlearn_arrayutils.results emlearn_cnn.results -dist: $(MODULES_PATH)/emlearn_trees.mpy $(MODULES_PATH)/emlearn_neighbors.mpy $(MODULES_PATH)/emlearn_iir.mpy $(MODULES_PATH)/emlearn_iir_q15.mpy $(MODULES_PATH)/emlearn_fft.mpy $(MODULES_PATH)/emlearn_kmeans.mpy $(MODULES_PATH)/emlearn_arrayutils.mpy $(MODULES_PATH)/emlearn_cnn.mpy +dist: $(MODULES_PATH)/emlearn_trees.mpy $(MODULES_PATH)/emlearn_neighbors.mpy $(MODULES_PATH)/emlearn_iir.mpy $(MODULES_PATH)/emlearn_iir_q15.mpy $(MODULES_PATH)/emlearn_fft.mpy $(MODULES_PATH)/emlearn_kmeans.mpy $(MODULES_PATH)/emlearn_arrayutils.mpy $(MODULES_PATH)/emlearn_cnn_int8.mpy $(MODULES_PATH)/emlearn_cnn_fp32.mpy diff --git a/src/tinymaix_cnn/Makefile b/src/tinymaix_cnn/Makefile index d8fd5d2..ee71fe4 100644 --- a/src/tinymaix_cnn/Makefile +++ b/src/tinymaix_cnn/Makefile @@ -4,6 +4,8 @@ MPY_DIR = ../../micropython # Architecture to build for (x86, x64, armv6m, armv7m, xtensa, xtensawin) ARCH = x64 +CONFIG := int8 + # The ABI version for .mpy files MPY_ABI_VERSION := 6.3 @@ -12,6 +14,8 @@ TINYMAIX_DIR := ../../dependencies/TinyMaix DIST_DIR := ../../dist/$(ARCH)_$(MPY_ABI_VERSION) +CONFIG_DIR := ./$(CONFIG) + # Name of module MOD = emlearn_cnn @@ -45,7 +49,7 @@ ifeq ($(SOFTFP_ENABLE), 1) endif # Releases -DIST_FILE = $(DIST_DIR)/$(MOD).mpy +DIST_FILE = $(DIST_DIR)/$(MOD)_$(CONFIG).mpy $(DIST_DIR): mkdir -p $@ @@ -68,6 +72,6 @@ _addsubdf3.o: _arm_addsubdf3.o: $(CROSS)ar -x $(LIBGCC_FILENAME) $(SOFTFP_O) -CFLAGS += -I$(TINYMAIX_DIR)/include -I$(TINYMAIX_DIR)/src -Wno-error=unused-variable -Wno-error=multichar +CFLAGS += -I$(CONFIG_DIR) -I$(TINYMAIX_DIR)/include -I$(TINYMAIX_DIR)/src -Wno-error=unused-variable -Wno-error=multichar dist: $(DIST_FILE) diff --git a/src/tinymaix_cnn/fp32/tm_port.h b/src/tinymaix_cnn/fp32/tm_port.h new file mode 100644 index 0000000..1868177 --- /dev/null +++ b/src/tinymaix_cnn/fp32/tm_port.h @@ -0,0 +1,97 @@ +/* +Configuration file for TinyMaix + +Copyright 2022 Sipeed Technology Co., Ltd. All Rights Reserved. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +#ifndef __TM_PORT_H +#define __TM_PORT_H + +#define TM_ARCH_CPU (0) //default, pure cpu compute +#define TM_ARCH_ARM_SIMD (1) //ARM Cortex M4/M7, etc. +#define TM_ARCH_ARM_NEON (2) //ARM Cortex A7, etc. +#define TM_ARCH_ARM_MVEI (3) //ARMv8.1: M55, etc. +#define TM_ARCH_RV32P (4) //T-head E907, etc. +#define TM_ARCH_RV64V (5) //T-head C906,C910, etc. +#define TM_ARCH_CSKYV2 (6) //cskyv2 with dsp core +#define TM_ARCH_X86_SSE2 (7) //x86 sse2 + +#define TM_OPT0 (0) //default, least code and buf +#define TM_OPT1 (1) //opt for speed, need more code and buf +#define TM_OPT2 (2) //TODO + +/******************************* PORT CONFIG ************************************/ +#define TM_ARCH TM_ARCH_CPU +#define TM_OPT_LEVEL TM_OPT0 +#define TM_MDL_TYPE TM_MDL_FP32 +#define TM_FASTSCALE (0) //enable if your chip don't have FPU, may speed up 1/3, but decrease accuracy +#define TM_LOCAL_MATH (1) //use local math func (like exp()) to avoid libm +#define TM_ENABLE_STAT (1) //enable mdl stat functions +#define TM_MAX_CSIZE (1000) //max channel num //used if INT8 mdl //cost TM_MAX_CSIZE*4 Byte +#define TM_MAX_KSIZE (5*5) //max kernel_size //cost TM_MAX_KSIZE*4 Byte +#define TM_MAX_KCSIZE (3*3*256) //max kernel_size*channels //cost TM_MAX_KSIZE*sizeof(mtype_t) Byte + +#define TM_INLINE __attribute__((always_inline)) static inline +#define TM_WEAK __attribute__((weak)) + +// Disable "static" (non-const) globals, since they are not supported by MicroPython mpy_ld.py +#define TM_STATIC + +// Use MicroPython for dynamic allocation +#define tm_malloc(x) m_malloc(x) +#define tm_free(x) m_free(x) + +// FIXME: set theese to use MicroPython primitives + +#define TM_PRINTF(...) //printf(__VA_ARGS__); +#define TM_DBG(...) TM_PRINTF("###L%d: ",__LINE__);TM_PRINTF(__VA_ARGS__); +#define TM_DBGL() //Serial.println(__LINE__); + + +// FIXME: enable profiling +#define TM_GET_US() (0) +#define TM_DBGT_INIT() ; +#define TM_DBGT_START() ; +#define TM_DBGT(x) ; + +/******************************* DBG PERFORMANCE CONFIG ************************************/ +//need clock tick to make accurate statistics +#define TM_EN_PERF 0 + +#if TM_EN_PERF + #define TM_GET_TICK(x) __ASM volatile("csrr %0, mcycle" : "=r"(x)); //edit your self + + #define TM_TICK_PERUS (380) //sysconf(_SC_CLK_TCK)/1000000) + #define TM_PERF_REG(x) uint64_t x=0; + #define TM_PERF_EXTREG(x) extern uint64_t x; + #define TM_PERF_INIT(x) uint64_t _##x##_t0, _##x##_t1; + #define TM_PERF_START(x) TM_GET_TICK(_##x##_t0); + #define TM_PERF_ADD(x) {TM_GET_TICK(_##x##_t1);(x)+=(_##x##_t1-_##x##_t0);TM_GET_TICK(_##x##_t0);}; + #define TM_PERF_PRINT(x) TM_PRINTF("PERF "#x": %ld us\r\n", (x)/TM_TICK_PERUS) +#else + #define TM_GET_TICK(x) + #define TM_TICK_PERUS + #define TM_PERF_REG(x) + #define TM_PERF_EXTREG(x) + #define TM_PERF_INIT(x) + #define TM_PERF_START(x) + #define TM_PERF_ADD(x) + #define TM_PERF_PRINT(x) +#endif + + +/******************************* OPS CONFIG ************************************/ + + + + +#endif diff --git a/src/tinymaix_cnn/tm_port.h b/src/tinymaix_cnn/int8/tm_port.h similarity index 100% rename from src/tinymaix_cnn/tm_port.h rename to src/tinymaix_cnn/int8/tm_port.h From b7c1c732017665ce02089ec5133fde841deb2f17 Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Thu, 9 Jan 2025 00:51:26 +0100 Subject: [PATCH 02/80] cnn: Update mnist and test to use both int8 and fp32 --- examples/mnist_cnn/README.md | 6 +- examples/mnist_cnn/mnist_cnn.h | 254 ------- examples/mnist_cnn/mnist_cnn.h5 | Bin 108960 -> 0 bytes examples/mnist_cnn/mnist_cnn.tflite | Bin 7976 -> 0 bytes examples/mnist_cnn/mnist_cnn.tmdl | Bin 3912 -> 0 bytes examples/mnist_cnn/mnist_cnn_fp32.h | 816 +++++++++++++++++++++++ examples/mnist_cnn/mnist_cnn_fp32.tflite | Bin 0 -> 15112 bytes examples/mnist_cnn/mnist_cnn_fp32.tmdl | Bin 0 -> 12896 bytes examples/mnist_cnn/mnist_cnn_int8.h | 254 +++++++ examples/mnist_cnn/mnist_cnn_int8.tflite | Bin 0 -> 7976 bytes examples/mnist_cnn/mnist_cnn_int8.tmdl | Bin 0 -> 3912 bytes examples/mnist_cnn/mnist_cnn_run.py | 4 +- examples/mnist_cnn/mnist_train.py | 35 +- examples/mnist_cnn/package.json | 3 +- tests/test_cnn.py | 26 +- 15 files changed, 1113 insertions(+), 285 deletions(-) delete mode 100644 examples/mnist_cnn/mnist_cnn.h delete mode 100644 examples/mnist_cnn/mnist_cnn.h5 delete mode 100644 examples/mnist_cnn/mnist_cnn.tflite delete mode 100644 examples/mnist_cnn/mnist_cnn.tmdl create mode 100644 examples/mnist_cnn/mnist_cnn_fp32.h create mode 100644 examples/mnist_cnn/mnist_cnn_fp32.tflite create mode 100644 examples/mnist_cnn/mnist_cnn_fp32.tmdl create mode 100644 examples/mnist_cnn/mnist_cnn_int8.h create mode 100644 examples/mnist_cnn/mnist_cnn_int8.tflite create mode 100644 examples/mnist_cnn/mnist_cnn_int8.tmdl diff --git a/examples/mnist_cnn/README.md b/examples/mnist_cnn/README.md index 5e94450..14e93c1 100644 --- a/examples/mnist_cnn/README.md +++ b/examples/mnist_cnn/README.md @@ -30,7 +30,7 @@ python mnist_train.py ## Running on host ```console -micropython -m mip install https://emlearn.github.io/emlearn-micropython/builds/master/x64_6.3/emlearn_cnn.mpy +micropython -m mip install https://emlearn.github.io/emlearn-micropython/builds/master/x64_6.3/emlearn_cnn_int8.mpy micropython mnist_cnn_run.py ``` @@ -50,11 +50,11 @@ Flash your device with a standard MicroPython firmware, from the MicroPython.org downloads page. ```console -mpremote mip install https://emlearn.github.io/emlearn-micropython/builds/master/xtensawin_6.3/emlearn_cnn.mpy +mpremote mip install https://emlearn.github.io/emlearn-micropython/builds/master/xtensawin_6.3/emlearn_cnn_int8.mpy ``` ```console -mpremote cp mnist_cnn.tmdl : +mpremote cp mnist_cnn_int8.tmdl : mpremote cp -r test_data/ : mpremote run mnist_cnn_run.py ``` diff --git a/examples/mnist_cnn/mnist_cnn.h b/examples/mnist_cnn/mnist_cnn.h deleted file mode 100644 index 08205c0..0000000 --- a/examples/mnist_cnn/mnist_cnn.h +++ /dev/null @@ -1,254 +0,0 @@ -#ifndef __MODEL_FILE__H -#define __MODEL_FILE__H - -#include -#define MDL_BUF_LEN (2136) -#define LBUF_LEN (2232) -const uint8_t mdl_data[3912]={\ - 0x4d, 0x41, 0x49, 0x58, 0x00, 0x01, 0x01, 0x00, 0x01, 0x00, 0x06, 0x00, 0x58, 0x08, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, - 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x03, 0x00, 0x00, - 0x03, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0x01, 0x00, 0x03, 0x00, 0x0d, 0x00, 0x0d, 0x00, 0x08, 0x00, - 0x81, 0x80, 0x80, 0x3b, 0x80, 0xff, 0xff, 0xff, 0xed, 0xea, 0x86, 0x3c, 0x80, 0xff, 0xff, 0xff, - 0x03, 0x03, 0x02, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0xb8, 0x00, 0x00, 0x00, - 0x4f, 0x03, 0x43, 0x3c, 0xff, 0xaf, 0x2d, 0x3c, 0xbd, 0x82, 0x41, 0x3c, 0x07, 0x2c, 0x4a, 0x3c, - 0x1a, 0x13, 0x7e, 0x3c, 0x19, 0xe0, 0xb0, 0x3c, 0x80, 0x3f, 0x7d, 0x3c, 0xf9, 0xa8, 0xbc, 0x3b, - 0x96, 0x3c, 0x36, 0x81, 0x30, 0x43, 0xcb, 0x4d, 0x33, 0x62, 0xb1, 0x81, 0x68, 0x5c, 0x8b, 0x33, - 0x66, 0xf7, 0x1b, 0xb5, 0x96, 0x14, 0x89, 0x57, 0xa4, 0x81, 0x34, 0xb4, 0x3f, 0x59, 0xfb, 0xdf, - 0x56, 0xbc, 0x81, 0x20, 0x81, 0xab, 0xba, 0x06, 0x0f, 0xfb, 0x18, 0x3f, 0x3a, 0x81, 0x17, 0x07, - 0xed, 0xb3, 0xfb, 0x08, 0x0b, 0xc1, 0xb2, 0x81, 0xcf, 0x4d, 0x30, 0xbd, 0x56, 0x43, 0x00, 0xe7, - 0x32, 0x65, 0x4b, 0xfb, 0x7f, 0x5a, 0x2d, 0x7c, 0x1c, 0x1e, 0x00, 0x00, 0xe3, 0x28, 0x00, 0x00, - 0x35, 0xa2, 0xff, 0xff, 0x76, 0xf1, 0xff, 0xff, 0x21, 0xda, 0xff, 0xff, 0x42, 0xbb, 0xff, 0xff, - 0x41, 0xe7, 0xff, 0xff, 0x86, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x04, 0x00, 0x00, - 0x10, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x0d, 0x00, 0x0d, 0x00, 0x08, 0x00, - 0x03, 0x00, 0x06, 0x00, 0x06, 0x00, 0x0c, 0x00, 0xed, 0xea, 0x86, 0x3c, 0x80, 0xff, 0xff, 0xff, - 0x3d, 0x08, 0x7e, 0x3c, 0x80, 0xff, 0xff, 0xff, 0x03, 0x03, 0x02, 0x02, 0x01, 0x01, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, - 0x80, 0x00, 0x00, 0x00, 0xe0, 0x03, 0x00, 0x00, 0x3c, 0x8e, 0x77, 0x3b, 0x94, 0x22, 0x2c, 0x3b, - 0x11, 0x30, 0xa8, 0x3b, 0xc1, 0x4a, 0x37, 0x3b, 0x53, 0x1b, 0x20, 0x3b, 0xd0, 0x77, 0x37, 0x3b, - 0x44, 0xfa, 0x20, 0x3b, 0xef, 0x62, 0xf8, 0x3a, 0xa6, 0xf0, 0x5a, 0x3b, 0xc5, 0xf1, 0x82, 0x3b, - 0xc1, 0x4a, 0x4c, 0x3b, 0xcf, 0x27, 0xa3, 0x3b, 0x38, 0x31, 0x0c, 0x1a, 0xb5, 0xd0, 0x15, 0xbf, - 0xea, 0x54, 0x0d, 0xcf, 0x00, 0xa3, 0x9e, 0xc4, 0xac, 0xec, 0x27, 0x0c, 0x0b, 0xd9, 0x18, 0x12, - 0x29, 0xeb, 0x2a, 0xff, 0x04, 0xe9, 0x06, 0x11, 0x05, 0x29, 0xdf, 0xdc, 0x02, 0x11, 0xf1, 0xd2, - 0x7f, 0x60, 0x3e, 0x3c, 0x3b, 0x35, 0xb4, 0xb8, 0x69, 0x23, 0xe2, 0x2f, 0xb8, 0xc0, 0xc4, 0xd0, - 0x0f, 0xeb, 0xb1, 0x35, 0x0d, 0x0a, 0xf1, 0x11, 0x35, 0x2c, 0x03, 0x00, 0xdd, 0x95, 0xc3, 0x37, - 0x2e, 0x34, 0xf5, 0x27, 0x2b, 0xd2, 0xc8, 0x95, 0x90, 0x4c, 0x24, 0x22, 0x40, 0x29, 0xfe, 0xd8, - 0xed, 0xe1, 0x27, 0xea, 0x07, 0xf7, 0xc0, 0x94, 0x1e, 0xcf, 0xc3, 0x1e, 0x26, 0x3a, 0xe4, 0xe8, - 0x0a, 0x17, 0xeb, 0x06, 0xc5, 0xaf, 0x02, 0xb2, 0xf6, 0xf7, 0x96, 0x38, 0xd9, 0xda, 0x21, 0x0c, - 0xcd, 0xe7, 0xba, 0x17, 0x7f, 0x33, 0xfc, 0x03, 0xef, 0xf6, 0xdd, 0x0c, 0x88, 0xe3, 0x1a, 0x08, - 0xf8, 0xd2, 0x5a, 0x70, 0x1d, 0xfd, 0x02, 0xf0, 0xc7, 0xa8, 0xfd, 0xe5, 0xb3, 0xd0, 0x0a, 0xf7, - 0xf2, 0x97, 0xd1, 0xe2, 0xea, 0x01, 0x11, 0x17, 0xf9, 0xfb, 0xd0, 0x81, 0xee, 0xfe, 0x9b, 0xae, - 0xef, 0xd8, 0xcf, 0x17, 0x45, 0x14, 0x0e, 0x2d, 0x10, 0x0f, 0x1d, 0x15, 0x5b, 0x37, 0x14, 0x22, - 0x23, 0x05, 0xf0, 0xe4, 0xef, 0x75, 0x52, 0x23, 0x17, 0x2d, 0x4d, 0xe5, 0x0a, 0x19, 0x19, 0x33, - 0x15, 0x07, 0x25, 0x09, 0x0f, 0x41, 0x28, 0xe1, 0xf4, 0xce, 0xd2, 0x1d, 0x1f, 0xdb, 0xf8, 0x25, - 0x40, 0x14, 0x2a, 0x4e, 0x2a, 0x1a, 0x15, 0x17, 0xe8, 0xad, 0xeb, 0xdb, 0xf3, 0x07, 0x11, 0xfd, - 0xd7, 0xf9, 0x1d, 0x03, 0x06, 0x1a, 0x47, 0x59, 0xe3, 0x27, 0x16, 0x20, 0x3e, 0x09, 0x10, 0x01, - 0xb6, 0xf5, 0xc8, 0xf9, 0x73, 0x1d, 0xfd, 0x19, 0xe2, 0xf8, 0x3c, 0xee, 0x0f, 0x7d, 0x1a, 0xe6, - 0x52, 0xd3, 0xda, 0x14, 0x0c, 0x45, 0xaf, 0xef, 0xd7, 0xce, 0x01, 0xa3, 0xc8, 0xd5, 0x96, 0xa2, - 0xdf, 0xcc, 0x02, 0x08, 0x9d, 0x40, 0x51, 0x81, 0xfb, 0xe1, 0xb3, 0xf1, 0xc9, 0xad, 0x0c, 0xdb, - 0xe3, 0xb6, 0x0c, 0xb0, 0xce, 0xda, 0xac, 0xe0, 0xb1, 0xdd, 0x2e, 0x1f, 0x60, 0x0d, 0x23, 0x3c, - 0x07, 0x1e, 0x41, 0x10, 0xdb, 0xd5, 0x0b, 0xff, 0xa5, 0xf6, 0xac, 0x9d, 0x54, 0x10, 0x2d, 0x30, - 0x0d, 0x2a, 0x37, 0x11, 0xf2, 0x7b, 0x2d, 0x30, 0x5f, 0x3d, 0xd7, 0x59, 0xfd, 0x25, 0x9c, 0xf7, - 0xac, 0x8f, 0xdc, 0xbf, 0xd5, 0xf3, 0xd7, 0xe6, 0xd3, 0xe0, 0xf3, 0x7f, 0x4b, 0xd7, 0x45, 0xec, - 0xf3, 0xe5, 0xe6, 0xfb, 0xf4, 0x03, 0x12, 0xea, 0x20, 0x57, 0x1e, 0xd9, 0x0f, 0x02, 0xda, 0x0d, - 0xe9, 0xf2, 0xaf, 0xb6, 0x98, 0x28, 0x69, 0x58, 0x0d, 0x44, 0x7f, 0xf7, 0x10, 0x00, 0x18, 0xd4, - 0xf5, 0x37, 0x2e, 0xfd, 0xff, 0x0e, 0x24, 0x0b, 0xe1, 0xcb, 0x16, 0xde, 0x91, 0xbb, 0xb0, 0x8a, - 0x14, 0x2b, 0xe4, 0x07, 0xfd, 0x04, 0x04, 0xe8, 0x1f, 0xf6, 0xc0, 0xb5, 0xf0, 0xe1, 0xe8, 0x45, - 0x3a, 0x0b, 0x1f, 0xf8, 0xe3, 0xda, 0xfa, 0xc0, 0x29, 0x47, 0x21, 0x0c, 0x4f, 0x3d, 0x11, 0x5d, - 0x52, 0x55, 0x35, 0x43, 0x0f, 0x0d, 0xfe, 0x15, 0x5b, 0x23, 0xf5, 0x24, 0x43, 0x05, 0x45, 0x19, - 0xe4, 0x22, 0x01, 0x12, 0x35, 0x30, 0x28, 0x47, 0x68, 0x29, 0x10, 0x1b, 0x9e, 0xb2, 0xc1, 0xc4, - 0x8a, 0x82, 0xd8, 0x88, 0x90, 0xeb, 0x01, 0xf2, 0x39, 0x2e, 0xf1, 0x38, 0x10, 0x81, 0xdd, 0x14, - 0x04, 0xfb, 0xdf, 0xf6, 0x0b, 0xf8, 0xf5, 0x11, 0xa4, 0x12, 0xac, 0xe9, 0xdf, 0xaa, 0x44, 0x41, - 0xf9, 0x27, 0x00, 0xdd, 0xc5, 0xfd, 0x1f, 0xdc, 0x38, 0x72, 0x40, 0xf9, 0x42, 0x05, 0xd5, 0x25, - 0xef, 0x08, 0xca, 0xa0, 0xb5, 0xe3, 0x94, 0xca, 0xad, 0x81, 0xed, 0xfd, 0x0d, 0x5b, 0x36, 0x3d, - 0x45, 0x2c, 0x01, 0x15, 0xa7, 0x8e, 0xc6, 0xc7, 0xa2, 0xfa, 0xac, 0xf3, 0xde, 0xcf, 0xba, 0xc4, - 0x4b, 0x2d, 0x0a, 0xfc, 0xec, 0xa0, 0xf8, 0x1f, 0x60, 0x2c, 0x60, 0x6b, 0x3e, 0x2b, 0xf4, 0x34, - 0x1f, 0x65, 0xe2, 0xd7, 0x5f, 0x0e, 0xd6, 0x05, 0x0a, 0xff, 0xbd, 0x13, 0xf4, 0xd8, 0x31, 0x37, - 0x27, 0xb2, 0x1c, 0x45, 0xf6, 0x14, 0x03, 0xec, 0x3b, 0x22, 0x60, 0x48, 0xf3, 0x24, 0xe8, 0xc6, - 0x26, 0x10, 0x0c, 0xe3, 0xb5, 0x08, 0x1a, 0xf5, 0xeb, 0x20, 0x1a, 0x01, 0x46, 0xf9, 0x21, 0xf7, - 0xa0, 0xe4, 0xb9, 0xd8, 0xe5, 0x71, 0x12, 0x5f, 0x7f, 0x3c, 0x46, 0x2d, 0x13, 0xf8, 0xb6, 0xff, - 0x27, 0xbb, 0x07, 0x26, 0xd7, 0x05, 0xfa, 0xaa, 0xce, 0xc5, 0xd6, 0xee, 0x0d, 0x16, 0xf9, 0x20, - 0x09, 0x47, 0x01, 0x10, 0x21, 0xda, 0x0c, 0x2b, 0x12, 0x1d, 0x3a, 0x22, 0x16, 0x2a, 0x1a, 0x0a, - 0x14, 0x21, 0xf9, 0x64, 0x32, 0x25, 0x7f, 0x3b, 0x3a, 0x49, 0x1b, 0xfc, 0xb3, 0xc1, 0x07, 0x9c, - 0xff, 0xfb, 0xe9, 0xf4, 0xdb, 0xfa, 0x4a, 0xb1, 0xba, 0xd9, 0xde, 0xc6, 0xc2, 0xe3, 0x4d, 0x04, - 0xc8, 0x29, 0xf9, 0xa5, 0xe4, 0x23, 0x0b, 0xc4, 0xc1, 0x0a, 0xaf, 0x99, 0xee, 0xbc, 0xd7, 0x48, - 0x0a, 0xbf, 0x0d, 0xf3, 0xdd, 0x1e, 0xf2, 0xba, 0xcf, 0xf6, 0x55, 0xff, 0x13, 0x2b, 0x1b, 0x23, - 0xf8, 0x35, 0x64, 0x70, 0xf2, 0x19, 0x2a, 0x13, 0x05, 0x0c, 0x81, 0x03, 0x6e, 0xe8, 0x2d, 0x5f, - 0x01, 0x15, 0xf2, 0x34, 0x5b, 0x34, 0x03, 0xfd, 0xfc, 0x02, 0xa9, 0xd3, 0xbb, 0x9e, 0xb8, 0x1d, - 0xd3, 0xb6, 0x26, 0x16, 0xf2, 0x3c, 0x58, 0x5a, 0x1e, 0xf4, 0xbe, 0x98, 0x9d, 0xa0, 0x55, 0x66, - 0x1d, 0x56, 0x0a, 0xe2, 0xfa, 0xb0, 0xba, 0xee, 0x03, 0xeb, 0x41, 0xf7, 0xf4, 0x42, 0x08, 0x1c, - 0x0f, 0xea, 0xf4, 0xf6, 0x14, 0x17, 0xef, 0x08, 0x36, 0xea, 0xe2, 0xff, 0x08, 0xe8, 0x06, 0xf0, - 0xfd, 0x08, 0x0a, 0xfc, 0x42, 0xde, 0x42, 0x42, 0x09, 0x2f, 0x3a, 0xf6, 0xfa, 0xda, 0x05, 0x04, - 0x01, 0xf3, 0xf8, 0x1a, 0x2f, 0x2f, 0x30, 0x37, 0xe6, 0xf0, 0x25, 0x03, 0xe1, 0x49, 0x44, 0x28, - 0xfc, 0xcf, 0xce, 0xf0, 0x98, 0x81, 0x04, 0xfc, 0xdd, 0xef, 0x20, 0xf5, 0x0a, 0xf6, 0xcf, 0xf1, - 0xda, 0x0d, 0xf4, 0x04, 0x17, 0xfe, 0x11, 0xff, 0xc2, 0xc0, 0xff, 0xff, 0x8d, 0x56, 0xff, 0xff, - 0x3e, 0x1a, 0x00, 0x00, 0x10, 0xdb, 0xff, 0xff, 0x0f, 0xf4, 0xfe, 0xff, 0xc6, 0x89, 0xff, 0xff, - 0x22, 0xe9, 0x00, 0x00, 0x15, 0x34, 0x00, 0x00, 0xda, 0x4d, 0x00, 0x00, 0x15, 0x93, 0xff, 0xff, - 0x54, 0xde, 0x00, 0x00, 0x79, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x08, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x10, 0x08, 0x00, 0x00, 0x03, 0x00, 0x06, 0x00, 0x06, 0x00, 0x0c, 0x00, - 0x03, 0x00, 0x02, 0x00, 0x02, 0x00, 0x12, 0x00, 0x3d, 0x08, 0x7e, 0x3c, 0x80, 0xff, 0xff, 0xff, - 0x77, 0x36, 0x2b, 0x3d, 0x80, 0xff, 0xff, 0xff, 0x03, 0x03, 0x02, 0x02, 0x01, 0x01, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, - 0x98, 0x00, 0x00, 0x00, 0x30, 0x08, 0x00, 0x00, 0x82, 0x18, 0x7f, 0x3c, 0x20, 0x98, 0x31, 0x3c, - 0x60, 0x7d, 0x25, 0x3c, 0xb5, 0x9a, 0x0a, 0x3c, 0x41, 0xf4, 0x5b, 0x3c, 0x64, 0xaa, 0x09, 0x3c, - 0xc9, 0xee, 0x14, 0x3c, 0xfd, 0x49, 0x1f, 0x3c, 0xf0, 0xb7, 0xf4, 0x3b, 0x49, 0x12, 0x15, 0x3c, - 0xe2, 0x4e, 0x06, 0x3c, 0xa8, 0x85, 0x2b, 0x3c, 0x8c, 0xe0, 0x0b, 0x3c, 0x5d, 0x6b, 0x37, 0x3c, - 0x23, 0xc5, 0x19, 0x3c, 0x5d, 0xc7, 0x21, 0x3c, 0x5b, 0x3b, 0x2e, 0x3c, 0xff, 0x23, 0x43, 0x3c, - 0xf0, 0xf8, 0xf0, 0xdb, 0xed, 0xf0, 0xcd, 0xd1, 0x0e, 0xe5, 0xe5, 0x09, 0x09, 0xdc, 0xe5, 0x24, - 0xf4, 0x07, 0x0a, 0x1c, 0x23, 0xc4, 0xfc, 0x13, 0xd5, 0xa1, 0xf4, 0xee, 0xdd, 0x04, 0x2b, 0xda, - 0xd2, 0xba, 0xe0, 0x00, 0x05, 0xda, 0xb5, 0x47, 0x7f, 0x0b, 0xd8, 0xd2, 0xcd, 0x00, 0xf3, 0x00, - 0x02, 0xe2, 0xf4, 0x00, 0xd9, 0xde, 0xfc, 0x00, 0x42, 0xe7, 0xf4, 0xe2, 0xf3, 0x0d, 0x18, 0x0e, - 0x07, 0x03, 0x06, 0x17, 0xdf, 0x2c, 0x1a, 0x03, 0x02, 0x0c, 0x13, 0xf0, 0xca, 0xb8, 0xf9, 0x00, - 0x2f, 0xf1, 0xfa, 0x00, 0x97, 0xf3, 0x04, 0xcf, 0xe5, 0xf3, 0xdf, 0x33, 0x18, 0xfa, 0xdf, 0xc7, - 0xe0, 0xd7, 0xe5, 0x06, 0xe3, 0xd8, 0x1e, 0x2d, 0x10, 0xf4, 0x13, 0x0d, 0x0c, 0x0d, 0x0b, 0x0a, - 0x3e, 0x22, 0x24, 0x11, 0x09, 0x12, 0xcf, 0x1f, 0x17, 0xfd, 0xfe, 0xec, 0x1f, 0xf3, 0x70, 0x29, - 0x0b, 0x35, 0xf4, 0xe5, 0xe9, 0xc8, 0xc8, 0x04, 0xef, 0x1d, 0x02, 0x35, 0x79, 0xfa, 0x38, 0x47, - 0x1b, 0x23, 0xe7, 0xbb, 0x9a, 0x93, 0xe4, 0x4d, 0xdd, 0x27, 0x3e, 0xcb, 0x11, 0xef, 0xfe, 0xfd, - 0x3a, 0x0a, 0xf5, 0x05, 0x0f, 0xfc, 0xdf, 0xcf, 0xee, 0x19, 0xea, 0x93, 0xed, 0xde, 0xca, 0xe8, - 0x2f, 0xd0, 0xf1, 0xfe, 0xa8, 0x40, 0x20, 0xc6, 0xd8, 0x0b, 0xdc, 0xe5, 0xed, 0xca, 0x1d, 0x3a, - 0x00, 0xf1, 0xcd, 0x0f, 0xe4, 0xde, 0x6e, 0x7f, 0x1b, 0x08, 0x6c, 0x29, 0xde, 0xf5, 0xd7, 0xf9, - 0xf7, 0xf6, 0xd3, 0xe8, 0x11, 0xcf, 0xdc, 0x14, 0x28, 0x61, 0x03, 0x22, 0x4d, 0x11, 0x05, 0x28, - 0x4d, 0xb7, 0x93, 0xf6, 0x81, 0xbf, 0xd0, 0xb6, 0xeb, 0x18, 0x60, 0x54, 0xf8, 0x54, 0x1b, 0xe5, - 0x14, 0xe2, 0xd6, 0x17, 0xf4, 0xea, 0x0d, 0x25, 0x15, 0xcd, 0xe1, 0xf6, 0x03, 0x9d, 0x97, 0x2b, - 0xf5, 0xfc, 0x05, 0xf7, 0xcb, 0xee, 0x21, 0x08, 0xd6, 0xbe, 0xae, 0xec, 0xc1, 0xde, 0xf7, 0xcb, - 0x29, 0xe3, 0xbb, 0x15, 0xef, 0xda, 0x92, 0xb2, 0x0a, 0xf9, 0xe0, 0x21, 0x1b, 0xee, 0x04, 0xd2, - 0xda, 0xda, 0x0d, 0xd7, 0x99, 0xc4, 0x11, 0xf3, 0x1d, 0x10, 0x1b, 0x62, 0xbd, 0x36, 0x33, 0x0d, - 0xec, 0xed, 0xf3, 0x0c, 0x2f, 0x0c, 0x53, 0x59, 0xe6, 0x29, 0x28, 0xd8, 0xd1, 0xfe, 0xd6, 0xad, - 0xcd, 0x14, 0xc5, 0xef, 0xfb, 0xd3, 0xcf, 0xbc, 0xf1, 0xdb, 0xe5, 0x00, 0x00, 0xc9, 0x39, 0xef, - 0x24, 0x0e, 0x13, 0xed, 0x20, 0x05, 0xb1, 0x81, 0xff, 0xcc, 0xa7, 0xac, 0x01, 0xe2, 0x06, 0xc1, - 0x0a, 0x16, 0x1c, 0x6b, 0x7e, 0xe8, 0xff, 0x05, 0xb1, 0xf5, 0x5c, 0xf8, 0xc6, 0xd4, 0xab, 0x15, - 0x01, 0xc7, 0xf6, 0xbb, 0xc4, 0xe3, 0xdd, 0xf5, 0x0d, 0xf8, 0xe7, 0xde, 0xde, 0x17, 0x44, 0x2c, - 0xfd, 0x28, 0xe9, 0xb0, 0xd2, 0x29, 0x07, 0xd8, 0xca, 0xe9, 0xfc, 0xdd, 0xf3, 0x33, 0x46, 0xfd, - 0x7e, 0x28, 0x29, 0x0c, 0xdf, 0x40, 0x3e, 0x10, 0x5b, 0x19, 0x2a, 0xfe, 0xb5, 0xc4, 0x0f, 0xe0, - 0xeb, 0x29, 0x3a, 0x45, 0x20, 0xfc, 0x0f, 0x47, 0x41, 0x49, 0x30, 0x2e, 0x0b, 0xfe, 0xe5, 0x18, - 0x08, 0x06, 0x03, 0xec, 0xd6, 0x04, 0xeb, 0x04, 0xf5, 0xfd, 0x02, 0x2f, 0x32, 0x04, 0x0b, 0x14, - 0xf2, 0xe7, 0xfe, 0x31, 0x2c, 0xf3, 0xef, 0xd5, 0x0b, 0x1f, 0xe1, 0x1b, 0xdb, 0xf9, 0x12, 0x05, - 0x33, 0xbc, 0xd7, 0x31, 0x38, 0x07, 0xfd, 0xed, 0xcf, 0xd0, 0xa8, 0x11, 0x35, 0xe1, 0x12, 0x05, - 0xd8, 0xf4, 0xe9, 0x0c, 0x19, 0x1a, 0xea, 0x19, 0x52, 0x11, 0x38, 0x2c, 0xe1, 0x1f, 0xec, 0xed, - 0x35, 0x2e, 0xac, 0xe9, 0x12, 0xeb, 0xf1, 0x11, 0x0f, 0x6b, 0x75, 0x25, 0x7f, 0x61, 0xcd, 0x02, - 0x2d, 0xe6, 0x1f, 0x0f, 0xda, 0x1e, 0x03, 0xd3, 0xe8, 0xf6, 0x23, 0xdc, 0xd5, 0x39, 0x06, 0xe4, - 0x48, 0x1e, 0xe9, 0x06, 0xfd, 0xf8, 0xec, 0x0a, 0x2e, 0x0c, 0xff, 0x29, 0xd4, 0xd7, 0xd0, 0x2a, - 0xfd, 0xb5, 0x01, 0xdc, 0xf0, 0x4f, 0x37, 0xec, 0x3a, 0x17, 0xf4, 0x05, 0xd7, 0xc1, 0xbe, 0xc3, - 0xc6, 0xda, 0x4f, 0xc2, 0xcc, 0x51, 0x27, 0x43, 0xfb, 0xf1, 0x1e, 0xb1, 0x8f, 0xa3, 0xef, 0x04, - 0xf4, 0xe6, 0xea, 0x08, 0xfa, 0xa6, 0x0d, 0x0f, 0x47, 0x8a, 0xe2, 0x7f, 0xdf, 0x0e, 0x28, 0xfe, - 0xf2, 0xf5, 0x5d, 0xf3, 0xc3, 0x16, 0xf9, 0x20, 0xc9, 0xc8, 0xe6, 0x51, 0x1a, 0x9d, 0xf5, 0x6a, - 0x04, 0x05, 0xf6, 0x08, 0x18, 0x2a, 0x89, 0x69, 0x52, 0x7e, 0x1b, 0x10, 0x15, 0x3b, 0xd0, 0x86, - 0xb6, 0xae, 0xbe, 0xd6, 0x09, 0x5a, 0x5e, 0xcf, 0x84, 0x3c, 0xfe, 0xb9, 0x64, 0x40, 0x02, 0xd2, - 0x21, 0x68, 0xed, 0xd9, 0x0a, 0xea, 0xd8, 0x92, 0x96, 0xa7, 0xed, 0x05, 0xd9, 0x1e, 0x23, 0x01, - 0xf6, 0x57, 0xfa, 0xff, 0x04, 0x03, 0x28, 0xcb, 0xae, 0xb1, 0xb6, 0xf3, 0xb5, 0x38, 0xef, 0xcd, - 0xce, 0xfc, 0x02, 0x31, 0xf0, 0xe5, 0xea, 0x9a, 0xba, 0x34, 0x20, 0xf2, 0xca, 0x1c, 0xd0, 0xeb, - 0x34, 0x11, 0x07, 0xfe, 0x43, 0xac, 0x81, 0x06, 0xfa, 0xe6, 0x1f, 0x39, 0x2d, 0x35, 0x05, 0x52, - 0x13, 0x20, 0xd9, 0xd8, 0xfc, 0x0f, 0x10, 0x2d, 0x05, 0x0c, 0x6d, 0x1a, 0xf8, 0xcb, 0xf6, 0xf1, - 0x2e, 0x37, 0xd9, 0xf4, 0x3d, 0xeb, 0xf5, 0xfd, 0x02, 0xe9, 0xe9, 0xb7, 0x93, 0x08, 0xde, 0x1d, - 0x21, 0x1a, 0xe2, 0xe1, 0xf6, 0x31, 0x12, 0x04, 0xfd, 0xed, 0xee, 0x15, 0x2c, 0xd8, 0xd5, 0x0f, - 0x42, 0x17, 0x42, 0x1f, 0xf3, 0xff, 0x0c, 0x30, 0xda, 0xfd, 0x45, 0x14, 0x0d, 0xdb, 0x03, 0xbb, - 0x18, 0x1a, 0xfd, 0x07, 0x3f, 0x47, 0x39, 0x3a, 0x38, 0x0a, 0xd7, 0xf5, 0x33, 0xdf, 0xf3, 0xee, - 0x30, 0xfa, 0xef, 0x34, 0x35, 0x29, 0x17, 0xfc, 0x37, 0xdd, 0xfa, 0x04, 0xce, 0x56, 0x49, 0x01, - 0x7f, 0x78, 0x2f, 0x14, 0x2b, 0x2a, 0x01, 0x26, 0x2b, 0x3b, 0x01, 0x39, 0x11, 0x14, 0x06, 0x54, - 0x16, 0xbb, 0xd7, 0x38, 0xd4, 0xf8, 0xd9, 0xdf, 0xef, 0xd0, 0x11, 0x13, 0xc7, 0xf8, 0x01, 0xf9, - 0xf3, 0x13, 0x1c, 0xe5, 0xe5, 0xe9, 0x37, 0x2b, 0x01, 0x53, 0x55, 0x32, 0x07, 0xf9, 0xf1, 0x14, - 0x38, 0x04, 0xfc, 0x02, 0xe3, 0xfc, 0xf5, 0xf2, 0x04, 0x40, 0x0a, 0x13, 0x32, 0xee, 0xf1, 0x1d, - 0xa9, 0xfd, 0xf7, 0xc9, 0xe9, 0x03, 0xd2, 0xc4, 0xed, 0xf2, 0xeb, 0x47, 0xe6, 0x47, 0xee, 0x10, - 0xcf, 0xa5, 0x34, 0xf1, 0xf0, 0xea, 0xe9, 0xc6, 0xaa, 0x28, 0x9f, 0xff, 0x0a, 0xf3, 0xf1, 0xca, - 0xdd, 0x32, 0xe1, 0x35, 0xb3, 0x0f, 0xe5, 0xff, 0x46, 0x38, 0x1a, 0x46, 0x00, 0x3b, 0xb9, 0xd8, - 0xdc, 0x04, 0xff, 0x01, 0x26, 0x25, 0xe9, 0xaf, 0x28, 0x0a, 0xc5, 0xf5, 0x3e, 0x41, 0x2d, 0x46, - 0x2c, 0x67, 0x05, 0xda, 0x30, 0x14, 0x0f, 0x03, 0x05, 0x0f, 0x06, 0x1b, 0xe1, 0x8b, 0xdf, 0xa3, - 0xf7, 0xb9, 0xda, 0x0b, 0x30, 0x06, 0x3f, 0xf8, 0x6e, 0x7f, 0xfe, 0x1a, 0x2f, 0x18, 0xb0, 0xd2, - 0xa8, 0xc7, 0xc1, 0xe7, 0xdd, 0x06, 0x0c, 0x51, 0x2f, 0x2f, 0x2e, 0x08, 0xf3, 0x03, 0xc6, 0xc6, - 0xb6, 0xa3, 0x9c, 0xdc, 0x9e, 0xf2, 0x18, 0x11, 0x03, 0xf1, 0xb7, 0x31, 0xdc, 0xd4, 0x34, 0x96, - 0xab, 0xba, 0x9a, 0xde, 0xe4, 0xd0, 0xc7, 0x06, 0x40, 0xfe, 0x44, 0x29, 0xe1, 0x0a, 0xde, 0xce, - 0xeb, 0xbc, 0xe3, 0xc4, 0x00, 0xd1, 0xcd, 0x1d, 0xfe, 0x0b, 0x92, 0x02, 0xca, 0xe7, 0x0b, 0xc6, - 0xbe, 0xcb, 0xe6, 0x01, 0xde, 0x44, 0xf3, 0x0f, 0xfe, 0xf6, 0x02, 0x0c, 0xf9, 0x10, 0xe7, 0x19, - 0x0f, 0xd8, 0x3f, 0x18, 0x81, 0x42, 0x0f, 0x07, 0x0f, 0xf5, 0xfb, 0xea, 0xf7, 0x98, 0x22, 0xfb, - 0x32, 0xe0, 0xa4, 0x13, 0xcc, 0xff, 0x33, 0x0c, 0xd4, 0x47, 0xf0, 0xe5, 0xf0, 0x23, 0xe5, 0xda, - 0x1d, 0x40, 0xd0, 0x2c, 0xe5, 0x1e, 0x02, 0xfe, 0xb7, 0xf0, 0x1f, 0x1c, 0x1c, 0xfc, 0x1f, 0x2c, - 0x38, 0x47, 0x13, 0x2e, 0xe0, 0xc2, 0x37, 0x29, 0x1c, 0x1b, 0x81, 0xf5, 0x13, 0xca, 0x0b, 0x46, - 0x58, 0x24, 0x3a, 0xfd, 0x6b, 0xc9, 0xbc, 0x1d, 0x23, 0x08, 0x17, 0x48, 0x20, 0x5b, 0x56, 0xe7, - 0xc4, 0xf0, 0xdf, 0xe3, 0x1d, 0x1d, 0x50, 0x20, 0x51, 0xff, 0xf4, 0x44, 0x4d, 0x1a, 0x0b, 0x30, - 0x2d, 0x38, 0x46, 0x10, 0xcf, 0x01, 0x09, 0x15, 0x3d, 0x56, 0x28, 0xdb, 0xe8, 0xd9, 0xb7, 0xad, - 0x1a, 0xed, 0xce, 0xcf, 0xf3, 0x2b, 0x5a, 0x0b, 0xe6, 0x36, 0xc2, 0x9b, 0x51, 0x1e, 0x3e, 0xce, - 0xf5, 0x52, 0xd1, 0xca, 0xe7, 0xd0, 0xf0, 0xf4, 0x0c, 0xed, 0x21, 0x09, 0xdf, 0x91, 0xdb, 0xef, - 0x09, 0xf7, 0x14, 0x48, 0xd6, 0xe4, 0x2f, 0x05, 0xd8, 0x06, 0xbc, 0x81, 0xbf, 0x2a, 0xc7, 0xd8, - 0x07, 0xe9, 0x36, 0xe9, 0xcb, 0x08, 0x31, 0x0e, 0x01, 0x3e, 0x17, 0xd8, 0x16, 0xf3, 0xd3, 0xef, - 0xf8, 0x37, 0xdd, 0xdb, 0x49, 0x09, 0xe2, 0xe6, 0x02, 0xff, 0xeb, 0xd1, 0x0d, 0x90, 0xe7, 0x42, - 0x2d, 0xf4, 0xc1, 0xb7, 0x04, 0x05, 0x3f, 0x2e, 0x3a, 0x1d, 0xd9, 0x4a, 0x06, 0xff, 0xf9, 0xe4, - 0x0b, 0x41, 0x35, 0x37, 0x0a, 0x1b, 0x11, 0xca, 0xcb, 0xed, 0xfc, 0xe4, 0xcb, 0x24, 0xed, 0xcb, - 0x30, 0x02, 0xd3, 0x36, 0x2b, 0xe9, 0xee, 0x19, 0xb0, 0xf9, 0xaa, 0x17, 0x61, 0x0e, 0x25, 0x2f, - 0x2c, 0xf0, 0x13, 0xf7, 0x1f, 0xc8, 0xee, 0xf2, 0xd4, 0xb5, 0xde, 0x47, 0x43, 0x2b, 0x32, 0x03, - 0x39, 0xee, 0x44, 0x01, 0xd8, 0xb5, 0xf2, 0x23, 0x2b, 0xfc, 0x09, 0xb8, 0x1f, 0x3a, 0x2e, 0x38, - 0x0d, 0x36, 0x07, 0xe4, 0x30, 0x0b, 0xf3, 0x06, 0xab, 0x14, 0x45, 0xd3, 0xfb, 0x10, 0x4d, 0x26, - 0xc8, 0xff, 0xbb, 0xb9, 0x01, 0xe0, 0x2f, 0xd6, 0x37, 0xf9, 0x23, 0x13, 0x7f, 0xf6, 0xc3, 0xc7, - 0x00, 0x43, 0x5c, 0xd8, 0x02, 0x2a, 0x02, 0x2f, 0xf8, 0x0f, 0xf5, 0xf9, 0x2a, 0xe8, 0xa6, 0xe1, - 0xfc, 0xeb, 0xd8, 0xde, 0xf4, 0xdd, 0x23, 0x17, 0x06, 0x2d, 0x0f, 0xf2, 0x07, 0x44, 0xfb, 0xe0, - 0xc5, 0x33, 0x4a, 0x21, 0x22, 0xcc, 0xea, 0x2d, 0xa9, 0xbd, 0x2a, 0x50, 0x19, 0x19, 0xc6, 0xe2, - 0x41, 0xbd, 0x9b, 0xd3, 0x14, 0x3c, 0x27, 0x40, 0x09, 0xb6, 0xf2, 0x93, 0x2d, 0x1e, 0x02, 0x1b, - 0x0b, 0xce, 0x3b, 0x0d, 0x01, 0xc5, 0x02, 0x3f, 0xa1, 0xc9, 0xf4, 0x92, 0xd6, 0xe3, 0x48, 0x1c, - 0xfa, 0x41, 0xff, 0x0e, 0x12, 0xd7, 0xef, 0xf0, 0xe7, 0xc4, 0x08, 0xf3, 0x99, 0x2b, 0x04, 0xe4, - 0xce, 0x2f, 0xd2, 0xd5, 0xe3, 0xbb, 0xfd, 0xf2, 0x02, 0x18, 0x23, 0x55, 0xee, 0xe5, 0xdb, 0xee, - 0xd6, 0xc9, 0x1e, 0xea, 0xa6, 0xec, 0xe5, 0xf3, 0x0d, 0xf9, 0xd5, 0x04, 0x2a, 0xf9, 0xc7, 0x04, - 0xf6, 0x00, 0xd7, 0xec, 0xe8, 0xcf, 0xb2, 0xe5, 0x81, 0x04, 0x31, 0xfe, 0xe8, 0xda, 0xba, 0xb6, - 0x69, 0xd4, 0xdd, 0x46, 0x50, 0x2e, 0x23, 0x06, 0xed, 0x09, 0x07, 0x3e, 0x03, 0x1e, 0xfe, 0xea, - 0xb9, 0xfd, 0xf7, 0xd5, 0xd6, 0xf9, 0xe3, 0x22, 0xa4, 0xd9, 0xf9, 0xe6, 0xc2, 0x18, 0x3f, 0x1f, - 0x06, 0x11, 0xea, 0xc5, 0x05, 0xdc, 0x08, 0x2f, 0x17, 0x3f, 0x81, 0x8a, 0xac, 0xd7, 0xdb, 0xeb, - 0x22, 0x1f, 0xdd, 0xee, 0xff, 0xf1, 0xbd, 0xe6, 0xd1, 0x46, 0x13, 0xf4, 0x8a, 0x2f, 0x0c, 0xa7, - 0xfb, 0xb1, 0x57, 0xf7, 0xdc, 0xc4, 0xcd, 0x00, 0xd6, 0xdd, 0x15, 0x1b, 0x22, 0x03, 0x0d, 0x1a, - 0xfb, 0xfe, 0x4c, 0x25, 0x1a, 0xc0, 0xfa, 0xa7, 0xbc, 0x89, 0x5b, 0x05, 0xfb, 0x1f, 0x0c, 0x03, - 0x0f, 0xce, 0xc3, 0xb3, 0x19, 0x11, 0x07, 0x0d, 0xf6, 0x27, 0xfb, 0x08, 0x97, 0xff, 0x18, 0xc1, - 0x21, 0x14, 0x0d, 0xdc, 0x14, 0x96, 0xcc, 0x38, 0xca, 0xd5, 0xf4, 0x13, 0x1b, 0xec, 0xc2, 0x13, - 0xf7, 0xb5, 0xf9, 0x0b, 0x1b, 0xbf, 0xea, 0xe6, 0xe6, 0x3c, 0xc2, 0xf8, 0x5d, 0x2d, 0x0f, 0xe2, - 0x3f, 0xd0, 0xf1, 0xfb, 0xf6, 0x15, 0xbc, 0xe2, 0x35, 0xc5, 0x2a, 0x44, 0xc3, 0xfe, 0xff, 0xf9, - 0x07, 0xa9, 0x0a, 0xe7, 0xd2, 0xf1, 0xed, 0xdf, 0xef, 0x22, 0xf2, 0x4d, 0x43, 0xda, 0xad, 0x81, - 0xb8, 0xcd, 0x3a, 0x28, 0x05, 0xb0, 0x14, 0xda, 0xd2, 0xe8, 0x33, 0xdf, 0xfa, 0xfa, 0x2a, 0x40, - 0x0c, 0xd6, 0xce, 0xec, 0xff, 0x38, 0x1d, 0x54, 0x0b, 0x05, 0x05, 0xdb, 0x2d, 0xd5, 0x94, 0xf9, - 0xc4, 0xc9, 0x15, 0x26, 0x38, 0x5e, 0x0d, 0xa0, 0x08, 0xde, 0xe4, 0x15, 0x05, 0x17, 0xd1, 0x8b, - 0xd5, 0xf0, 0xfe, 0xfa, 0x2c, 0x0f, 0x24, 0x39, 0x29, 0xf8, 0x1d, 0xc4, 0xc0, 0xdd, 0xcc, 0xd4, - 0xf0, 0xf8, 0x1a, 0xd2, 0xed, 0xf3, 0xbb, 0xb7, 0xae, 0x16, 0xbd, 0xc1, 0xed, 0x11, 0x36, 0x2e, - 0x0c, 0x2d, 0xe0, 0xfd, 0xd9, 0x0b, 0x15, 0xf4, 0xb6, 0xc6, 0xca, 0xdf, 0x09, 0x46, 0xeb, 0x58, - 0x35, 0xd0, 0xf1, 0xdb, 0x13, 0xe8, 0xe0, 0xe4, 0xf6, 0x15, 0xde, 0x40, 0xab, 0x19, 0x0f, 0x17, - 0x08, 0xf4, 0x15, 0xcf, 0x2e, 0x44, 0xff, 0x04, 0x43, 0xed, 0x05, 0xca, 0x2c, 0x37, 0x1b, 0xd2, - 0x11, 0x0d, 0xd8, 0xde, 0xd6, 0x0d, 0xc9, 0x19, 0xbe, 0xec, 0xe4, 0xce, 0xcb, 0x14, 0x03, 0xc3, - 0xde, 0xef, 0x2a, 0x1e, 0x62, 0xf2, 0xd5, 0x3b, 0xda, 0x81, 0x32, 0x13, 0x42, 0xfb, 0xf3, 0xfb, - 0x08, 0xf5, 0xeb, 0x0a, 0x12, 0xdc, 0xf4, 0xf2, 0x02, 0x24, 0x55, 0x38, 0x13, 0x3d, 0x0c, 0x17, - 0xec, 0x0b, 0x7f, 0x1c, 0xe4, 0xcb, 0x5a, 0x24, 0xfd, 0x17, 0x1e, 0x04, 0x05, 0x0b, 0xf3, 0xde, - 0xc2, 0xfd, 0xdf, 0xff, 0x67, 0x27, 0xcd, 0xcc, 0xe2, 0xc2, 0xd3, 0xb5, 0x30, 0x2a, 0x20, 0x36, - 0xfa, 0x05, 0x37, 0xf2, 0x8b, 0xe3, 0x00, 0x11, 0xf7, 0x07, 0xb7, 0x97, 0x25, 0xf4, 0x08, 0x63, - 0xdd, 0xbc, 0xd0, 0xde, 0x1d, 0xf4, 0xdb, 0xfc, 0xdd, 0xe3, 0xed, 0x09, 0xed, 0x13, 0x05, 0xf7, - 0x1e, 0xe6, 0xeb, 0x05, 0xfd, 0xca, 0xfa, 0xf3, 0x12, 0xd2, 0xea, 0x10, 0x00, 0xe3, 0x1d, 0x47, - 0xa1, 0x46, 0x23, 0xda, 0x33, 0x24, 0xb8, 0x18, 0xf8, 0xaf, 0x06, 0x15, 0x1b, 0x1f, 0x22, 0xa2, - 0xc8, 0xd7, 0x0b, 0xfa, 0xe9, 0x04, 0xf8, 0x18, 0x9a, 0x6d, 0xfe, 0xff, 0x9c, 0x2e, 0x00, 0x00, - 0xa5, 0x70, 0xfe, 0xff, 0xe6, 0x0f, 0x00, 0x00, 0x15, 0x39, 0x01, 0x00, 0x21, 0x5f, 0xff, 0xff, - 0x49, 0x7e, 0xff, 0xff, 0x53, 0x77, 0x02, 0x00, 0x17, 0xb3, 0xff, 0xff, 0xaf, 0x86, 0xfd, 0xff, - 0x7b, 0xbf, 0x01, 0x00, 0x1c, 0xcf, 0xff, 0xff, 0x6c, 0x79, 0x00, 0x00, 0xc5, 0x59, 0xfe, 0xff, - 0xe7, 0xea, 0xfd, 0xff, 0xce, 0xd9, 0xfe, 0xff, 0x1e, 0x62, 0xff, 0xff, 0x51, 0x70, 0xff, 0xff, - 0x01, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x10, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x02, 0x00, 0x02, 0x00, 0x12, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x12, 0x00, - 0x77, 0x36, 0x2b, 0x3d, 0x80, 0xff, 0xff, 0xff, 0xe3, 0xde, 0xae, 0x3c, 0x80, 0xff, 0xff, 0xff, - 0x02, 0x00, 0x00, 0x00, 0x48, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x08, 0x00, 0x00, - 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x12, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x0a, 0x00, - 0xe3, 0xde, 0xae, 0x3c, 0x80, 0xff, 0xff, 0xff, 0xfa, 0x8f, 0xcf, 0x3d, 0x21, 0x00, 0x00, 0x00, - 0x40, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x81, 0x22, 0x59, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2a, 0x27, 0xbd, 0x3a, 0x4d, 0x9b, 0xa4, 0x9d, - 0x02, 0xee, 0x2f, 0xe4, 0xc7, 0xef, 0xc2, 0x3b, 0x3b, 0xb3, 0xb8, 0xc1, 0xe9, 0x3d, 0x37, 0xd1, - 0xd8, 0x52, 0x30, 0x28, 0x02, 0xbc, 0xd2, 0xd1, 0xb0, 0xb1, 0xb9, 0x34, 0x3f, 0x48, 0x4e, 0xc6, - 0xe8, 0xc1, 0xd1, 0x3d, 0xab, 0xe2, 0xb0, 0x10, 0x41, 0xf9, 0xbf, 0xbe, 0x42, 0xd4, 0xc1, 0x2b, - 0x50, 0xef, 0xbe, 0x1c, 0xab, 0xcc, 0xb4, 0xf4, 0x06, 0xa9, 0xb2, 0x56, 0xf5, 0x26, 0x9f, 0x3b, - 0xcf, 0x3a, 0xc3, 0x38, 0x39, 0xfd, 0x47, 0xb1, 0xde, 0x0f, 0xd8, 0x43, 0xd4, 0xb6, 0x3b, 0x81, - 0xfc, 0x16, 0xe8, 0xbd, 0xb5, 0xdb, 0x0e, 0x5b, 0x11, 0xeb, 0x97, 0xba, 0x3a, 0xeb, 0x31, 0x2b, - 0xeb, 0x54, 0xa5, 0xc2, 0xeb, 0xb9, 0x9c, 0x52, 0xb0, 0x09, 0xce, 0x20, 0xb8, 0x43, 0xc0, 0xd4, - 0x5c, 0xa9, 0x2a, 0x1f, 0x0c, 0x90, 0x8f, 0x25, 0xe3, 0xa5, 0xcc, 0xbd, 0xfc, 0x2b, 0x38, 0xbe, - 0x3c, 0x41, 0xf2, 0x67, 0xe0, 0x99, 0x26, 0xe2, 0x45, 0xc8, 0x0b, 0xc6, 0xf5, 0x2c, 0x21, 0xb2, - 0x4f, 0x51, 0x9d, 0x14, 0xcf, 0x37, 0xaa, 0xf4, 0xdd, 0xa0, 0x39, 0xa5, 0xcc, 0xb7, 0x9a, 0xdd, - 0x53, 0xfc, 0x2a, 0xae, 0x15, 0x40, 0xb1, 0xf5, 0x45, 0x1a, 0xba, 0x32, 0x00, 0x00, 0x00, 0x00, - 0xd9, 0x88, 0xff, 0xff, 0x40, 0x6e, 0xff, 0xff, 0x6d, 0xb5, 0xff, 0xff, 0xef, 0x68, 0xff, 0xff, - 0x20, 0xc5, 0xff, 0xff, 0x14, 0x89, 0xff, 0xff, 0x12, 0x5b, 0xff, 0xff, 0x12, 0x9c, 0xff, 0xff, - 0x3b, 0xa3, 0xff, 0xff, 0x05, 0xa3, 0xff, 0xff, 0x03, 0x00, 0x01, 0x00, 0x30, 0x00, 0x00, 0x00, - 0x48, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x0a, 0x00, - 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x0a, 0x00, 0xfa, 0x8f, 0xcf, 0x3d, 0x21, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x80, 0x3b, 0x80, 0xff, 0xff, 0xff, }; - -#endif diff --git a/examples/mnist_cnn/mnist_cnn.h5 b/examples/mnist_cnn/mnist_cnn.h5 deleted file mode 100644 index 9aee050a27ede73f60151e580b282d6c2440c623..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 108960 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

+Ece4{&?JA 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 +#define MDL_BUF_LEN (8544) +#define LBUF_LEN (7992) +const uint8_t mdl_data[12896]={\ + 0x4d, 0x41, 0x49, 0x58, 0x02, 0x00, 0x01, 0x00, 0x01, 0x00, 0x06, 0x00, 0x60, 0x21, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x90, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x0c, 0x00, 0x00, + 0x03, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0x01, 0x00, 0x03, 0x00, 0x0d, 0x00, 0x0d, 0x00, 0x08, 0x00, + 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x03, 0x02, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x70, 0x01, 0x00, 0x00, + 0x03, 0xef, 0x88, 0x3d, 0xdf, 0xc6, 0xa5, 0x3e, 0xe4, 0x0a, 0x8d, 0x3e, 0xaa, 0xb1, 0xf3, 0x3e, + 0x54, 0x5f, 0xe0, 0x3e, 0x84, 0x49, 0x98, 0x3e, 0x89, 0x3c, 0x95, 0x3e, 0x91, 0xb7, 0xd6, 0x3e, + 0xff, 0xab, 0x94, 0x3e, 0xb9, 0x1e, 0x57, 0x3e, 0xc1, 0xc9, 0x69, 0x3f, 0x7b, 0xf1, 0x9c, 0x3f, + 0xf6, 0x11, 0x1c, 0xbe, 0xe3, 0xa2, 0xb7, 0x3e, 0x3c, 0x50, 0xe5, 0x3d, 0xfa, 0x6a, 0x05, 0x3f, + 0xb0, 0x0c, 0x55, 0x3d, 0x32, 0xf8, 0x71, 0xbf, 0x23, 0x1b, 0xa4, 0x3e, 0x10, 0x9f, 0x5e, 0x3f, + 0x0a, 0xf5, 0x43, 0x3f, 0xd6, 0x74, 0xa1, 0xbe, 0x6b, 0xbd, 0xd3, 0x3e, 0x16, 0x3a, 0x3d, 0x3f, + 0xf4, 0x58, 0xd8, 0xbf, 0x25, 0x07, 0x9c, 0xbf, 0x02, 0x6d, 0x11, 0xc0, 0xa3, 0xb2, 0x38, 0x3f, + 0x5a, 0xe3, 0x95, 0xbf, 0xbc, 0x5d, 0x0f, 0xc0, 0xb9, 0x8d, 0x8f, 0x3e, 0xa1, 0xe9, 0x0f, 0x3e, + 0x2b, 0x70, 0x34, 0x3f, 0x8e, 0xf2, 0x2a, 0xbf, 0xfe, 0xb4, 0x81, 0x3f, 0x0b, 0x5a, 0xb0, 0x3f, + 0xdf, 0x52, 0x1a, 0xbc, 0x6e, 0xf8, 0xfd, 0xbf, 0x68, 0x73, 0x6c, 0xbf, 0x3b, 0x0a, 0x9f, 0xbf, + 0x07, 0x55, 0x62, 0x3e, 0x92, 0xfd, 0x2a, 0x3f, 0x06, 0x2c, 0xd4, 0xbf, 0xa8, 0x71, 0xe5, 0x3a, + 0x5f, 0x19, 0x7f, 0x3f, 0x11, 0x9b, 0x2a, 0xbe, 0x0e, 0xee, 0x36, 0xbf, 0x4b, 0x31, 0x9c, 0xbf, + 0x45, 0x86, 0x16, 0x3f, 0x5b, 0x20, 0x06, 0xbe, 0xce, 0x54, 0x1e, 0xbf, 0xd6, 0x86, 0x15, 0xbf, + 0x0d, 0xd3, 0x57, 0xbf, 0x40, 0x50, 0xd7, 0xbf, 0xc0, 0xcb, 0x99, 0xbf, 0x47, 0x3d, 0x5c, 0x3f, + 0x57, 0xa1, 0xdf, 0x3e, 0x71, 0xd1, 0x67, 0xbf, 0x0a, 0x98, 0xf3, 0xbf, 0x63, 0xfd, 0x0c, 0xc0, + 0x69, 0xa9, 0x6b, 0x3e, 0x9a, 0xf0, 0x10, 0x3e, 0x42, 0x1f, 0x32, 0xbe, 0xcc, 0x6c, 0x39, 0x3f, + 0x4a, 0xe4, 0x1d, 0xbe, 0x95, 0xa2, 0xe2, 0x3e, 0x55, 0xd1, 0x10, 0x3f, 0x70, 0x19, 0xc7, 0x3e, + 0xf2, 0x0e, 0x1f, 0x3f, 0x89, 0xf5, 0x42, 0x3d, 0x6f, 0x9f, 0x54, 0xbd, 0xb3, 0xce, 0x90, 0x3e, + 0xd2, 0x86, 0xa9, 0xbe, 0x03, 0xf3, 0x51, 0xbe, 0xcc, 0xc9, 0xfb, 0x3e, 0xa0, 0x87, 0x34, 0x3d, + 0x46, 0x35, 0x23, 0x3f, 0x4b, 0xe7, 0x52, 0x3f, 0xc1, 0x25, 0x88, 0x3f, 0x81, 0x4b, 0xaf, 0xbe, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x40, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x0d, 0x00, 0x0d, 0x00, 0x08, 0x00, 0x03, 0x00, 0x06, 0x00, 0x06, 0x00, 0x0c, 0x00, + 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x03, 0x02, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0xd0, 0x0d, 0x00, 0x00, + 0x89, 0xf3, 0x53, 0xbc, 0x39, 0xc4, 0xb3, 0x3d, 0xf5, 0x5c, 0x32, 0x3e, 0x4d, 0x6e, 0x28, 0x3d, + 0x76, 0xa7, 0x85, 0xbb, 0x78, 0xa2, 0x18, 0xbe, 0xba, 0xac, 0xbb, 0xbd, 0xe5, 0x51, 0x3c, 0xbc, + 0x62, 0x72, 0x4d, 0xbd, 0x2c, 0x82, 0x16, 0x3e, 0x7b, 0xc5, 0x3c, 0x3e, 0x69, 0x72, 0x70, 0xbe, + 0x5a, 0x26, 0x7c, 0x3e, 0x18, 0xc8, 0xa8, 0x3d, 0x87, 0xc9, 0x6d, 0xbc, 0x31, 0x14, 0x2c, 0x3e, + 0x99, 0xf2, 0x67, 0x3e, 0xed, 0x14, 0x37, 0x3c, 0x30, 0xc1, 0x00, 0xbf, 0x28, 0x56, 0xbf, 0xbd, + 0x9b, 0x60, 0xda, 0xbd, 0x9e, 0xe9, 0xc7, 0x3d, 0xcd, 0x5f, 0x35, 0xbe, 0xb1, 0x3c, 0x51, 0xbd, + 0x63, 0xd0, 0x78, 0x3e, 0x6a, 0xc7, 0x11, 0xbe, 0xde, 0x43, 0xce, 0x3d, 0x0d, 0x57, 0x3f, 0xbe, + 0xc1, 0x32, 0x5b, 0xbc, 0x32, 0x83, 0x06, 0xbe, 0x09, 0x79, 0xde, 0xbd, 0x71, 0xd2, 0xc8, 0x3d, + 0x39, 0xfb, 0xbd, 0xbd, 0x5d, 0xe8, 0x13, 0x3e, 0xd2, 0x57, 0xd9, 0xbc, 0x1e, 0x70, 0x2f, 0xbe, + 0x01, 0x10, 0x27, 0x3f, 0x49, 0x19, 0xcd, 0xbd, 0xad, 0xea, 0xde, 0x3c, 0x03, 0x04, 0xb1, 0x3e, + 0xaf, 0xb9, 0xc9, 0x3d, 0xd6, 0x0f, 0xbe, 0x3c, 0x47, 0x27, 0xad, 0x3d, 0x6f, 0xf5, 0xbf, 0x3e, + 0x34, 0x09, 0x40, 0x3e, 0xad, 0x6b, 0xe6, 0x3e, 0x82, 0x98, 0x9c, 0x3e, 0xae, 0xbc, 0xda, 0x3e, + 0x48, 0x52, 0x17, 0x3f, 0x81, 0xb7, 0x3a, 0x3c, 0xe4, 0xb2, 0x82, 0x3e, 0xec, 0x9e, 0xaa, 0x3d, + 0xc0, 0x07, 0x3f, 0x3c, 0xa2, 0x5e, 0x97, 0x3e, 0xb6, 0x19, 0x50, 0x3f, 0x40, 0x70, 0x10, 0x3e, + 0xff, 0x0a, 0x93, 0x3e, 0xb3, 0x46, 0x7c, 0x3e, 0xf7, 0x62, 0xe8, 0x3e, 0x03, 0x79, 0x97, 0x3d, + 0x4b, 0x76, 0xd9, 0x3e, 0xb1, 0x49, 0x8c, 0x3e, 0x9a, 0x45, 0x2d, 0x3e, 0xe1, 0x27, 0xd2, 0xbd, + 0xb2, 0xf7, 0x17, 0xbe, 0xd9, 0x34, 0xdf, 0x3c, 0xaf, 0xa0, 0xd9, 0x3c, 0x98, 0x33, 0xb1, 0x3d, + 0x37, 0xc7, 0x04, 0x3e, 0x99, 0x95, 0x94, 0x3d, 0x5b, 0x88, 0x5e, 0xbd, 0xb5, 0x95, 0x63, 0xbc, + 0xd9, 0x3f, 0xe2, 0xbd, 0xfe, 0xa0, 0x61, 0x3d, 0xd5, 0xd0, 0xb8, 0x3d, 0x09, 0x8e, 0x04, 0xbe, + 0x8a, 0x85, 0xc1, 0x3d, 0xb4, 0x1c, 0xb5, 0xbd, 0xad, 0x64, 0x89, 0xbe, 0x86, 0xb7, 0x75, 0xbe, + 0xb6, 0x7e, 0xa5, 0xbc, 0xf3, 0x4c, 0xf2, 0xbd, 0xcf, 0xd8, 0x68, 0x3e, 0xfe, 0xf0, 0xf6, 0x3d, + 0xcb, 0x4c, 0x00, 0x3e, 0x9c, 0xa1, 0xe5, 0xbc, 0xb6, 0x13, 0x61, 0x3b, 0xf3, 0xd4, 0x05, 0x3e, + 0x92, 0xc4, 0x0e, 0xbd, 0xfd, 0xcb, 0xf2, 0xbd, 0x31, 0x60, 0x6e, 0x3d, 0xa0, 0x14, 0xcd, 0xbd, + 0xec, 0xa2, 0x42, 0x3d, 0xb2, 0x8a, 0xf8, 0xbd, 0x9d, 0xbc, 0x3f, 0xbe, 0x3e, 0xd2, 0x76, 0x3c, + 0xdb, 0xe9, 0x90, 0xbe, 0x55, 0xb5, 0xa2, 0xbd, 0x09, 0x76, 0x48, 0x3d, 0x09, 0x54, 0xa6, 0x3d, + 0xc5, 0xa8, 0x42, 0x3e, 0x76, 0x8b, 0xd9, 0x3d, 0x3a, 0xc3, 0xed, 0xbc, 0x75, 0x4f, 0xb5, 0x3b, + 0x08, 0xdc, 0xff, 0x3c, 0xef, 0xb8, 0xa8, 0xbe, 0x43, 0x96, 0xb1, 0xbe, 0xd1, 0xcd, 0x90, 0xbd, + 0xce, 0x15, 0x8f, 0xbe, 0xf5, 0x44, 0x1b, 0xbf, 0x34, 0x49, 0xad, 0xbe, 0x20, 0xe3, 0x09, 0xbd, + 0xf1, 0xde, 0xe8, 0xbc, 0xc7, 0xb5, 0x2d, 0xbd, 0x4f, 0xf8, 0x8f, 0x3e, 0xf2, 0x20, 0xbb, 0x39, + 0x51, 0xcf, 0x39, 0xbe, 0x7e, 0xf9, 0xbf, 0xbe, 0xd6, 0xb3, 0x35, 0xbe, 0xa4, 0xeb, 0x69, 0xbe, + 0x25, 0x96, 0x72, 0xbe, 0x50, 0x4d, 0x95, 0x3c, 0xd9, 0xfa, 0x17, 0xbd, 0x95, 0xd9, 0xcb, 0x3e, + 0x81, 0x53, 0x8c, 0x3e, 0x56, 0x13, 0x04, 0x3e, 0x55, 0x66, 0x9e, 0x3d, 0x5d, 0x42, 0xe3, 0xbd, + 0xf1, 0x77, 0x61, 0x3c, 0x5d, 0x55, 0x74, 0x3d, 0xdf, 0x61, 0x69, 0x3e, 0xf6, 0x04, 0xc8, 0x3d, + 0x1c, 0x98, 0x42, 0xbe, 0xb4, 0xc8, 0xd2, 0xbd, 0x48, 0x85, 0x56, 0x3e, 0x53, 0x0b, 0xbc, 0x3d, + 0x00, 0x76, 0x6f, 0xbd, 0xe2, 0xb2, 0xb2, 0x3d, 0xbb, 0x20, 0x49, 0x3d, 0xb9, 0x16, 0xd3, 0x3b, + 0xf3, 0x5a, 0xa4, 0x3e, 0xdc, 0x9a, 0x50, 0xbe, 0xc1, 0x97, 0x98, 0xbd, 0x5c, 0x28, 0x5c, 0x3d, + 0x84, 0xf8, 0x5f, 0xbd, 0x6c, 0xd2, 0x33, 0xbc, 0x5e, 0x95, 0x8e, 0xbd, 0xde, 0xc3, 0x8f, 0xbd, + 0x31, 0x2e, 0x1e, 0x3d, 0x62, 0xef, 0xc5, 0x3c, 0x9b, 0xe1, 0x04, 0x3d, 0xe2, 0xad, 0x03, 0xbe, + 0xa2, 0x80, 0x0f, 0x3e, 0x1b, 0xe4, 0xb7, 0xba, 0xc7, 0x03, 0x2d, 0x3c, 0xd8, 0xa1, 0xa1, 0xbc, + 0xcf, 0x4a, 0x12, 0xbd, 0x17, 0xcb, 0xe9, 0xbd, 0x60, 0xb8, 0xff, 0x3a, 0x08, 0xe9, 0x10, 0xbe, + 0x03, 0x9a, 0x73, 0xbd, 0xc3, 0xad, 0x86, 0xbd, 0x48, 0x69, 0x50, 0xbd, 0xfb, 0x73, 0xb3, 0x3c, + 0xae, 0xb2, 0xa9, 0xbd, 0x88, 0x3b, 0x84, 0xbe, 0xf9, 0x2b, 0xa3, 0xbc, 0x27, 0x45, 0x2c, 0x3e, + 0x0c, 0xfb, 0xfd, 0xbd, 0xca, 0x8a, 0x2b, 0xbe, 0x6b, 0xc7, 0xbb, 0x3c, 0x72, 0x3d, 0x1c, 0x3e, + 0x32, 0xbd, 0xa0, 0x3d, 0x0a, 0x7b, 0x5b, 0x3e, 0xc7, 0x0e, 0x72, 0x3d, 0x10, 0x26, 0x35, 0x3e, + 0x86, 0x11, 0xf5, 0x3d, 0xd6, 0x89, 0x1b, 0xbd, 0xfd, 0x98, 0xed, 0x3d, 0xcc, 0x47, 0x26, 0x3a, + 0x94, 0xfa, 0xa1, 0xbe, 0x0b, 0xa8, 0x89, 0xbe, 0x23, 0xa2, 0x75, 0xbe, 0xcd, 0x61, 0x77, 0xbe, + 0x28, 0x07, 0xee, 0xbe, 0x70, 0x67, 0xba, 0xbe, 0x2d, 0x46, 0x16, 0xbe, 0x01, 0xe3, 0xbb, 0xbe, + 0xf1, 0x66, 0x4a, 0xbe, 0x9c, 0x1e, 0xe4, 0xbd, 0x44, 0x5b, 0x90, 0xbe, 0xfd, 0xe6, 0xab, 0x3c, + 0xc7, 0x8c, 0x6b, 0x3e, 0x2e, 0x00, 0x53, 0xbd, 0xa6, 0x97, 0x89, 0xbe, 0x4f, 0xfa, 0x6d, 0xbe, + 0x61, 0x73, 0xe5, 0xbe, 0x89, 0x54, 0x03, 0xbf, 0x59, 0xeb, 0x75, 0xbe, 0xdc, 0x6e, 0x75, 0xbe, + 0xb8, 0x6c, 0x2f, 0x3e, 0xf3, 0x1b, 0x1e, 0xbe, 0x33, 0x9a, 0x35, 0xbe, 0xbe, 0x25, 0x77, 0x3c, + 0xd8, 0xac, 0x32, 0xbe, 0x0d, 0x76, 0xcd, 0xbd, 0x9f, 0x81, 0xc0, 0xbb, 0x23, 0xb4, 0x45, 0xbd, + 0x7b, 0x6c, 0x6f, 0x3d, 0xfe, 0x25, 0x12, 0xbe, 0x8b, 0x94, 0x6a, 0xbe, 0x68, 0x86, 0x8f, 0xbc, + 0x89, 0x12, 0x8d, 0xbd, 0x7b, 0xb8, 0x7d, 0xbc, 0xf3, 0x68, 0x1c, 0x3c, 0x73, 0xfc, 0x70, 0x3c, + 0x9f, 0x42, 0xe3, 0x3c, 0x63, 0xff, 0x94, 0x3d, 0xde, 0x34, 0xcb, 0x3d, 0xf6, 0x46, 0x58, 0x3d, + 0xa2, 0x22, 0xfe, 0x3d, 0xf1, 0x1e, 0x0f, 0xbd, 0xa8, 0xae, 0x92, 0x3d, 0x23, 0x5f, 0x51, 0xbc, + 0x5d, 0x27, 0xcb, 0xbd, 0xd6, 0xf2, 0x02, 0x3e, 0xb3, 0xdf, 0x46, 0x3d, 0x31, 0x85, 0x81, 0x3d, + 0x44, 0x04, 0x59, 0x3d, 0xd5, 0xbd, 0x12, 0x3e, 0xdb, 0x1a, 0x97, 0x3d, 0xd2, 0xcc, 0xa7, 0x3d, + 0x6d, 0xe5, 0x29, 0x3e, 0x6c, 0x5a, 0x9f, 0x3d, 0x49, 0x98, 0x45, 0xbd, 0x6d, 0xa5, 0x13, 0x3d, + 0x02, 0x96, 0x25, 0xbc, 0x6f, 0x8e, 0xa2, 0xbd, 0x36, 0x48, 0x0e, 0x3d, 0xea, 0xd8, 0x79, 0xbd, + 0x30, 0x6c, 0xc4, 0xbd, 0xa5, 0x08, 0x55, 0x3d, 0x71, 0x1b, 0xac, 0x3d, 0x9a, 0x81, 0x17, 0xbd, + 0x31, 0x88, 0xe7, 0xbd, 0xc4, 0x5b, 0xd5, 0xbc, 0x8a, 0x51, 0x97, 0xbd, 0xae, 0x67, 0x1b, 0xbe, + 0xd9, 0xb5, 0xa2, 0xbd, 0xf1, 0x12, 0x55, 0x3d, 0xb2, 0x4a, 0xb0, 0xbc, 0x8b, 0x1e, 0x52, 0xbd, + 0x07, 0x5d, 0x0b, 0xbe, 0xc4, 0xf5, 0x2e, 0xbd, 0x92, 0x85, 0x0a, 0xbd, 0xbf, 0xf6, 0xe1, 0x3b, + 0x9f, 0x4e, 0x97, 0x3d, 0xa8, 0x61, 0x26, 0x3d, 0x0c, 0x8d, 0xf3, 0xbb, 0x10, 0x8f, 0xfe, 0xbd, + 0x7b, 0xf1, 0x26, 0xbe, 0xdf, 0x75, 0xf2, 0xbc, 0xa5, 0x48, 0x28, 0x3d, 0xd7, 0x13, 0x22, 0xbe, + 0xe8, 0x85, 0x20, 0x3d, 0x39, 0xfe, 0xea, 0x3d, 0x59, 0x5a, 0x6f, 0x3d, 0xfd, 0x1f, 0x4b, 0xbe, + 0x3d, 0x32, 0x11, 0xbd, 0xa7, 0x5c, 0x81, 0xbd, 0xf6, 0x7e, 0xb3, 0xbd, 0x0e, 0xd1, 0x15, 0x3c, + 0xd8, 0xd1, 0x01, 0xbd, 0x71, 0x5e, 0xd5, 0xbd, 0x97, 0xca, 0x4b, 0xbd, 0xfd, 0x23, 0xbb, 0xbd, + 0xf8, 0xb4, 0x51, 0xbe, 0x58, 0x8d, 0xe9, 0xbc, 0xb4, 0x11, 0xbf, 0xbd, 0x5c, 0x9e, 0x1f, 0x3d, + 0x1a, 0xbf, 0x95, 0xbc, 0x00, 0xb2, 0x55, 0x3d, 0x2e, 0x55, 0xde, 0xbc, 0x67, 0xbc, 0xb1, 0xbb, + 0x67, 0xf4, 0x80, 0x3c, 0x30, 0xbb, 0x1c, 0x3d, 0xe2, 0x26, 0x03, 0x3e, 0x65, 0x94, 0xbf, 0xbd, + 0x68, 0x90, 0x00, 0xbe, 0xae, 0xc0, 0x31, 0xbe, 0x88, 0xad, 0x57, 0x3a, 0x2f, 0xec, 0xb4, 0x3d, + 0xb4, 0x9e, 0xc0, 0x3d, 0x0d, 0x11, 0x29, 0xbc, 0x52, 0x97, 0x68, 0x3d, 0x2d, 0xbe, 0x40, 0x3d, + 0x89, 0xbf, 0x5f, 0xbe, 0x4c, 0xea, 0x71, 0xbd, 0xe1, 0x1c, 0x6b, 0xbd, 0x59, 0xea, 0xe1, 0xbd, + 0xfe, 0x2f, 0x03, 0x3e, 0x71, 0x27, 0xc5, 0x3b, 0x7b, 0xc0, 0x18, 0xbe, 0x4d, 0x6b, 0xa0, 0x3c, + 0x91, 0x6c, 0xba, 0x3c, 0xe4, 0xe4, 0xe2, 0x3c, 0x2f, 0x2b, 0x82, 0xbd, 0xd8, 0xf3, 0xe6, 0x3d, + 0x9b, 0xa5, 0x2c, 0x3e, 0x11, 0x5c, 0xe0, 0xbd, 0xc6, 0x5c, 0x29, 0x3c, 0x36, 0xa6, 0x64, 0x3d, + 0x16, 0xea, 0x6f, 0xb9, 0x4c, 0x57, 0xe1, 0x3c, 0x71, 0x7e, 0x0d, 0xbd, 0xde, 0x7a, 0x07, 0xbd, + 0x04, 0x11, 0x47, 0xbd, 0x13, 0x04, 0x9c, 0x3c, 0xb2, 0xe9, 0x6f, 0x3d, 0x4f, 0x96, 0x56, 0x3d, + 0x70, 0x4b, 0x19, 0x3e, 0x8d, 0xb6, 0x83, 0x3d, 0x14, 0xf6, 0x05, 0x3e, 0x63, 0x61, 0x48, 0x3e, + 0xad, 0xee, 0x82, 0xbd, 0x44, 0x8b, 0x3d, 0x3e, 0xf3, 0xb0, 0x13, 0x3e, 0x82, 0x87, 0x33, 0xbe, + 0xc3, 0x1d, 0x87, 0xbd, 0xf8, 0x6f, 0x76, 0xbe, 0x12, 0xc3, 0xac, 0xbd, 0xe2, 0x4b, 0x96, 0xbe, + 0x7a, 0x42, 0xcb, 0xbe, 0x6a, 0xd9, 0x33, 0x3d, 0x53, 0x60, 0x24, 0x3e, 0xb9, 0x7f, 0x33, 0x3e, + 0x0c, 0x38, 0x1d, 0xbd, 0x3e, 0x87, 0x93, 0x3d, 0xf6, 0x64, 0xa0, 0x3d, 0x56, 0xa1, 0xae, 0xbe, + 0x7f, 0x2d, 0xac, 0xbe, 0x4a, 0xfd, 0x98, 0xbe, 0xd5, 0x28, 0x7c, 0x3d, 0x59, 0x7c, 0x49, 0x3e, + 0xd9, 0xed, 0x84, 0x3e, 0xfd, 0x9e, 0x00, 0xbe, 0x96, 0xee, 0x35, 0x3e, 0x8b, 0x5b, 0x5a, 0x3d, + 0x71, 0x5d, 0x50, 0x3d, 0xaf, 0x34, 0x84, 0x3c, 0x11, 0xc3, 0x63, 0x3d, 0xea, 0x29, 0xca, 0x3c, + 0x04, 0x30, 0xcb, 0x3d, 0x05, 0x59, 0x31, 0xbd, 0x78, 0x2c, 0xb8, 0x3d, 0x2f, 0x95, 0x6a, 0x3d, + 0xf1, 0xf6, 0x55, 0xbe, 0xf3, 0x15, 0xd1, 0x3d, 0x9b, 0xd7, 0xb3, 0x3d, 0x42, 0xf6, 0x85, 0x3d, + 0xd8, 0x21, 0x40, 0xbe, 0x55, 0x0d, 0x70, 0x3c, 0xaf, 0x10, 0xc0, 0x3b, 0x6a, 0xad, 0x0a, 0x3e, + 0x87, 0x51, 0x3f, 0x3d, 0xad, 0x65, 0x3e, 0xbe, 0x61, 0x7e, 0x86, 0x3d, 0xf7, 0x77, 0x58, 0xbc, + 0xfe, 0x60, 0xd0, 0xbd, 0x94, 0xe0, 0x68, 0xbe, 0x5d, 0xaf, 0x00, 0xbf, 0xaf, 0x36, 0x3a, 0xbe, + 0x77, 0xa5, 0x1b, 0x3d, 0xbc, 0x37, 0xe5, 0xbd, 0x26, 0x68, 0xc2, 0xbd, 0xcc, 0xb6, 0x54, 0xbe, + 0x21, 0xac, 0x16, 0xbe, 0x33, 0x8e, 0x84, 0x3c, 0x98, 0xa1, 0x53, 0x3e, 0xb2, 0xd9, 0xdd, 0x3e, + 0x2e, 0xd5, 0x8e, 0x3e, 0x88, 0x80, 0xbb, 0xbd, 0x63, 0x15, 0x88, 0x3e, 0x6e, 0x3d, 0x88, 0x3e, + 0xa8, 0x82, 0x1c, 0x3e, 0x9c, 0x42, 0x04, 0x3e, 0xcc, 0x11, 0x1f, 0x3e, 0x4b, 0xdd, 0x27, 0x3e, + 0x78, 0x6b, 0xb0, 0x3e, 0x03, 0x12, 0x2a, 0x3e, 0x49, 0x94, 0x88, 0xbc, 0x61, 0x28, 0x64, 0x3e, + 0xce, 0xa4, 0xbf, 0x3d, 0x79, 0x61, 0x9f, 0xbd, 0x48, 0xdd, 0x04, 0x3d, 0xa0, 0xf3, 0x80, 0x3d, + 0xcc, 0x56, 0x3e, 0x3e, 0x95, 0xd2, 0xe0, 0xbd, 0xef, 0xe0, 0x36, 0xbd, 0xb2, 0x56, 0x40, 0xbe, + 0x8f, 0x64, 0x9d, 0xbe, 0xd0, 0xdc, 0x8d, 0xbe, 0x76, 0xfc, 0xf0, 0x3c, 0x28, 0xf1, 0x28, 0xbe, + 0xfd, 0xd8, 0x6e, 0xbd, 0x28, 0x16, 0x77, 0x3e, 0x2f, 0xf1, 0x27, 0xbd, 0xf0, 0x03, 0x84, 0xbd, + 0x94, 0x2e, 0x7d, 0xbe, 0x59, 0x5a, 0xed, 0xbd, 0x89, 0xcd, 0x84, 0xbe, 0xd4, 0x43, 0x34, 0x3e, + 0x51, 0x2e, 0x0f, 0xbe, 0xdf, 0xf6, 0x09, 0xbe, 0x66, 0xb9, 0xe7, 0x3e, 0x9a, 0x21, 0x98, 0x3e, + 0x2e, 0x2f, 0x8e, 0x3e, 0x41, 0xd5, 0xfe, 0xbd, 0x60, 0xdd, 0x38, 0x3e, 0x3d, 0xd1, 0x87, 0x3e, + 0xe8, 0xae, 0xfa, 0x3b, 0x75, 0x37, 0xec, 0xbb, 0x84, 0x50, 0x7e, 0x3d, 0xef, 0x8a, 0xba, 0xbe, + 0x32, 0xd9, 0x0e, 0xbe, 0xe0, 0xcd, 0x73, 0x3d, 0xaf, 0x3f, 0x50, 0x3e, 0xb5, 0x64, 0x6d, 0x3e, + 0xe2, 0x22, 0x98, 0x3e, 0x00, 0xa6, 0x2c, 0xbc, 0xaf, 0x6e, 0xd6, 0xbc, 0x43, 0xad, 0xb3, 0x3d, + 0x08, 0x79, 0x3b, 0xbe, 0x28, 0x3f, 0x07, 0xbd, 0x98, 0x0c, 0xe0, 0xbc, 0x03, 0x13, 0x94, 0xbe, + 0xc5, 0x9a, 0xb0, 0xbd, 0x26, 0x5f, 0x3d, 0xbc, 0x34, 0x4a, 0x08, 0x3e, 0x21, 0xd2, 0xe4, 0xbd, + 0x45, 0x64, 0x1c, 0xbe, 0x7a, 0x0c, 0x5d, 0xbe, 0x33, 0x80, 0x52, 0xbc, 0xd7, 0xef, 0x08, 0x3e, + 0xa1, 0x76, 0x44, 0x3c, 0x25, 0xc1, 0x9c, 0x3d, 0xa5, 0x16, 0x01, 0x3e, 0x16, 0x36, 0xac, 0x3d, + 0x65, 0x66, 0xaf, 0xbd, 0x73, 0x14, 0x07, 0xbd, 0x0e, 0x05, 0x1e, 0xbe, 0x2e, 0xf7, 0x28, 0x3e, + 0x95, 0x3a, 0x54, 0x3e, 0x5d, 0x2d, 0x82, 0x3e, 0x54, 0x78, 0x34, 0x3e, 0xad, 0x1b, 0x65, 0x3e, + 0x7d, 0x4f, 0xd5, 0x3d, 0x54, 0xa6, 0xc6, 0x3e, 0xf0, 0x1c, 0xa6, 0x3e, 0x00, 0xf8, 0x9e, 0xbe, + 0x1c, 0xfb, 0x32, 0xbe, 0x4b, 0x99, 0x11, 0xbd, 0xef, 0x7a, 0x7d, 0xbe, 0x5a, 0xcb, 0x2d, 0xbd, + 0x89, 0x18, 0x12, 0xbd, 0xd3, 0xb6, 0x5c, 0x3e, 0x08, 0x37, 0x8e, 0x3e, 0x67, 0xca, 0xa5, 0x3d, + 0xa7, 0xd4, 0x2e, 0x3d, 0x75, 0x09, 0x8c, 0xbd, 0x0e, 0xe7, 0x9e, 0xbd, 0xbe, 0x99, 0x1d, 0xbd, + 0x4e, 0xc4, 0x92, 0xbd, 0x59, 0x8d, 0x06, 0x3e, 0x4e, 0xdf, 0x33, 0xbe, 0x95, 0xad, 0x17, 0x3e, + 0x14, 0xfc, 0x9f, 0x3e, 0x49, 0x86, 0x92, 0x3d, 0xfe, 0x15, 0x8f, 0xbe, 0x2d, 0x18, 0x2e, 0xbf, + 0x0f, 0xb8, 0xaa, 0x3e, 0x0e, 0x36, 0x56, 0xbe, 0xc0, 0x42, 0x77, 0xbe, 0x76, 0x1b, 0xd2, 0xbe, + 0x5a, 0x97, 0xbd, 0xbe, 0x7c, 0xc6, 0x96, 0x3e, 0x3a, 0x4b, 0xf0, 0xbd, 0xc2, 0xf9, 0x24, 0xbe, + 0xcb, 0x84, 0x95, 0xbe, 0x7e, 0xcf, 0xaf, 0x3e, 0x63, 0x5f, 0xda, 0x3e, 0xc1, 0x6f, 0x8b, 0x3d, + 0xaf, 0xe0, 0x96, 0x3e, 0x7e, 0x9e, 0xa5, 0x3e, 0x64, 0xfe, 0x9b, 0x3e, 0xa3, 0x2b, 0xc4, 0xbc, + 0x13, 0x70, 0x01, 0x3e, 0xda, 0xf6, 0x90, 0x3d, 0xfe, 0x8d, 0x9d, 0xbe, 0x84, 0x32, 0x0e, 0x3d, + 0x4a, 0x78, 0x21, 0x3e, 0xee, 0x92, 0xd8, 0xbd, 0x2d, 0x36, 0x18, 0x3e, 0x51, 0x1c, 0xd9, 0xbc, + 0x1c, 0x6e, 0xcd, 0xbd, 0x1b, 0xcb, 0x05, 0xbe, 0xd6, 0xde, 0xdf, 0xbd, 0x0f, 0xd4, 0x01, 0xbd, + 0x69, 0x55, 0x21, 0x3e, 0x96, 0x13, 0x34, 0xbd, 0x5d, 0x6a, 0x0d, 0x3e, 0xc7, 0x31, 0x3c, 0x3e, + 0xbc, 0xac, 0x46, 0x3b, 0x85, 0xe4, 0x19, 0xbe, 0x7e, 0x05, 0xf7, 0xbd, 0xb7, 0xcd, 0x35, 0xbe, + 0x67, 0xbf, 0x0a, 0xbe, 0x65, 0x91, 0x4e, 0x3e, 0xf5, 0x10, 0x02, 0x3e, 0x37, 0xb2, 0x17, 0x3e, + 0x80, 0x7f, 0xba, 0xb9, 0xfc, 0x2f, 0x2d, 0xbd, 0x8e, 0xd2, 0xb5, 0x3e, 0xd6, 0x7b, 0x8c, 0x3e, + 0x55, 0x55, 0x75, 0x3e, 0x37, 0xdb, 0x7d, 0x3d, 0xb5, 0xaf, 0x50, 0xbd, 0x9f, 0x54, 0x85, 0x3e, + 0x8a, 0xf4, 0x36, 0xbe, 0x09, 0x7a, 0xe6, 0xbb, 0x2f, 0x5b, 0xb3, 0x3d, 0xda, 0xa8, 0xb6, 0xbd, + 0xcd, 0xac, 0xfc, 0xbd, 0x3b, 0x32, 0x78, 0x3c, 0x92, 0x2c, 0x75, 0xba, 0x45, 0x20, 0x46, 0x3e, + 0x9d, 0xc4, 0x82, 0x3e, 0xd2, 0x89, 0xbd, 0xbb, 0x04, 0x33, 0x1d, 0x3e, 0xaf, 0xd5, 0x1e, 0x3e, + 0xa0, 0x69, 0xbc, 0xbd, 0x1e, 0x36, 0x0d, 0xbd, 0x60, 0x3a, 0x08, 0xbe, 0xda, 0x0e, 0xc6, 0x3e, + 0x08, 0x04, 0xa3, 0x3e, 0x10, 0xd8, 0x4d, 0xbd, 0xc2, 0x5f, 0x12, 0xba, 0xaa, 0x95, 0x8c, 0xbb, + 0xec, 0xa0, 0xe1, 0xbd, 0x57, 0x44, 0xcd, 0x3d, 0x2d, 0x4e, 0x08, 0x3e, 0xce, 0x0a, 0x37, 0x3e, + 0x2d, 0x29, 0x61, 0x3e, 0xc3, 0x46, 0x0a, 0xbd, 0x99, 0x4e, 0x6a, 0xbe, 0xb5, 0x0f, 0x16, 0xbe, + 0x74, 0x80, 0x33, 0xbe, 0x4b, 0xff, 0x80, 0xbe, 0x20, 0xb1, 0x08, 0x3e, 0x2f, 0xc5, 0x0b, 0x3d, + 0xed, 0x99, 0x98, 0x3e, 0x46, 0xcd, 0xed, 0x3d, 0x23, 0x63, 0x92, 0x3d, 0x1c, 0xa3, 0x97, 0x3d, + 0x37, 0xad, 0xaf, 0x3d, 0x46, 0xe7, 0x31, 0x3c, 0x54, 0xef, 0x3b, 0x3d, 0x08, 0xcc, 0xff, 0xbc, + 0x17, 0x66, 0x66, 0xbe, 0xb3, 0x37, 0xf1, 0xbd, 0x7f, 0xf0, 0x3c, 0xbe, 0xbf, 0x7a, 0xa0, 0xbd, + 0x1b, 0x2b, 0x89, 0x3e, 0x11, 0x97, 0x0a, 0x3d, 0x6d, 0xdb, 0x43, 0xbd, 0xfe, 0x22, 0x97, 0x3c, + 0xdd, 0x1a, 0xf9, 0xbd, 0x1f, 0xd9, 0x0b, 0xbe, 0xf9, 0x93, 0x41, 0xbd, 0x72, 0x41, 0x90, 0xbe, + 0x18, 0xd2, 0xa8, 0xbe, 0x4d, 0xce, 0xdd, 0xbd, 0x74, 0xdc, 0x33, 0xbd, 0x40, 0xd4, 0xdf, 0xbd, + 0x2a, 0x1e, 0xf4, 0x3d, 0x01, 0xb7, 0x19, 0xbe, 0x82, 0x35, 0x28, 0x3b, 0x5a, 0xa7, 0xdc, 0xbd, + 0x9b, 0xd4, 0xf7, 0xbd, 0x5b, 0x04, 0xb7, 0xbc, 0x6f, 0x5a, 0x55, 0xbe, 0xbd, 0xdd, 0xaf, 0xbd, + 0xc5, 0x33, 0x6d, 0xbe, 0xd3, 0x8e, 0x01, 0xbd, 0xe2, 0x06, 0xa4, 0x3e, 0xff, 0x89, 0xd5, 0x3d, + 0xcb, 0xfb, 0x01, 0xbe, 0xf6, 0x89, 0x07, 0x3f, 0x39, 0xbd, 0xc6, 0x3e, 0x7d, 0x78, 0x6e, 0x3d, + 0x20, 0x4b, 0xdd, 0xbd, 0xd2, 0xab, 0x0e, 0xbe, 0xdd, 0xd6, 0x2a, 0xbd, 0xbc, 0x4e, 0xae, 0xbb, + 0x51, 0x83, 0xdb, 0xbd, 0x58, 0x12, 0x80, 0xbe, 0xac, 0xed, 0x41, 0x3e, 0xb8, 0x69, 0x1a, 0x3e, + 0x0b, 0x43, 0x5e, 0xbe, 0x8e, 0x45, 0xac, 0xbd, 0x70, 0xb8, 0x01, 0xbe, 0x0b, 0xdc, 0x47, 0xbd, + 0x3e, 0xaf, 0x22, 0xbd, 0xc4, 0x8c, 0xa7, 0x3d, 0xb2, 0x25, 0xf2, 0x3d, 0xb2, 0x23, 0xb5, 0xbc, + 0xd9, 0xd3, 0x0b, 0xbe, 0x75, 0xe0, 0xb5, 0x3d, 0xd8, 0x03, 0xb7, 0x3d, 0xd7, 0x0d, 0x75, 0xbd, + 0x8b, 0x14, 0xef, 0xbd, 0x0a, 0x3d, 0x9d, 0xbd, 0xb6, 0x00, 0x6e, 0x3d, 0x8b, 0xa0, 0x82, 0xbc, + 0x2c, 0x03, 0x76, 0xbd, 0x33, 0xa5, 0x8f, 0xbc, 0xfa, 0x56, 0x78, 0x3e, 0x59, 0x3a, 0x17, 0xbe, + 0x09, 0xc5, 0x49, 0xbd, 0xfd, 0x1f, 0x90, 0x3c, 0x55, 0xb1, 0x66, 0x3e, 0x85, 0xe5, 0x00, 0x3e, + 0xc7, 0xc0, 0x67, 0xbe, 0x0d, 0x7d, 0x8f, 0x3e, 0x8d, 0xda, 0x4a, 0x3e, 0xca, 0x11, 0x7a, 0x3e, + 0xdd, 0xeb, 0x4d, 0xbe, 0x25, 0x19, 0x67, 0xbd, 0x0e, 0x3d, 0x4e, 0x3e, 0x1b, 0x02, 0xbc, 0x3d, + 0x1a, 0x56, 0x4a, 0x3e, 0x7c, 0x28, 0x3f, 0xbe, 0x10, 0xcc, 0x9a, 0xbd, 0xdc, 0xce, 0xe9, 0x3c, + 0xcd, 0xde, 0x5e, 0xbe, 0xa5, 0x35, 0x52, 0x3e, 0xf5, 0x23, 0x5e, 0x3e, 0x27, 0x3a, 0x09, 0x3e, + 0xcb, 0x4d, 0x37, 0xbe, 0x54, 0x06, 0x44, 0x3c, 0x9e, 0xf3, 0xb0, 0xbd, 0x0a, 0xe3, 0xd0, 0xbb, + 0xb6, 0xf4, 0xb6, 0xbd, 0xc2, 0x20, 0x99, 0x3e, 0x81, 0xe5, 0x8d, 0xbd, 0x5c, 0x59, 0x53, 0xbc, + 0xd5, 0x66, 0x5a, 0x3d, 0xa3, 0xcf, 0xc8, 0x3b, 0x04, 0xa5, 0x1f, 0xbe, 0x68, 0xd2, 0x06, 0xbe, + 0x34, 0xc8, 0x17, 0xbe, 0x52, 0xaa, 0x2c, 0x3e, 0x8a, 0x0a, 0xa6, 0xbc, 0x78, 0x2f, 0x3d, 0x3e, + 0xc0, 0xa4, 0x47, 0x3e, 0xc5, 0x6f, 0x3b, 0xbd, 0xa2, 0xf7, 0xc9, 0x3d, 0x0a, 0x24, 0x78, 0x3e, + 0x4a, 0x29, 0xf3, 0xbb, 0x08, 0x79, 0xb3, 0x3e, 0x49, 0x96, 0x88, 0x3d, 0x60, 0xd5, 0x41, 0xbe, + 0xaf, 0xf7, 0x0c, 0x3e, 0x09, 0xe0, 0x4c, 0xbd, 0xbf, 0x64, 0x5f, 0xbd, 0x8f, 0xdc, 0xc1, 0x3d, + 0x11, 0x50, 0xcb, 0x3c, 0xf4, 0x1f, 0xb9, 0xbd, 0xc0, 0xdb, 0xf6, 0x3c, 0x44, 0xc8, 0x12, 0x3e, + 0x5d, 0xc4, 0x0b, 0xbb, 0xd0, 0x56, 0x8a, 0x3d, 0xd6, 0x7a, 0xd3, 0x3d, 0x75, 0x7a, 0x89, 0xbd, + 0xc6, 0x9f, 0x4b, 0xbe, 0x7b, 0x1b, 0xab, 0xbe, 0xd3, 0x3b, 0xac, 0xbe, 0x41, 0x40, 0x80, 0xbd, + 0xdb, 0x57, 0x6e, 0xbe, 0xfc, 0xa5, 0xd6, 0xbd, 0xb9, 0x97, 0x31, 0x3e, 0x63, 0x42, 0x5f, 0x3d, + 0xd4, 0xa2, 0x27, 0x3e, 0x54, 0xde, 0x16, 0xbe, 0x3d, 0x59, 0xf8, 0xbd, 0x26, 0x14, 0xd6, 0x3c, + 0x94, 0xc0, 0x46, 0xbe, 0x74, 0x5e, 0xdc, 0xbc, 0xe6, 0x19, 0x84, 0x3d, 0xff, 0xd2, 0x23, 0xbe, + 0x3e, 0x5e, 0x0d, 0xbe, 0x0e, 0x63, 0xb2, 0xbe, 0x1e, 0xfa, 0x1e, 0x3c, 0xfb, 0x8d, 0x04, 0x3e, + 0x8f, 0xd0, 0x8d, 0xbd, 0xb7, 0xc1, 0x26, 0x3d, 0x88, 0x0e, 0x08, 0x3e, 0x09, 0xa5, 0xec, 0x3a, + 0x94, 0x94, 0xbf, 0x3d, 0x8b, 0x8a, 0x6a, 0xbe, 0x48, 0x67, 0xae, 0xbe, 0xfc, 0xfd, 0xd5, 0x3d, + 0xa4, 0x2a, 0x05, 0x3e, 0x0a, 0xa4, 0x60, 0x3d, 0x24, 0x52, 0xa1, 0xbd, 0xb6, 0xb2, 0x98, 0x3d, + 0x0b, 0xe2, 0x05, 0x3e, 0xc5, 0xfb, 0xa4, 0xbd, 0xa9, 0x73, 0x24, 0xbd, 0x9e, 0xea, 0xff, 0x3d, + 0xa3, 0x61, 0x9a, 0xbc, 0x71, 0x87, 0x9c, 0x3d, 0xbe, 0xfa, 0x8c, 0x3d, 0xd9, 0xa6, 0x82, 0xbc, + 0x6e, 0xf2, 0x04, 0x3e, 0x0b, 0x9e, 0xc6, 0x3d, 0x5c, 0x15, 0x5d, 0xbd, 0xfc, 0x97, 0x21, 0x3e, + 0x11, 0x8f, 0xf8, 0xbb, 0x4f, 0x1f, 0x9f, 0xbd, 0x15, 0xa7, 0x01, 0xbd, 0xcd, 0x92, 0xa9, 0xbd, + 0xb0, 0xa8, 0x5d, 0x3d, 0x85, 0x5d, 0x88, 0x3d, 0xd6, 0x9c, 0x13, 0x3c, 0x2f, 0x46, 0x0d, 0xbe, + 0x33, 0x38, 0x5a, 0x3c, 0x12, 0xd4, 0x2b, 0x3d, 0x59, 0xf7, 0x06, 0xbe, 0x61, 0x35, 0xcd, 0xbe, + 0x27, 0xbd, 0x39, 0xbe, 0x20, 0x6c, 0x36, 0xbd, 0xb3, 0x2f, 0x2d, 0xbe, 0x35, 0x46, 0xcb, 0x3d, + 0x20, 0xb5, 0xd6, 0xbd, 0xd0, 0x3d, 0x13, 0xbe, 0x23, 0x49, 0x59, 0xbd, 0x3f, 0x20, 0x79, 0xbc, + 0x3b, 0x0c, 0x4f, 0x3d, 0xf0, 0xca, 0x95, 0x3d, 0xe3, 0x38, 0x5e, 0xbe, 0x21, 0xa9, 0xe1, 0xbd, + 0xb5, 0xbf, 0xa9, 0x3d, 0xdf, 0x8c, 0x43, 0xbe, 0x3a, 0x65, 0xe3, 0xbb, 0xcb, 0x38, 0xb7, 0x3d, + 0x0a, 0x7e, 0xc8, 0x3d, 0x44, 0x38, 0x6b, 0xbd, 0x4e, 0x5e, 0x04, 0x3d, 0x6b, 0x17, 0xb0, 0x3d, + 0xe4, 0x55, 0x6d, 0xbd, 0x61, 0x2d, 0x66, 0xbd, 0x10, 0xa9, 0x39, 0xbc, 0xc6, 0xa2, 0x0d, 0xbd, + 0x6f, 0xeb, 0x45, 0xbd, 0x59, 0xf3, 0x24, 0x3e, 0x71, 0x4f, 0x76, 0xbb, 0x06, 0xaa, 0xd4, 0x3d, + 0x7d, 0xe4, 0x0b, 0x3e, 0x46, 0x6c, 0xdc, 0xbd, 0x40, 0x0f, 0xf9, 0x3d, 0xe4, 0x84, 0xad, 0x3e, + 0x62, 0x60, 0x29, 0xbc, 0xa6, 0x19, 0x85, 0xbe, 0xc9, 0xda, 0x4b, 0x3e, 0xd3, 0xfb, 0x6a, 0xbc, + 0x30, 0x48, 0xaa, 0x3d, 0x22, 0x90, 0x19, 0xbe, 0x67, 0x11, 0xce, 0xbe, 0x50, 0x84, 0x33, 0x3e, + 0xb3, 0x88, 0xbf, 0xbd, 0x58, 0xe1, 0x53, 0xbe, 0x84, 0xc7, 0x29, 0xbd, 0x4e, 0x43, 0x26, 0xbe, + 0xa5, 0xef, 0x41, 0x3e, 0xed, 0x21, 0x8f, 0xbd, 0xef, 0xbf, 0x3f, 0xbe, 0xbb, 0xff, 0xf7, 0x3d, + 0x91, 0x97, 0x6e, 0x3d, 0x6a, 0xc0, 0x99, 0xbe, 0xe3, 0x44, 0x35, 0x3d, 0x8b, 0x53, 0xe3, 0x3c, + 0x9e, 0x86, 0xc8, 0xbc, 0x41, 0x57, 0x82, 0xbe, 0x83, 0x60, 0x19, 0xbe, 0xdd, 0xde, 0xa7, 0x3d, + 0xd2, 0x0b, 0xa9, 0x3d, 0x5f, 0x37, 0xca, 0x3d, 0xd1, 0x05, 0x81, 0xbc, 0x37, 0xa8, 0x06, 0x3e, + 0x65, 0xc8, 0x21, 0xbe, 0x4f, 0x81, 0x9e, 0x3c, 0x7f, 0x78, 0x1c, 0x3e, 0xd8, 0xc1, 0x6b, 0x3e, + 0x8a, 0xf7, 0x8d, 0x3c, 0x78, 0xb8, 0x0a, 0xbc, 0x4e, 0x3a, 0x06, 0xbd, 0x62, 0x80, 0x67, 0xbe, + 0x0d, 0xe0, 0x90, 0xbe, 0x4a, 0xcf, 0x2f, 0x3d, 0xa0, 0x4c, 0xf9, 0xbb, 0xa1, 0x4a, 0x07, 0x3c, + 0xcf, 0x23, 0x11, 0xbb, 0x26, 0x5f, 0x1e, 0x3e, 0x6a, 0x5f, 0xa2, 0x3e, 0x4b, 0x2f, 0x92, 0x3e, + 0x9e, 0xad, 0x98, 0x3e, 0xef, 0xf4, 0xb8, 0x3e, 0xe3, 0x38, 0x83, 0x3e, 0xe0, 0x36, 0xc9, 0xbd, + 0xb3, 0x9e, 0x9f, 0x3d, 0x33, 0x03, 0xad, 0x3c, 0x13, 0xdc, 0x95, 0x3d, 0x81, 0x9a, 0x0b, 0x3e, + 0x8f, 0xaa, 0x0d, 0x3d, 0x08, 0x6b, 0x38, 0xbd, 0xba, 0x6b, 0xb7, 0x3d, 0x80, 0xa3, 0x04, 0x3e, + 0x14, 0x4c, 0xe8, 0x3d, 0x58, 0x91, 0x7f, 0x3d, 0xdd, 0xc8, 0x75, 0xbd, 0xf7, 0x28, 0x35, 0xbe, + 0xf6, 0xd5, 0xb9, 0xbe, 0x4a, 0x38, 0x8c, 0xbd, 0x98, 0x45, 0xd3, 0xbd, 0xf3, 0xac, 0x19, 0xbf, + 0xb5, 0x04, 0xbf, 0xbe, 0xb0, 0xe0, 0x77, 0xbe, 0x01, 0x97, 0x7b, 0xbe, 0x25, 0x99, 0xcb, 0xbe, + 0xb2, 0x3e, 0x53, 0xbe, 0x86, 0x96, 0x2e, 0x3c, 0xac, 0xe1, 0x3e, 0xbd, 0x93, 0x34, 0x14, 0x3c, + 0xdf, 0x03, 0x59, 0xbe, 0xa7, 0x79, 0xef, 0xbd, 0x4a, 0x08, 0xa4, 0xbe, 0xa7, 0xaa, 0x26, 0xbe, + 0x8b, 0xa3, 0xb2, 0xbe, 0xf9, 0x9a, 0x84, 0xbe, 0x80, 0xb6, 0x19, 0x3e, 0xc7, 0x67, 0x5b, 0x3e, + 0x2e, 0xdf, 0x96, 0x3e, 0x92, 0x34, 0xb6, 0x3e, 0x97, 0xdc, 0x36, 0x3e, 0x4a, 0xe6, 0xbd, 0xbd, + 0xdb, 0x1c, 0xa0, 0x3c, 0xa0, 0x0e, 0x22, 0xbd, 0xbd, 0x25, 0x15, 0x3e, 0x6a, 0xb6, 0xbf, 0xbb, + 0x99, 0x2a, 0xa8, 0xbc, 0xa6, 0xb7, 0xc7, 0x3e, 0xb3, 0x4d, 0x11, 0x3d, 0x87, 0x61, 0x91, 0x3e, + 0x61, 0x2b, 0x85, 0xc0, 0xa0, 0xb9, 0x56, 0x3f, 0x46, 0xf1, 0x49, 0x40, 0xd3, 0x7e, 0x35, 0x3f, + 0x63, 0xbb, 0x2a, 0x3e, 0xba, 0x47, 0x67, 0xbf, 0xe7, 0x9b, 0x19, 0xbf, 0x30, 0xaf, 0x90, 0xbf, + 0xb1, 0x3d, 0x34, 0xbd, 0x59, 0x3e, 0xf4, 0x3e, 0x76, 0xf4, 0x16, 0x3f, 0x72, 0xff, 0x10, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0xf8, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x20, 0x00, 0x00, + 0x03, 0x00, 0x06, 0x00, 0x06, 0x00, 0x0c, 0x00, 0x03, 0x00, 0x02, 0x00, 0x02, 0x00, 0x12, 0x00, + 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x03, 0x02, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0xb0, 0x1e, 0x00, 0x00, + 0x8d, 0x9e, 0x59, 0x3f, 0x92, 0x2c, 0xab, 0x3e, 0x8a, 0x03, 0x51, 0xbf, 0x14, 0xce, 0x2c, 0x3f, + 0x46, 0xe3, 0x3f, 0x3d, 0xd8, 0x55, 0xa3, 0xbe, 0x7d, 0xc6, 0x93, 0xbe, 0xe2, 0xfc, 0xa8, 0xbf, + 0x4e, 0xa2, 0x0e, 0x3e, 0x23, 0x15, 0xab, 0xbe, 0xc5, 0xb5, 0x11, 0xbe, 0xf1, 0x8b, 0xb7, 0xbd, + 0x0d, 0xf8, 0x0c, 0xbd, 0xbf, 0x7b, 0xdf, 0x3d, 0xb2, 0x17, 0x86, 0xbe, 0x9b, 0xb9, 0xc8, 0x3b, + 0x9f, 0x5e, 0x8f, 0xbf, 0xe2, 0xec, 0x77, 0xbf, 0x23, 0xc0, 0x0f, 0x3f, 0x9f, 0x52, 0x0d, 0x3f, + 0x6b, 0xbe, 0x81, 0xbe, 0x9b, 0x16, 0x73, 0x3e, 0x6e, 0x7d, 0x58, 0x3f, 0x0c, 0xf3, 0xe7, 0x3e, + 0xb7, 0xea, 0x69, 0x3e, 0x89, 0x8a, 0x28, 0x3e, 0x9b, 0x66, 0x48, 0xbe, 0x85, 0xc8, 0xd7, 0xbe, + 0xae, 0x9e, 0xd8, 0xbd, 0x66, 0xf9, 0x9c, 0xbd, 0x78, 0xd2, 0x54, 0xbe, 0x5e, 0xbc, 0x19, 0x3e, + 0x8f, 0x05, 0x97, 0xbe, 0x8d, 0x9a, 0x22, 0xbe, 0x6d, 0xdf, 0x97, 0xbe, 0xfe, 0xb9, 0x85, 0xbe, + 0x09, 0x32, 0x9e, 0x3d, 0x99, 0xf7, 0x7b, 0xbe, 0x5a, 0x82, 0xd8, 0xbe, 0xbf, 0x81, 0x16, 0xbf, + 0x66, 0xf7, 0x1c, 0x3e, 0x2f, 0xdc, 0x0f, 0x3f, 0x36, 0x5d, 0xa1, 0xbe, 0xa2, 0x35, 0xb6, 0xbe, + 0xa6, 0x7d, 0x1b, 0xbf, 0xab, 0x93, 0x34, 0xbf, 0xed, 0xd0, 0x04, 0xbf, 0xae, 0xc9, 0x4c, 0x3e, + 0x07, 0xe3, 0xf5, 0xbc, 0x98, 0x14, 0xb4, 0xbd, 0x51, 0x30, 0x98, 0xbe, 0x06, 0x14, 0xa9, 0x3e, + 0x34, 0xd5, 0xae, 0x3e, 0xfc, 0x53, 0x3b, 0xbf, 0x10, 0xa2, 0x64, 0x3e, 0xde, 0xb6, 0x85, 0xbe, + 0x71, 0xb5, 0x6b, 0xbe, 0xbc, 0x9c, 0xf3, 0x3e, 0x70, 0x97, 0x93, 0x3f, 0x9b, 0xcf, 0x8e, 0x3e, + 0x47, 0xad, 0x24, 0x3e, 0x00, 0xc6, 0xdb, 0x3e, 0xa0, 0x93, 0x11, 0x3e, 0xd9, 0x04, 0x6a, 0xbf, + 0x47, 0x0d, 0xe5, 0xbe, 0x7b, 0xe4, 0xd6, 0x37, 0xed, 0x27, 0xee, 0x3e, 0xf5, 0x2d, 0x37, 0xbe, + 0x0d, 0x6a, 0x4f, 0xbf, 0x59, 0xca, 0x56, 0x3e, 0x46, 0xff, 0xd5, 0x3e, 0x8c, 0x2f, 0x66, 0xbe, + 0x27, 0xc3, 0xe3, 0xbe, 0x29, 0xa7, 0xa6, 0x3e, 0x85, 0xb7, 0x0e, 0xbf, 0x6d, 0xb6, 0xdb, 0x3e, + 0x14, 0xde, 0xb7, 0x3e, 0x7d, 0xfb, 0x26, 0x3f, 0x93, 0x4e, 0x25, 0xbf, 0x6d, 0x48, 0x4f, 0xbf, + 0x51, 0x75, 0x94, 0xbe, 0xbc, 0xcf, 0x28, 0x3f, 0x48, 0x6d, 0x5f, 0x3e, 0xea, 0xa8, 0x21, 0xbf, + 0xd3, 0x6a, 0x27, 0x3e, 0x8c, 0x7f, 0xc7, 0xbe, 0x11, 0x2b, 0x6d, 0xbc, 0x2a, 0xba, 0xc0, 0xbd, + 0x96, 0x7c, 0xd1, 0x3e, 0x21, 0x71, 0xa0, 0xbd, 0xf7, 0x7d, 0xb4, 0x3e, 0x05, 0x95, 0x4b, 0xbf, + 0x1f, 0xbe, 0x1c, 0xbe, 0xb8, 0x6e, 0x60, 0x3e, 0x91, 0x5b, 0x32, 0xbe, 0x58, 0xa3, 0x0b, 0x3f, + 0x46, 0x9d, 0xa7, 0xbe, 0xea, 0x81, 0x6a, 0xbd, 0x70, 0xd2, 0x4d, 0xbe, 0xa9, 0x16, 0x7f, 0xbe, + 0x3c, 0x8c, 0x5c, 0x3f, 0xa3, 0xcd, 0xb5, 0x3f, 0x1e, 0x6c, 0x2a, 0x3f, 0xa6, 0x22, 0xf6, 0x3e, + 0xeb, 0x88, 0x0c, 0x3f, 0x70, 0x8d, 0xf2, 0x3d, 0x96, 0x96, 0x8b, 0xbe, 0x22, 0x34, 0x12, 0xbf, + 0x75, 0x7b, 0x83, 0xbf, 0xf4, 0xaa, 0x12, 0xbe, 0xf2, 0xa6, 0xca, 0x3e, 0xed, 0x6d, 0xc2, 0x3f, + 0xf2, 0x7c, 0x51, 0x3f, 0x1d, 0x34, 0xd4, 0xbe, 0xe7, 0x3d, 0x34, 0x3f, 0x53, 0x4a, 0xb9, 0x3e, + 0xbc, 0x0f, 0x83, 0xbd, 0x07, 0x24, 0x5e, 0xbf, 0xd5, 0x81, 0xb6, 0xbe, 0x00, 0xac, 0x40, 0xbc, + 0xf7, 0x5f, 0x7f, 0x3d, 0xe9, 0x1f, 0x40, 0x3f, 0x1f, 0x37, 0x64, 0xbb, 0x26, 0x40, 0x4c, 0x3f, + 0xd7, 0x3f, 0x06, 0x3e, 0x6a, 0xb0, 0x2b, 0xbf, 0xc1, 0x68, 0x80, 0xbd, 0x95, 0xb1, 0x11, 0x3f, + 0xaa, 0x53, 0x15, 0x3f, 0xfb, 0x0c, 0x98, 0x3f, 0x89, 0x4c, 0x96, 0x3f, 0xdf, 0xea, 0x8b, 0xbe, + 0x66, 0xe7, 0x22, 0x3f, 0x13, 0x41, 0x24, 0x3f, 0x15, 0x39, 0x81, 0x3e, 0xd5, 0x3d, 0x94, 0xbf, + 0xc9, 0xbe, 0x53, 0xbf, 0x94, 0x02, 0x9e, 0x3e, 0x23, 0xde, 0x14, 0xbf, 0xcf, 0x31, 0xf4, 0xbe, + 0x03, 0x33, 0x27, 0xbe, 0xa0, 0x28, 0xd2, 0xbe, 0x6b, 0x57, 0xab, 0xbe, 0xd7, 0x31, 0x43, 0x3e, + 0x01, 0x47, 0xc5, 0xbe, 0xf9, 0xfe, 0x71, 0xbe, 0xfa, 0xea, 0xaa, 0xbe, 0x6e, 0x1b, 0x80, 0x3e, + 0xc2, 0xf5, 0x62, 0xbe, 0x8b, 0x5a, 0xa4, 0xbe, 0xc7, 0x5e, 0xa8, 0x3c, 0x2f, 0xe2, 0x1e, 0xbf, + 0x46, 0x5e, 0x1d, 0xbf, 0xca, 0x1a, 0x08, 0xbf, 0x88, 0x72, 0x23, 0xbf, 0xe5, 0xaa, 0xb7, 0x3e, + 0x65, 0x81, 0x2f, 0xbf, 0x6d, 0xb0, 0x64, 0x3e, 0xa7, 0x7d, 0x4e, 0xbe, 0x6c, 0xa4, 0x04, 0x3d, + 0x43, 0x3b, 0xd8, 0x3d, 0xa2, 0xf6, 0xcd, 0xbe, 0xb0, 0xd3, 0xa8, 0xbf, 0x60, 0x72, 0x71, 0xbf, + 0x95, 0x20, 0xd8, 0x3e, 0xa2, 0x7e, 0xdd, 0x3b, 0x84, 0x30, 0x44, 0xbf, 0x73, 0x82, 0x26, 0xbd, + 0xe2, 0xcf, 0xa4, 0x3e, 0x31, 0xd0, 0x8b, 0x3e, 0x48, 0x39, 0x93, 0xbe, 0x1e, 0xca, 0x3c, 0xbf, + 0x8f, 0xb6, 0xc0, 0xbe, 0x34, 0xac, 0xc5, 0x3d, 0x8f, 0x46, 0x1a, 0xbf, 0xc2, 0x32, 0x4a, 0xbf, + 0xc2, 0x56, 0x57, 0x3e, 0x4b, 0xfe, 0xc7, 0xbe, 0x4b, 0x8e, 0x97, 0xbe, 0x85, 0xb1, 0x6f, 0x3d, + 0x82, 0x3e, 0x8e, 0xbf, 0xe2, 0x80, 0xb2, 0xbf, 0x21, 0xc2, 0x12, 0x3e, 0xe8, 0xf7, 0x01, 0xbe, + 0x6d, 0xc8, 0xa6, 0xbf, 0x02, 0xc2, 0xb6, 0xbe, 0x01, 0x9b, 0x2b, 0xbf, 0x80, 0x82, 0xd8, 0xbe, + 0x48, 0x0b, 0x11, 0xbe, 0xdb, 0x8c, 0xee, 0xbf, 0xbd, 0x0b, 0xe0, 0xbb, 0x0d, 0xec, 0x3b, 0x3e, + 0x31, 0x7d, 0x0e, 0xbf, 0xf0, 0x8e, 0xa5, 0xbc, 0x21, 0x33, 0xa0, 0x3e, 0xc2, 0x3c, 0xb4, 0x3e, + 0x85, 0x79, 0x27, 0x3d, 0x79, 0xdc, 0x84, 0x3e, 0x0c, 0xad, 0x04, 0xbf, 0xae, 0x37, 0x7c, 0xbe, + 0x82, 0x90, 0x99, 0xbe, 0x39, 0x68, 0xee, 0x3e, 0xcf, 0x9d, 0xb0, 0x3b, 0x0e, 0x8c, 0xba, 0xbe, + 0x2a, 0xa4, 0x2a, 0xbd, 0x61, 0x4f, 0xb9, 0xbd, 0x9d, 0xc7, 0x39, 0x3e, 0xad, 0xd8, 0x03, 0xbf, + 0x5e, 0xc4, 0x75, 0x3f, 0xe8, 0x6f, 0xf0, 0x3e, 0x42, 0x94, 0x1d, 0x3f, 0xdf, 0xbc, 0x1f, 0x3d, + 0x8e, 0x39, 0x16, 0x3b, 0x57, 0xbf, 0x94, 0x3e, 0x51, 0x74, 0x15, 0x3e, 0x34, 0xfb, 0xdc, 0x3e, + 0xa1, 0x4e, 0x9b, 0xbd, 0xbb, 0xe2, 0x80, 0xbe, 0x5a, 0x96, 0x1c, 0x3f, 0xd6, 0x63, 0x46, 0xbf, + 0x2f, 0xdc, 0x93, 0xbe, 0x32, 0x03, 0xea, 0x3e, 0xcf, 0xb7, 0x14, 0xbf, 0x88, 0x71, 0xe8, 0x3e, + 0xb3, 0x34, 0xec, 0xbe, 0x93, 0x4d, 0xa2, 0xbe, 0x23, 0xb8, 0x78, 0xbc, 0x66, 0x54, 0x0a, 0xbe, + 0x0c, 0xd8, 0xf6, 0xbd, 0xc6, 0xb2, 0x81, 0x3d, 0xe6, 0x8e, 0x2e, 0xbe, 0xc9, 0x43, 0xad, 0xbd, + 0x93, 0x93, 0x14, 0x3f, 0x38, 0x84, 0xdf, 0x3e, 0x3a, 0xa5, 0xd5, 0xbe, 0x3f, 0x0e, 0xb3, 0xbe, + 0x51, 0x9c, 0x13, 0xbe, 0x86, 0x48, 0x49, 0xbf, 0xd8, 0xc6, 0x2b, 0xbf, 0x4f, 0x17, 0xc6, 0xbe, + 0x72, 0xa3, 0x7d, 0xbe, 0x17, 0x10, 0x24, 0xbe, 0x5d, 0x5f, 0x88, 0x3e, 0x68, 0xfa, 0x9c, 0x3e, + 0x63, 0xf0, 0x2d, 0xbe, 0x4c, 0x96, 0xd0, 0x3d, 0xb3, 0x7c, 0x38, 0xbe, 0x17, 0x48, 0xc7, 0xbd, + 0x4b, 0x69, 0x28, 0xbe, 0x25, 0xa4, 0x63, 0x3f, 0xb1, 0x48, 0x20, 0x3f, 0x0d, 0xa6, 0x11, 0x3f, + 0xca, 0x9c, 0xe5, 0xbe, 0xfb, 0xca, 0x31, 0xbe, 0xbd, 0xc2, 0xef, 0xbe, 0x9a, 0xa0, 0x08, 0x3f, + 0x46, 0x2f, 0xbb, 0x3e, 0x37, 0x07, 0x4e, 0xbe, 0xa9, 0x58, 0x81, 0x3e, 0xef, 0x98, 0xa7, 0x3e, + 0x2d, 0x56, 0x8b, 0x3e, 0x1c, 0x97, 0x0a, 0x3e, 0x1e, 0x5c, 0x03, 0x3d, 0xae, 0x90, 0x02, 0xbf, + 0x7e, 0xa2, 0xf1, 0xbe, 0xfc, 0x68, 0x4c, 0xbf, 0x11, 0xca, 0x1f, 0xbf, 0x6a, 0x57, 0xba, 0xbe, + 0xbd, 0xcc, 0xbe, 0xbe, 0x85, 0x33, 0x33, 0xbc, 0xb1, 0x92, 0xd8, 0x3e, 0x1e, 0x2b, 0xe0, 0x3d, + 0x4d, 0x0a, 0x93, 0x3e, 0x4f, 0xdd, 0x60, 0xbe, 0xed, 0xd2, 0xac, 0xbc, 0xe4, 0xc7, 0x3b, 0xbf, + 0x67, 0x1d, 0x0d, 0xbf, 0x86, 0x85, 0x34, 0xbe, 0x83, 0xe9, 0x16, 0xbf, 0xa0, 0x6d, 0x5b, 0xbe, + 0xb2, 0x6c, 0xea, 0xbd, 0xf5, 0x51, 0xa1, 0x3e, 0xf3, 0x1f, 0xae, 0xbc, 0x69, 0xee, 0x02, 0xbf, + 0xed, 0x58, 0xbb, 0xbe, 0xaa, 0x37, 0x84, 0x3e, 0xab, 0xd0, 0xc8, 0x3e, 0xdb, 0x48, 0x3a, 0x3e, + 0xab, 0xae, 0xb2, 0xbe, 0x5e, 0xb8, 0x19, 0xbf, 0x13, 0x49, 0xfc, 0xbd, 0xec, 0xc9, 0x0e, 0x3f, + 0xb4, 0xfc, 0xd6, 0xbe, 0x1c, 0x9b, 0xa1, 0x3e, 0x5f, 0x3f, 0xa8, 0xbb, 0x7f, 0xa8, 0xf1, 0x3e, + 0xfa, 0x38, 0xf8, 0x3d, 0x24, 0x8b, 0xcb, 0xbd, 0x24, 0xf4, 0x33, 0xbf, 0xd4, 0x7b, 0xbf, 0xbe, + 0xbf, 0xae, 0xf8, 0xbd, 0x94, 0x9e, 0xc3, 0xbe, 0x76, 0xd8, 0xce, 0xbe, 0x5b, 0x66, 0x59, 0xbe, + 0x07, 0x9a, 0x39, 0xbe, 0x42, 0x18, 0xd4, 0xbd, 0xbf, 0x2d, 0x41, 0x3e, 0x85, 0xc4, 0x80, 0xbe, + 0x09, 0xfd, 0x99, 0xbe, 0x42, 0x19, 0xb3, 0x3d, 0x6c, 0x75, 0xb5, 0x3e, 0x4e, 0xfb, 0x8a, 0x3e, + 0x12, 0x39, 0xac, 0x3d, 0x34, 0x24, 0xbd, 0x3e, 0xdc, 0xae, 0x6f, 0x3f, 0x63, 0x9a, 0xef, 0x3d, + 0xd5, 0xc8, 0x1c, 0xbf, 0x27, 0x5b, 0x4f, 0xbe, 0xa9, 0xb5, 0x10, 0xbe, 0x6c, 0x75, 0x60, 0xbe, + 0xf6, 0xe7, 0xc2, 0xbd, 0x31, 0x73, 0x1c, 0xbe, 0xac, 0x0c, 0x77, 0x3e, 0xbc, 0x18, 0x1d, 0x3f, + 0xa7, 0x02, 0xae, 0x3e, 0xd1, 0x74, 0x47, 0xbf, 0x54, 0xa4, 0xb7, 0xbe, 0xfb, 0x30, 0x97, 0x3e, + 0x4e, 0x4c, 0x32, 0xbe, 0x24, 0x5c, 0x80, 0x3e, 0xc9, 0xd1, 0x21, 0x3d, 0xec, 0xf7, 0x01, 0x3f, + 0x28, 0xbb, 0xbf, 0x3e, 0x42, 0x39, 0xe5, 0x3e, 0xa5, 0xd9, 0x95, 0x3c, 0x3b, 0xb3, 0x72, 0x3e, + 0xe2, 0xdb, 0x9e, 0xbd, 0xb3, 0x64, 0x24, 0xbf, 0x6e, 0x91, 0x13, 0xbf, 0x28, 0x21, 0x9b, 0x3d, + 0xae, 0x40, 0x2c, 0xbf, 0xfb, 0x56, 0x91, 0x3d, 0x83, 0x9b, 0xdd, 0x3e, 0xcf, 0xe9, 0x26, 0x3e, + 0x83, 0xab, 0x94, 0xbe, 0x91, 0xcd, 0xfe, 0x3c, 0x06, 0xda, 0x24, 0xbf, 0x60, 0xb5, 0x82, 0xbe, + 0x23, 0x81, 0xa6, 0x3d, 0x7f, 0x6a, 0x00, 0xbf, 0x19, 0xed, 0x2d, 0xbf, 0x07, 0x51, 0x38, 0x3f, + 0x62, 0x3b, 0xbc, 0x3e, 0x66, 0x82, 0x65, 0x3f, 0x40, 0x34, 0xbc, 0x3e, 0xa6, 0xeb, 0x02, 0xbc, + 0x86, 0xd4, 0xb3, 0xbe, 0xfe, 0x1a, 0xbf, 0x3e, 0x84, 0x48, 0xab, 0x3d, 0x40, 0xf2, 0x84, 0xbe, + 0x3a, 0x8a, 0x4e, 0x3e, 0x72, 0xdd, 0x24, 0x3f, 0x75, 0xc2, 0x05, 0xbf, 0x5d, 0x08, 0xb9, 0xbe, + 0xc7, 0x68, 0xc5, 0x3e, 0x63, 0xb6, 0xca, 0xbe, 0x25, 0xe3, 0x2c, 0xbf, 0x55, 0xdc, 0xf2, 0xbc, + 0xae, 0x5a, 0xe9, 0x3e, 0x5a, 0x06, 0xf8, 0xbe, 0x48, 0x03, 0x0f, 0x3f, 0x90, 0x1f, 0xa7, 0x3f, + 0x65, 0xa7, 0x73, 0xbe, 0x5d, 0x49, 0xa7, 0x3e, 0x45, 0x94, 0x1a, 0x3f, 0x8a, 0x54, 0xf2, 0x3e, + 0x4f, 0x0f, 0x62, 0x3e, 0x71, 0x99, 0x1c, 0x3e, 0x8b, 0x5b, 0x1f, 0xbe, 0x5f, 0x13, 0xbf, 0x3e, + 0xf1, 0x0d, 0x0b, 0x3f, 0x92, 0x2d, 0xb7, 0xbe, 0xfd, 0xdd, 0x47, 0xbe, 0x2b, 0xe7, 0x9a, 0xbf, + 0x57, 0x4a, 0xfa, 0xbe, 0x36, 0x15, 0x76, 0x3e, 0xe3, 0x5c, 0x37, 0xbf, 0x7b, 0xaf, 0x3b, 0xbf, + 0xf8, 0x2f, 0x72, 0xbe, 0x33, 0xab, 0x5e, 0x3f, 0xbd, 0x2c, 0x25, 0xbf, 0xaa, 0x34, 0x67, 0x3e, + 0x88, 0x55, 0xb7, 0xbe, 0xdb, 0x3a, 0xab, 0x3e, 0xa6, 0x5a, 0x3d, 0x3f, 0x7d, 0xa3, 0x75, 0xbe, + 0x31, 0x9f, 0xa5, 0xbb, 0xe3, 0x6f, 0xfc, 0xbe, 0xf2, 0xb8, 0x80, 0x3f, 0xe6, 0xe8, 0x70, 0x3b, + 0x6f, 0x8c, 0xd2, 0x3d, 0x75, 0x91, 0x62, 0x3f, 0x28, 0x4f, 0x14, 0x3f, 0x35, 0x58, 0x93, 0x3f, + 0x5b, 0x55, 0x6f, 0x3e, 0x57, 0xe1, 0x6c, 0xbd, 0x62, 0x1c, 0x5c, 0x3e, 0x86, 0x81, 0xaf, 0xbe, + 0xb0, 0xe7, 0xe4, 0xbe, 0x17, 0xfd, 0xb5, 0x3e, 0x81, 0x89, 0x56, 0xbe, 0xf5, 0x17, 0xc3, 0xbd, + 0x3f, 0xc6, 0x5a, 0x3e, 0x57, 0x30, 0x2d, 0x3f, 0x77, 0xc1, 0x9e, 0xbd, 0x48, 0xaa, 0x67, 0x3e, + 0xa2, 0x93, 0x86, 0xbd, 0x56, 0xbf, 0xd9, 0x3d, 0x93, 0x47, 0x16, 0xbe, 0xf4, 0xa7, 0x1f, 0x3e, + 0x82, 0x7f, 0xb7, 0x3e, 0x59, 0x0a, 0xcb, 0xbd, 0xcf, 0x05, 0x65, 0xbe, 0x37, 0x45, 0x38, 0xbf, + 0x4d, 0x5f, 0xc0, 0x3e, 0x85, 0xa9, 0x88, 0xbe, 0xf4, 0x77, 0xa7, 0xbe, 0x86, 0xe3, 0x7d, 0x3e, + 0x93, 0x58, 0xc4, 0xbe, 0x80, 0x8a, 0x5e, 0x3e, 0x30, 0x34, 0xdc, 0x3b, 0xdc, 0x21, 0xef, 0xbe, + 0xde, 0x26, 0x35, 0xbf, 0x1a, 0x9a, 0xd7, 0x3e, 0x19, 0x8a, 0x22, 0x3c, 0xcf, 0xb2, 0x8f, 0xbe, + 0x02, 0xe0, 0xce, 0xbe, 0x7f, 0x7f, 0x6c, 0xbf, 0x73, 0x9b, 0xb4, 0x3e, 0x1d, 0xe0, 0xc1, 0x3e, + 0x7b, 0xc5, 0x61, 0x3e, 0x2d, 0xa5, 0x8d, 0x3d, 0x47, 0x77, 0xbd, 0xbe, 0x59, 0x8c, 0x8e, 0xbe, + 0x50, 0x65, 0x0b, 0x3e, 0x54, 0x3e, 0xd4, 0x3e, 0x6c, 0x5d, 0xbc, 0x3d, 0x0a, 0x50, 0xc0, 0x3e, + 0x99, 0x2e, 0x71, 0x3f, 0xb7, 0x09, 0x72, 0xbe, 0x79, 0x8b, 0xf5, 0xbe, 0x9e, 0xc7, 0xea, 0x3e, + 0xa6, 0x1d, 0xbb, 0x3d, 0x16, 0xca, 0xb4, 0xbd, 0xd8, 0x2c, 0x95, 0xbe, 0x15, 0x77, 0x23, 0xbe, + 0x9f, 0x1d, 0x2f, 0xbf, 0xeb, 0xe6, 0x2c, 0xbf, 0xff, 0xcc, 0x80, 0xbe, 0xc7, 0x94, 0x81, 0x3e, + 0x95, 0xde, 0x41, 0xbe, 0x08, 0x5c, 0x9a, 0xbe, 0xd0, 0x8f, 0x08, 0xbf, 0x54, 0x9d, 0x16, 0x3f, + 0x85, 0xeb, 0xe9, 0x3e, 0xe8, 0x45, 0x08, 0xbf, 0x60, 0x91, 0x9f, 0x3d, 0x07, 0x3a, 0x8d, 0xbc, + 0x50, 0x45, 0x95, 0x3e, 0x8e, 0xae, 0x97, 0xbc, 0x78, 0x04, 0x4e, 0x3d, 0xf4, 0x13, 0x4c, 0x3f, + 0xc0, 0x33, 0x1a, 0xbf, 0xa8, 0x5c, 0x11, 0xbe, 0x47, 0xf2, 0xf4, 0xbd, 0x3b, 0xfb, 0xd1, 0xbe, + 0xef, 0x12, 0x6d, 0xbe, 0xcb, 0x5f, 0x93, 0x3e, 0x99, 0x23, 0xcb, 0xbb, 0x17, 0xb8, 0xf4, 0xbe, + 0x8a, 0x1a, 0x1d, 0x3e, 0x82, 0x34, 0xd2, 0x3e, 0x4b, 0xb2, 0x80, 0x3e, 0x88, 0x47, 0x70, 0xbd, + 0x4d, 0xb6, 0x0e, 0x3f, 0x4c, 0x2c, 0x99, 0x3e, 0x74, 0x60, 0xc0, 0x3e, 0x8e, 0x32, 0xf3, 0xbb, + 0x2a, 0xb7, 0xf7, 0xbc, 0xdc, 0x62, 0x0d, 0x3f, 0x73, 0x34, 0xe9, 0x3e, 0x76, 0x04, 0xc5, 0xbb, + 0x90, 0x63, 0xfb, 0x3e, 0xec, 0x6a, 0xd9, 0xbe, 0x56, 0x56, 0x1f, 0xbf, 0x1c, 0x32, 0x84, 0x3e, + 0x83, 0x25, 0xba, 0xbe, 0x41, 0x56, 0xdd, 0xbe, 0x49, 0xae, 0x4d, 0xbd, 0xeb, 0xcd, 0xe8, 0x3c, + 0x1d, 0xcb, 0x12, 0x3f, 0xda, 0xa1, 0xa1, 0x3e, 0x39, 0xcc, 0xc1, 0x3e, 0x2e, 0xa6, 0x06, 0x3f, + 0xe0, 0x25, 0x7f, 0x3b, 0x76, 0x62, 0x8d, 0xbe, 0x29, 0x51, 0x12, 0xbf, 0xf2, 0xa3, 0xaf, 0x3e, + 0x6c, 0x41, 0x77, 0xbe, 0xd8, 0x95, 0xf7, 0xbd, 0xc0, 0x86, 0x17, 0xbf, 0x14, 0xc0, 0xc1, 0xbe, + 0x35, 0xfa, 0x1b, 0x3f, 0xf1, 0x90, 0x79, 0xbd, 0x07, 0xfd, 0x52, 0x3d, 0x5b, 0x39, 0xc6, 0xbe, + 0xb1, 0x61, 0xc3, 0x3e, 0x9e, 0x5b, 0x23, 0x3f, 0x64, 0x6c, 0xe0, 0x3e, 0xd4, 0x7d, 0x25, 0x3f, + 0x82, 0x49, 0xd1, 0x3d, 0xe8, 0xba, 0x1b, 0xbe, 0x3c, 0x06, 0xe4, 0x3e, 0x65, 0x3c, 0xaa, 0xbe, + 0x6a, 0x88, 0xf1, 0x3e, 0xc3, 0x28, 0xc8, 0x3c, 0xd8, 0xc8, 0x53, 0xbd, 0xa1, 0x55, 0xc9, 0x3d, + 0xec, 0xaf, 0xeb, 0xbe, 0x54, 0x81, 0x70, 0x3e, 0x9d, 0xd1, 0x73, 0xbe, 0x3b, 0x15, 0xfe, 0x3e, + 0x1e, 0x51, 0x96, 0xbc, 0x43, 0x71, 0x36, 0x3e, 0xda, 0x66, 0x6b, 0x3e, 0x04, 0x2d, 0x86, 0xbe, + 0x23, 0xda, 0x85, 0xbd, 0xbd, 0x08, 0xd8, 0xbc, 0x4b, 0x17, 0x67, 0xbf, 0x68, 0x64, 0x30, 0xbf, + 0x08, 0x56, 0xc1, 0x3e, 0xc4, 0x18, 0x2e, 0xbe, 0xca, 0x3e, 0x9b, 0xbe, 0xcb, 0xb1, 0x5b, 0xbe, + 0xff, 0xf9, 0xac, 0xbe, 0x6a, 0x09, 0xe7, 0xbe, 0xb6, 0xaf, 0x23, 0x3f, 0xf1, 0x5d, 0xb1, 0x3e, + 0x2c, 0x1e, 0x61, 0xbe, 0x88, 0x1f, 0x32, 0xbe, 0xa9, 0x40, 0x27, 0xbb, 0xb5, 0x7d, 0x3c, 0xbe, + 0xcc, 0x09, 0x29, 0xbe, 0x9a, 0xee, 0x6b, 0x3e, 0xeb, 0xbb, 0x37, 0x3e, 0x2c, 0x11, 0x59, 0x39, + 0x09, 0x31, 0x76, 0x3e, 0xc2, 0x5a, 0xe3, 0x3e, 0xdd, 0x79, 0xaf, 0xbe, 0x55, 0x4d, 0x2c, 0xbe, + 0x0e, 0x0f, 0x11, 0xbe, 0xc3, 0x89, 0x0b, 0x3e, 0x84, 0x59, 0x6e, 0x3e, 0x8f, 0x7e, 0x27, 0x3d, + 0xf8, 0x38, 0x11, 0x3f, 0x6c, 0x00, 0xe3, 0x3e, 0x7b, 0x4a, 0x2f, 0xbe, 0xe3, 0x9f, 0xac, 0xbe, + 0x52, 0xec, 0x82, 0xbf, 0xb8, 0x8c, 0x06, 0xbf, 0x52, 0xcd, 0x8a, 0x3e, 0x9a, 0x16, 0x5b, 0xbe, + 0xbf, 0x4f, 0x24, 0x3e, 0xb2, 0xf0, 0x3e, 0x3e, 0x0d, 0x4b, 0xd0, 0xbd, 0x66, 0xed, 0x38, 0x3e, + 0x1c, 0x5b, 0xc7, 0xbe, 0x68, 0xbd, 0x87, 0x3d, 0xbb, 0xf6, 0x13, 0x3f, 0xb2, 0xd7, 0xd3, 0xbe, + 0x04, 0xdc, 0x55, 0x3e, 0xd0, 0x31, 0x1a, 0xbd, 0xed, 0xba, 0x3f, 0x3e, 0x70, 0xc6, 0x07, 0x3f, + 0x9a, 0x2d, 0x67, 0x3e, 0x46, 0xf0, 0x16, 0xbf, 0x14, 0xf8, 0xc1, 0x3e, 0x6b, 0xa1, 0xfe, 0x3e, + 0x1f, 0x4b, 0xd7, 0xbd, 0xcc, 0x84, 0x8f, 0xbe, 0x8b, 0x46, 0xf5, 0xbc, 0xb7, 0xa4, 0x2d, 0xbf, + 0x78, 0xaf, 0x7a, 0xbe, 0x65, 0xe5, 0x79, 0xbc, 0x63, 0x9a, 0xc7, 0x3d, 0x49, 0x46, 0xb9, 0xbe, + 0xc4, 0xc8, 0xb2, 0x3e, 0x93, 0xec, 0x5e, 0xbe, 0x12, 0x6a, 0x7f, 0xbf, 0xc5, 0x15, 0x68, 0xbf, + 0x25, 0xdf, 0xe5, 0xbd, 0xea, 0x69, 0x9a, 0xbd, 0xdf, 0x5a, 0x9f, 0x3e, 0xf1, 0xba, 0x85, 0x3d, + 0xfe, 0x8e, 0xa9, 0xbc, 0x21, 0x6a, 0x31, 0x3e, 0xa8, 0xf1, 0x09, 0xbf, 0x87, 0x1a, 0x93, 0xbe, + 0xc4, 0x7d, 0xc7, 0x3e, 0xad, 0x37, 0xfb, 0xbd, 0x05, 0x9d, 0xb2, 0xbe, 0x87, 0x67, 0xf3, 0xbd, + 0x61, 0xba, 0x84, 0xbe, 0xbd, 0x6f, 0x9d, 0xbf, 0xc3, 0x9e, 0xce, 0xbe, 0xaa, 0xc0, 0x33, 0x3d, + 0xef, 0xa9, 0x31, 0xbe, 0xb4, 0xff, 0xef, 0xbe, 0x26, 0x68, 0xfb, 0xbe, 0xe1, 0x47, 0x15, 0x3f, + 0x51, 0x97, 0x1c, 0x3f, 0x52, 0xe2, 0x90, 0xbe, 0xf5, 0x7a, 0x16, 0xbf, 0x8a, 0x78, 0x10, 0x3e, + 0x6d, 0xe5, 0x13, 0xbf, 0xa9, 0xe6, 0x03, 0xbe, 0x2a, 0x74, 0x05, 0xbf, 0x96, 0xa2, 0x04, 0xbf, + 0x70, 0x8f, 0xc9, 0xbe, 0x1e, 0x76, 0x19, 0xbf, 0x23, 0xec, 0xd8, 0xbd, 0xa8, 0x76, 0x45, 0x3e, + 0xc9, 0xeb, 0x12, 0x3f, 0x52, 0xb5, 0x0c, 0x3f, 0x54, 0x32, 0x77, 0xbd, 0xaf, 0x9f, 0x96, 0xbc, + 0x0d, 0x57, 0x2b, 0xbb, 0xa0, 0xe5, 0xe3, 0x3b, 0x2f, 0xd3, 0x72, 0x3e, 0x67, 0xd4, 0xba, 0xbd, + 0x93, 0x42, 0xb5, 0xbd, 0x16, 0xc9, 0xdd, 0xbe, 0xbc, 0xf8, 0x22, 0x3e, 0x9d, 0xaf, 0x09, 0x3f, + 0xe5, 0x94, 0x19, 0x3f, 0xf1, 0x69, 0x13, 0x3f, 0x22, 0x3d, 0x40, 0x3f, 0x0a, 0x41, 0x2b, 0x3f, + 0xba, 0x19, 0x75, 0x39, 0xe2, 0x69, 0x76, 0xbf, 0xe6, 0x09, 0x00, 0xbf, 0x5d, 0x61, 0x19, 0x3e, + 0xe9, 0xba, 0xbf, 0x3d, 0x14, 0xc1, 0x21, 0x3f, 0x51, 0xf4, 0xd2, 0xbe, 0xd6, 0xb8, 0x03, 0x3e, + 0xb3, 0x45, 0x88, 0xbe, 0x01, 0xd6, 0x42, 0x3e, 0x06, 0x3c, 0x81, 0x3e, 0x61, 0xc4, 0x56, 0x3d, + 0xd9, 0x81, 0xa9, 0xbe, 0xa4, 0xad, 0x02, 0x3e, 0x39, 0x5f, 0xbc, 0xbd, 0xba, 0x44, 0xc3, 0xbe, + 0x16, 0xa4, 0xcb, 0xbd, 0x04, 0x17, 0xa4, 0xbc, 0xba, 0x3c, 0x96, 0xbe, 0x28, 0xb3, 0x41, 0xbe, + 0x3e, 0x4b, 0x1f, 0x3e, 0x4a, 0x29, 0x56, 0xbe, 0x2d, 0x25, 0xf7, 0x3e, 0x5f, 0xeb, 0x06, 0x3f, + 0xfc, 0x3d, 0xa3, 0xbe, 0xe6, 0x03, 0x84, 0xbe, 0xaf, 0x0d, 0x1c, 0x3f, 0xde, 0x1d, 0xb6, 0x3d, + 0xf9, 0x1b, 0x30, 0x3d, 0xb8, 0x35, 0x22, 0xbf, 0x79, 0xcf, 0xb7, 0x3b, 0x0c, 0xba, 0xb1, 0xbb, + 0x53, 0xd9, 0x8a, 0xbe, 0x8b, 0x4f, 0xba, 0xbe, 0xe5, 0xc2, 0x78, 0xbe, 0xca, 0xff, 0xfa, 0x3c, + 0xfe, 0x4a, 0xf5, 0x3d, 0x58, 0x50, 0x39, 0x3f, 0xe4, 0x9b, 0x64, 0xbd, 0x18, 0x66, 0xbb, 0xbe, + 0x4c, 0x32, 0x09, 0x3e, 0x38, 0x66, 0x33, 0xbd, 0x00, 0x84, 0xc0, 0x3c, 0x06, 0x93, 0xf7, 0xbe, + 0xb0, 0x4d, 0x33, 0xbf, 0xd8, 0x22, 0xc2, 0x3f, 0x48, 0xd6, 0xcd, 0x3e, 0x01, 0xc9, 0x7d, 0x3d, + 0x88, 0x0c, 0x8d, 0x3e, 0x02, 0xca, 0xab, 0x3d, 0xe9, 0x32, 0xa0, 0xbe, 0xff, 0xa2, 0x5b, 0xbe, + 0xdc, 0x74, 0x42, 0xbc, 0x0b, 0xdb, 0xd9, 0x3c, 0x87, 0xc3, 0x91, 0x3d, 0xeb, 0xaf, 0x55, 0x3f, + 0xb1, 0xd7, 0xee, 0x3e, 0xd2, 0xb1, 0x4c, 0x3e, 0x53, 0x23, 0x92, 0x3e, 0xaf, 0x0a, 0x2c, 0x3e, + 0x59, 0x77, 0xdd, 0x3d, 0xb8, 0x8e, 0x82, 0xbc, 0x70, 0xe8, 0x99, 0xbc, 0x2d, 0x66, 0x50, 0x3f, + 0xd9, 0x5b, 0xc7, 0x3e, 0xae, 0x7a, 0xd0, 0xbe, 0xc6, 0x2e, 0x27, 0x3f, 0x19, 0x66, 0x4b, 0x3f, + 0x11, 0x1d, 0x56, 0xbe, 0x0d, 0xd9, 0x96, 0x3e, 0xfc, 0x70, 0xb1, 0xbc, 0xa2, 0x97, 0x21, 0x3e, + 0xaf, 0x6a, 0xab, 0x3d, 0x25, 0xe4, 0xf7, 0x3e, 0xb1, 0x2f, 0x1f, 0x3f, 0xcf, 0xb4, 0xda, 0xbd, + 0xaf, 0x44, 0x63, 0x3e, 0x4f, 0x96, 0x35, 0xbe, 0x89, 0xac, 0x29, 0xbe, 0x78, 0xb8, 0xc1, 0xbe, + 0x36, 0x87, 0x7f, 0xbf, 0x22, 0x58, 0x22, 0x3f, 0xe1, 0xdb, 0x1c, 0x3f, 0x35, 0x58, 0xf5, 0x3e, + 0xae, 0x12, 0x12, 0x3f, 0x34, 0x91, 0x5f, 0x3d, 0xbb, 0x34, 0xfe, 0x3e, 0x28, 0x03, 0x9c, 0xbe, + 0x81, 0xa8, 0x97, 0xbe, 0x6a, 0x09, 0x29, 0xbe, 0xbc, 0x2c, 0x1a, 0xbe, 0xa9, 0x90, 0x0b, 0x3d, + 0xec, 0xf5, 0x1d, 0xbf, 0x84, 0x80, 0xa2, 0xbe, 0x04, 0x3d, 0x10, 0x3d, 0x8b, 0xc3, 0x80, 0x3b, + 0x1b, 0xc3, 0xad, 0x3e, 0x09, 0x5b, 0x83, 0xbe, 0xae, 0x87, 0x85, 0xbe, 0xc2, 0x7c, 0x50, 0xbf, + 0xc9, 0x6d, 0xdd, 0xbe, 0xec, 0x53, 0x9f, 0xbe, 0x20, 0xc4, 0xfd, 0x3d, 0x08, 0x74, 0xcc, 0xbd, + 0x71, 0xfa, 0x21, 0x3c, 0x86, 0x61, 0x61, 0xbe, 0x12, 0x14, 0x6d, 0x3e, 0xe3, 0x98, 0x1b, 0x3e, + 0xf2, 0xce, 0x2d, 0x3f, 0x55, 0x00, 0x14, 0xbe, 0xb4, 0x1e, 0xc3, 0xbf, 0x3a, 0x4e, 0x56, 0x3e, + 0x30, 0x76, 0x44, 0xbe, 0x27, 0x7a, 0x9e, 0x3d, 0xa1, 0x37, 0x9a, 0xbe, 0xa7, 0x29, 0xad, 0xbf, + 0x03, 0x1d, 0xe1, 0x3e, 0xc7, 0x41, 0x90, 0x3e, 0x6d, 0x13, 0x68, 0xbd, 0x8d, 0x41, 0xc9, 0x3e, + 0x31, 0x7c, 0xe5, 0x3c, 0x35, 0x8c, 0xee, 0xbe, 0x63, 0xca, 0x41, 0xbd, 0xed, 0x8c, 0x2b, 0xbf, + 0x17, 0x2d, 0x14, 0xbf, 0x9f, 0xed, 0x34, 0xbf, 0x64, 0x85, 0xc7, 0x3e, 0x44, 0x25, 0x1a, 0x3e, + 0x5a, 0xaf, 0x57, 0x3d, 0x83, 0xbd, 0x28, 0x3e, 0x5a, 0xc0, 0x2c, 0x3f, 0x90, 0x92, 0xb4, 0x3e, + 0xe7, 0x71, 0x0f, 0x3f, 0x5e, 0xf6, 0x04, 0xbc, 0x6b, 0x47, 0x34, 0x3e, 0xa5, 0x13, 0x91, 0xbd, + 0x9b, 0x46, 0xce, 0xbd, 0xd9, 0xee, 0x9d, 0x3d, 0x27, 0x43, 0xa4, 0x3e, 0xe4, 0x3b, 0xd0, 0xbd, + 0x45, 0x2d, 0x7d, 0x3e, 0xb6, 0x85, 0x92, 0xbe, 0x24, 0x88, 0x4c, 0xbd, 0x6c, 0x85, 0x52, 0xbe, + 0x21, 0xe7, 0x70, 0x3e, 0xdc, 0xcc, 0x0c, 0x3f, 0x12, 0xda, 0x7e, 0x3d, 0x7a, 0x0c, 0xb3, 0xbe, + 0x90, 0xb5, 0x68, 0x3e, 0xda, 0x68, 0xcd, 0x3c, 0xa7, 0xeb, 0xf3, 0xbe, 0x71, 0xa1, 0xd9, 0xbb, + 0x5a, 0xd4, 0x03, 0x3e, 0x48, 0x5c, 0xd4, 0xbc, 0x74, 0x8d, 0x32, 0x3e, 0x22, 0x76, 0xab, 0x3d, + 0xf3, 0xd4, 0x16, 0x3b, 0xd6, 0x91, 0x15, 0x3f, 0x41, 0x14, 0x38, 0x3f, 0x36, 0x99, 0x8c, 0x3d, + 0x02, 0x8e, 0x90, 0xbd, 0x25, 0x30, 0x85, 0xbe, 0xc0, 0xb2, 0x18, 0x3d, 0xdb, 0x7a, 0x98, 0xbe, + 0xe8, 0x5e, 0x17, 0x3f, 0x02, 0x29, 0x17, 0x3e, 0xb1, 0x80, 0x08, 0x3f, 0xd8, 0x43, 0x5b, 0x3f, + 0x02, 0x19, 0x13, 0xbe, 0x94, 0xdf, 0x0e, 0x3d, 0xf7, 0x12, 0x76, 0xbd, 0x58, 0xe8, 0x91, 0x3e, + 0xb9, 0xbe, 0x48, 0xbd, 0xd1, 0x74, 0x98, 0xbe, 0x2a, 0x12, 0x4c, 0x3f, 0xab, 0xc1, 0x51, 0x3e, + 0x55, 0x88, 0x88, 0xbd, 0x18, 0xfc, 0x86, 0x3e, 0x5a, 0xa2, 0x9a, 0x3e, 0x99, 0x7b, 0x28, 0x3e, + 0xa9, 0xc1, 0x81, 0x3e, 0xef, 0xd3, 0x52, 0x3f, 0xa5, 0xf9, 0x71, 0x3e, 0x7b, 0x6b, 0xd0, 0x3e, + 0x05, 0xa0, 0xde, 0x3e, 0x93, 0x96, 0x03, 0x3f, 0xf2, 0x2b, 0x5e, 0xbe, 0x37, 0x71, 0xe1, 0xbd, + 0x5d, 0xf5, 0x08, 0x3f, 0xe8, 0x39, 0x42, 0xbe, 0x24, 0xe8, 0x1c, 0x3e, 0x40, 0x15, 0xbe, 0xbe, + 0xc1, 0x8f, 0xac, 0x3d, 0xf0, 0xe8, 0x79, 0x3e, 0x4e, 0x2c, 0x1d, 0xbe, 0xdb, 0x53, 0x96, 0xbd, + 0xd0, 0x4c, 0xce, 0x3d, 0x42, 0x9b, 0xff, 0xbd, 0x59, 0xa1, 0xee, 0xbb, 0x59, 0xca, 0xa4, 0xbe, + 0xf0, 0xb7, 0x3c, 0x3e, 0x8e, 0x06, 0x80, 0x3e, 0xf4, 0xa7, 0x18, 0xbf, 0x84, 0xd4, 0x94, 0xbc, + 0xbe, 0xf8, 0x2f, 0xbe, 0x81, 0x77, 0x71, 0x3e, 0x28, 0xfc, 0xcb, 0x3d, 0x14, 0x03, 0xf8, 0xbd, + 0x08, 0x4d, 0x8a, 0x3e, 0xaa, 0xf2, 0xde, 0x3e, 0xa1, 0xfe, 0x55, 0x3e, 0x31, 0xd7, 0xcb, 0xbd, + 0x3d, 0x0a, 0x47, 0xbe, 0x04, 0x3c, 0x88, 0xbe, 0x3e, 0xfb, 0x85, 0x3e, 0xf8, 0x54, 0x10, 0xbe, + 0xc9, 0x54, 0x21, 0x3f, 0xaa, 0x10, 0x06, 0x3f, 0xa5, 0xe7, 0x0c, 0x3f, 0x03, 0x6d, 0x04, 0xbf, + 0xea, 0xb2, 0x1e, 0xbe, 0xec, 0x9d, 0x09, 0xbe, 0x48, 0xeb, 0xaf, 0xbf, 0xf0, 0x2a, 0x08, 0x3e, + 0x72, 0x78, 0x1b, 0x3e, 0x37, 0x43, 0x09, 0xbf, 0x7c, 0x91, 0xcb, 0x3e, 0x2f, 0x1d, 0x48, 0xbe, + 0x6a, 0x71, 0x10, 0xbe, 0xc6, 0xbe, 0xfc, 0xbe, 0x72, 0x81, 0xd7, 0xbe, 0xc1, 0xed, 0x11, 0x3f, + 0xff, 0x85, 0x1a, 0xbe, 0x5b, 0x4a, 0x66, 0x3e, 0xe6, 0x5a, 0xf2, 0x3c, 0xce, 0xc2, 0xed, 0xbe, + 0x34, 0x7b, 0x34, 0xbe, 0x12, 0xc6, 0xd5, 0xbd, 0xa6, 0x35, 0xb2, 0xbd, 0x7d, 0xa5, 0xda, 0x3e, + 0x42, 0x09, 0x24, 0xbe, 0xa4, 0x59, 0xbd, 0x3e, 0x2a, 0x23, 0xc1, 0x3e, 0xcd, 0x02, 0xe5, 0xbe, + 0x9d, 0xc4, 0x07, 0x3f, 0x4d, 0x27, 0xbf, 0xbe, 0x5e, 0xcd, 0x07, 0xbf, 0x71, 0x2a, 0x81, 0xbd, + 0xa5, 0xaa, 0x95, 0xbd, 0x7b, 0xac, 0xad, 0x3e, 0x73, 0x7a, 0xc4, 0x3e, 0xca, 0x34, 0xff, 0x3e, + 0x25, 0x7d, 0xc3, 0xbe, 0x9b, 0xd4, 0xf0, 0xbe, 0xd4, 0x24, 0x2d, 0x3d, 0xc0, 0xc6, 0xda, 0x3e, + 0xb4, 0xaa, 0xe6, 0xbe, 0x95, 0xb6, 0x76, 0xbc, 0xd3, 0x65, 0x2e, 0x3f, 0x99, 0x80, 0x49, 0x3f, + 0x10, 0x25, 0xdf, 0xbd, 0x16, 0x07, 0x86, 0x3f, 0xa1, 0xe9, 0xf7, 0xbe, 0x34, 0xdf, 0xd5, 0xbe, + 0xb6, 0x1f, 0xac, 0xbe, 0xb3, 0xce, 0x84, 0xbf, 0x6b, 0x0f, 0x6e, 0xbf, 0x88, 0xe8, 0x1c, 0x3e, + 0x76, 0x40, 0xaf, 0xbe, 0xa8, 0xa4, 0xf9, 0xbe, 0x9d, 0xa7, 0x8e, 0x3e, 0x69, 0x89, 0x80, 0x3e, + 0x23, 0xdd, 0x02, 0xbf, 0xbd, 0x52, 0x49, 0xbe, 0x44, 0x9e, 0x87, 0x3e, 0x14, 0xbe, 0x82, 0x3e, + 0x08, 0x85, 0xaa, 0xbc, 0x0f, 0x3a, 0x80, 0x3e, 0xf2, 0x80, 0x90, 0xbe, 0x90, 0xd8, 0xf9, 0x3c, + 0x0a, 0x9f, 0x29, 0xbf, 0x25, 0xa6, 0x00, 0xbe, 0xed, 0xb1, 0x79, 0xbf, 0x19, 0x06, 0x73, 0xbf, + 0xbd, 0x08, 0x5f, 0xbf, 0x75, 0xa0, 0xbe, 0x3e, 0xb0, 0xe9, 0x8a, 0x3e, 0xc2, 0xd7, 0xec, 0xbc, + 0xbc, 0xac, 0xcc, 0x3e, 0x03, 0xd5, 0x3d, 0x3f, 0x9c, 0x67, 0xaf, 0x3e, 0x4f, 0x1a, 0xf4, 0xbe, + 0x64, 0x75, 0x84, 0xbd, 0xc9, 0x24, 0xae, 0xbf, 0x5c, 0x7f, 0xf5, 0xbe, 0xb7, 0x9c, 0xdb, 0xbd, + 0x03, 0xf1, 0x1b, 0xbf, 0x72, 0xca, 0xc4, 0x3e, 0xe0, 0xf0, 0x30, 0xbf, 0x1a, 0x5a, 0x82, 0x3c, + 0xdb, 0xd0, 0x04, 0x3f, 0xf4, 0x83, 0xa7, 0xbe, 0x37, 0x11, 0xb4, 0xbd, 0xb0, 0xb5, 0x04, 0xbe, + 0xaf, 0x49, 0x67, 0xbe, 0x7d, 0x27, 0x07, 0xbf, 0x47, 0xcd, 0xf7, 0xbe, 0x43, 0xa0, 0x85, 0x3c, + 0xba, 0xc7, 0x1c, 0xbf, 0x12, 0x52, 0xc8, 0x3e, 0xfe, 0xdf, 0x02, 0x3e, 0x63, 0xf6, 0x17, 0x3f, + 0xf2, 0xa6, 0x80, 0x3a, 0x40, 0x97, 0x16, 0x3e, 0x93, 0xa2, 0xaa, 0xbe, 0xa1, 0x20, 0x4e, 0xbe, + 0x74, 0x1a, 0xe7, 0x3e, 0x77, 0x87, 0x5f, 0x3f, 0x63, 0x2e, 0xa0, 0xbe, 0xff, 0x57, 0xe5, 0x3e, + 0xd5, 0xf3, 0x79, 0x3e, 0x15, 0xac, 0x10, 0xbe, 0xc4, 0x54, 0x50, 0x3d, 0x5e, 0xb8, 0xda, 0xbe, + 0xda, 0xb9, 0x94, 0x3e, 0xe1, 0x89, 0x69, 0xbe, 0xaf, 0xca, 0x63, 0xbf, 0x2f, 0x7b, 0x58, 0x3f, + 0xae, 0x2c, 0x2d, 0x3f, 0x9f, 0xc4, 0xaf, 0x3e, 0xba, 0x2c, 0x88, 0x3e, 0x41, 0x02, 0xe2, 0x3e, + 0x96, 0xb6, 0xcf, 0x3e, 0x8b, 0x13, 0xd3, 0x3d, 0xfa, 0x37, 0x93, 0xbe, 0x4c, 0x20, 0x1b, 0xbb, + 0x40, 0xd4, 0x91, 0x3e, 0x5d, 0x18, 0xe2, 0x3d, 0x8e, 0xe1, 0xcd, 0x3d, 0xe7, 0xc2, 0xfd, 0x3e, + 0x43, 0x69, 0x62, 0xbf, 0x71, 0x4d, 0x56, 0xbe, 0x87, 0x75, 0x03, 0x3f, 0x13, 0x67, 0xc1, 0x3e, + 0x4c, 0x16, 0x2b, 0x3e, 0x21, 0x05, 0x84, 0x3e, 0x5c, 0x88, 0x06, 0xbe, 0x77, 0x6d, 0x88, 0x3d, + 0xa4, 0x1d, 0x2b, 0xbe, 0x5a, 0x07, 0xcb, 0x3e, 0x23, 0x57, 0x10, 0x3f, 0x93, 0xd0, 0xcb, 0xbe, + 0x2b, 0xbf, 0xf0, 0x3e, 0x3a, 0x3d, 0x2f, 0xbf, 0x03, 0x14, 0xb7, 0x3e, 0x4f, 0x12, 0x07, 0x3f, + 0x15, 0x34, 0x0e, 0xbf, 0xa0, 0xad, 0xaf, 0x3d, 0x61, 0x70, 0x95, 0x3e, 0xc3, 0xbf, 0xfa, 0x3c, + 0x5f, 0xd8, 0xcc, 0xbd, 0xe4, 0xb8, 0x30, 0x3e, 0x1b, 0xc5, 0xfe, 0xbe, 0xea, 0x3a, 0xee, 0xbd, + 0x2c, 0xcb, 0x62, 0xbe, 0xa5, 0xab, 0x63, 0xbe, 0xd4, 0xa4, 0x41, 0xbe, 0x75, 0x6f, 0x87, 0xbd, + 0x76, 0x5a, 0x85, 0x3e, 0x1c, 0xac, 0x98, 0x3e, 0x91, 0x77, 0x1f, 0xbe, 0x6c, 0x25, 0x9e, 0xbe, + 0x4b, 0x52, 0x54, 0xbe, 0x28, 0x7c, 0x17, 0x3e, 0x62, 0xfb, 0x8f, 0xbe, 0x0c, 0xa5, 0x55, 0xbe, + 0x4a, 0x76, 0xcb, 0xbe, 0xbf, 0xe5, 0xb3, 0x3e, 0xc7, 0xdf, 0x5b, 0x3c, 0x73, 0xc8, 0x9e, 0xbe, + 0x83, 0x1d, 0xb5, 0xbe, 0x4f, 0x12, 0xa9, 0xbd, 0x01, 0xec, 0x59, 0xbf, 0x5b, 0xad, 0x47, 0xbf, + 0x14, 0xf3, 0x81, 0xbf, 0x02, 0x67, 0xbd, 0xbe, 0xaa, 0x93, 0x03, 0x3c, 0xe6, 0xea, 0x32, 0xbf, + 0x68, 0x27, 0xd8, 0xbe, 0x0a, 0xe5, 0xf0, 0xbb, 0x0f, 0x3e, 0x66, 0xbd, 0x68, 0x48, 0x35, 0x3f, + 0xca, 0x85, 0x92, 0x3d, 0x01, 0x53, 0x18, 0x3d, 0x12, 0x96, 0x37, 0xbf, 0xfc, 0xbf, 0xb6, 0xbe, + 0x64, 0xf8, 0x9c, 0x3b, 0x45, 0x4d, 0x3f, 0x3d, 0xcc, 0x2b, 0xd3, 0x3a, 0xb8, 0x90, 0xd8, 0x3d, + 0xa5, 0xcc, 0xa4, 0xbe, 0x8b, 0x4b, 0x13, 0xbf, 0xaa, 0x5f, 0x13, 0x3d, 0x3c, 0x9a, 0xc9, 0xbe, + 0x8b, 0xe2, 0x04, 0x3e, 0xb1, 0xf3, 0x64, 0xbd, 0x87, 0x72, 0x19, 0x3e, 0xbd, 0x9e, 0x6b, 0x3f, + 0x8b, 0xc1, 0x43, 0x3f, 0xd8, 0x0d, 0x41, 0xbe, 0x05, 0x17, 0xd9, 0x3c, 0x7a, 0x68, 0x8e, 0xbf, + 0xeb, 0x49, 0xb4, 0x3a, 0xbd, 0xa2, 0x9f, 0x3d, 0x9b, 0xa3, 0xff, 0x3e, 0x98, 0x54, 0xe7, 0x3d, + 0x38, 0xc9, 0x3a, 0xbe, 0xa0, 0xd6, 0x97, 0xbe, 0x7f, 0xd4, 0x00, 0xbf, 0xc3, 0xe1, 0xad, 0xbe, + 0xe7, 0xff, 0x0b, 0x3d, 0x68, 0xd3, 0xa3, 0x3e, 0x13, 0x4c, 0x79, 0xbe, 0xe0, 0xc6, 0xfd, 0x3e, + 0x2c, 0x43, 0x0c, 0xbf, 0xc2, 0x8a, 0xe6, 0xbd, 0xaf, 0x42, 0xe8, 0xbe, 0x8d, 0xcf, 0x0a, 0x3f, + 0x86, 0x08, 0xe9, 0xbd, 0x49, 0x94, 0x72, 0x3d, 0xc1, 0xfd, 0xd0, 0x3e, 0x40, 0x8b, 0x32, 0xbd, + 0x7c, 0xb8, 0xa4, 0x3e, 0xc5, 0xfe, 0xe9, 0x3e, 0xf6, 0x2f, 0x0b, 0xbf, 0x88, 0x00, 0xe0, 0xbe, + 0xcd, 0xa3, 0x25, 0x3f, 0x27, 0xc0, 0x9d, 0xbf, 0x01, 0xaf, 0x86, 0xbf, 0x0b, 0x3a, 0xbc, 0xbe, + 0x58, 0xaa, 0x60, 0x3e, 0xcb, 0x83, 0x0b, 0xbf, 0x5e, 0x75, 0x69, 0x3f, 0xa5, 0xda, 0xb2, 0x3e, + 0x4c, 0xb5, 0x72, 0xbd, 0xaa, 0x4f, 0x2a, 0x3e, 0x4b, 0xce, 0xb0, 0x3e, 0x19, 0xf5, 0x45, 0x3e, + 0xe3, 0xba, 0xba, 0xbc, 0x30, 0x95, 0x1a, 0xbe, 0x9d, 0x2f, 0xdd, 0x3e, 0x36, 0x25, 0x9f, 0xbe, + 0x1b, 0xdc, 0xbc, 0xbd, 0xe1, 0x4a, 0x81, 0xbc, 0x53, 0xb1, 0xbb, 0xbd, 0x11, 0x2a, 0xe5, 0xbd, + 0xc0, 0xec, 0x1a, 0x3c, 0xa9, 0x64, 0xa9, 0x3e, 0xc8, 0xa9, 0xc0, 0x3c, 0x7e, 0xbf, 0x60, 0x3f, + 0x7e, 0x47, 0x13, 0xbf, 0x87, 0x2c, 0x6b, 0xbe, 0x6c, 0xab, 0x02, 0x3f, 0xce, 0xc1, 0x08, 0xbf, + 0x17, 0x53, 0x64, 0xbd, 0x51, 0xfa, 0xcd, 0x3e, 0x0a, 0x33, 0x90, 0x3e, 0x84, 0x98, 0xd4, 0xbe, + 0x30, 0x8c, 0xad, 0xbd, 0x70, 0x04, 0xac, 0x3e, 0xce, 0x33, 0x3c, 0x3d, 0xb8, 0xdd, 0xc1, 0x3e, + 0x4a, 0x84, 0xfa, 0x3e, 0x5a, 0x31, 0x08, 0xbd, 0x88, 0xf1, 0x33, 0x3f, 0x7a, 0xd8, 0xa3, 0xbb, + 0x91, 0x4f, 0xc1, 0xbd, 0xfe, 0xdb, 0x5e, 0xbf, 0x80, 0xf1, 0x39, 0x3d, 0x70, 0x6d, 0x5f, 0x3e, + 0x78, 0xd3, 0xef, 0xbe, 0x27, 0x9c, 0x16, 0xbd, 0xf8, 0x8e, 0x81, 0xbe, 0x6f, 0x38, 0x18, 0xbe, + 0x69, 0x48, 0x0a, 0xbf, 0xa3, 0x3c, 0x06, 0x3e, 0x62, 0x28, 0xb5, 0xbe, 0x6a, 0x55, 0xfb, 0xbe, + 0x77, 0x85, 0x2d, 0x3e, 0x8a, 0x02, 0x9c, 0xbd, 0x29, 0xaa, 0x6b, 0xbd, 0x1b, 0xe9, 0xc6, 0xbb, + 0x74, 0xf6, 0x93, 0xbe, 0xf4, 0x93, 0x08, 0x3e, 0xc7, 0x51, 0x0e, 0x3e, 0xaa, 0x3d, 0x47, 0xbf, + 0x7b, 0x1e, 0xa2, 0x3e, 0x0f, 0x43, 0xee, 0x3e, 0x24, 0x8b, 0x95, 0xbf, 0xe8, 0xd4, 0xf1, 0xbc, + 0x3c, 0x52, 0xdc, 0x3e, 0x4a, 0x25, 0xa5, 0xbe, 0xe7, 0xc1, 0x83, 0xbe, 0x7d, 0x48, 0x1b, 0xbf, + 0x1c, 0xc5, 0x03, 0xbf, 0xf7, 0xb7, 0xf3, 0x3e, 0xdf, 0x99, 0xa6, 0x3e, 0x1b, 0x45, 0x20, 0xbf, + 0x26, 0x1f, 0xae, 0x3e, 0x7b, 0x91, 0x1f, 0xbe, 0x36, 0x44, 0xe5, 0xbe, 0xcf, 0x9e, 0xd1, 0xbe, + 0xf2, 0x62, 0x1c, 0x3e, 0x07, 0xe7, 0x21, 0xbf, 0x95, 0x41, 0x54, 0xbf, 0x61, 0xfb, 0x1b, 0xbf, + 0xa1, 0x9e, 0x15, 0xbf, 0xe0, 0x7d, 0x01, 0xbf, 0x07, 0x77, 0xbe, 0xbe, 0x80, 0x63, 0x76, 0xbe, + 0x87, 0x2b, 0x8a, 0xbe, 0xd9, 0xc0, 0x45, 0xbe, 0xf0, 0x51, 0x06, 0x3e, 0x6d, 0xbc, 0xe9, 0x3d, + 0xcb, 0xab, 0xeb, 0x3e, 0x82, 0xf8, 0xcd, 0x3d, 0x63, 0x1e, 0x5d, 0xbe, 0x62, 0xe8, 0x91, 0xbe, + 0x25, 0xfd, 0xaf, 0x3e, 0x59, 0x5a, 0xad, 0xbd, 0x6c, 0x19, 0x3f, 0xbe, 0xab, 0xc6, 0x25, 0x3e, + 0x09, 0xc4, 0x14, 0x3f, 0x0a, 0x9d, 0x30, 0x3f, 0x61, 0x87, 0xcf, 0xbc, 0x59, 0x7e, 0x0e, 0x3f, + 0x15, 0xd5, 0x90, 0x3f, 0xec, 0xf2, 0x94, 0xbe, 0x37, 0xa3, 0x97, 0x3e, 0x9d, 0xaa, 0x69, 0x3d, + 0x83, 0x1c, 0x37, 0xbf, 0x9f, 0x0f, 0xc2, 0xbd, 0xf3, 0xe5, 0x0c, 0xbe, 0x9c, 0x5b, 0xe1, 0xbe, + 0xec, 0x9b, 0xc4, 0x3e, 0xd3, 0xf3, 0xe2, 0xbe, 0x2a, 0x8e, 0x4a, 0xbd, 0x19, 0x73, 0xa5, 0xbe, + 0x93, 0xce, 0xe1, 0xbe, 0xe1, 0xd2, 0x7f, 0x3e, 0x28, 0x37, 0x03, 0x3f, 0xad, 0xce, 0xcd, 0x3e, + 0x6e, 0xaf, 0xd5, 0xbd, 0x92, 0x05, 0x45, 0xbf, 0x13, 0x9b, 0x89, 0xbe, 0x0c, 0x5b, 0xd7, 0x3c, + 0x73, 0x22, 0x83, 0xbe, 0xb1, 0xf7, 0x69, 0xbe, 0x1f, 0x03, 0x42, 0xbe, 0x40, 0x30, 0x51, 0x3d, + 0x99, 0x1d, 0xa4, 0xbd, 0x67, 0x25, 0xad, 0xbe, 0xb5, 0x9e, 0x21, 0xbe, 0xe9, 0x1c, 0x1a, 0xbe, + 0x89, 0x41, 0xb3, 0xbe, 0xd6, 0xf2, 0x17, 0xbf, 0xe1, 0x1a, 0x93, 0xbe, 0x63, 0x54, 0x2f, 0x3f, + 0xe9, 0x92, 0x77, 0x3f, 0x7b, 0x29, 0x18, 0x3f, 0x89, 0x8f, 0x8d, 0xbd, 0xe8, 0x0b, 0x38, 0xbd, + 0xdd, 0x0f, 0xd0, 0x3e, 0x17, 0x4d, 0x07, 0xbe, 0x4c, 0xcf, 0xdb, 0x3d, 0x4a, 0x9b, 0xfe, 0x3d, + 0x14, 0xce, 0xf4, 0xbe, 0xf5, 0xf5, 0x7e, 0xbd, 0x02, 0xc5, 0x46, 0x3c, 0x69, 0x5d, 0x4e, 0xbe, + 0x5c, 0x93, 0x42, 0xbe, 0xcf, 0xac, 0xdb, 0xbe, 0x85, 0x66, 0xea, 0x3e, 0xe5, 0x9a, 0x21, 0xbe, + 0x8d, 0x97, 0xf3, 0xbe, 0x6a, 0xff, 0xb3, 0x3e, 0x2c, 0x93, 0xda, 0xbd, 0xe3, 0x5c, 0xb0, 0x3c, + 0x6f, 0xf6, 0xad, 0xbd, 0x4d, 0x4b, 0x76, 0x3e, 0xe2, 0xaa, 0x43, 0x3f, 0x2a, 0xd5, 0x58, 0x3e, + 0x15, 0xff, 0x52, 0x3f, 0xec, 0x17, 0x93, 0x3e, 0x5e, 0x43, 0x99, 0xbe, 0xe8, 0x24, 0x13, 0x3d, + 0xcc, 0x62, 0xfc, 0x3e, 0xba, 0xe3, 0x4f, 0x3e, 0xee, 0x49, 0xce, 0x3e, 0xc7, 0x72, 0xff, 0x3e, + 0x77, 0x96, 0x09, 0x3f, 0x77, 0x3d, 0x8b, 0x3e, 0x06, 0x12, 0xae, 0x3e, 0xb3, 0xba, 0x54, 0x3e, + 0x6c, 0x9f, 0x72, 0xbc, 0x80, 0xf9, 0x42, 0xbe, 0x42, 0x89, 0xd4, 0x3c, 0xda, 0x46, 0x99, 0xbe, + 0x72, 0x28, 0x14, 0x3d, 0xfd, 0x0b, 0x74, 0xbc, 0x58, 0x7a, 0x50, 0x3d, 0x68, 0xfd, 0xb5, 0x3d, + 0x25, 0x9d, 0x94, 0xbc, 0xc9, 0x69, 0x5f, 0x3c, 0x11, 0x83, 0x2e, 0xbe, 0x4f, 0x40, 0xc8, 0x3d, + 0xa6, 0x23, 0xaf, 0x3b, 0x2a, 0x6b, 0xaf, 0xbd, 0x71, 0x4c, 0x19, 0x3f, 0xff, 0x6c, 0x90, 0x3e, + 0x76, 0x80, 0x73, 0x3e, 0x1a, 0x46, 0x33, 0xbe, 0xab, 0x84, 0x55, 0x3e, 0xb0, 0x69, 0x14, 0x3f, + 0xcb, 0x3a, 0xcc, 0xbe, 0x5c, 0xba, 0x01, 0xbd, 0x1c, 0xaf, 0x63, 0xbe, 0x83, 0x53, 0x86, 0x3e, + 0xa0, 0xef, 0x96, 0x3e, 0x45, 0x28, 0x54, 0x3e, 0xd5, 0x83, 0x8a, 0x3e, 0x0b, 0x20, 0xbf, 0xbd, + 0x6f, 0x44, 0x92, 0xbe, 0x7b, 0x74, 0xb6, 0xbd, 0x5e, 0x13, 0x34, 0x3e, 0xcb, 0xe2, 0x64, 0x3e, + 0x3a, 0x4a, 0x70, 0xbe, 0xa0, 0x94, 0xf0, 0xbe, 0x1f, 0x62, 0x93, 0xbf, 0x2f, 0x84, 0x86, 0xbe, + 0x35, 0x45, 0x1f, 0xbe, 0x55, 0x53, 0xb6, 0x3c, 0x93, 0x86, 0xeb, 0xbe, 0x38, 0xb5, 0x71, 0xbf, + 0xb1, 0xbd, 0x2c, 0xbf, 0x68, 0x5d, 0x5c, 0x3e, 0x9e, 0x74, 0x77, 0xbe, 0xcb, 0x5f, 0x15, 0xbf, + 0xca, 0xb5, 0xbd, 0xbe, 0xbd, 0x26, 0xde, 0xbf, 0x60, 0x9e, 0x16, 0xbf, 0x52, 0xeb, 0xbb, 0xbb, + 0xb0, 0xeb, 0xc7, 0xbe, 0xc3, 0xfb, 0x0d, 0x3f, 0x0b, 0xac, 0xe9, 0x3e, 0xf4, 0xa8, 0x69, 0x3d, + 0x4c, 0xe8, 0x2a, 0xbf, 0xd1, 0xfc, 0xcd, 0xbd, 0x54, 0x76, 0x39, 0xbe, 0xf3, 0x50, 0xd0, 0xbe, + 0x05, 0xbc, 0xce, 0xbd, 0xa0, 0x8b, 0x01, 0xbf, 0x14, 0x26, 0x1d, 0xbf, 0x78, 0x88, 0x4e, 0xbf, + 0x7d, 0x50, 0xd0, 0xbe, 0xaf, 0x58, 0x68, 0x3d, 0x77, 0x5f, 0x5c, 0xbf, 0xf2, 0xa9, 0x2a, 0xbe, + 0xb3, 0xc0, 0x2c, 0xbd, 0x17, 0xf4, 0x96, 0x3e, 0x83, 0xda, 0x1f, 0xbc, 0xec, 0x75, 0x64, 0x3e, + 0x62, 0xbf, 0x19, 0x3e, 0x91, 0xd9, 0x77, 0x3f, 0x5c, 0xf0, 0x77, 0x3e, 0x37, 0x55, 0xaa, 0xbe, + 0x28, 0xb1, 0x24, 0x3e, 0xa3, 0x66, 0x9c, 0x3d, 0xde, 0x95, 0x85, 0x3e, 0xb9, 0xc5, 0x6e, 0xbf, + 0xc2, 0xb1, 0x76, 0xbe, 0xbf, 0x37, 0x85, 0xbe, 0x94, 0x01, 0xab, 0x3d, 0x0e, 0x35, 0xc0, 0x3d, + 0xd6, 0xaf, 0xde, 0x3b, 0x77, 0x4d, 0x23, 0xbe, 0xb8, 0xb4, 0x5e, 0xbf, 0x0b, 0xd6, 0x27, 0xbf, + 0x5a, 0x4a, 0x79, 0x3d, 0x4d, 0xb7, 0x46, 0xbf, 0xd3, 0x1b, 0x4f, 0xbf, 0x7c, 0x6f, 0x50, 0xbe, + 0x1d, 0xbe, 0x6c, 0xbe, 0xb0, 0x34, 0x85, 0x3f, 0x5e, 0x54, 0x39, 0x3f, 0x51, 0x23, 0x17, 0xbf, + 0x9c, 0xa7, 0x16, 0x3f, 0xa5, 0x33, 0x21, 0x3d, 0xf2, 0xe3, 0xfd, 0xbc, 0x4c, 0x30, 0xac, 0x3d, + 0x6e, 0xa5, 0x40, 0xbf, 0x8b, 0x16, 0x05, 0xbf, 0x9f, 0x98, 0x89, 0x3f, 0x84, 0xf6, 0x59, 0x3f, + 0x00, 0x19, 0xed, 0xbe, 0xc5, 0xb9, 0xf9, 0x3e, 0x3b, 0x5d, 0xff, 0x3d, 0xf7, 0x98, 0xa6, 0x3d, + 0xb7, 0xb3, 0x01, 0x3e, 0x30, 0x66, 0xfb, 0xbe, 0xce, 0x99, 0x46, 0x3e, 0x38, 0x42, 0xff, 0x3e, + 0xbe, 0xf8, 0x57, 0x3f, 0x67, 0x59, 0x2d, 0x3e, 0x48, 0x75, 0x81, 0x3f, 0xb2, 0x8d, 0x21, 0x3e, + 0x43, 0x27, 0xe7, 0x3c, 0x7a, 0x4c, 0x23, 0x3f, 0x0c, 0xdd, 0x8f, 0x3d, 0x13, 0x26, 0x04, 0x3f, + 0xae, 0xd4, 0x40, 0xbe, 0xa6, 0x69, 0x9f, 0xbe, 0x60, 0x04, 0x91, 0xbf, 0xbc, 0x2a, 0xbb, 0x3e, + 0xed, 0x4a, 0xc1, 0xbe, 0x69, 0x4e, 0x15, 0xbe, 0xff, 0x78, 0x9b, 0x3e, 0x70, 0x20, 0x9b, 0xbe, + 0xd2, 0x67, 0xe7, 0xbe, 0x11, 0x23, 0x71, 0x3f, 0x80, 0xb6, 0xbc, 0x3e, 0xed, 0x5c, 0xd1, 0x3d, + 0x54, 0x4a, 0xe2, 0x3e, 0xa0, 0x38, 0xe4, 0x3e, 0xee, 0xd1, 0x0c, 0xbe, 0x74, 0x66, 0x48, 0xbe, + 0x45, 0x53, 0x14, 0xbe, 0x3e, 0x1d, 0x08, 0xbf, 0xf9, 0x87, 0x23, 0xbf, 0x20, 0xf2, 0x24, 0xbe, + 0xb4, 0x58, 0x50, 0xbf, 0x7c, 0x93, 0xc7, 0xbe, 0x3e, 0x4a, 0xa7, 0xbe, 0xf9, 0xc4, 0x7e, 0x3e, + 0x51, 0xa1, 0x97, 0x3d, 0xde, 0xea, 0x28, 0xbf, 0xab, 0xce, 0x17, 0x3e, 0xf6, 0x97, 0x69, 0xbf, + 0xc3, 0xcf, 0x71, 0xbe, 0x80, 0x09, 0x6c, 0xbf, 0x73, 0x93, 0xac, 0xbe, 0x42, 0x72, 0xdd, 0x3e, + 0xc4, 0x7c, 0xa6, 0xbe, 0xe4, 0xa9, 0xaa, 0x3e, 0x44, 0x58, 0xdc, 0x3e, 0x07, 0x1c, 0x44, 0x3f, + 0xbc, 0xb1, 0x33, 0xbf, 0x60, 0xdd, 0x22, 0x3f, 0xce, 0x26, 0x83, 0x3f, 0x35, 0x95, 0x38, 0x3d, + 0xcd, 0xb2, 0x9e, 0x3e, 0x78, 0xd2, 0x75, 0x3e, 0x0b, 0xfa, 0xf7, 0xbe, 0x55, 0x33, 0x56, 0xbf, + 0x3e, 0x42, 0x9d, 0xbf, 0x10, 0xa8, 0x98, 0x3d, 0x1f, 0x97, 0x3a, 0x3f, 0xf3, 0xa7, 0x79, 0xbd, + 0x17, 0xd7, 0xc2, 0x3d, 0xcf, 0xbb, 0xe5, 0x3d, 0xcb, 0x3a, 0xbe, 0xbe, 0xdc, 0x3d, 0x0b, 0x3e, + 0x49, 0x36, 0xdc, 0x3e, 0xdc, 0x81, 0x68, 0x3e, 0xac, 0x9a, 0x3a, 0x3e, 0x36, 0xb0, 0x20, 0x3f, + 0x23, 0xf4, 0x3e, 0x3f, 0x50, 0x3e, 0xf9, 0xbd, 0xf3, 0x58, 0x83, 0x3e, 0x89, 0x29, 0x83, 0xbe, + 0x57, 0xe5, 0x2b, 0xbe, 0x77, 0x31, 0x8e, 0xbe, 0xc9, 0xf6, 0x35, 0x3e, 0x4c, 0xb2, 0x19, 0x3f, + 0x92, 0x80, 0xb0, 0x3e, 0xc8, 0x4d, 0x1b, 0x3f, 0x2d, 0xd3, 0x48, 0x3d, 0xa6, 0x9f, 0x15, 0x3c, + 0xe9, 0xd5, 0x8b, 0x3e, 0x88, 0xe3, 0x7b, 0xbc, 0x36, 0x29, 0x13, 0xbf, 0x6c, 0xa9, 0x55, 0xbe, + 0x97, 0xb6, 0x0f, 0xbf, 0x4d, 0x48, 0x0f, 0xbf, 0xd8, 0xab, 0xb2, 0x3e, 0x8a, 0x38, 0x65, 0x3d, + 0x5b, 0x28, 0x88, 0xbe, 0xcb, 0xbf, 0x94, 0xbe, 0x7e, 0xa6, 0x6e, 0x3f, 0xb2, 0x82, 0xc7, 0xbd, + 0xdc, 0x41, 0x06, 0xbf, 0x0c, 0x50, 0x68, 0xbd, 0xf9, 0xd4, 0xb1, 0xbd, 0xd6, 0x25, 0xf1, 0xbd, + 0x16, 0x12, 0xbd, 0x3e, 0x49, 0x4a, 0x0e, 0x3f, 0xb6, 0x5e, 0xf6, 0x3e, 0xf0, 0x43, 0x91, 0xbe, + 0x9d, 0x32, 0x00, 0x3f, 0xd7, 0x83, 0x6e, 0x3c, 0xce, 0xfe, 0xc2, 0xbe, 0x86, 0x05, 0xb5, 0x3e, + 0xb4, 0x95, 0xc4, 0x3e, 0xda, 0xb4, 0xb3, 0xbe, 0x73, 0xe3, 0xae, 0x3c, 0xa9, 0x1e, 0x86, 0x3d, + 0x94, 0x7e, 0x1e, 0xbf, 0x1d, 0x9f, 0x5a, 0xbf, 0xe8, 0x05, 0x3f, 0xbe, 0xfd, 0x58, 0xbf, 0x3e, + 0x01, 0x8c, 0x83, 0x3f, 0x17, 0xa1, 0x66, 0xbe, 0x87, 0xc6, 0xf7, 0x3e, 0xc9, 0xa8, 0x06, 0x3f, + 0xea, 0x27, 0xce, 0xbd, 0x0c, 0x08, 0xf2, 0xbd, 0xb6, 0x55, 0x7b, 0x3d, 0x20, 0x10, 0x8b, 0xbe, + 0xb6, 0x6b, 0x9c, 0x3e, 0xd3, 0xb7, 0x9e, 0x3e, 0x22, 0xf3, 0x8e, 0xbe, 0x2c, 0x39, 0x1e, 0x3c, + 0x6a, 0x2a, 0xa6, 0x3b, 0x6e, 0x2f, 0x8d, 0xbd, 0x90, 0xc6, 0x8b, 0xbe, 0xdb, 0x99, 0x48, 0xbf, + 0x93, 0xbc, 0xfd, 0xbe, 0xd0, 0x28, 0x06, 0x3e, 0x84, 0xb3, 0x36, 0x3f, 0xfd, 0xc8, 0x23, 0x3f, + 0xdd, 0x33, 0x05, 0xbf, 0x15, 0xaa, 0xa1, 0x3e, 0x09, 0xe0, 0xa9, 0x3e, 0x7d, 0x52, 0xfe, 0xbd, + 0x2e, 0xa3, 0xd1, 0xbd, 0x71, 0x9d, 0xbd, 0xba, 0x42, 0x6b, 0xa4, 0x3e, 0xbc, 0xb1, 0x64, 0x3b, + 0x67, 0x3b, 0x5c, 0xbd, 0x4f, 0x33, 0x10, 0x3e, 0x06, 0xf5, 0xdc, 0xbe, 0x88, 0xec, 0x75, 0xbe, + 0x0f, 0xf1, 0xa0, 0x3e, 0x3f, 0x21, 0xcc, 0x3c, 0xe7, 0xd8, 0x5a, 0x3e, 0x15, 0x4c, 0xd6, 0x3e, + 0xa6, 0x79, 0xe5, 0xbe, 0x38, 0x44, 0x93, 0xbf, 0x0d, 0xd9, 0x1e, 0x3e, 0xd9, 0xe5, 0x67, 0xbf, + 0xb8, 0x1f, 0x07, 0xbf, 0x27, 0xca, 0xc5, 0xbd, 0xa7, 0x20, 0xa5, 0xbe, 0xa4, 0x0d, 0xdd, 0xbe, + 0x4a, 0x31, 0x07, 0x3e, 0xb9, 0x3b, 0x52, 0x3d, 0x16, 0x1b, 0x5d, 0xbf, 0x8b, 0x09, 0x2b, 0xbe, + 0x59, 0xad, 0x3c, 0xbf, 0x8f, 0xaf, 0xdf, 0x3e, 0xa1, 0xa1, 0x31, 0x3f, 0xd1, 0xc2, 0xf3, 0x3e, + 0x39, 0xa7, 0x91, 0x3f, 0x86, 0xdd, 0x7f, 0x3e, 0x1f, 0xd0, 0x2e, 0xbf, 0x2f, 0xfd, 0xc4, 0xbd, + 0x93, 0x56, 0x8b, 0xbd, 0x71, 0xd1, 0xec, 0xbd, 0xd4, 0xc3, 0x8c, 0xbe, 0x81, 0x43, 0x9f, 0xbe, + 0x1b, 0xe2, 0x14, 0x3f, 0x55, 0x4d, 0x7f, 0x3e, 0x54, 0x3e, 0x34, 0x3f, 0xef, 0x1c, 0xa1, 0x3e, + 0x6c, 0xb1, 0xba, 0xbe, 0xef, 0xeb, 0x26, 0x3e, 0x77, 0x78, 0xdb, 0xbe, 0x46, 0x40, 0x7b, 0xbe, + 0x8b, 0x1e, 0x05, 0x3e, 0x2f, 0x40, 0x2e, 0xbe, 0x15, 0xcd, 0xac, 0x3d, 0x03, 0x07, 0x0b, 0xbe, + 0x51, 0xe0, 0x97, 0x3e, 0x88, 0x52, 0xbb, 0x3e, 0xac, 0x36, 0x21, 0xbe, 0xa7, 0x32, 0x40, 0x3d, + 0xdb, 0xb1, 0x62, 0x3e, 0x47, 0x86, 0x37, 0x3e, 0x48, 0xce, 0x0b, 0x3c, 0x9d, 0x9f, 0x13, 0x3c, + 0x22, 0x74, 0xe2, 0xbe, 0x38, 0x44, 0x0c, 0xbe, 0x5e, 0xda, 0x80, 0x3e, 0x84, 0x03, 0xb2, 0xbd, + 0x4a, 0xa2, 0x75, 0xbd, 0x22, 0xe6, 0x1c, 0x3e, 0x1d, 0x43, 0x01, 0x3d, 0xde, 0x69, 0x70, 0x3e, + 0xaf, 0x9f, 0xbe, 0x3e, 0x0c, 0x81, 0x8f, 0x3e, 0x4f, 0x47, 0x41, 0x3e, 0x65, 0xe5, 0x2e, 0xbf, + 0xec, 0xd9, 0x15, 0x3f, 0x22, 0xb9, 0xac, 0x3e, 0x74, 0xcc, 0x30, 0xbd, 0x61, 0x31, 0x56, 0xbe, + 0xc8, 0x22, 0x62, 0x3e, 0x84, 0xe9, 0x35, 0x3f, 0x18, 0x3a, 0x59, 0x3f, 0x2a, 0xbb, 0x65, 0x3e, + 0xb8, 0xe9, 0x8e, 0xbd, 0xf8, 0x95, 0x95, 0xbd, 0xc7, 0x52, 0x93, 0x3f, 0xfd, 0xd4, 0xc2, 0xbd, + 0xe9, 0x92, 0x63, 0xbe, 0xee, 0x41, 0x4a, 0x3d, 0xf1, 0x2e, 0x02, 0x3f, 0xe0, 0xc5, 0x0d, 0xbe, + 0x77, 0x69, 0x19, 0xbf, 0x33, 0x32, 0xb6, 0xbe, 0x17, 0x15, 0xb2, 0x3e, 0x23, 0xb1, 0xd6, 0x3e, + 0x33, 0xf8, 0x3b, 0x3f, 0x71, 0x9a, 0xa9, 0x3d, 0x75, 0xa5, 0x0a, 0x3f, 0x34, 0x01, 0x6f, 0x3e, + 0x3b, 0x5f, 0x52, 0x3f, 0x45, 0xbd, 0xf1, 0x3d, 0x53, 0x2d, 0xdb, 0xbc, 0xc8, 0xdc, 0x1a, 0x3d, + 0xa0, 0xf8, 0x70, 0x3f, 0x1d, 0xe5, 0x29, 0x3f, 0x2b, 0x8d, 0xe6, 0xbe, 0x42, 0x77, 0x03, 0x3f, + 0xe4, 0xd1, 0x91, 0x3f, 0x87, 0x4b, 0xa1, 0x3e, 0x28, 0xe9, 0x59, 0x3e, 0x9b, 0x6e, 0x46, 0xbf, + 0xf7, 0xbc, 0xe8, 0x3e, 0x01, 0x69, 0x3e, 0x3e, 0xb5, 0x77, 0xc1, 0xbe, 0x3f, 0xf2, 0x61, 0x3e, + 0x79, 0xe4, 0x1c, 0x3e, 0x4a, 0x56, 0xae, 0xbe, 0xe9, 0x76, 0x74, 0xbf, 0x81, 0xd6, 0xdc, 0xbd, + 0x7d, 0x51, 0x7e, 0x3f, 0x2e, 0x4c, 0xd0, 0x3e, 0xc1, 0xff, 0x80, 0xbe, 0xf5, 0x0e, 0xfe, 0xbe, + 0xba, 0xca, 0x49, 0x3e, 0x34, 0x03, 0x64, 0x3d, 0x44, 0xfc, 0x9a, 0x3c, 0x89, 0xd9, 0x75, 0x3d, + 0xe2, 0xc7, 0x36, 0xbf, 0xad, 0xaf, 0xf6, 0xbe, 0x24, 0xc4, 0x07, 0x3e, 0xfe, 0xc5, 0x6c, 0xbe, + 0x93, 0x82, 0x3b, 0xbe, 0xcf, 0x6f, 0x19, 0x3e, 0x34, 0x40, 0x92, 0x3e, 0x95, 0x8b, 0x93, 0x3e, + 0x9f, 0x29, 0x38, 0xbe, 0x30, 0x48, 0x0d, 0xbf, 0x78, 0x80, 0x22, 0xbe, 0x3a, 0xa7, 0x54, 0x3f, + 0xac, 0x06, 0xf2, 0xbe, 0x68, 0x7a, 0xac, 0xbd, 0xbc, 0x6e, 0xa7, 0xbe, 0xbe, 0x94, 0x3f, 0x3e, + 0x04, 0xdb, 0x29, 0x3f, 0xb9, 0xd1, 0x72, 0x3e, 0xaf, 0xe3, 0x44, 0xbf, 0x2c, 0x9b, 0xc2, 0xbe, + 0x27, 0x6c, 0xf3, 0x3e, 0x7b, 0x8d, 0x0e, 0x3e, 0x15, 0xc8, 0x3b, 0x3e, 0xcb, 0xde, 0x2d, 0x3f, + 0xd1, 0xde, 0x82, 0x3e, 0xaf, 0xf1, 0x46, 0xbe, 0xfd, 0xab, 0x34, 0xbe, 0x73, 0x48, 0x8c, 0x3c, + 0x96, 0x54, 0xfc, 0xbe, 0xb6, 0xb7, 0x9e, 0xbe, 0x86, 0x5a, 0x1b, 0xbf, 0x0a, 0xa1, 0xc4, 0xbe, + 0x18, 0xa4, 0x89, 0xbf, 0x1b, 0xdf, 0x50, 0xbf, 0xd0, 0xd7, 0x8e, 0xbe, 0x8a, 0x51, 0x38, 0xbe, + 0x1d, 0xc4, 0xb3, 0xbd, 0xac, 0xb4, 0xda, 0xbe, 0xab, 0x76, 0x30, 0x3e, 0xc9, 0x3a, 0x84, 0x3f, + 0x9b, 0x5d, 0x1d, 0x3e, 0xa8, 0x90, 0xa0, 0xbe, 0x0a, 0x46, 0x68, 0xbe, 0x32, 0x54, 0x1e, 0x3f, + 0xd2, 0x9a, 0xf9, 0xbe, 0xf3, 0x39, 0x99, 0xbd, 0x54, 0x2b, 0x4e, 0xbe, 0xe5, 0x68, 0x0b, 0x3f, + 0x3b, 0x93, 0x37, 0x3e, 0x43, 0x3c, 0xd0, 0xbe, 0xa5, 0x49, 0xdb, 0xbd, 0xc0, 0x14, 0x1a, 0xbf, + 0xeb, 0x7d, 0x9c, 0xbe, 0x53, 0x72, 0x13, 0x3f, 0xbc, 0x48, 0x25, 0xbf, 0xe4, 0x91, 0x67, 0xbf, + 0x22, 0x3b, 0x31, 0xbe, 0x74, 0x11, 0xec, 0x3d, 0x65, 0x66, 0xff, 0xbe, 0xcc, 0x94, 0x49, 0x3f, + 0x58, 0x2c, 0xd0, 0x3e, 0x42, 0xce, 0xf7, 0x3d, 0xd5, 0xeb, 0x86, 0x3e, 0x45, 0xaa, 0x04, 0x3f, + 0xb9, 0x06, 0xb6, 0xbf, 0xd1, 0x46, 0xdb, 0x3e, 0xa4, 0x9b, 0x32, 0x3f, 0xfa, 0xe9, 0x04, 0x3f, + 0xa5, 0xef, 0x4b, 0x3f, 0x0f, 0x03, 0x88, 0x3f, 0xc0, 0x33, 0x36, 0xbe, 0x93, 0x74, 0xa0, 0x3c, + 0xc9, 0x07, 0x16, 0x3f, 0x77, 0x6b, 0x48, 0xbe, 0x2a, 0x59, 0xa8, 0x3c, 0x38, 0x79, 0x3c, 0x3c, + 0x2d, 0x18, 0x09, 0xbf, 0x46, 0xfc, 0x18, 0x3f, 0xa8, 0x9a, 0x61, 0x3f, 0x07, 0xeb, 0xb8, 0x3e, + 0xaa, 0xd8, 0x46, 0xbf, 0xa9, 0x40, 0x57, 0xbc, 0x09, 0x86, 0x32, 0xbe, 0x4d, 0x27, 0xee, 0xbc, + 0x95, 0xd1, 0x67, 0xbf, 0xea, 0xb2, 0x25, 0x3e, 0xd9, 0xc3, 0x28, 0x3f, 0x1c, 0xe1, 0xd8, 0x3d, + 0xc1, 0x57, 0xf8, 0x3d, 0x4c, 0x1e, 0xbd, 0x3e, 0xc4, 0xb9, 0xc5, 0xbe, 0x45, 0x0d, 0x82, 0xbd, + 0x4c, 0x4e, 0x15, 0x3e, 0xc6, 0x99, 0x46, 0xbc, 0x64, 0x03, 0x0e, 0x3e, 0x9e, 0x43, 0x60, 0x3e, + 0xf2, 0xa1, 0xb9, 0x3d, 0xc0, 0x6a, 0xb9, 0xbc, 0x9e, 0x09, 0x82, 0x3c, 0x4f, 0xc5, 0x5f, 0x3d, + 0x72, 0xde, 0x3e, 0x3e, 0xe2, 0x80, 0x38, 0x3f, 0x5a, 0xa8, 0x15, 0x3f, 0xd3, 0x0a, 0x0b, 0xbc, + 0xc0, 0xe1, 0xc8, 0xbe, 0x4d, 0x83, 0xc8, 0xbe, 0x7c, 0x76, 0x4d, 0xbe, 0xaa, 0x26, 0x28, 0x3e, + 0xe2, 0x14, 0x7b, 0xbe, 0xba, 0x13, 0x30, 0xbf, 0xcb, 0x92, 0x12, 0x3f, 0x43, 0x7c, 0xb1, 0xbe, + 0x48, 0x18, 0x02, 0xbf, 0xab, 0xe4, 0x01, 0x3f, 0xc9, 0x7f, 0xee, 0x3d, 0xab, 0xbc, 0x1d, 0xbf, + 0xce, 0xe8, 0x5f, 0x3f, 0x85, 0x3a, 0x6d, 0xbd, 0x3b, 0x02, 0x06, 0xbf, 0xff, 0x79, 0x8f, 0xbf, + 0x8b, 0xf0, 0x81, 0x3e, 0x44, 0x2c, 0x33, 0xbf, 0xb4, 0x03, 0x43, 0xbf, 0xbb, 0xee, 0x33, 0xbe, + 0x74, 0x83, 0xa2, 0x3b, 0x68, 0x8a, 0x62, 0xbe, 0xd6, 0xce, 0x7d, 0xbe, 0xf4, 0x42, 0x23, 0xbf, + 0x51, 0xf9, 0xe0, 0x3c, 0x9b, 0x2d, 0x8f, 0x3e, 0x93, 0xd2, 0x31, 0x3f, 0x37, 0xbc, 0x41, 0x3d, + 0xaf, 0x6f, 0x75, 0x3f, 0xe3, 0xea, 0x9d, 0x3e, 0x5d, 0xa1, 0xc3, 0x3e, 0xd9, 0x13, 0x05, 0x3f, + 0xe5, 0x3c, 0x91, 0x3e, 0x3f, 0x02, 0x7d, 0xbd, 0x3a, 0x37, 0xab, 0x3e, 0x2b, 0x7d, 0x50, 0x3e, + 0x35, 0xea, 0x57, 0xbe, 0xbc, 0x76, 0x8d, 0xbc, 0x5f, 0x13, 0x3f, 0x3c, 0xed, 0x4d, 0xdb, 0x3e, + 0xb1, 0xa1, 0x46, 0xbf, 0x7d, 0x83, 0xcd, 0xbe, 0x81, 0x43, 0x88, 0xbd, 0x5d, 0x19, 0xee, 0xbd, + 0xab, 0x2d, 0x35, 0x3e, 0x9f, 0x87, 0x21, 0x3e, 0x91, 0x42, 0x77, 0xbe, 0x53, 0x74, 0x04, 0x3e, + 0x69, 0x80, 0xdd, 0x3e, 0xfe, 0x0e, 0xe9, 0xbe, 0x38, 0xce, 0x49, 0x3d, 0x54, 0x09, 0x46, 0x3e, + 0xc8, 0xdc, 0xe8, 0x3d, 0xcd, 0xeb, 0x4c, 0x3e, 0xb1, 0xf8, 0xd9, 0x3d, 0x2d, 0xc1, 0x7f, 0xbe, + 0x93, 0xc4, 0x07, 0xbe, 0x8d, 0x1d, 0x07, 0x3f, 0x1b, 0xd5, 0x1d, 0x3f, 0xe6, 0x9b, 0x4d, 0xbf, + 0x43, 0xb1, 0x9b, 0xbe, 0x2f, 0x2f, 0xa0, 0xbe, 0xb5, 0x1f, 0xb0, 0xbd, 0xad, 0xa9, 0xcb, 0xbd, + 0x12, 0x2c, 0xc0, 0xbe, 0x01, 0xab, 0x44, 0x3e, 0x59, 0x6e, 0x48, 0x3f, 0xba, 0x17, 0x66, 0x3f, + 0x61, 0x12, 0x82, 0x3e, 0xf5, 0x7f, 0xe0, 0xbd, 0xbf, 0xe8, 0xbf, 0xbe, 0x7b, 0x8f, 0x10, 0xbf, + 0xc9, 0x2e, 0x18, 0xbf, 0x3b, 0x08, 0x8e, 0xbe, 0x41, 0xa0, 0xa0, 0xbe, 0x3c, 0xdf, 0x43, 0x3e, + 0xc0, 0x7f, 0x47, 0xbf, 0x04, 0xcc, 0x88, 0xbf, 0x85, 0xbf, 0xa7, 0xbe, 0x29, 0x65, 0x2a, 0xbf, + 0x9c, 0xa4, 0x3c, 0x3d, 0xaa, 0x61, 0x28, 0xbe, 0x5e, 0x8d, 0x4e, 0xbf, 0x03, 0xad, 0x88, 0xbc, + 0x64, 0xb5, 0xdb, 0x3e, 0x8e, 0xcf, 0x25, 0x3f, 0x62, 0x36, 0x1e, 0xbf, 0x01, 0x2e, 0x2e, 0xbd, + 0x25, 0x4a, 0xc0, 0x3e, 0x4f, 0x2a, 0xb8, 0xbe, 0xd4, 0x1c, 0x73, 0xbf, 0x1d, 0xfb, 0x06, 0xbf, + 0x6d, 0xd4, 0xfd, 0xbe, 0x2e, 0xed, 0x6b, 0x3f, 0x07, 0x31, 0x4c, 0xbe, 0x94, 0x31, 0xfd, 0xbe, + 0xef, 0x8b, 0x35, 0x3e, 0x85, 0x8c, 0x5b, 0xbe, 0xb4, 0xbd, 0x94, 0xbe, 0x8f, 0x3a, 0x07, 0x3d, + 0x60, 0xa0, 0x04, 0xbf, 0x77, 0x7b, 0x33, 0xbf, 0x79, 0x98, 0xe0, 0xbd, 0x9d, 0x1f, 0xa7, 0xbe, + 0x5c, 0x12, 0x29, 0x3f, 0x23, 0xb9, 0x0c, 0xbd, 0x83, 0x38, 0x91, 0xbd, 0x76, 0x1b, 0x91, 0x3d, + 0x87, 0x31, 0xe2, 0xbd, 0xe8, 0x12, 0xd4, 0x3e, 0x06, 0x53, 0x70, 0xbd, 0x83, 0x87, 0xff, 0x3e, + 0x18, 0xf3, 0x50, 0xbf, 0x38, 0xdc, 0xa2, 0xbe, 0x22, 0xde, 0x5e, 0xbd, 0x4c, 0x46, 0x1f, 0x3d, + 0x8d, 0x2d, 0x7d, 0x3f, 0x14, 0xcd, 0xa5, 0xbe, 0x38, 0xf8, 0x46, 0xbd, 0x15, 0xfb, 0x55, 0x3e, + 0x1b, 0xb7, 0x82, 0x3e, 0x80, 0x33, 0xf9, 0x3e, 0x00, 0x1d, 0xe3, 0xbe, 0x27, 0xc0, 0x65, 0x3e, + 0xec, 0x62, 0x3f, 0xbd, 0xff, 0xb9, 0xf4, 0xbe, 0x61, 0xdf, 0x24, 0x3f, 0x9f, 0xba, 0x2c, 0x3e, + 0xb2, 0x3e, 0xfa, 0x3c, 0x5b, 0x58, 0x09, 0x3f, 0x10, 0x07, 0x84, 0x3e, 0x6e, 0xba, 0x4e, 0x3e, + 0x75, 0xa9, 0xaa, 0x3e, 0x05, 0x31, 0x01, 0x3e, 0xb4, 0xd4, 0xc0, 0x3e, 0x64, 0x5e, 0xb9, 0x3d, + 0x88, 0xdd, 0x10, 0xbd, 0x87, 0x15, 0xad, 0x3c, 0x12, 0xa5, 0x28, 0x3f, 0x7a, 0x9a, 0xc4, 0x3e, + 0x99, 0x65, 0x70, 0x3e, 0xe5, 0xfe, 0x15, 0x3f, 0xe1, 0xb0, 0x85, 0x3e, 0x5c, 0x90, 0x0c, 0x3d, + 0xec, 0x93, 0xbd, 0xba, 0x99, 0x23, 0x06, 0x3f, 0xbf, 0xa5, 0xb4, 0xbe, 0x23, 0x88, 0xc3, 0xbe, + 0xa7, 0x23, 0x28, 0xbe, 0xf4, 0x5c, 0xcf, 0x3d, 0xa4, 0x9b, 0x84, 0xbe, 0xd2, 0xc8, 0xd1, 0x3e, + 0xe5, 0xbb, 0x71, 0x3d, 0x22, 0x92, 0x87, 0xbe, 0xed, 0x3a, 0x02, 0xbf, 0x8b, 0xdd, 0xf8, 0x3b, + 0xc3, 0xc2, 0x40, 0x3f, 0x44, 0x73, 0x68, 0x3e, 0x63, 0xb5, 0x23, 0xbf, 0x0a, 0xd4, 0x80, 0x3e, + 0x06, 0x1e, 0x7f, 0xbe, 0xe2, 0x91, 0x21, 0xbe, 0x78, 0xe3, 0x00, 0x3d, 0x5b, 0x1b, 0x75, 0xbe, + 0x8a, 0x29, 0xcf, 0xbe, 0x2d, 0xc8, 0x05, 0x3f, 0xba, 0x2b, 0x65, 0xbe, 0x08, 0x11, 0x7f, 0xbe, + 0x5a, 0x62, 0x02, 0x3f, 0xef, 0x11, 0x85, 0xbe, 0x26, 0xa8, 0xe4, 0xbd, 0xd6, 0x3b, 0x0c, 0xbf, + 0x01, 0x33, 0xc2, 0x3c, 0xc5, 0xbc, 0x04, 0x3f, 0x34, 0x42, 0x75, 0x3f, 0x0c, 0xef, 0x80, 0x3e, + 0x0d, 0x5a, 0x0b, 0xbd, 0x3f, 0x84, 0xe1, 0xbe, 0x60, 0x31, 0x10, 0xbf, 0x6e, 0xb6, 0xee, 0xbe, + 0xcf, 0xe3, 0x98, 0xbe, 0x25, 0x5a, 0xe5, 0xbd, 0xe1, 0x03, 0x27, 0xbf, 0x3a, 0x8b, 0x55, 0xbe, + 0x01, 0xbc, 0x70, 0xbe, 0x49, 0x23, 0xc7, 0xbd, 0xc2, 0x3e, 0x05, 0x3e, 0x26, 0x26, 0xc0, 0x3e, + 0x7f, 0x1c, 0x01, 0x3e, 0x73, 0x78, 0x01, 0xbf, 0xa1, 0xe2, 0x14, 0xbe, 0x2e, 0xc3, 0xa8, 0xbd, + 0xe6, 0xd5, 0xe5, 0x3d, 0x0e, 0x8c, 0x12, 0xbe, 0x66, 0x1e, 0x93, 0xbf, 0xc1, 0xe5, 0xa2, 0xbe, + 0x46, 0x81, 0x79, 0xbe, 0xa6, 0x04, 0x8d, 0x3b, 0x9f, 0xc7, 0xe4, 0xbe, 0x29, 0xda, 0x13, 0xbe, + 0x70, 0x65, 0x9e, 0xbf, 0xfc, 0x39, 0xb7, 0x3e, 0x98, 0xc2, 0xc9, 0x3e, 0x1d, 0xf9, 0xed, 0x3e, + 0x5d, 0x40, 0x14, 0x3f, 0x19, 0x9b, 0xb9, 0x3d, 0x67, 0x4a, 0xf9, 0x3e, 0x6f, 0x01, 0xc9, 0xbe, + 0x2c, 0x2f, 0x25, 0xbf, 0x84, 0xf0, 0x8a, 0xbe, 0x0c, 0xb1, 0xc2, 0xbc, 0xcb, 0x7c, 0x68, 0xbe, + 0x99, 0xdf, 0x5d, 0xbf, 0xc0, 0xeb, 0x4c, 0xbe, 0xfe, 0x2d, 0xd0, 0xbe, 0x66, 0x44, 0xdb, 0xbe, + 0x36, 0x5d, 0x0a, 0x3f, 0x95, 0x85, 0x81, 0x3e, 0x63, 0x22, 0xe4, 0xbe, 0x30, 0x48, 0xd5, 0xbe, + 0x6a, 0x7e, 0xa0, 0xbd, 0x54, 0x57, 0xd5, 0x3c, 0x86, 0x44, 0xd4, 0xbe, 0xf6, 0xfd, 0xf0, 0x3e, + 0x8c, 0x93, 0xf1, 0x3d, 0x38, 0x50, 0x73, 0xbe, 0x93, 0x20, 0xdd, 0xbe, 0xfe, 0x7e, 0xe9, 0xbe, + 0x72, 0xf2, 0xb2, 0x3f, 0xf8, 0x3a, 0x93, 0x40, 0xf9, 0x29, 0x83, 0x40, 0x58, 0x99, 0x85, 0x3e, + 0xd2, 0x0d, 0xce, 0x3f, 0x8e, 0xf0, 0x09, 0x40, 0x2e, 0xa0, 0x4f, 0xbf, 0xe8, 0xb4, 0x31, 0xc0, + 0xfc, 0x33, 0x33, 0x40, 0xeb, 0x4f, 0xd9, 0x3f, 0x31, 0xe2, 0x6b, 0x40, 0xff, 0x7e, 0x25, 0x40, + 0xb4, 0xd0, 0x45, 0x40, 0x53, 0x6e, 0x67, 0x3f, 0xb5, 0x69, 0x62, 0xc0, 0xd1, 0x00, 0x07, 0x40, + 0xfc, 0x97, 0x4a, 0x40, 0xd0, 0x80, 0x0a, 0x40, 0x01, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x02, 0x00, 0x02, 0x00, 0x12, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x12, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x38, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x38, 0x21, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x12, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, + 0x10, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdd, 0xc5, 0x8f, 0xbf, 0xf2, 0xc5, 0xa7, 0xbe, + 0x7b, 0xa2, 0x46, 0x3f, 0xee, 0x66, 0x6d, 0xbf, 0x24, 0x72, 0xd2, 0xbf, 0xbc, 0xb4, 0x44, 0xbe, + 0x4c, 0x4f, 0x86, 0x3f, 0x93, 0x26, 0x20, 0xbf, 0x84, 0xc2, 0x00, 0x3f, 0x16, 0x44, 0x84, 0xbe, + 0x71, 0x01, 0x81, 0x3f, 0xfe, 0x55, 0x82, 0x3f, 0x46, 0x8d, 0xfc, 0xbe, 0x7d, 0x0b, 0x83, 0x3f, + 0x22, 0x2f, 0x5c, 0xbf, 0x06, 0x1e, 0xb9, 0xbf, 0x92, 0xe0, 0x97, 0x3d, 0x4e, 0x18, 0x1f, 0xbf, + 0x46, 0xa1, 0x68, 0xbf, 0xb5, 0xae, 0x95, 0xbf, 0x2f, 0x45, 0x53, 0x3f, 0xb9, 0x98, 0x4f, 0x3f, + 0x34, 0x52, 0x59, 0xbf, 0x52, 0x70, 0x9f, 0xbf, 0x42, 0x3f, 0x18, 0xbf, 0x7f, 0x92, 0x8f, 0xbf, + 0x00, 0xdb, 0x45, 0x3f, 0x71, 0x4d, 0x88, 0x3f, 0xf5, 0xe6, 0xd9, 0xbe, 0x59, 0xd1, 0x62, 0x3f, + 0xd8, 0x62, 0x4a, 0xbf, 0x36, 0xd2, 0x9e, 0xbf, 0x36, 0x8a, 0x05, 0x3e, 0x8e, 0x54, 0x3c, 0x3f, + 0x21, 0x6d, 0x5e, 0xbf, 0x30, 0x4b, 0x20, 0xbe, 0xfe, 0x76, 0x9f, 0xbf, 0xac, 0x01, 0x8e, 0xbf, + 0xfe, 0x1c, 0x02, 0x3f, 0x5e, 0x1b, 0x13, 0xbf, 0xfd, 0xfb, 0x44, 0xbf, 0x75, 0xf0, 0x1a, 0x3f, + 0xb9, 0xb6, 0x8f, 0x3f, 0xc0, 0x14, 0x5c, 0x3e, 0xd2, 0xdc, 0x5a, 0xbf, 0x52, 0x87, 0x88, 0x3e, + 0xdc, 0x1f, 0x64, 0xbf, 0x59, 0xfb, 0x2b, 0xbf, 0xc9, 0x15, 0x89, 0x3f, 0x69, 0xd3, 0x39, 0xbf, + 0xa8, 0x56, 0x62, 0x3f, 0xd0, 0x9c, 0x89, 0x3e, 0xf8, 0xd8, 0x61, 0x3f, 0x0c, 0x4b, 0xba, 0xbe, + 0xbb, 0xcf, 0x6f, 0x3f, 0xc8, 0xad, 0x84, 0xbe, 0x29, 0x92, 0xde, 0xbf, 0x42, 0x6f, 0x8d, 0xbe, + 0x59, 0xa2, 0xa8, 0xbe, 0x49, 0xf6, 0x8d, 0xbf, 0xff, 0xa3, 0x32, 0x3f, 0xe7, 0x63, 0x26, 0x3f, + 0x67, 0xb0, 0xd5, 0x3e, 0x6a, 0x20, 0x8b, 0xbf, 0x65, 0xba, 0x98, 0xbf, 0xbd, 0x80, 0x59, 0x3f, + 0x8c, 0x40, 0x8a, 0x3f, 0xd1, 0xbb, 0x18, 0xbf, 0xd7, 0xd5, 0x02, 0xbe, 0x68, 0x22, 0x6b, 0x3f, + 0xa1, 0xd4, 0xb1, 0xbf, 0xa9, 0x14, 0x16, 0xbf, 0xf5, 0x86, 0x2f, 0x3d, 0xcd, 0xf3, 0x5b, 0xbc, + 0x90, 0x33, 0x5f, 0x3f, 0xe6, 0xe7, 0x97, 0xbf, 0xf9, 0x5f, 0x8c, 0x3f, 0xfe, 0x03, 0x8d, 0x3d, + 0x3c, 0x80, 0xa0, 0xbf, 0x63, 0x67, 0xb6, 0xbf, 0x62, 0x3a, 0xa0, 0x3f, 0xc6, 0x6b, 0x4b, 0xbf, + 0x7f, 0x5d, 0x71, 0x3e, 0x70, 0x17, 0xb3, 0xbf, 0x15, 0xba, 0x7b, 0xbf, 0x95, 0x2f, 0x90, 0xbe, + 0x65, 0x6e, 0x16, 0xbf, 0x3c, 0x18, 0x64, 0x3f, 0x87, 0x5b, 0x6a, 0x3f, 0xbb, 0x17, 0x83, 0xbe, + 0x7e, 0x75, 0x87, 0x3f, 0x72, 0x04, 0x09, 0xbf, 0x1d, 0x52, 0x18, 0xbf, 0xb1, 0x52, 0x82, 0x3f, + 0x5c, 0x71, 0x28, 0xbf, 0x2f, 0x2d, 0x01, 0x3f, 0x3c, 0x79, 0x28, 0xbf, 0xdb, 0xf1, 0x8d, 0x3f, + 0xac, 0x67, 0x74, 0xbf, 0xca, 0x63, 0x30, 0xbf, 0xd9, 0x66, 0x73, 0x3f, 0xc6, 0x08, 0xae, 0x3e, + 0xae, 0xa1, 0xb1, 0xbf, 0xce, 0xb4, 0x33, 0xbf, 0x1f, 0x59, 0xde, 0x3e, 0xb6, 0x63, 0xa0, 0x3c, + 0x22, 0x0e, 0x1b, 0xbe, 0x8c, 0xb9, 0xab, 0xbf, 0xaf, 0xdf, 0x84, 0xbf, 0x40, 0x4d, 0x87, 0x3f, + 0x84, 0xa4, 0x55, 0x3f, 0xd2, 0xe6, 0x2f, 0x3f, 0x7e, 0x36, 0xa1, 0xbf, 0x41, 0x85, 0xa7, 0x3f, + 0xda, 0xf2, 0xbc, 0xbf, 0x08, 0xda, 0x50, 0xbf, 0x45, 0x86, 0x12, 0xbf, 0x5d, 0xa2, 0x31, 0xbe, + 0xd4, 0x5d, 0x51, 0x3f, 0x60, 0x49, 0x8a, 0xbf, 0x29, 0xf4, 0x2f, 0x3f, 0xef, 0x42, 0x06, 0xbf, + 0x9b, 0xf1, 0x87, 0xbf, 0xf6, 0x33, 0xe0, 0xbd, 0xfb, 0x3d, 0x89, 0xbf, 0xb4, 0x00, 0xa7, 0xbf, + 0x8f, 0xfa, 0x18, 0xbf, 0x56, 0x2c, 0xa2, 0xbf, 0x0b, 0x61, 0x82, 0xbb, 0xe7, 0x32, 0x70, 0x3f, + 0xee, 0x8e, 0x4b, 0x3f, 0xd4, 0xbf, 0xc3, 0xbf, 0x41, 0xa8, 0x04, 0x3e, 0x5c, 0x25, 0x33, 0xbf, + 0x94, 0xa8, 0x5d, 0xbf, 0xc4, 0x14, 0x4f, 0xbf, 0x15, 0xa7, 0x65, 0xbf, 0x44, 0x40, 0x42, 0x3f, + 0xcd, 0x62, 0xe1, 0xbe, 0x17, 0xf3, 0x42, 0x3f, 0xc6, 0x39, 0x23, 0x3f, 0xbf, 0x84, 0x00, 0xbf, + 0xcb, 0x66, 0x5c, 0x3f, 0xd0, 0x22, 0x39, 0x3f, 0x17, 0xdc, 0x50, 0x3f, 0xbb, 0xe6, 0xae, 0x3e, + 0x7f, 0x18, 0x62, 0xbf, 0xab, 0xd3, 0xdb, 0x3e, 0xe1, 0x94, 0x71, 0xbf, 0xc9, 0x49, 0x55, 0xbe, + 0x03, 0x6e, 0x08, 0x3f, 0xd6, 0x2f, 0x86, 0xbd, 0x00, 0x02, 0x4a, 0xbe, 0x58, 0x8f, 0x76, 0x3f, + 0x1b, 0x44, 0x9a, 0x3e, 0x55, 0x4d, 0x7f, 0xbf, 0x2e, 0x7b, 0x8e, 0x3d, 0xb1, 0xb9, 0x66, 0xbe, + 0xdc, 0xf2, 0x8f, 0xbf, 0xcc, 0xa7, 0xa6, 0xbf, 0xab, 0x7d, 0x61, 0xbe, 0xe2, 0x5f, 0xa2, 0x3f, + 0x1b, 0xed, 0xef, 0x3e, 0x54, 0x6d, 0x8d, 0x3f, 0x4c, 0xa0, 0x84, 0xbf, 0xc1, 0x4a, 0x82, 0xbf, + 0xad, 0x7e, 0x49, 0x3f, 0x57, 0x90, 0x64, 0xbe, 0x7f, 0x6a, 0x46, 0xbf, 0xde, 0x5b, 0x8d, 0x3e, + 0x4a, 0x2b, 0x88, 0xbf, 0x0c, 0xcd, 0xf1, 0x3d, 0x1c, 0xb7, 0x8d, 0xbf, 0xb2, 0x91, 0xe6, 0x3c, + 0x4b, 0xeb, 0x8a, 0xbf, 0x42, 0xfa, 0x9c, 0x3f, 0x3b, 0x17, 0x9b, 0xbe, 0x57, 0x00, 0x3c, 0x3f, + 0xc3, 0x66, 0xd0, 0xbe, 0xb6, 0xda, 0x9e, 0xbe, 0x55, 0xd5, 0xb6, 0x3d, 0x0f, 0x1f, 0xec, 0x3c, + 0x29, 0x87, 0x9f, 0xbd, 0xa8, 0x0b, 0x1f, 0xbc, 0x56, 0xe0, 0x8d, 0x3b, 0xf9, 0x5e, 0x74, 0x3c, + 0x50, 0x4a, 0x99, 0x3a, 0xa3, 0xdd, 0x9d, 0xbe, 0x69, 0xd0, 0x3e, 0x3d, 0x8a, 0x13, 0x8f, 0x3e, + 0x03, 0x00, 0x01, 0x00, 0x30, 0x00, 0x00, 0x00, 0x38, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x0a, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x0a, 0x00, + 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, + }; + +#endif diff --git a/examples/mnist_cnn/mnist_cnn_fp32.tflite b/examples/mnist_cnn/mnist_cnn_fp32.tflite new file mode 100644 index 0000000000000000000000000000000000000000..c79fd9cf7b0a4e5021ba30892bab8433fc7cbea7 GIT binary patch literal 15112 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 literal 0 HcmV?d00001 diff --git a/examples/mnist_cnn/mnist_cnn_int8.h b/examples/mnist_cnn/mnist_cnn_int8.h new file mode 100644 index 0000000..5f46419 --- /dev/null +++ b/examples/mnist_cnn/mnist_cnn_int8.h @@ -0,0 +1,254 @@ +#ifndef __MODEL_FILE__H +#define __MODEL_FILE__H + +#include +#define MDL_BUF_LEN (2136) +#define LBUF_LEN (2232) +const uint8_t mdl_data[3912]={\ + 0x4d, 0x41, 0x49, 0x58, 0x00, 0x01, 0x01, 0x00, 0x01, 0x00, 0x06, 0x00, 0x58, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x03, 0x00, 0x00, + 0x03, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0x01, 0x00, 0x03, 0x00, 0x0d, 0x00, 0x0d, 0x00, 0x08, 0x00, + 0x81, 0x80, 0x80, 0x3b, 0x80, 0xff, 0xff, 0xff, 0x07, 0xeb, 0x5b, 0x3c, 0x80, 0xff, 0xff, 0xff, + 0x03, 0x03, 0x02, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0xb8, 0x00, 0x00, 0x00, + 0xe4, 0x9c, 0x75, 0x3b, 0xd7, 0x2d, 0x1e, 0x3c, 0x26, 0x92, 0x92, 0x3c, 0xb9, 0x7e, 0x90, 0x3c, + 0x5f, 0xf8, 0x7f, 0x3c, 0x45, 0x02, 0x59, 0x3c, 0x96, 0x19, 0x8e, 0x3c, 0x91, 0xe2, 0xba, 0x3b, + 0x12, 0x56, 0x4a, 0x7f, 0x75, 0x4f, 0x4e, 0x70, 0x4d, 0x16, 0x5f, 0x7f, 0xf0, 0x25, 0x0c, 0x36, + 0x05, 0x9e, 0x12, 0x31, 0x2b, 0xee, 0x17, 0x29, 0xa2, 0xbc, 0x81, 0x29, 0xbe, 0x81, 0x10, 0x08, + 0x28, 0xda, 0x39, 0x4e, 0xff, 0x81, 0xc5, 0xb0, 0x0e, 0x2b, 0x96, 0x00, 0x40, 0xf3, 0xca, 0xa4, + 0x2c, 0xf6, 0xd1, 0xd4, 0xc0, 0x81, 0xbb, 0x32, 0x19, 0xcc, 0x92, 0x81, 0x0d, 0x08, 0xf6, 0x7f, + 0xe5, 0x4e, 0x63, 0x44, 0x6d, 0x08, 0xf7, 0x32, 0xff, 0x27, 0x01, 0x00, 0xd9, 0x61, 0x00, 0x00, + 0xe1, 0xd8, 0xff, 0xff, 0xfd, 0x06, 0x00, 0x00, 0xa6, 0xaa, 0xff, 0xff, 0x75, 0x72, 0xff, 0xff, + 0x14, 0xb5, 0xff, 0xff, 0xb4, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x04, 0x00, 0x00, + 0x10, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x0d, 0x00, 0x0d, 0x00, 0x08, 0x00, + 0x03, 0x00, 0x06, 0x00, 0x06, 0x00, 0x0c, 0x00, 0x07, 0xeb, 0x5b, 0x3c, 0x80, 0xff, 0xff, 0xff, + 0x8b, 0x82, 0x84, 0x3c, 0x80, 0xff, 0xff, 0xff, 0x03, 0x03, 0x02, 0x02, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x00, 0xe0, 0x03, 0x00, 0x00, 0x30, 0xbd, 0xd1, 0x3b, 0xf1, 0x7d, 0x9c, 0x3b, + 0x44, 0x5d, 0x84, 0x3b, 0xaf, 0x5b, 0xd3, 0x3a, 0x32, 0xdc, 0x4c, 0x3b, 0xc3, 0xb2, 0x81, 0x3b, + 0x1b, 0x77, 0xaf, 0x3b, 0x16, 0x9e, 0x47, 0x3b, 0x2c, 0x9b, 0x88, 0x3b, 0xce, 0xe2, 0x34, 0x3b, + 0xc9, 0xb0, 0x4f, 0x3b, 0xb8, 0xe2, 0x9a, 0x3b, 0xfe, 0x0e, 0x1b, 0x06, 0xff, 0xe9, 0xf2, 0xfe, + 0xf8, 0x17, 0x1d, 0xdb, 0x26, 0x0d, 0xfe, 0x1a, 0x23, 0x02, 0xb1, 0xf1, 0xef, 0x0f, 0xe4, 0xf8, + 0x26, 0xea, 0x10, 0xe3, 0xfe, 0xeb, 0xef, 0x0f, 0xf2, 0x17, 0xfc, 0xe5, 0x66, 0xf0, 0x04, 0x36, + 0x0f, 0x04, 0x0d, 0x3b, 0x1d, 0x46, 0x30, 0x43, 0x5c, 0x02, 0x28, 0x0d, 0x02, 0x2e, 0x7f, 0x16, + 0x2d, 0x26, 0x47, 0x0c, 0x42, 0x2b, 0x1a, 0xf0, 0xe9, 0x04, 0x04, 0x0e, 0x14, 0x0b, 0xf8, 0xfe, + 0xe9, 0x0c, 0x13, 0xe5, 0x14, 0xed, 0xc8, 0xce, 0xfc, 0xe7, 0x30, 0x19, 0x1a, 0xfa, 0x01, 0x1b, + 0xf9, 0xe7, 0x0c, 0xeb, 0x0a, 0xe7, 0xd9, 0x03, 0xc5, 0xef, 0x0a, 0x11, 0x28, 0x16, 0xfa, 0x01, + 0x07, 0xbb, 0xb7, 0xf1, 0xc5, 0x81, 0xb9, 0xf9, 0xfa, 0xf7, 0x3b, 0x00, 0xda, 0xb1, 0xdb, 0xd0, + 0xce, 0x04, 0xf8, 0x53, 0x39, 0x1b, 0x10, 0xe9, 0x03, 0x0c, 0x30, 0x14, 0xd8, 0xea, 0x2c, 0x13, + 0xf4, 0x12, 0x0a, 0x01, 0x43, 0xd5, 0xf0, 0x0b, 0xf2, 0xfd, 0xef, 0xef, 0x0a, 0x06, 0x08, 0xe0, + 0x23, 0x00, 0x03, 0xfb, 0xf7, 0xe4, 0x00, 0xdd, 0xf1, 0xf0, 0xf3, 0x05, 0xeb, 0xc0, 0xfb, 0x2a, + 0xe1, 0xd7, 0x06, 0x26, 0x13, 0x35, 0x0f, 0x2c, 0x1e, 0xf7, 0x1d, 0x00, 0xb2, 0xbd, 0xc5, 0xc4, + 0x8d, 0xa6, 0xdc, 0xa5, 0xcf, 0xe4, 0xba, 0x05, 0x39, 0xf3, 0xbd, 0xc6, 0x91, 0x81, 0xc5, 0xc5, + 0x2a, 0xda, 0xd4, 0x04, 0xd5, 0xe7, 0xff, 0xf4, 0x0e, 0xdd, 0xc7, 0xfc, 0xef, 0xfc, 0x02, 0x04, + 0x11, 0x2d, 0x3e, 0x21, 0x4d, 0xea, 0x2c, 0xf8, 0xc2, 0x4f, 0x1e, 0x27, 0x21, 0x59, 0x2e, 0x33, + 0x67, 0x30, 0xe2, 0x16, 0xfa, 0xcf, 0x16, 0xda, 0xc5, 0x20, 0x34, 0xe9, 0xba, 0xf0, 0xd2, 0xa2, + 0xcf, 0x20, 0xf3, 0xe0, 0xac, 0xe6, 0xeb, 0x04, 0x2e, 0x19, 0xfb, 0xb3, 0x9b, 0xee, 0x19, 0x9e, + 0x18, 0x47, 0x24, 0x85, 0xea, 0xd9, 0xca, 0x06, 0xec, 0xbf, 0xe1, 0xc7, 0x81, 0xee, 0xc6, 0x18, + 0xf5, 0x20, 0xef, 0xfd, 0x0a, 0x18, 0x4f, 0xc6, 0xd8, 0xc8, 0x00, 0x1c, 0x1e, 0xfd, 0x12, 0x0f, + 0xba, 0xed, 0xee, 0xdd, 0x29, 0x02, 0xd0, 0x06, 0x07, 0x09, 0xec, 0x24, 0x36, 0xdd, 0x03, 0x12, + 0x00, 0x09, 0xf5, 0xf5, 0xf0, 0x06, 0x13, 0x11, 0x30, 0x15, 0x2a, 0x3f, 0xec, 0x3b, 0x2e, 0xc8, + 0xeb, 0xb3, 0xe5, 0xa2, 0x81, 0x0e, 0x33, 0x38, 0xf4, 0x17, 0x19, 0x93, 0x94, 0xa0, 0x14, 0x3f, + 0x53, 0xd8, 0x39, 0x11, 0x10, 0x05, 0x12, 0x08, 0x20, 0xf2, 0x1d, 0x12, 0xbd, 0x21, 0x1c, 0x15, + 0xd1, 0x04, 0x01, 0x22, 0x0c, 0xd1, 0x11, 0xfd, 0xe6, 0xc7, 0x81, 0xd2, 0x0a, 0xe4, 0xe8, 0xcc, + 0xdb, 0x04, 0x34, 0x6d, 0x46, 0xe9, 0x43, 0x43, 0x27, 0x21, 0x27, 0x29, 0x57, 0x2a, 0xfc, 0x38, + 0x18, 0xec, 0x08, 0x10, 0x2f, 0xe4, 0xf5, 0xd1, 0xb2, 0xba, 0x07, 0xd6, 0xf1, 0x3d, 0xf6, 0xf0, + 0xc2, 0xe3, 0xbe, 0x2c, 0xdd, 0xde, 0x72, 0x4b, 0x46, 0xe1, 0x2e, 0x43, 0x02, 0xfe, 0x10, 0xa4, + 0xdd, 0x0f, 0x33, 0x3b, 0x4b, 0xfd, 0xf9, 0x16, 0xde, 0xfa, 0xfb, 0xca, 0xf0, 0xfe, 0x19, 0xeb, + 0xe3, 0xd8, 0xfe, 0x19, 0x02, 0x0e, 0x18, 0x10, 0xf0, 0xfa, 0xe3, 0x1f, 0x27, 0x2f, 0x21, 0x2a, + 0x13, 0x48, 0x3d, 0xc6, 0xdf, 0xf9, 0xd2, 0xf8, 0xf9, 0x28, 0x34, 0x0f, 0x08, 0xf3, 0xf2, 0xf9, + 0xf3, 0x19, 0xdf, 0x1c, 0x3a, 0x0d, 0xcc, 0x81, 0x3e, 0xd9, 0xd3, 0xb3, 0xbb, 0x37, 0xea, 0xe2, + 0xc9, 0x40, 0x50, 0x0d, 0x37, 0x3c, 0x39, 0xfc, 0x18, 0x0d, 0xc7, 0x06, 0x1d, 0xec, 0x1c, 0xfb, + 0xdf, 0xd5, 0xdc, 0xf6, 0x34, 0xf2, 0x2d, 0x3c, 0x01, 0xcf, 0xd8, 0xc6, 0xd4, 0x42, 0x2a, 0x31, + 0x00, 0xf2, 0x75, 0x5a, 0x4f, 0x14, 0xef, 0x55, 0xc5, 0xfe, 0x1d, 0xe3, 0xd7, 0x05, 0x00, 0x40, + 0x54, 0xfe, 0x32, 0x33, 0xe2, 0xf5, 0xd4, 0x7f, 0x69, 0xf0, 0x00, 0xff, 0xdc, 0x21, 0x2c, 0x3b, + 0x48, 0xf5, 0xb5, 0xd0, 0xc6, 0xad, 0x2c, 0x0b, 0x62, 0x26, 0x17, 0x18, 0x1c, 0x04, 0x0f, 0xf6, + 0xb6, 0xd9, 0xc3, 0xe6, 0x58, 0x0b, 0xf0, 0x06, 0xe3, 0xdf, 0xf5, 0xbc, 0xb1, 0xe6, 0xf5, 0xe6, + 0x1d, 0xdc, 0x01, 0xe6, 0xe3, 0xfb, 0xce, 0xeb, 0xc8, 0xf8, 0x4d, 0x19, 0xe2, 0x7f, 0x5d, 0x0e, + 0xe6, 0xdf, 0xf6, 0xff, 0xe6, 0xc4, 0x2d, 0x24, 0xcc, 0xec, 0xe2, 0xf4, 0xf6, 0x14, 0x1c, 0xfb, + 0xdf, 0x15, 0x15, 0xf2, 0xe4, 0xee, 0x0e, 0xfc, 0xf2, 0xfc, 0x3a, 0xdd, 0xf4, 0x04, 0x36, 0x1e, + 0xca, 0x43, 0x30, 0x3b, 0xd0, 0xf2, 0x30, 0x16, 0x2f, 0xd3, 0xee, 0x07, 0xcc, 0x31, 0x34, 0x20, + 0xbf, 0x04, 0xe1, 0xfe, 0xe0, 0x6c, 0xe7, 0xfb, 0x13, 0x02, 0xc8, 0xd0, 0xca, 0x3d, 0xf9, 0x43, + 0x47, 0xef, 0x24, 0x58, 0xfd, 0x7f, 0x18, 0xbb, 0x32, 0xee, 0xec, 0x22, 0x09, 0xdf, 0x0b, 0x34, + 0xff, 0x18, 0x25, 0xe8, 0xb8, 0x87, 0x86, 0xe9, 0xac, 0xda, 0x3f, 0x14, 0x3b, 0xcb, 0xd4, 0x09, + 0xba, 0xf6, 0x17, 0xc6, 0xce, 0x82, 0x04, 0x2f, 0xe7, 0x0f, 0x30, 0x01, 0x22, 0xad, 0x85, 0x26, + 0x2f, 0x14, 0xe3, 0x1b, 0x2f, 0xe3, 0xf1, 0x2d, 0xfa, 0x18, 0x16, 0xfb, 0x29, 0x1f, 0xef, 0x32, + 0xfe, 0xe7, 0xf6, 0xe6, 0x11, 0x15, 0x03, 0xd4, 0x04, 0x0d, 0xd6, 0x82, 0xc7, 0xf2, 0xcb, 0x1f, + 0xdf, 0xd3, 0xef, 0xfb, 0x10, 0x17, 0xbc, 0xdd, 0x1a, 0xc4, 0xfe, 0x1c, 0x1f, 0xee, 0x0a, 0x1b, + 0xee, 0xee, 0xfc, 0xf5, 0xf1, 0x33, 0xff, 0x21, 0x2b, 0xde, 0x26, 0x6b, 0xfd, 0xae, 0x3f, 0xfb, + 0x1a, 0xd1, 0x81, 0x37, 0xe2, 0xbf, 0xf3, 0xcd, 0x3c, 0xea, 0xc5, 0x26, 0x12, 0xa1, 0x0e, 0x09, + 0xfb, 0xca, 0xe0, 0x11, 0x11, 0x15, 0xfd, 0x1c, 0xdf, 0x04, 0x20, 0x31, 0x04, 0xfe, 0xf9, 0xd0, + 0xc4, 0x09, 0xfe, 0x02, 0x00, 0x21, 0x43, 0x3c, 0x3f, 0x4c, 0x36, 0xeb, 0x10, 0x04, 0x0f, 0x1d, + 0x07, 0xf6, 0x13, 0x1b, 0x18, 0x0d, 0xf3, 0xdb, 0xb3, 0xf2, 0xea, 0x81, 0xb1, 0xcd, 0xcc, 0xac, + 0xd4, 0x02, 0xf6, 0x02, 0xd3, 0xe7, 0xbc, 0xde, 0xb6, 0xc9, 0x20, 0x2d, 0x3e, 0x4b, 0x26, 0xec, + 0x04, 0xf8, 0x1f, 0xff, 0xfc, 0x53, 0x08, 0x3c, 0xca, 0x4d, 0x01, 0x00, 0x1d, 0x4c, 0xff, 0xff, + 0xd3, 0x1c, 0xfe, 0xff, 0xf3, 0xbd, 0xff, 0xff, 0x86, 0xfd, 0xff, 0xff, 0x13, 0x6c, 0x00, 0x00, + 0x64, 0x22, 0x00, 0x00, 0x81, 0xec, 0x00, 0x00, 0xee, 0xcd, 0xff, 0xff, 0x4c, 0x95, 0xff, 0xff, + 0xa6, 0x5b, 0xff, 0xff, 0x38, 0xdc, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x78, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x08, 0x00, 0x00, 0x03, 0x00, 0x06, 0x00, 0x06, 0x00, 0x0c, 0x00, + 0x03, 0x00, 0x02, 0x00, 0x02, 0x00, 0x12, 0x00, 0x8b, 0x82, 0x84, 0x3c, 0x80, 0xff, 0xff, 0xff, + 0x78, 0x24, 0x2f, 0x3d, 0x80, 0xff, 0xff, 0xff, 0x03, 0x03, 0x02, 0x02, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, + 0x98, 0x00, 0x00, 0x00, 0x30, 0x08, 0x00, 0x00, 0x1b, 0x3c, 0x37, 0x3c, 0xb6, 0x6d, 0x70, 0x3c, + 0x00, 0x92, 0xf1, 0x3b, 0x71, 0x70, 0x28, 0x3c, 0xc3, 0x14, 0xf3, 0x3b, 0x17, 0xad, 0x1e, 0x3c, + 0x2c, 0xaa, 0x43, 0x3c, 0x04, 0xa8, 0x44, 0x3c, 0xe4, 0x4d, 0x31, 0x3c, 0x89, 0x87, 0x0f, 0x3c, + 0x23, 0xfe, 0x1e, 0x3c, 0x35, 0x8b, 0x14, 0x3c, 0x8a, 0xe6, 0x5f, 0x3c, 0x3c, 0x7f, 0x1e, 0x3c, + 0xbe, 0x7b, 0x14, 0x3c, 0xa4, 0x75, 0x37, 0x3c, 0x35, 0x9b, 0x10, 0x3c, 0xb9, 0xa4, 0x1f, 0x3c, + 0x4c, 0x1e, 0xb7, 0x3c, 0x04, 0xe3, 0xe6, 0x8a, 0x0c, 0xe2, 0xf3, 0xf8, 0xfd, 0x0a, 0xe9, 0x01, + 0x9c, 0xa9, 0x32, 0x31, 0xe9, 0x15, 0x4c, 0x29, 0x14, 0x0f, 0xef, 0xda, 0xf7, 0xf9, 0xed, 0x0d, + 0xe6, 0xf2, 0xe5, 0xe9, 0x07, 0xea, 0xda, 0xcb, 0x0e, 0x32, 0xe4, 0xe0, 0xca, 0xc1, 0xd2, 0x12, + 0xfd, 0xf8, 0xe5, 0x1e, 0x1f, 0xbf, 0x14, 0xe9, 0xeb, 0x2b, 0x67, 0x19, 0x0e, 0x26, 0x0d, 0xae, + 0xd8, 0x00, 0x2a, 0xf0, 0xb8, 0x13, 0x25, 0xec, 0xd8, 0x1d, 0xce, 0x26, 0x20, 0x3a, 0xc6, 0xb8, + 0xe6, 0x3b, 0x14, 0xc8, 0x0f, 0xdd, 0xff, 0xf8, 0x25, 0xf9, 0x20, 0xb9, 0xf2, 0x14, 0xf0, 0x31, + 0xe3, 0xfb, 0xee, 0xea, 0x4d, 0x7f, 0x3c, 0x2b, 0x31, 0x0b, 0xe8, 0xcd, 0xba, 0xf6, 0x1b, 0x68, + 0x38, 0xe4, 0x30, 0x19, 0xfc, 0xc5, 0xe8, 0xff, 0x04, 0x33, 0x00, 0x36, 0x09, 0xd2, 0xfc, 0x27, + 0x28, 0x51, 0x50, 0xed, 0x2b, 0x2c, 0x11, 0xb1, 0xc8, 0x15, 0xd8, 0xdf, 0xf5, 0xe4, 0xe9, 0x0d, + 0xe6, 0xf0, 0xe9, 0x11, 0xf1, 0xea, 0x01, 0xd6, 0xd6, 0xdc, 0xd4, 0x18, 0xd1, 0x0f, 0xf2, 0x02, + 0x07, 0xe5, 0xa6, 0xc0, 0x1d, 0x00, 0xcc, 0xfd, 0x16, 0x13, 0xec, 0xce, 0xe6, 0x07, 0xd7, 0xca, + 0x0e, 0xe5, 0xec, 0x04, 0xb4, 0xa1, 0x0a, 0xf7, 0xa7, 0xe8, 0xd2, 0xe3, 0xf6, 0x81, 0x00, 0x0d, + 0xda, 0xff, 0x15, 0x18, 0x03, 0x12, 0xdd, 0xef, 0xec, 0x20, 0x00, 0xe7, 0xfd, 0xfa, 0x0c, 0xdd, + 0x41, 0x20, 0x2a, 0x03, 0x00, 0x14, 0x0a, 0x1d, 0xf6, 0xde, 0x53, 0x97, 0xd9, 0x3e, 0xb1, 0x3e, + 0xc1, 0xd5, 0xfe, 0xee, 0xf0, 0x09, 0xe9, 0xf5, 0x4f, 0x3b, 0xc7, 0xd1, 0xec, 0x95, 0xa5, 0xcc, + 0xde, 0xea, 0x24, 0x2a, 0xe9, 0x0e, 0xe8, 0xf3, 0xea, 0x79, 0x55, 0x4d, 0xc3, 0xe8, 0xc0, 0x48, + 0x32, 0xe5, 0x22, 0x2c, 0x25, 0x12, 0x04, 0xbb, 0xc0, 0x94, 0xab, 0xcf, 0xcd, 0xff, 0x39, 0x0f, + 0x27, 0xe2, 0xfd, 0x9d, 0xb5, 0xe8, 0xb0, 0xe3, 0xf0, 0x2b, 0xfd, 0xbb, 0xce, 0x23, 0x35, 0x19, + 0xd1, 0xaf, 0xef, 0x4c, 0xc7, 0x2b, 0xff, 0x40, 0x10, 0xf3, 0xa1, 0xcd, 0xf0, 0xcc, 0xc9, 0xe3, + 0xe7, 0xf2, 0x1a, 0xde, 0xd7, 0x0c, 0x30, 0x25, 0x0b, 0x32, 0x7f, 0x10, 0xad, 0xe5, 0xed, 0xe2, + 0xf3, 0xeb, 0x21, 0x53, 0x21, 0xb4, 0xdd, 0x1d, 0xef, 0x18, 0x04, 0x31, 0x24, 0x2c, 0x02, 0x17, + 0xf8, 0xc2, 0xc8, 0x07, 0xbf, 0x07, 0x2a, 0x10, 0xe4, 0x03, 0xc1, 0xe7, 0x08, 0xcf, 0xbe, 0x46, + 0x24, 0x57, 0x24, 0xff, 0xde, 0x24, 0x08, 0xe7, 0x14, 0x3f, 0xcd, 0xdd, 0x26, 0xd9, 0xbe, 0xfd, + 0x2c, 0xd1, 0x36, 0x7f, 0xe9, 0x20, 0x3b, 0x2e, 0x15, 0x0f, 0xf1, 0x24, 0x35, 0xdd, 0xed, 0x8a, + 0xd0, 0x17, 0xba, 0xb9, 0xe9, 0x55, 0xc1, 0x16, 0xdd, 0x21, 0x48, 0xe9, 0x00, 0xd0, 0x62, 0x00, + 0x0a, 0x56, 0x38, 0x70, 0x17, 0xfa, 0x15, 0xdf, 0xd5, 0x23, 0xec, 0xf7, 0x15, 0x42, 0xf8, 0x16, + 0xfa, 0x0a, 0xf2, 0x0f, 0x23, 0xf6, 0xea, 0xba, 0x25, 0xe6, 0xe0, 0x18, 0xdb, 0x15, 0x01, 0xd3, + 0xa1, 0x39, 0x01, 0xda, 0xca, 0x83, 0x30, 0x33, 0x1e, 0x09, 0xce, 0xda, 0x12, 0x38, 0x0c, 0x33, + 0x7f, 0xe0, 0xbf, 0x3e, 0x0c, 0xf4, 0xd9, 0xea, 0xa4, 0xa5, 0xde, 0x22, 0xe6, 0xd7, 0xb8, 0x4f, + 0x3e, 0xb8, 0x0b, 0xfe, 0x27, 0xfe, 0x07, 0x6b, 0xaf, 0xed, 0xf0, 0xc9, 0xe1, 0x27, 0xff, 0xc0, + 0x15, 0x37, 0x22, 0xf8, 0x4b, 0x28, 0x33, 0xff, 0xfc, 0x4a, 0x3d, 0xff, 0x42, 0xc7, 0xac, 0x23, + 0xcf, 0xc6, 0xf9, 0x04, 0x4d, 0x2b, 0x33, 0x47, 0x01, 0xdb, 0xb3, 0x2e, 0xdf, 0xf0, 0xb0, 0xcd, + 0x52, 0xf8, 0x07, 0xcc, 0x33, 0x56, 0x3b, 0x57, 0x0e, 0xeb, 0x3c, 0xd3, 0x40, 0x03, 0xf9, 0x0d, + 0xc2, 0x20, 0xe0, 0x43, 0xfe, 0x18, 0x1f, 0xdd, 0xf7, 0xfc, 0x86, 0xa3, 0x27, 0xee, 0xe1, 0xea, + 0xdd, 0xd1, 0x42, 0x24, 0xe9, 0xee, 0x00, 0xed, 0xef, 0x18, 0x13, 0x00, 0x19, 0x2e, 0xdd, 0xef, + 0xf1, 0x0e, 0x18, 0x04, 0x3b, 0x2e, 0xee, 0xdd, 0x96, 0xca, 0x1c, 0xea, 0x11, 0x13, 0xf5, 0x13, + 0xd8, 0x07, 0x3c, 0xd5, 0x16, 0xfc, 0x13, 0x37, 0x17, 0xc3, 0x27, 0x33, 0xf5, 0xe3, 0xfd, 0xba, + 0xe7, 0xfe, 0x0a, 0xdb, 0x24, 0xea, 0x99, 0xa2, 0xf4, 0xf8, 0x20, 0x07, 0xfe, 0x12, 0xc8, 0xe2, + 0x28, 0xf3, 0xdc, 0xf4, 0xe5, 0x81, 0xd6, 0x05, 0xee, 0xd0, 0xcd, 0x3c, 0x3f, 0xe3, 0xc3, 0x0f, + 0xc4, 0xf3, 0xca, 0xcb, 0xd7, 0xc2, 0xf5, 0x14, 0x3b, 0x39, 0xfa, 0xfe, 0x00, 0x01, 0x18, 0xf7, + 0xf7, 0xd3, 0x10, 0x38, 0x3e, 0x3b, 0x4e, 0x45, 0x00, 0xaf, 0xd6, 0x0d, 0x08, 0x35, 0xdd, 0x0b, + 0xea, 0x10, 0x15, 0x04, 0xe4, 0x0b, 0xf8, 0xe0, 0xf8, 0xfe, 0xe7, 0xf0, 0x0d, 0xee, 0x28, 0x2c, + 0xe5, 0xea, 0x33, 0x07, 0x04, 0xcb, 0x00, 0x00, 0xe9, 0xe2, 0xec, 0x03, 0x0a, 0x3d, 0xfb, 0xe1, + 0x0b, 0xfc, 0x02, 0xd8, 0xc5, 0x7f, 0x22, 0x05, 0x17, 0x07, 0xe6, 0xee, 0xff, 0x02, 0x06, 0x46, + 0x27, 0x11, 0x18, 0x0e, 0x09, 0xff, 0xfe, 0x44, 0x21, 0xde, 0x37, 0x43, 0xee, 0x19, 0xfe, 0x0d, + 0x07, 0x29, 0x34, 0xf7, 0x13, 0xf1, 0xf2, 0xe0, 0xac, 0x35, 0x33, 0x28, 0x30, 0x05, 0x2a, 0xe6, + 0xe7, 0xf2, 0xf3, 0x03, 0xcc, 0xe5, 0x03, 0x00, 0x1c, 0xeb, 0xea, 0xbc, 0xdc, 0xe6, 0x0a, 0xf8, + 0x01, 0xee, 0x13, 0x0d, 0x39, 0xf4, 0x81, 0x11, 0xf0, 0x06, 0xe7, 0x8f, 0x25, 0x17, 0xfb, 0x21, + 0x02, 0xd9, 0xfc, 0xc8, 0xd0, 0xc5, 0x20, 0x0d, 0x04, 0x0e, 0x38, 0x1d, 0x2f, 0xff, 0x0f, 0xfa, + 0xf8, 0x06, 0x1b, 0xf8, 0x15, 0xe8, 0xfc, 0xef, 0x14, 0x2e, 0x05, 0xe3, 0x13, 0x02, 0xd8, 0xff, + 0x0b, 0xfe, 0x0f, 0x07, 0x00, 0x31, 0x3c, 0x06, 0xfa, 0xea, 0x03, 0xe7, 0x31, 0x0c, 0x2c, 0x47, + 0xf4, 0x03, 0xfb, 0x18, 0xfc, 0xe7, 0x42, 0x11, 0xfa, 0x16, 0x19, 0x0e, 0x15, 0x45, 0x14, 0x22, + 0x24, 0x2b, 0xee, 0xf7, 0x2d, 0xf0, 0x0d, 0xe1, 0x07, 0x14, 0xf3, 0xfa, 0x08, 0xf6, 0xff, 0xe5, + 0x0f, 0x15, 0xce, 0xfe, 0xf2, 0x14, 0x08, 0xf6, 0x17, 0x24, 0x11, 0xf8, 0xf0, 0xea, 0x16, 0xf4, + 0x3a, 0x30, 0x33, 0xd0, 0xf2, 0xf4, 0x81, 0x0c, 0x0e, 0xce, 0x25, 0xee, 0xf3, 0xd2, 0xd9, 0x35, + 0xf2, 0x15, 0x03, 0xd5, 0xf0, 0xf6, 0xf8, 0x27, 0xf1, 0x22, 0x23, 0xd7, 0x31, 0xde, 0xcf, 0xfa, + 0xf9, 0x1f, 0x23, 0x2e, 0xdd, 0xd5, 0x04, 0x27, 0xd6, 0xff, 0x3f, 0x49, 0xf6, 0x61, 0xd3, 0xd9, + 0xe1, 0xa0, 0xaa, 0x0e, 0xe0, 0xd3, 0x1a, 0x17, 0xd1, 0xee, 0x18, 0x18, 0xfe, 0x17, 0xe6, 0x03, + 0xc3, 0xf4, 0xa6, 0xa8, 0xaf, 0x22, 0x19, 0xfd, 0x25, 0x45, 0x20, 0xd4, 0xfa, 0x82, 0xd4, 0xf6, + 0xc8, 0x24, 0xc0, 0x01, 0x30, 0xe2, 0xf8, 0xf4, 0xeb, 0xcf, 0xd3, 0x02, 0xc7, 0x24, 0x0c, 0x37, + 0x00, 0x0e, 0xe1, 0xed, 0x2a, 0x51, 0xe3, 0x29, 0x17, 0xf3, 0x05, 0xd9, 0x21, 0xe6, 0x9a, 0x61, + 0x4d, 0x27, 0x1e, 0x32, 0x2e, 0x0c, 0xdf, 0x00, 0x21, 0x0d, 0x0b, 0x39, 0x9b, 0xe8, 0x3b, 0x2b, + 0x13, 0x1d, 0xf1, 0x08, 0xed, 0x2d, 0x40, 0xd3, 0x36, 0xb2, 0x29, 0x3c, 0xc1, 0x0a, 0x21, 0x03, + 0xf5, 0x14, 0xc7, 0xf3, 0xe7, 0xe7, 0xea, 0xf8, 0x1e, 0x22, 0xee, 0xdd, 0xe8, 0x11, 0xe0, 0xe8, + 0xd3, 0x28, 0x02, 0xdd, 0xd8, 0xf7, 0x9f, 0xa7, 0x8c, 0xd6, 0x01, 0xb0, 0xd0, 0xff, 0xfa, 0x51, + 0x08, 0x04, 0xae, 0xd7, 0x01, 0x05, 0x00, 0x0c, 0xdb, 0xbe, 0x04, 0xd3, 0x0f, 0xfa, 0x11, 0x69, + 0x57, 0xea, 0x03, 0x81, 0x00, 0x09, 0x39, 0x0d, 0xeb, 0xde, 0xc7, 0xd9, 0x04, 0x25, 0xe4, 0x39, + 0xc1, 0xf3, 0xcc, 0x3e, 0xf3, 0x07, 0x2f, 0xfb, 0x21, 0x2f, 0xc8, 0xd3, 0x43, 0x81, 0x94, 0xda, + 0x17, 0xc8, 0x5e, 0x24, 0xfa, 0x11, 0x24, 0x14, 0xfe, 0xf0, 0x2d, 0xe0, 0xf6, 0xfe, 0xf7, 0xf4, + 0x01, 0x22, 0x02, 0x5a, 0xc5, 0xe8, 0x35, 0xc9, 0xfa, 0x29, 0x1d, 0xd5, 0xf7, 0x23, 0x05, 0x27, + 0x32, 0xfd, 0x48, 0xff, 0xf6, 0xa6, 0x05, 0x16, 0xd0, 0xfc, 0xe6, 0xf1, 0xc8, 0x0e, 0xdc, 0xcd, + 0x11, 0xf8, 0xfa, 0xff, 0xe2, 0x0e, 0x0e, 0xb0, 0x21, 0x30, 0x88, 0xfd, 0x2c, 0xdf, 0xe5, 0xc1, + 0xcb, 0x31, 0x22, 0xbf, 0x23, 0xf0, 0xd2, 0xd6, 0x10, 0xbf, 0xab, 0xc1, 0xc4, 0xcc, 0xda, 0xe7, + 0xe4, 0xec, 0x0e, 0x0c, 0x2f, 0x0a, 0xea, 0xe3, 0x23, 0xf7, 0xed, 0x11, 0x3c, 0x47, 0xfd, 0x39, + 0x75, 0xe2, 0x1f, 0x06, 0xb1, 0xf6, 0xf1, 0xcf, 0x2a, 0xcf, 0xfb, 0xdc, 0xcf, 0x1c, 0x39, 0x2c, + 0xf4, 0xab, 0xe2, 0x03, 0xe4, 0xe7, 0xeb, 0x06, 0xf7, 0xdb, 0xef, 0xef, 0xd9, 0xbf, 0xe0, 0x4c, + 0x6b, 0x42, 0xf8, 0xfb, 0x2d, 0xf1, 0x0c, 0x0e, 0xcb, 0xf9, 0x01, 0xea, 0xeb, 0xd1, 0x32, 0xef, + 0xcc, 0x27, 0xf4, 0x02, 0xf7, 0x1b, 0x54, 0x17, 0x5b, 0x20, 0xdf, 0x04, 0x36, 0x16, 0x2c, 0x37, + 0x3b, 0x1e, 0x25, 0x17, 0xfe, 0xeb, 0x03, 0xdf, 0x04, 0xfe, 0x06, 0x0a, 0xfe, 0x02, 0xed, 0x0b, + 0x01, 0xf7, 0x42, 0x1f, 0x1a, 0xed, 0x17, 0x40, 0xd4, 0xfd, 0xe7, 0x1d, 0x21, 0x17, 0x1e, 0xf6, + 0xe0, 0xf6, 0x13, 0x19, 0xe6, 0xcc, 0x81, 0xe3, 0xef, 0x02, 0xcd, 0x98, 0xb6, 0x18, 0xe5, 0xc0, + 0xe5, 0x81, 0xd5, 0x00, 0xe3, 0x29, 0x21, 0x04, 0xcf, 0xf9, 0xf3, 0xe2, 0xf9, 0xdb, 0xd3, 0xc5, + 0xe2, 0x04, 0xc1, 0xf4, 0xfd, 0x16, 0xff, 0x10, 0x0b, 0x47, 0x12, 0xe8, 0x0c, 0x06, 0x13, 0xbc, + 0xee, 0xed, 0x06, 0x07, 0x00, 0xf4, 0xc0, 0xd0, 0x04, 0xc7, 0xc5, 0xf1, 0xef, 0x4c, 0x35, 0xd5, + 0x2b, 0x03, 0xfe, 0x06, 0xc9, 0xda, 0x4f, 0x3e, 0xde, 0x24, 0x09, 0x06, 0x09, 0xdc, 0x0e, 0x24, + 0x3e, 0x0c, 0x4a, 0x0c, 0x02, 0x2f, 0x05, 0x26, 0xf2, 0xe9, 0xad, 0x1b, 0xe4, 0xf5, 0x16, 0xea, + 0xdf, 0x45, 0x1b, 0x07, 0x20, 0x21, 0xf6, 0xf2, 0xf5, 0xd9, 0xd1, 0xf4, 0xc4, 0xe3, 0xe8, 0x12, + 0x05, 0xd0, 0x0b, 0xbd, 0xef, 0xbd, 0xe7, 0x20, 0xe8, 0x18, 0x1f, 0x38, 0xb7, 0x42, 0x6a, 0x05, + 0x20, 0x19, 0xce, 0xaa, 0x81, 0x08, 0x4b, 0xfa, 0x0a, 0x0c, 0xda, 0x0e, 0x2c, 0x17, 0x13, 0x41, + 0x4d, 0xf3, 0x1b, 0xe6, 0xef, 0xe3, 0x12, 0x3e, 0x24, 0x3f, 0x05, 0x01, 0x1c, 0xfe, 0xc5, 0xea, + 0xc6, 0xc6, 0x24, 0x06, 0xe5, 0xe2, 0x60, 0xf6, 0xca, 0xfa, 0xf7, 0xf4, 0x26, 0x39, 0x32, 0xe3, + 0x34, 0x02, 0xd9, 0x25, 0x28, 0xdc, 0x02, 0x07, 0xc0, 0xa8, 0xed, 0x27, 0x6a, 0xe9, 0x32, 0x36, + 0xf6, 0xf4, 0x06, 0xe4, 0x20, 0x20, 0xe3, 0x01, 0x01, 0xf9, 0xe4, 0xaf, 0xcd, 0x0e, 0x4a, 0x42, + 0xca, 0x21, 0x22, 0xf3, 0xf5, 0x00, 0x21, 0x00, 0xfa, 0x0f, 0xd3, 0xe7, 0x20, 0x03, 0x16, 0x2b, + 0xd2, 0x89, 0x10, 0xa2, 0xc9, 0xf6, 0xdf, 0xd3, 0x0f, 0x06, 0xa1, 0xee, 0xaf, 0x30, 0x4d, 0x35, + 0x7e, 0x1c, 0xb5, 0xf5, 0xf8, 0xf3, 0xe2, 0xde, 0x40, 0x1c, 0x4e, 0x23, 0xd8, 0x12, 0xd1, 0xe5, + 0x0e, 0xed, 0x09, 0xf1, 0x21, 0x28, 0xef, 0x05, 0x18, 0x14, 0x01, 0x01, 0xcf, 0xf1, 0x1c, 0xf6, + 0xf9, 0x11, 0x03, 0x1a, 0x29, 0x1f, 0x15, 0xb5, 0x41, 0x25, 0xfb, 0xe9, 0x18, 0x4e, 0x5e, 0x19, + 0xf8, 0xf8, 0x7f, 0xf6, 0xe7, 0x05, 0x38, 0xf1, 0xbe, 0xd9, 0x26, 0x2e, 0x51, 0x09, 0x3c, 0x1a, + 0x5b, 0x0d, 0xfd, 0x04, 0x68, 0x49, 0xce, 0x39, 0x7e, 0x23, 0x17, 0xaa, 0x32, 0x15, 0xd6, 0x18, + 0x11, 0xda, 0x97, 0xf4, 0x6e, 0x2d, 0xe4, 0xc9, 0x16, 0x06, 0x02, 0x07, 0xb1, 0xcb, 0x0f, 0xe6, + 0xec, 0x11, 0x20, 0x20, 0xf0, 0xcf, 0xf2, 0x4a, 0xd6, 0xf8, 0xe3, 0x11, 0x3b, 0x15, 0xbb, 0xde, + 0x2a, 0x0c, 0x10, 0x3d, 0x17, 0xef, 0xf0, 0x02, 0xd4, 0xe4, 0xca, 0xde, 0xa0, 0xb7, 0xe7, 0xf0, + 0xf8, 0xda, 0x0f, 0x5c, 0x0e, 0xe4, 0xec, 0x37, 0xd4, 0xf9, 0xee, 0x31, 0x10, 0xdc, 0xf6, 0xca, + 0xe5, 0x33, 0xc6, 0xaf, 0xf1, 0x0a, 0xd3, 0x46, 0x24, 0x0b, 0x18, 0x2e, 0x81, 0x26, 0x3e, 0x2e, + 0x47, 0x5f, 0xf0, 0x02, 0x34, 0xef, 0x02, 0x01, 0xd0, 0x35, 0x4f, 0x20, 0xbb, 0xff, 0xf0, 0xfd, + 0xaf, 0x0e, 0x3b, 0x09, 0x0b, 0x21, 0xde, 0xfa, 0x0d, 0xff, 0x0c, 0x14, 0x08, 0xfe, 0x01, 0x05, + 0x11, 0x40, 0x34, 0xff, 0xdd, 0xdd, 0xee, 0x0f, 0xea, 0xc3, 0x33, 0xe1, 0xd3, 0x2d, 0x0a, 0xc9, + 0x63, 0xf9, 0xc5, 0x81, 0x1d, 0xb1, 0xaa, 0xec, 0x01, 0xe7, 0xe4, 0xb8, 0x03, 0x20, 0x4f, 0x05, + 0x6d, 0x23, 0x2b, 0x3b, 0x20, 0xf9, 0x26, 0x17, 0xe8, 0xfe, 0x01, 0x31, 0xa8, 0xd3, 0xf8, 0xf3, + 0x14, 0x12, 0xe5, 0x0f, 0x31, 0xcc, 0x06, 0x16, 0x0d, 0x17, 0x0c, 0xe4, 0xf1, 0x3c, 0x46, 0xa5, + 0xde, 0xdd, 0xf6, 0xf5, 0xd5, 0x16, 0x59, 0x66, 0x1d, 0xf4, 0xd6, 0xc0, 0xbd, 0xe1, 0xdc, 0x16, + 0xa8, 0x87, 0xdb, 0xb5, 0x05, 0xed, 0xa5, 0xfe, 0x31, 0x49, 0xba, 0xfb, 0x2b, 0xd7, 0x94, 0xc4, + 0xc8, 0x68, 0xe9, 0xc8, 0x14, 0xe8, 0xdf, 0x04, 0xc5, 0xb1, 0xf4, 0xdb, 0x4b, 0xfc, 0xf8, 0x08, + 0xf3, 0x2f, 0xf9, 0x39, 0xa4, 0xdc, 0xfa, 0x04, 0x70, 0xdb, 0xfa, 0x18, 0x1a, 0x32, 0xd2, 0x17, + 0xfb, 0xcf, 0x42, 0x11, 0x03, 0x37, 0x1a, 0x15, 0x22, 0x0d, 0x27, 0x09, 0xfc, 0x02, 0x44, 0x27, + 0x18, 0x3c, 0x1b, 0x04, 0x00, 0x36, 0xdc, 0xd9, 0xef, 0x0a, 0xe5, 0x2a, 0x06, 0xe5, 0xcc, 0x01, + 0x4d, 0x17, 0xbe, 0x1a, 0xe6, 0xf0, 0x03, 0xe7, 0xd6, 0x36, 0xe9, 0xe6, 0x34, 0xe5, 0xf5, 0xc8, + 0x02, 0x35, 0x62, 0x1a, 0xfd, 0xd3, 0xc6, 0xd0, 0xe1, 0xf5, 0xbd, 0xeb, 0xe8, 0xf6, 0x0d, 0x27, + 0x0d, 0xcc, 0xf1, 0xf8, 0x0c, 0xf1, 0x8a, 0xdf, 0xe7, 0x00, 0xd2, 0xf1, 0x81, 0x25, 0x28, 0x30, + 0x3b, 0x09, 0x32, 0xd8, 0xbe, 0xe4, 0xfe, 0xe9, 0xa7, 0xeb, 0xd6, 0xd4, 0x37, 0x1a, 0xd2, 0xd5, + 0xf8, 0x03, 0xd5, 0x30, 0x0c, 0xe8, 0xd4, 0xd1, 0xb0, 0x8c, 0xff, 0xff, 0xb7, 0xca, 0xfe, 0xff, + 0x45, 0xa6, 0xfe, 0xff, 0xa1, 0xa2, 0x00, 0x00, 0xe8, 0x58, 0x00, 0x00, 0xbe, 0x47, 0xff, 0xff, + 0x1a, 0x5a, 0x00, 0x00, 0xa3, 0xbe, 0x00, 0x00, 0x7c, 0xed, 0xfe, 0xff, 0xcd, 0x5f, 0xff, 0xff, + 0x38, 0x5f, 0xff, 0xff, 0x61, 0xdf, 0xff, 0xff, 0x1f, 0x25, 0xff, 0xff, 0x91, 0x29, 0x00, 0x00, + 0x3b, 0xca, 0x01, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x1d, 0xdb, 0xfe, 0xff, 0x23, 0x6b, 0xff, 0xff, + 0x01, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x10, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x02, 0x00, 0x02, 0x00, 0x12, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x12, 0x00, + 0x78, 0x24, 0x2f, 0x3d, 0x80, 0xff, 0xff, 0xff, 0x16, 0x74, 0x8c, 0x3c, 0x80, 0xff, 0xff, 0xff, + 0x02, 0x00, 0x00, 0x00, 0x48, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x08, 0x00, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x12, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x0a, 0x00, + 0x16, 0x74, 0x8c, 0x3c, 0x80, 0xff, 0xff, 0xff, 0x6e, 0x60, 0x03, 0x3e, 0x22, 0x00, 0x00, 0x00, + 0x40, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xcf, 0x52, 0x60, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xae, 0xe8, 0x39, 0xbc, 0x88, 0xf2, 0x4d, 0xd2, + 0x25, 0xed, 0x4a, 0x4a, 0xdc, 0x4b, 0xc1, 0x96, 0x05, 0xd3, 0xbe, 0xab, 0x3c, 0x3b, 0xc2, 0xa5, + 0xd5, 0xae, 0x38, 0x4e, 0xe1, 0x41, 0xc6, 0xa5, 0x0a, 0x36, 0xc1, 0xf5, 0xa5, 0xaf, 0x25, 0xd6, + 0xc8, 0x2c, 0x52, 0x10, 0xc2, 0x13, 0xbf, 0xcf, 0x4e, 0xcb, 0x41, 0x14, 0x40, 0xe5, 0x44, 0xed, + 0x81, 0xec, 0xe8, 0xaf, 0x33, 0x2f, 0x1e, 0xb1, 0xa9, 0x3e, 0x4f, 0xd4, 0xf7, 0x43, 0x9b, 0xd5, + 0x03, 0xff, 0x40, 0xa9, 0x50, 0x05, 0xa4, 0x98, 0x5b, 0xc6, 0x11, 0x9a, 0xb8, 0xeb, 0xd5, 0x41, + 0x43, 0xed, 0x4d, 0xd9, 0xd5, 0x4a, 0xd0, 0x25, 0xd0, 0x51, 0xba, 0xce, 0x45, 0x19, 0x9b, 0xcd, + 0x20, 0x01, 0xf5, 0x9e, 0xb4, 0x4d, 0x3d, 0x32, 0xa4, 0x60, 0x94, 0xc4, 0xd6, 0xf3, 0x3c, 0xb1, + 0x32, 0xda, 0xb2, 0xf8, 0xb2, 0xa1, 0xd4, 0xa3, 0x00, 0x45, 0x3a, 0x90, 0x09, 0xcd, 0xc1, 0xc5, + 0xbe, 0x37, 0xe0, 0x38, 0x2f, 0xdb, 0x3f, 0x35, 0x3c, 0x19, 0xbf, 0x1f, 0xbb, 0xf1, 0x27, 0xfb, + 0xf2, 0x46, 0x16, 0xb7, 0x05, 0xf0, 0xae, 0xa1, 0xf0, 0x5d, 0x22, 0x51, 0xb4, 0xb6, 0x39, 0xf0, + 0xc7, 0x14, 0xb2, 0x09, 0xaf, 0x02, 0xb1, 0x5a, 0xea, 0x36, 0xe2, 0xe9, 0x00, 0x00, 0x00, 0x00, + 0x7c, 0x91, 0xff, 0xff, 0xfb, 0x69, 0xff, 0xff, 0x34, 0xcc, 0xff, 0xff, 0xd7, 0x8c, 0xff, 0xff, + 0x92, 0x98, 0xff, 0xff, 0xc0, 0xae, 0xff, 0xff, 0x85, 0x45, 0xff, 0xff, 0x5f, 0xb1, 0xff, 0xff, + 0x46, 0xcc, 0xff, 0xff, 0x26, 0xa6, 0xff, 0xff, 0x03, 0x00, 0x01, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x48, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x0a, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x0a, 0x00, 0x6e, 0x60, 0x03, 0x3e, 0x22, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x80, 0x3b, 0x80, 0xff, 0xff, 0xff, }; + +#endif diff --git a/examples/mnist_cnn/mnist_cnn_int8.tflite b/examples/mnist_cnn/mnist_cnn_int8.tflite new file mode 100644 index 0000000000000000000000000000000000000000..d7131def390aeaf2afab6bfb01afc9b8c8c898bd GIT binary patch literal 7976 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 H<+uL}vSIjp literal 0 HcmV?d00001 diff --git a/examples/mnist_cnn/mnist_cnn_run.py b/examples/mnist_cnn/mnist_cnn_run.py index 92700e5..1340970 100644 --- a/examples/mnist_cnn/mnist_cnn_run.py +++ b/examples/mnist_cnn/mnist_cnn_run.py @@ -4,9 +4,9 @@ import time import gc -import emlearn_cnn +import emlearn_cnn_int8 -MODEL = 'mnist_cnn.tmdl' +MODEL = 'mnist_cnn_int8.tmdl' TEST_DATA_DIR = 'test_data' def argmax(arr): diff --git a/examples/mnist_cnn/mnist_train.py b/examples/mnist_cnn/mnist_train.py index c1a7b74..ef96cfc 100644 --- a/examples/mnist_cnn/mnist_train.py +++ b/examples/mnist_cnn/mnist_train.py @@ -86,6 +86,7 @@ def generate_test_files(out_dir, x, y, samples_per_class=5): def generate_tinymaix_model(h5_file, + out_file : str, input_shape : tuple[int], output_shape : tuple[int], tools_dir, @@ -99,7 +100,7 @@ def generate_tinymaix_model(h5_file, # Convert .h5 to .tflite file assert h5_file.endswith('.h5'), 'Keras model HDF5 file must end with .h5' - tflite_file = h5_file.replace('.h5', '.tflite') + tflite_file = out_file + '.tflite' args = [ python_bin, @@ -157,26 +158,26 @@ def main(): tinymaix_tools_dir = '../../dependencies/TinyMaix/tools' assert os.path.exists(tinymaix_tools_dir), tinymaix_tools_dir - quantize_data = None # disables quantization - quantize_data = os.path.join(tinymaix_tools_dir, 'quant_img_mnist/') - if quantize_data is not None: - assert os.path.exists(quantize_data) - precision = 'int8' if quantize_data else 'fp32' - # Run training train_mnist(h5_file) - #data = x_test[1] - # Export the model using TinyMaix - out = generate_tinymaix_model(h5_file, - input_shape=(28,28,1), - output_shape=(1,), - tools_dir=tinymaix_tools_dir, - precision=precision, - quantize_data=quantize_data, - ) - print('Wrote model to', out) + # both with quantization and without + for config in ('int8', 'fp32'): + if config == 'int8': + quantize_data = os.path.join(tinymaix_tools_dir, 'quant_img_mnist/') + else: + quantize_data = None # disables quantization + + out = generate_tinymaix_model(h5_file, + out_file=h5_file.replace('.h5', '')+f'_{config}', + input_shape=(28,28,1), + output_shape=(1,), + tools_dir=tinymaix_tools_dir, + precision=config, + quantize_data=quantize_data, + ) + print('Wrote model to', out) if __name__ == '__main__': main() diff --git a/examples/mnist_cnn/package.json b/examples/mnist_cnn/package.json index 54e7dba..2b42079 100644 --- a/examples/mnist_cnn/package.json +++ b/examples/mnist_cnn/package.json @@ -7,7 +7,8 @@ "readme": "github:emlearn/emlearn-micropython/blob/master/examples/mnist_cnn/README.md", "keywords": "machinelearning,cnn,convolutionalneuralnetwork,classification,mnist,digits", "urls": [ - ["fs:mnist_cnn.tmdl", "mnist_cnn.tmdl"], + ["fs:mnist_cnn_int8.tmdl", "mnist_cnn_int8.tmdl"], + ["fs:mnist_cnn_fp32.tmdl", "mnist_cnn_fp32.tmdl"], ["fs:data/mnist_example_0.bin", "data/mnist_example_0.bin"], ["fs:data/mnist_example_1.bin", "data/mnist_example_1.bin"], ["fs:data/mnist_example_2.bin", "data/mnist_example_2.bin"], diff --git a/tests/test_cnn.py b/tests/test_cnn.py index b1b3aa0..190ed5e 100644 --- a/tests/test_cnn.py +++ b/tests/test_cnn.py @@ -1,16 +1,18 @@ import array -import emlearn_cnn +import emlearn_cnn_int8 +import emlearn_cnn_fp32 -MNIST_MODEL = 'examples/mnist_cnn/mnist_cnn.tmdl' +MNIST_MODEL_INT8 = 'examples/mnist_cnn/mnist_cnn_int8.tmdl' +MNIST_MODEL_FP32 = 'examples/mnist_cnn/mnist_cnn_fp32.tmdl' MNIST_DATA_DIR = 'examples/mnist_cnn/data/' def test_cnn_create(): model = None - with open(MNIST_MODEL, 'rb') as f: + with open(MNIST_MODEL_FP32, 'rb') as f: model_data = array.array('B', f.read()) - model = emlearn_cnn.new(model_data) + model = emlearn_cnn_fp32.new(model_data) out_shape = model.output_dimensions() assert out_shape == (10,), (out_shape) @@ -48,12 +50,12 @@ def argmax(arr): return idx_max -def test_cnn_mnist(): +def check_cnn_mnist(cnn_module, model_path): model = None - with open(MNIST_MODEL, 'rb') as f: + with open(model_path, 'rb') as f: model_data = array.array('B', f.read()) - model = emlearn_cnn.new(model_data) + model = cnn_module.new(model_data) probabilities = array.array('f', (-1 for _ in range(10))) @@ -75,6 +77,14 @@ def test_cnn_mnist(): assert correct >= 9, correct +def test_cnn_mnist_int8(): + check_cnn_mnist(emlearn_cnn_int8, MNIST_MODEL_INT8) + + +def test_cnn_mnist_fp32(): + check_cnn_mnist(emlearn_cnn_fp32, MNIST_MODEL_FP32) + test_cnn_create() -test_cnn_mnist() +test_cnn_mnist_int8() +test_cnn_mnist_fp32() From cf954d3d8732808a3727b34d7b7359ce387ad7f2 Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Thu, 9 Jan 2025 00:59:10 +0100 Subject: [PATCH 03/80] cnn: Disable debug --- src/tinymaix_cnn/mod_cnn.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tinymaix_cnn/mod_cnn.c b/src/tinymaix_cnn/mod_cnn.c index b6312fd..135f1ce 100644 --- a/src/tinymaix_cnn/mod_cnn.c +++ b/src/tinymaix_cnn/mod_cnn.c @@ -13,7 +13,7 @@ #include -#define DEBUG (1) +#define DEBUG (0) // memset is used by some standard C constructs From 4761fbad0863acc9790b77f231c4995336e8f20c Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Thu, 9 Jan 2025 01:20:06 +0100 Subject: [PATCH 04/80] cnn: Try to avoid double operations --- dependencies/TinyMaix | 2 +- src/tinymaix_cnn/Makefile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dependencies/TinyMaix b/dependencies/TinyMaix index 09ae9b7..7bcb087 160000 --- a/dependencies/TinyMaix +++ b/dependencies/TinyMaix @@ -1 +1 @@ -Subproject commit 09ae9b70ecb1eb3afff8b669db46a49a1b1605ef +Subproject commit 7bcb087b6a8ff69fa3e2ac89e67df5b35aa86df9 diff --git a/src/tinymaix_cnn/Makefile b/src/tinymaix_cnn/Makefile index ee71fe4..affaba7 100644 --- a/src/tinymaix_cnn/Makefile +++ b/src/tinymaix_cnn/Makefile @@ -72,6 +72,6 @@ _addsubdf3.o: _arm_addsubdf3.o: $(CROSS)ar -x $(LIBGCC_FILENAME) $(SOFTFP_O) -CFLAGS += -I$(CONFIG_DIR) -I$(TINYMAIX_DIR)/include -I$(TINYMAIX_DIR)/src -Wno-error=unused-variable -Wno-error=multichar +CFLAGS += -I$(CONFIG_DIR) -I$(TINYMAIX_DIR)/include -I$(TINYMAIX_DIR)/src -Wno-error=unused-variable -Wno-error=multichar -Wdouble-promotion dist: $(DIST_FILE) From 9fd8c0f0f0e1caf8e46a31b15b0bb962bbc67106 Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Wed, 15 Jan 2025 07:24:24 +0100 Subject: [PATCH 05/80] Add LICENSE file --- LICENSE.md | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 LICENSE.md diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..d0ab090 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,9 @@ +## The MIT License + +Copyright 2024-2025 Jon Nordby et. al + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. From e0642939fcd3a9634fbaad9c3e52a70c5f7d2e64 Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Wed, 15 Jan 2025 07:41:05 +0100 Subject: [PATCH 06/80] docs: Skeleton setup --- docs/Makefile | 19 ++++++++ docs/getting_started_micropython.rst | 27 +++++++++++ .../images/event_detection_labels_windows.png | Bin 0 -> 94928 bytes docs/index.rst | 21 +++++++++ docs/static/css/emlearn.css | 5 ++ docs/static/js/custom.js | 43 ++++++++++++++++++ docs/user_guide.rst | 18 ++++++++ 7 files changed, 133 insertions(+) create mode 100644 docs/Makefile create mode 100644 docs/getting_started_micropython.rst create mode 100644 docs/images/event_detection_labels_windows.png create mode 100644 docs/index.rst create mode 100644 docs/static/css/emlearn.css create mode 100644 docs/static/js/custom.js create mode 100644 docs/user_guide.rst diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..298ea9e --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,19 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +SOURCEDIR = . +BUILDDIR = _build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) \ No newline at end of file diff --git a/docs/getting_started_micropython.rst b/docs/getting_started_micropython.rst new file mode 100644 index 0000000..f6e6e17 --- /dev/null +++ b/docs/getting_started_micropython.rst @@ -0,0 +1,27 @@ + +.. Places parent toc into the sidebar + +:parenttoc: True + +.. _getting_started_micropython: + +================================== +Getting started with MicroPython +================================== + +.. currentmodule:: emlearn + +emlearn has dedicated support for `MicroPython `_ +though `emlearn-micropython `_. + +It enables using emlearn to get efficient inference, +but by programming only in Python and not having to deal with the underlying C code. + +MicroPython supports a wide range of microcontroller devices, +and enables interactive programming. +It also supports modern features such as file-system access, USB mass storage, etc. + +Information about the supported ML models and +how to get started, can be found in the `emlearn-micropython README `_. + + diff --git a/docs/images/event_detection_labels_windows.png b/docs/images/event_detection_labels_windows.png new file mode 100644 index 0000000000000000000000000000000000000000..334d941e4afa3a7f7163946af0dbb2936114001b GIT binary patch literal 94928 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 literal 0 HcmV?d00001 diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000..a2d4f1e --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,21 @@ +.. emlearn-micropython documentation master file + +Welcome to emlearn's documentation! +=================================== + +.. toctree:: + :maxdepth: 3 + :caption: Contents: + + source/README.md + user_guide + api_reference + source/LICENSE.md + + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/docs/static/css/emlearn.css b/docs/static/css/emlearn.css new file mode 100644 index 0000000..bb7694e --- /dev/null +++ b/docs/static/css/emlearn.css @@ -0,0 +1,5 @@ + +.wy-side-nav-search { + /* Make background light / near white, so that logo has good contrast */ + background-color: #e4fdff; +} diff --git a/docs/static/js/custom.js b/docs/static/js/custom.js new file mode 100644 index 0000000..1c411c0 --- /dev/null +++ b/docs/static/js/custom.js @@ -0,0 +1,43 @@ + +window.addEventListener('load', (_event) => { + var menu = document.querySelector(".wy-menu ul li:first-child") + recurse(menu) +}); + +/** + * Given a Node, it recursively goes through every child and checks if the child is expandable, it + * expands it unless it is already expanded. + * + * @param {Node} node + */ +function recurse(node) { + if (is_expandable(node) && !is_expanded(node)) { + node.classList.add("current") + } + + // By default, children are not arrays, so we need to convert them + children = Array.prototype.slice.call(node.children) + + children.forEach(recurse) +} + +/** + * Returns whether or not the given node is an expandable list. + * + * @param {Node} node + * @returns {boolean} true if the node is a toctree that can be expanded, false otherwise. + */ +function is_expandable(node) { + return node.className.includes("toctree-l") +} + +/** + * Returns whether or not the given expandable node is already expanded. + * Nodes are considered expandaded if they are 'current'ly selected, so we take advantage of this. + * + * @param {Node} node + * @returns {boolean} true if the node is already expanded, false otherwise. + */ +function is_expanded(node) { + return node.classList.contains("current") +} diff --git a/docs/user_guide.rst b/docs/user_guide.rst new file mode 100644 index 0000000..6c756c6 --- /dev/null +++ b/docs/user_guide.rst @@ -0,0 +1,18 @@ +.. Places parent toc into the sidebar + +:parenttoc: True + +.. title:: User guide: contents + +.. _user_guide: + +========== +User Guide +========== + +.. toctree:: + :numbered: + :maxdepth: 3 + + getting_started_micropython.rst + From 688d6f4f2482bcd536db9141a0604289cf3231bc Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Sat, 18 Jan 2025 19:30:16 +0100 Subject: [PATCH 07/80] docs: Try setup build --- docs/.gitignore | 7 ++ docs/conf.py | 211 +++++++++++++++++++++++++++++++++++++++++++ requirements.dev.txt | 5 + 3 files changed, 223 insertions(+) create mode 100644 docs/.gitignore create mode 100644 docs/conf.py create mode 100644 requirements.dev.txt diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 0000000..88d5b65 --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,7 @@ +_build +_static +_templates +auto_examples/ + +doxygen/xml +doxygen/html diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000..c35f0b7 --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,211 @@ +# -*- coding: utf-8 -*- +# +# Configuration file for the Sphinx documentation builder. +# +# This file does only contain a selection of the most common options. For a +# full list see the documentation: +# http://www.sphinx-doc.org/en/master/config + +# -- Path setup -------------------------------------------------------------- + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# + +import os +import sys +import subprocess + +sys.path.insert(0, os.path.abspath('..')) + +# -- Project information ----------------------------------------------------- + +project = 'emlearn-micropython' +copyright = '2014-2025, Jon Nordby' +author = 'Jon Nordby' + +# The short X.Y version +version = '' +# The full version, including alpha/beta/rc tags +release = '' + + +# -- General configuration --------------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. +# +# needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + 'sphinx.ext.mathjax', + 'sphinx.ext.viewcode', + 'sphinx.ext.autodoc', + 'sphinx.ext.doctest', + 'sphinx.ext.coverage', + #'sphinx.ext.autosectionlabel', + #'sphinx_gallery.gen_gallery', + 'm2r2', +] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix(es) of source filenames. +from recommonmark.parser import CommonMarkParser + +source_parsers = { + '.md': CommonMarkParser, +} +source_suffix = ['.rst', '.md'] + +# The master toctree document. +master_doc = 'index' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +language = 'en' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path. +exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = None + + +# -- Options for HTML output ------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = "sphinx_rtd_theme" + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +# +html_theme_options = { + "collapse_navigation": False, + 'navigation_depth': 3, + 'logo_only': True, +} + +html_js_files = [ + #'js/custom.js' +] + +html_css_files = [ + 'css/emlearn.css', +] + +html_logo = '../brand/emlearn-logo-wordmark-wide-600px.png' + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['static'] + +# Custom sidebar templates, must be a dictionary that maps document names +# to template names. +# +# The default sidebars (for documents that don't match any pattern) are +# defined by theme itself. Builtin themes are using these templates by +# default: ``['localtoc.html', 'relations.html', 'sourcelink.html', +# 'searchbox.html']``. +# +# html_sidebars = {} + + +# -- Options for HTMLHelp output --------------------------------------------- + +# Output file base name for HTML help builder. +htmlhelp_basename = 'emlearn_micropython_doc' + + +# -- Options for LaTeX output ------------------------------------------------ + +latex_elements = { + # The paper size ('letterpaper' or 'a4paper'). + # + # 'papersize': 'letterpaper', + + # The font size ('10pt', '11pt' or '12pt'). + # + # 'pointsize': '10pt', + + # Additional stuff for the LaTeX preamble. + # + # 'preamble': '', + + # Latex figure (float) alignment + # + # 'figure_align': 'htbp', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + (master_doc, 'emlearn-micropython.tex', 'emlearn-micropython Documentation', + 'Jon Nordby', 'manual'), +] + + +# -- Options for manual page output ------------------------------------------ + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + (master_doc, 'emlearn-micropython', 'emlearn-micropython Documentation', + [author], 1) +] + + +# -- Options for Texinfo output ---------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + (master_doc, 'emlearn-micropython', 'emlearn-micropython Documentation', + author, 'emlearn-micropython', 'One line description of project.', + 'Miscellaneous'), +] + + +# -- Options for Epub output ------------------------------------------------- + +# Bibliographic Dublin Core info. +epub_title = project + +# The unique identifier of the text. This can be a ISBN number +# or the project homepage. +# +# epub_identifier = '' + +# A unique identification for the text. +# +# epub_uid = '' + +# A list of files that should not be packed into the epub file. +epub_exclude_files = ['search.html'] + + +# -- Extension configuration ------------------------------------------------- + + +sphinx_gallery_conf = { + 'examples_dirs': '../examples', # path to your example scripts + 'gallery_dirs': 'auto_examples', # path to where to save gallery generated output + 'filename_pattern': '/', # execute all .py files, not just those with plot_ prefix +} + + diff --git a/requirements.dev.txt b/requirements.dev.txt new file mode 100644 index 0000000..d09e48e --- /dev/null +++ b/requirements.dev.txt @@ -0,0 +1,5 @@ +sphinx>=4.0.0 +recommonmark>=0.6.0 +sphinx_rtd_theme>=0.5.2 +sphinx-gallery>=0.10.1 +m2r2>=0.3.2 From 3a3b74d1aa538a619f1afa4d7e74b2ed007c66ff Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Sat, 18 Jan 2025 19:52:31 +0100 Subject: [PATCH 08/80] docs: Fix image --- docs/conf.py | 8 ++++++-- .../images/emlearn-logo-wordmark-wide-600px.png | Bin 0 -> 14722 bytes docs/index.rst | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) create mode 100644 docs/images/emlearn-logo-wordmark-wide-600px.png diff --git a/docs/conf.py b/docs/conf.py index c35f0b7..9ba9b42 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -60,7 +60,11 @@ source_parsers = { '.md': CommonMarkParser, } -source_suffix = ['.rst', '.md'] + +source_suffix = { + '.rst': 'restructuredtext', + '.md': 'markdown', +} # The master toctree document. master_doc = 'index' @@ -106,7 +110,7 @@ 'css/emlearn.css', ] -html_logo = '../brand/emlearn-logo-wordmark-wide-600px.png' +html_logo = './images/emlearn-logo-wordmark-wide-600px.png' # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, diff --git a/docs/images/emlearn-logo-wordmark-wide-600px.png b/docs/images/emlearn-logo-wordmark-wide-600px.png new file mode 100644 index 0000000000000000000000000000000000000000..ef650697a361dc8d383cf3577eafbc8ad7239720 GIT binary patch literal 14722 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 BVy*xH literal 0 HcmV?d00001 diff --git a/docs/index.rst b/docs/index.rst index a2d4f1e..1187abf 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,6 +1,6 @@ .. emlearn-micropython documentation master file -Welcome to emlearn's documentation! +Welcome to emlearn-micropython's documentation! =================================== .. toctree:: From 5f2d0b1ef9e78c0c139266b3bf9b4115bf43d87e Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Sat, 18 Jan 2025 20:02:29 +0100 Subject: [PATCH 09/80] docs: Initial ReadTheDocs configuration --- .readthedocs.yaml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 .readthedocs.yaml diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 0000000..ab53229 --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,16 @@ +# Read the Docs configuration file +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details + +# Required +version: 2 + +# Set the OS, Python version, and other tools you might need +build: + os: ubuntu-24.04 + tools: + python: "3.12" + +python: + install: + - requirements: requirements.dev.txt + From ff76b68c892389f19995d3e171dafd88119dc8f4 Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Sat, 18 Jan 2025 20:18:56 +0100 Subject: [PATCH 10/80] docs: Include the source files --- docs/source/LICENSE.md | 1 + docs/source/README.md | 1 + 2 files changed, 2 insertions(+) create mode 120000 docs/source/LICENSE.md create mode 120000 docs/source/README.md diff --git a/docs/source/LICENSE.md b/docs/source/LICENSE.md new file mode 120000 index 0000000..f0608a6 --- /dev/null +++ b/docs/source/LICENSE.md @@ -0,0 +1 @@ +../../LICENSE.md \ No newline at end of file diff --git a/docs/source/README.md b/docs/source/README.md new file mode 120000 index 0000000..fe84005 --- /dev/null +++ b/docs/source/README.md @@ -0,0 +1 @@ +../../README.md \ No newline at end of file From cb01b7d0695bf838faccd5cf95a5f408db2089a0 Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Sat, 18 Jan 2025 21:49:10 +0100 Subject: [PATCH 11/80] docs: Use more recent Markdown support --- docs/conf.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 9ba9b42..c0c7e12 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -46,21 +46,15 @@ 'sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.coverage', - #'sphinx.ext.autosectionlabel', + 'sphinx.ext.autosectionlabel', #'sphinx_gallery.gen_gallery', - 'm2r2', + 'myst_parser', ] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix(es) of source filenames. -from recommonmark.parser import CommonMarkParser - -source_parsers = { - '.md': CommonMarkParser, -} - source_suffix = { '.rst': 'restructuredtext', '.md': 'markdown', From 829fa310d2c26566fc91105c5d4a29751c324248 Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Sat, 18 Jan 2025 21:49:32 +0100 Subject: [PATCH 12/80] docs: Enable intersphinx --- docs/conf.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/conf.py b/docs/conf.py index c0c7e12..97237c4 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -49,6 +49,7 @@ 'sphinx.ext.autosectionlabel', #'sphinx_gallery.gen_gallery', 'myst_parser', + "sphinx.ext.intersphinx", ] # Add any paths that contain templates here, relative to this directory. @@ -207,3 +208,9 @@ } + +intersphinx_mapping = { + "emlearn": ("https://emlearn.readthedocs.io/en/latest/", None), +} + + From e3c793cc621f61dd580481fe604a91da847b4aff Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Sat, 18 Jan 2025 22:20:36 +0100 Subject: [PATCH 13/80] docs: Use recommended intersphinx config --- docs/conf.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/conf.py b/docs/conf.py index 97237c4..519ec2f 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -213,4 +213,9 @@ "emlearn": ("https://emlearn.readthedocs.io/en/latest/", None), } +# Sphinx defaults to automatically resolve *unresolved* labels using all your Intersphinx mappings. +# This behavior has unintended side-effects, namely that documentations local references can +# suddenly resolve to an external location. See also: +# https://www.sphinx-doc.org/en/master/usage/extensions/intersphinx.html#confval-intersphinx_disabled_reftypes +intersphinx_disabled_reftypes = ["*"] From 0b1c24369522dbafdc816731fbd2829faebdf4f0 Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Sat, 18 Jan 2025 22:20:50 +0100 Subject: [PATCH 14/80] docs: Fix missing requirements update --- requirements.dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.dev.txt b/requirements.dev.txt index d09e48e..aadf663 100644 --- a/requirements.dev.txt +++ b/requirements.dev.txt @@ -2,4 +2,4 @@ sphinx>=4.0.0 recommonmark>=0.6.0 sphinx_rtd_theme>=0.5.2 sphinx-gallery>=0.10.1 -m2r2>=0.3.2 +myst_parser>=4.0.0 From 6c9cafec15a977fe46aa430b734c300ed462e842 Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Sat, 18 Jan 2025 22:36:56 +0100 Subject: [PATCH 15/80] brand: Add a wide wordmark --- brand/README.md | 9 + ...n-micropython-logo-wordmark-wide-600px.png | Bin 0 -> 20030 bytes ...emlearn-micropython-logo-wordmark-wide.png | Bin 0 -> 41111 bytes ...emlearn-micropython-logo-wordmark-wide.svg | 217 ++++++++++++++++++ docs/conf.py | 2 +- 5 files changed, 227 insertions(+), 1 deletion(-) create mode 100644 brand/README.md create mode 100644 brand/emlearn-micropython-logo-wordmark-wide-600px.png create mode 100644 brand/emlearn-micropython-logo-wordmark-wide.png create mode 100644 brand/emlearn-micropython-logo-wordmark-wide.svg diff --git a/brand/README.md b/brand/README.md new file mode 100644 index 0000000..a985a1a --- /dev/null +++ b/brand/README.md @@ -0,0 +1,9 @@ + +# emlearn-micropython brand guidelines + +The emlearn-micropython brand is derived from that of the parent project, emlearn. +See the [emlearn brand guidelines](https://github.com/emlearn/emlearn/blob/master/brand/README.md). + +#### Wide logo with wordmark + +Download: [SVG](./emlearn-micropython-logo-wordmark-wide.svg), [PNG](./emlearn-micropython-logo-wordmark-wide.png) diff --git a/brand/emlearn-micropython-logo-wordmark-wide-600px.png b/brand/emlearn-micropython-logo-wordmark-wide-600px.png new file mode 100644 index 0000000000000000000000000000000000000000..74822189a097f021bf21c3aa73b0b5cec3abf206 GIT binary patch literal 20030 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 literal 0 HcmV?d00001 diff --git a/brand/emlearn-micropython-logo-wordmark-wide.png b/brand/emlearn-micropython-logo-wordmark-wide.png new file mode 100644 index 0000000000000000000000000000000000000000..71af5633a4086aed2afca37311a6dac455829d9d GIT binary patch literal 41111 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

2#rn;78peZ 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 literal 0 HcmV?d00001 diff --git a/brand/emlearn-micropython-logo-wordmark-wide.svg b/brand/emlearn-micropython-logo-wordmark-wide.svg new file mode 100644 index 0000000..6dd6de1 --- /dev/null +++ b/brand/emlearn-micropython-logo-wordmark-wide.svg @@ -0,0 +1,217 @@ + + + +emlearn-micropython + + + + + + + + + + + + + + + + + + + + diff --git a/docs/conf.py b/docs/conf.py index 519ec2f..7dc05bc 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -105,7 +105,7 @@ 'css/emlearn.css', ] -html_logo = './images/emlearn-logo-wordmark-wide-600px.png' +html_logo = '../brand/emlearn-micropython-logo-wordmark-wide-600px.png' # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, From ca92124ee5488de25c260d58fe8d0025fa487f2f Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Sat, 18 Jan 2025 22:37:20 +0100 Subject: [PATCH 16/80] CI: Setup documentation build --- .github/workflows/build.yaml | 37 ++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 60ff103..9304af2 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -68,3 +68,40 @@ jobs: publish_dir: ./dist keep_files: true destination_dir: builds/${{ github.ref_name }} + + docs: + + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ['3.10'] + + steps: + - uses: actions/checkout@v4 + with: + submodules: true + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - name: Install OS dependencies + run: | + sudo apt-get update + sudo apt-get install -yqq libsndfile1 libsndfile1-dev doxygen + - name: Install Python dependencies + run: | + python -m pip install -U 'pip<20' + pip install flake8 pytest + if [ -f requirements.txt ]; then pip install -r requirements.txt; fi + if [ -f requirements.dev.txt ]; then pip install -r requirements.dev.txt; fi + - name: Build package + run: | + python setup.py build + - name: Build documentation using Sphinx + working-directory: ./docs + env: + PYTHONPATH: ../:../build/lib.linux-x86_64-cpython-310 + READTHEDOCS: 'True' + run: | + make html From 3e42476435a56abbbe01c870d31841a8d4b9ec6a Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Sat, 18 Jan 2025 22:40:52 +0100 Subject: [PATCH 17/80] CI: There is no Python package to build before docs --- .github/workflows/build.yaml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 9304af2..672d6e4 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -95,9 +95,6 @@ jobs: pip install flake8 pytest if [ -f requirements.txt ]; then pip install -r requirements.txt; fi if [ -f requirements.dev.txt ]; then pip install -r requirements.dev.txt; fi - - name: Build package - run: | - python setup.py build - name: Build documentation using Sphinx working-directory: ./docs env: From 978b1a7ab88729581bb551d1e7604fb0120bb824 Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Sun, 19 Jan 2025 00:23:13 +0100 Subject: [PATCH 18/80] docs: Add examples, links to emlearn --- docs/examples.rst | 34 +++++ ...opython.rst => getting_started_device.rst} | 0 docs/getting_started_host.rst | 124 ++++++++++++++++++ docs/index.rst | 2 + docs/more.rst | 35 +++++ docs/notes.md | 16 +++ docs/user_guide.rst | 3 +- 7 files changed, 213 insertions(+), 1 deletion(-) create mode 100644 docs/examples.rst rename docs/{getting_started_micropython.rst => getting_started_device.rst} (100%) create mode 100644 docs/getting_started_host.rst create mode 100644 docs/more.rst create mode 100644 docs/notes.md diff --git a/docs/examples.rst b/docs/examples.rst new file mode 100644 index 0000000..7da2c2c --- /dev/null +++ b/docs/examples.rst @@ -0,0 +1,34 @@ + + +========= +Examples +========= + +.. _./examples/soundlevel_iir/: +Soundlevel using IIR filters (soundlevel_iir) +=============================================== + +`Example on Github `_. + + +.. _./examples/har_trees/: +Human Activity Detection using classification trees (har_trees) +============================================================================================== + +`Example on Github `_. + + +.. _./examples/mnist_cnn/: +Digits recognition using Convolutional Neural Networks (mnist_cnn) +======================================================================== + +`Example on Github `_. + + + +.. _./examples/xor_trees/: +XOR classification using trees (xor_trees) +======================================================================== + +`Example on Github `_. + diff --git a/docs/getting_started_micropython.rst b/docs/getting_started_device.rst similarity index 100% rename from docs/getting_started_micropython.rst rename to docs/getting_started_device.rst diff --git a/docs/getting_started_host.rst b/docs/getting_started_host.rst new file mode 100644 index 0000000..55322c9 --- /dev/null +++ b/docs/getting_started_host.rst @@ -0,0 +1,124 @@ + +.. Places parent toc into the sidebar + +:parenttoc: True + +.. _getting_started_host: + +========================= +Getting started on PC (Linux/MacOS/Windows) +========================= + +.. currentmodule:: emlearn-micropython + +emlearn models work anywhere there is a C99 compiler available. +This includes common desktop platforms such as Linux, Mac OS, Windows, etc. +Since you need such a host platform to develop the Python machine-learning, +it is convenient also to do the first tests of the model on the host. + +Prerequisites +=========================== + +You need to have installed **Python** (version 3.6+), +and a **C99 compiler** for your platform (GCC/Clang/MSVC). + +On Windows, the Windows Subsystem for Linux (WSL) is recommended, +but MSCV and cmd.exe/Powershell can also be used. + +Install scikit-learn +=========================== + +In this example, **scikit-learn** is used to train the models. + +.. code-block:: console + + pip install scikit-learn + +Install emlearn +=========================== + +**emlearn** will be used to convert the scikit-learn models to C code. + +.. code-block:: console + + pip install emlearn + + +Create model in Python +=========================== + +We will train a simple model to learn the XOR function. +The same steps will be used for model of any complexity. +Copy and save this as file ``xor_train.py``. + +.. literalinclude:: helloworld_xor/xor_train.py + :language: python + :emphasize-lines: 1,21-24 + :linenos: + +Run the script + +.. code-block:: console + + python xor_train.py + +It will generate a file ``xor_model.h`` containing the C code for our model. + +Use in C code +======================== + +To run our model we use a simple C program that +takes data on the commandline, and prints out the detected class. + +Copy and save this as file ``xor_host.c``. + +.. literalinclude:: helloworld_xor/xor_host.c + :language: c + :emphasize-lines: 1,18-19 + :linenos: + + +On Linux / MacOS / WSL with GCC + +.. code-block:: console + + export EMLEARN_INCLUDE_DIR=`python -c 'import emlearn; print(emlearn.includedir)'` + gcc -o xor_host xor_host.c -I${EMLEARN_INCLUDE_DIR} + +On Windows with cmd.exe + +.. code-block:: console + + python -c "import emlearn; print(emlearn.includedir)" + + set EMLEARN_INCLUDE_DIR= output from above command + + cl xor_host.c /I %EMLEARN_INCLUDE_DIR% /link /out:xor_host.exe + +Try it out +======================== + +In our training data input values above ``0.5`` is considered "true". +So for the XOR function, if **one and only one** of the values is above ``0.5``, should get class **1** as output - else class **0**. + +The following should output 1 + +.. code-block:: console + + ./xor_host 0.6 0.0 + ./xor_host 0.1 0.7 + +The following should output 0 + +.. code-block:: console + + ./xor_host 0.8 0.7 + ./xor_host 0.0 0.0 + +Next +======== + +Now you have the emlearn-micropython running on your PC. +You may be interested in trying it out on a hardware device. +See for example :doc:`getting_started_device`. + diff --git a/docs/index.rst b/docs/index.rst index 1187abf..afc4105 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -9,7 +9,9 @@ Welcome to emlearn-micropython's documentation! source/README.md user_guide + examples api_reference + more source/LICENSE.md diff --git a/docs/more.rst b/docs/more.rst new file mode 100644 index 0000000..b8661c6 --- /dev/null +++ b/docs/more.rst @@ -0,0 +1,35 @@ + +.. Places parent toc into the sidebar +:parenttoc: True + +.. _more: + +========================= +More +========================= + +emlearn documentation +=========================== + +Many general topics relevant to emlearn-micropython are covered in the emlearn documentation. +This includes: + +- :doc:`emlearn:feature_extraction` +- :doc:`emlearn:classification` +- :doc:`emlearn:regression` +- :doc:`emlearn:anomaly_detection` +- :doc:`emlearn:model_optimization` +- :doc:`emlearn:tree_based_models` + +Presentations +==================== + + +Machine Learning on microcontrollers using MicroPython and emlearn +------------------------------------------------------------------ + +Presented at PyCon DE & PyData Berlin 2024, by Jon Nordby. + +`Video recording (YouTube) `_. + +`Slides and notes `_. diff --git a/docs/notes.md b/docs/notes.md new file mode 100644 index 0000000..24a9b1c --- /dev/null +++ b/docs/notes.md @@ -0,0 +1,16 @@ + + +## TODO + +- Getting started for device +- Getting started for host + +Computer vision section. +Write a bit around Convolutional Neural Networks? +Ideally would have hints about object detection also? + +## Later + +- How emlearn-micropython is implemented? +Link to MicroPython native module documentation +Link to array module diff --git a/docs/user_guide.rst b/docs/user_guide.rst index 6c756c6..551dd96 100644 --- a/docs/user_guide.rst +++ b/docs/user_guide.rst @@ -14,5 +14,6 @@ User Guide :numbered: :maxdepth: 3 - getting_started_micropython.rst + getting_started_host.rst + getting_started_device.rst From bb0fdf98076234e204cef134ea0af48d0df87fbe Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Sun, 19 Jan 2025 13:15:39 +0100 Subject: [PATCH 19/80] docs: Start of API reference, for emlearn_cnn --- docs/api_reference.rst | 24 ++++++++++++++++++++++ docs/conf.py | 20 +++++++++--------- stubs/emlearn_cnn.py | 46 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 80 insertions(+), 10 deletions(-) create mode 100644 docs/api_reference.rst create mode 100644 stubs/emlearn_cnn.py diff --git a/docs/api_reference.rst b/docs/api_reference.rst new file mode 100644 index 0000000..e95cdb7 --- /dev/null +++ b/docs/api_reference.rst @@ -0,0 +1,24 @@ + +.. Places parent toc into the sidebar +:parenttoc: True + +.. title:: API: contents + +.. _api_reference: + +==================== +API reference +==================== + + +.. toctree:: + :numbered: + :maxdepth: 2 + :titlesonly: + + +emlearn_cnn +------------ + +.. automodule:: emlearn_cnn + :members: diff --git a/docs/conf.py b/docs/conf.py index 7dc05bc..e64d7fc 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -17,7 +17,8 @@ import sys import subprocess -sys.path.insert(0, os.path.abspath('..')) +# Adjust path so we can find the .py files with API documentation +sys.path.insert(0, os.path.abspath('../stubs')) # -- Project information ----------------------------------------------------- @@ -44,6 +45,7 @@ 'sphinx.ext.mathjax', 'sphinx.ext.viewcode', 'sphinx.ext.autodoc', + 'sphinx_autodoc_typehints', 'sphinx.ext.doctest', 'sphinx.ext.coverage', 'sphinx.ext.autosectionlabel', @@ -200,15 +202,7 @@ # -- Extension configuration ------------------------------------------------- - -sphinx_gallery_conf = { - 'examples_dirs': '../examples', # path to your example scripts - 'gallery_dirs': 'auto_examples', # path to where to save gallery generated output - 'filename_pattern': '/', # execute all .py files, not just those with plot_ prefix -} - - - +# -- Intersphinx intersphinx_mapping = { "emlearn": ("https://emlearn.readthedocs.io/en/latest/", None), } @@ -219,3 +213,9 @@ # https://www.sphinx-doc.org/en/master/usage/extensions/intersphinx.html#confval-intersphinx_disabled_reftypes intersphinx_disabled_reftypes = ["*"] +# -- autodoc typehints +always_document_param_types = True +typehints_fully_qualified = True +always_use_bars_union = True + + diff --git a/stubs/emlearn_cnn.py b/stubs/emlearn_cnn.py new file mode 100644 index 0000000..fe39c2c --- /dev/null +++ b/stubs/emlearn_cnn.py @@ -0,0 +1,46 @@ +# Stub file (PEP 484) with API definitions and documentation for native module +# Is called .py because Sphinx autodoc currently does not support .pyi files + +""" +Convolutional Neural Network module. + +Implemented using TinyMaix (https://github.com/sipeed/TinyMaix/). + +Note that multiple variants of this module is provided. +So the import should either be **emlearn_cnn_fp32** (for 32 bit floating point), +or **emlearn_cnn_int8** (for 8 bit integers, quantized model). + +""" + +import array + +class Model(): + """A Convolutional Neural Network + + Note: May not be constructed directly. Instead use the new() function. + """ + def run(self, inputs : array.array, outputs: array.array): + """ + Run inference using the model + + :param inputs: the input data + :param outputs: where to put model outputs + """ + pass + + def output_dimensions(self) -> tuple[int]: + """ + Get the output dimensions/size of the model + + Useful to know how large an array to pass to run() + """ + pass + +def new(model_data : array.array) -> Model: + """ + Construct a new Convolutional Neural Network + + :param model_data: The model definition (.TMDL file, as bytes). + """ + pass + From 5708c122894912dbbcf94a5b240d635c6532b8a5 Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Sun, 19 Jan 2025 13:42:47 +0100 Subject: [PATCH 20/80] docs: Add emlearn_trees to API reference --- docs/api_reference.rst | 9 ++++ stubs/emlearn_trees.py | 101 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 110 insertions(+) create mode 100644 stubs/emlearn_trees.py diff --git a/docs/api_reference.rst b/docs/api_reference.rst index e95cdb7..68d3f08 100644 --- a/docs/api_reference.rst +++ b/docs/api_reference.rst @@ -17,8 +17,17 @@ API reference :titlesonly: +emlearn_trees +------------ + +.. automodule:: emlearn_trees + :members: + + emlearn_cnn ------------ .. automodule:: emlearn_cnn :members: + + diff --git a/stubs/emlearn_trees.py b/stubs/emlearn_trees.py new file mode 100644 index 0000000..7e1c718 --- /dev/null +++ b/stubs/emlearn_trees.py @@ -0,0 +1,101 @@ +# Stub file (PEP 484) with API definitions and documentation for native module +# Is called .py because Sphinx autodoc currently does not support .pyi files + +""" +Tree-based models (Random Forest et.c.) + +Implemented using emlearn C library (https://github.com/emlearn/emlearn). +""" + +import array +import typing + +class Model(): + """A tree-based ensemble model + + Note: Normally not constructed directly. Instead use + """ + def predict(self, inputs : array.array, outputs: array.array): + """ + Run inference using the model + + :param inputs: the input data. Typecode 'h' (int16) + :param outputs: where to put model outputs. Typecode 'f' (float) + """ + pass + + def outputs(self) -> int: + """ + Get the output dimensions/size of the model + + Useful to know how large an array to pass to predict() + """ + pass + + def setdata(self, features : int, classes : int): + """ + Set data about the model + + Note: Usually not used directly. Instead use load_model(). + + :param features: Number of input features + :param classes: Number of classes + """ + pass + + def addroot(self, root): + """ + Add a tree root + + Note: Usually not used directly. Instead use load_model(). + + :param root: Offset into nodes for the initial decision node of a tree + """ + pass + + def addnode(self, left : int, right : int, feature : int, value : int): + """ + Add a decision node + + Note: Usually not used directly. Instead use load_model(). + + :param left: Left child (node or leaf) + :param right: Right child (node or leaf) + :param feature: Feature index + :param value: Threshold to compute feature to + """ + pass + + def addleaf(self, value : int): + """ + Add a leaf node + + Note: Usually not used directly. Instead use load_model(). + + :param value: + """ + pass + +def new(max_trees : int, max_nodes : int, max_leaves : int) -> Model: + """ + Construct an empty tree-based model + + The model is created with a specified maximum capacity. + Memory usage will be determined by this capacity. + + :param max_trees: Maximum number of trees in ensemble + :param max_nodes: Maximum number of decision nodes (across all trees) + :param max_leaves: Maximum number of leaves (across all trees) + """ + pass + +def load_model(trees : Model, file : typing.BinaryIO): + + """ + Load model definition from a file + + The model must be constructed with sufficient capacity (trees, nodes, leaves). + Otherwise will raise exception. + """ + pass + From b93add3a8d09501e1953eeb8b16031cb7c2ede9b Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Sun, 19 Jan 2025 14:41:42 +0100 Subject: [PATCH 21/80] docs: Add API reference for emlearn_neighbors --- docs/api_reference.rst | 10 ++++-- stubs/emlearn_neighbors.py | 67 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+), 2 deletions(-) create mode 100644 stubs/emlearn_neighbors.py diff --git a/docs/api_reference.rst b/docs/api_reference.rst index 68d3f08..7de0043 100644 --- a/docs/api_reference.rst +++ b/docs/api_reference.rst @@ -18,16 +18,22 @@ API reference emlearn_trees ------------- +------------- .. automodule:: emlearn_trees :members: emlearn_cnn ------------- +----------- .. automodule:: emlearn_cnn :members: +emlearn_neighbors +------------------ + +.. automodule:: emlearn_neighbors + :members: + diff --git a/stubs/emlearn_neighbors.py b/stubs/emlearn_neighbors.py new file mode 100644 index 0000000..dcc16d1 --- /dev/null +++ b/stubs/emlearn_neighbors.py @@ -0,0 +1,67 @@ + +# Stub file (PEP 484) with API definitions and documentation for native module +# Is called .py because Sphinx autodoc currently does not support .pyi files + +""" +K-nearest neighbors + +Implemented using *eml_neighbors* from the emlearn C library (https://github.com/emlearn/emlearn). +""" + +import array +import typing + + +class Model(): + """A nearest-neighbors model + """ + def predict(self, inputs : array.array) -> int: + """ + Run inference using the model + + :param inputs: the input data. Typecode 'h' (int16) + :return: the resulting label/class + """ + pass + + def additem(self, values : array.array, label : int): + """ + Add an item into the model + + :param inputs: the data/features of this item. Typecode 'h' (int16) + :param inputs: the label/class to associate with this item + """ + pass + + def getitem(self, item : int, outputs : array.array): + """ + Access data of an item stored in the model + + :param item: Index of item + :param outputs: Where to copy the data from the item. Typecode 'h' (int16) + """ + pass + + def getresult(self, idx : int) -> tuple[int, int, int]: + """ + Get details on the comparisons between predict() data and items stored in model + + :param item: Index of the comparison to retrieve. Smaller number are the nearest neighbors. + :return: Tuple with (item-index, distance-to-item, label-of-item) + """ + pass + +def new(max_items : int, features : int, k_neighbors : int) -> Model: + """ + Construct an empty neighbors model + + The model is created with a specified maximum capacity. + Memory usage will be determined by this capacity. + + :param max_items: Maximum number of items in the dataset + :param features: Number of features in a data item + :param k_neighbors: Number of neighbors to consider + """ + pass + + From 21533b0f192cac14284a227eea61c0de1de4b5ba Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Sun, 19 Jan 2025 15:29:35 +0100 Subject: [PATCH 22/80] docs: Add API reference for emlearn_fft --- docs/api_reference.rst | 6 +++++ stubs/emlearn_fft.py | 56 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 stubs/emlearn_fft.py diff --git a/docs/api_reference.rst b/docs/api_reference.rst index 7de0043..1eafeba 100644 --- a/docs/api_reference.rst +++ b/docs/api_reference.rst @@ -37,3 +37,9 @@ emlearn_neighbors .. automodule:: emlearn_neighbors :members: + +emlearn_fft +------------------ + +.. automodule:: emlearn_fft + :members: diff --git a/stubs/emlearn_fft.py b/stubs/emlearn_fft.py new file mode 100644 index 0000000..4c07762 --- /dev/null +++ b/stubs/emlearn_fft.py @@ -0,0 +1,56 @@ + +# Stub file (PEP 484) with API definitions and documentation for native module +# Is called .py because Sphinx autodoc currently does not support .pyi files + +""" +Fast Fourier Transform (FFT) + +Implemented using *eml_fft* from the emlearn C library (https://github.com/emlearn/emlearn). +""" + +import array +import typing + + +class FFT(): + """Fast Fourier Transform (FFT) + """ + def __init__(self, length : int): + pass + + def run(self, real : array.array, imag : array.array): + """ + Perform the FFT transformation + + Note: operates in-place, will modify both arrays. + + To perform a real-only (RFFT), let imag be all zeroes. + + :param real: the real part of data. Typecode 'f' (float) + :param imag: the imaginary part of data. Typecode 'f' (float) + """ + pass + + def fill(self, sin : array.array, cos : array.array): + """ + Set up FFT coefficients + + Note: Do not use this directly. Instead use the module-level fill() function. + + :param sin: Precomputed coefficients + :param cos: Precomputed coefficients + """ + pass + +def fill(fft : FFT, n : int): + """ + Set up FFT coefficients + + NB: Must be called before attempting to use FFT.run() + + :param fft: FFT instance + :param n: Length of the FFT transform + """ + pass + + From 0f64ab0216c49343269136bd7501345cd5adbf53 Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Sun, 19 Jan 2025 17:40:21 +0100 Subject: [PATCH 23/80] docs: Add API reference for emlearn_iir --- docs/api_reference.rst | 30 ++++++++++++++++++++------- stubs/emlearn_iir.py | 47 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 8 deletions(-) create mode 100644 stubs/emlearn_iir.py diff --git a/docs/api_reference.rst b/docs/api_reference.rst index 1eafeba..18b3e95 100644 --- a/docs/api_reference.rst +++ b/docs/api_reference.rst @@ -17,29 +17,43 @@ API reference :titlesonly: -emlearn_trees -------------- +.. _emlearn_trees: +emlearn_trees - Decision tree ensembles +---------------------------------------------------- .. automodule:: emlearn_trees :members: -emlearn_cnn ------------ +.. _emlearn_cnn: +emlearn_cnn - Convolutional Neural Networks +---------------------------------------------------------- .. automodule:: emlearn_cnn :members: -emlearn_neighbors ------------------- +.. _emlearn_neighbors: +emlearn_neighbors - K Nearest Neighbors (KNN) +--------------------------------------------------------- .. automodule:: emlearn_neighbors :members: -emlearn_fft ------------------- +.. _emlearn_fft: +emlearn_fft - Fast Fourier Transform +--------------------------------------------------------- .. automodule:: emlearn_fft :members: + + +.. _emlearn_iir: +emlearn_iir - Infinite Impulse Reponse filters +--------------------------------------------------------- + +.. automodule:: emlearn_iir + :members: + + diff --git a/stubs/emlearn_iir.py b/stubs/emlearn_iir.py new file mode 100644 index 0000000..ebe8843 --- /dev/null +++ b/stubs/emlearn_iir.py @@ -0,0 +1,47 @@ + +# Stub file (PEP 484) with API definitions and documentation for native module +# Is called .py because Sphinx autodoc currently does not support .pyi files + +""" +Infinite Impulse Response (IIR) filters + +Uses a cascade of second-order filter sections (SOS). +The conventions are designed to match that of scipy-signal (https://docs.scipy.org/doc/scipy/reference/signal.html), +so one can use design tools such as scipy.signal.iirfilter or scipy.signal.iirdesign +to create the IIR filter coefficients. + +Implemented using *eml_iir* from the emlearn C library (https://github.com/emlearn/emlearn). +""" + +import array +import typing + + +class IIR(): + """Infinite Impulse Response filter + """ + def run(self, values : array.array): + """ + Perform the filter + + Note: operates in-place, will modify the input array data. + + :param values: the data to filter. Typecode 'f' (float) + """ + pass + + +def new(coefficients : array.array) -> IIR: + """ + Create IIR filter + + There must be 6 coefficients per second-order stage. + The format of each stage is on form Transposed Direct Form II: (b0, b1, b2, 1.0, -a1, -a2). + Multiple stages are formed by concatenating the coefficients of each stage. + This is the same used by scipy.signal.sosfilt. + + :param coefficients: IIR filter coefficients + """ + pass + + From 8d30ea8b0b71c1c333f2f04907ed7804d3b18ac9 Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Sun, 19 Jan 2025 17:56:43 +0100 Subject: [PATCH 24/80] docs: Add API reference for emlearn_arrayutils --- docs/api_reference.rst | 7 +++++++ stubs/emlearn_arrayutils.py | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 stubs/emlearn_arrayutils.py diff --git a/docs/api_reference.rst b/docs/api_reference.rst index 18b3e95..cc3640d 100644 --- a/docs/api_reference.rst +++ b/docs/api_reference.rst @@ -57,3 +57,10 @@ emlearn_iir - Infinite Impulse Reponse filters :members: +.. _emlearn_arrayutils: +emlearn_arrayutils - Efficient utilities for array.array +--------------------------------------------------------- + +.. automodule:: emlearn_arrayutils + :members: + diff --git a/stubs/emlearn_arrayutils.py b/stubs/emlearn_arrayutils.py new file mode 100644 index 0000000..4e78692 --- /dev/null +++ b/stubs/emlearn_arrayutils.py @@ -0,0 +1,37 @@ + +# Stub file (PEP 484) with API definitions and documentation for native module +# Is called .py because Sphinx autodoc currently does not support .pyi files + +""" +Array utility functions + +Some simple operations on arrays, implemented in C for efficiency. +""" + +import array +import typing + +def linear_map(inputs : array.array, outputs: array.array, + in_min : float, in_max : float, + out_min : float, out_max : float + ): + """ + Compute an element-wise linear mapping/scaling. + + The range (in_min, in_max) will be mapped to (out_min, out_max). + The function supports in and out being different data widths (array typecodes). + + The following pairs are supported: + - float (f) to int16 (h) + - int16 (h) to float (f) + + :param inputs: The input data + :param outputs: Where output is placed + :param in_min: + :param in_max: + :param out_min: + :param out_max: + """ + pass + + From 2b8767b5c965781dce03a1b275a0f637cd0647e1 Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Sun, 19 Jan 2025 17:58:07 +0100 Subject: [PATCH 25/80] docs: Update API reference --- stubs/emlearn_neighbors.py | 4 ++-- stubs/emlearn_trees.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/stubs/emlearn_neighbors.py b/stubs/emlearn_neighbors.py index dcc16d1..b817858 100644 --- a/stubs/emlearn_neighbors.py +++ b/stubs/emlearn_neighbors.py @@ -28,8 +28,8 @@ def additem(self, values : array.array, label : int): """ Add an item into the model - :param inputs: the data/features of this item. Typecode 'h' (int16) - :param inputs: the label/class to associate with this item + :param values: the data/features of this item. Typecode 'h' (int16) + :param label: the label/class to associate with this item """ pass diff --git a/stubs/emlearn_trees.py b/stubs/emlearn_trees.py index 7e1c718..a914277 100644 --- a/stubs/emlearn_trees.py +++ b/stubs/emlearn_trees.py @@ -4,7 +4,7 @@ """ Tree-based models (Random Forest et.c.) -Implemented using emlearn C library (https://github.com/emlearn/emlearn). +Implemented using *eml_trees* from the emlearn C library (https://github.com/emlearn/emlearn). """ import array From 6a2d4712ee4c04c34e1a79d2333496e98453db31 Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Sun, 19 Jan 2025 17:58:31 +0100 Subject: [PATCH 26/80] requirements: Add typehints autodoc --- requirements.dev.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.dev.txt b/requirements.dev.txt index aadf663..d33d003 100644 --- a/requirements.dev.txt +++ b/requirements.dev.txt @@ -2,4 +2,5 @@ sphinx>=4.0.0 recommonmark>=0.6.0 sphinx_rtd_theme>=0.5.2 sphinx-gallery>=0.10.1 +sphinx-autodoc-typehints>=3.0.1 myst_parser>=4.0.0 From b8c2eb3b0f34ca2014a6e536a40a6451f2ae02f8 Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Sun, 19 Jan 2025 17:58:51 +0100 Subject: [PATCH 27/80] README: Minor updates --- README.md | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 0395aa3..c0a45b6 100644 --- a/README.md +++ b/README.md @@ -2,17 +2,18 @@ # emlearn-micropython -[Micropython](https://micropython.org) integration for the [emlearn](https://emlearn.org) Machine Learning library for microcontrollers. - -It enables MicroPython applications to run efficient Machine Learning models on microcontroller, -without having to touch any C code. - -> scikit-learn for Microcontrollers +Machine Learning and Digital Signal Processing for [MicroPython](https://micropython.org). +Provides convenient and efficient MicroPython modules, and enables MicroPython application developers +to run efficient Machine Learning models on microcontroller, without having to touch any C code. This is a [TinyML](https://www.tinyml.org/) library, particularly well suited for low-compexity and low-power classification tasks. It can be combined with feature preprocessing, including neural networks to address more complex tasks. +Builds on [emlearn](https://emlearn.org), a C99 library for Machine Learning on microcontrollers and embedded system. + +> scikit-learn for Microcontrollers + ## Status **Minimally useful** @@ -24,7 +25,7 @@ It can be combined with feature preprocessing, including neural networks to addr - Classification with [RandomForest](https://en.wikipedia.org/wiki/Random_forest)/DecisionTree models - Classification and on-device learning with [K-Nearest Neighbors (KNN)](https://en.wikipedia.org/wiki/K-nearest_neighbors_algorithm) - Classification with Convolutional Neural Network (CNN), using [TinyMaix](https://github.com/sipeed/TinyMaix/) library. -- Fast Fourier Transform (FFT) for feature preprocessing, or general DSP +- [Fast Fourier Transform (FFT)](https://en.wikipedia.org/wiki/Fast_Fourier_transform) for feature preprocessing, or general DSP - Infinite Impulse Response (IIR) filters for feature preprocessing, or general DSP - Clustering using K-means - Scaling and data type transformations for `array`, using `emlearn_arrayutils`. @@ -42,6 +43,10 @@ It can be combined with feature preprocessing, including neural networks to addr - [har_trees](./examples/har_trees/). Accelerometer-based Human Activity Recognition, using Random Forest - [soundlevel_iir](./examples/soundlevel_iir/). Sound Level Meter, using Infinite Impulse Response (IIR) filters. +## Documentation + +Complete [documentation on ReadTheDocs](https://emlearn-micropython.readthedocs.io/en/latest/user_guide.htmls). + ## Prerequisites Minimally you will need From ffb8f80835efc9a0b60f02521a6477bc8a191727 Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Thu, 20 Mar 2025 19:22:54 +0100 Subject: [PATCH 28/80] Update notes --- doc/TODO.md | 16 ++++++++++------ doc/micropython-tinyml-status.md | 9 +++++++-- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/doc/TODO.md b/doc/TODO.md index d32258a..7da2de8 100644 --- a/doc/TODO.md +++ b/doc/TODO.md @@ -11,11 +11,13 @@ # Milestones -- Can run example in browser -- Can run on-device training example -- First demo video published -- First test by other users +- First demo video published. DONE, toothbrush +- First test by other users. DONE, Cornell university +- Complete example for full flow. Data collect, train, deploy. DONE, HAR - First course held +- Can run on-device training example +- Can run example in browser + # TODO @@ -42,8 +44,10 @@ sequence. On-device training demo - Add a couple of different sized models to benchmark? - Add another application/dataset for benchmark -In-browser demo +#### In-browser demo -- Test MicroPython build for WASM/browser +- Test MicroPython build for WASM/browser. +Requires user/external C module build support. +https://github.com/emlearn/emlearn-micropython/issues/18 - Test getting audio input into MicroPython Webassembly - Test getting IMU data (ie on phone), in browser diff --git a/doc/micropython-tinyml-status.md b/doc/micropython-tinyml-status.md index 06e5d42..62f17b6 100644 --- a/doc/micropython-tinyml-status.md +++ b/doc/micropython-tinyml-status.md @@ -14,10 +14,10 @@ and that it has many of the key properties needed to be highly suitable for this ## Native modules -FIXED. Completely broken on at least one port, due to module functions being garbage collected. +**FIXED.** Was completely broken on at least one port, due to module functions being garbage collected. https://github.com/micropython/micropython/issues/6769 -Using floating point / math operations is difficult / very tedious. +**FIXED.** Using floating point / math operations is difficult / was very tedious. Need to manually resolve all symbols. https://github.com/micropython/micropython/issues/5629 @@ -58,6 +58,8 @@ Would want to initialize empty / with unspecified values, or initialized filled Inefficient copies of `array.array`. +Missing efficient sorting function for `array.array`. + ## Data formats #### Binary encoding into strings @@ -85,6 +87,9 @@ Ideally would be compatible with that. !No mip-installable library for JPEG files. +[cnadler86/micropython-camera-API](https://github.com/cnadler86/micropython-camera-API?tab=readme-ov-file#convert-image-to-another-format) +has support for JPEG decode/encode, as a function in the camera module (ESP32 only). + !No mip installable library for PNG files. OpenMV [omv.image](https://docs.openmv.io/library/omv.image.html) module supports loading/saving JPEG/PNG. From 44ab80265da6873985a5eceb9c05dd53bd34d785 Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Thu, 20 Mar 2025 21:17:02 +0100 Subject: [PATCH 29/80] CI: Try fix missing download info --- .github/workflows/build.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 672d6e4..37925f7 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -50,8 +50,7 @@ jobs: uses: actions/upload-artifact@v3 with: name: dist - path: | - dist + path: dist - name: Prepare release run: make release - name: Archive release artifacts From 24f2667d3e5fb9d69e618851b79236aae59a5336 Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Thu, 20 Mar 2025 21:17:56 +0100 Subject: [PATCH 30/80] CI: Update upload-artifact to v4 --- .github/workflows/build.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 37925f7..c51eb98 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -47,14 +47,14 @@ jobs: - name: Build module xtensawin run: source micropython/esp-idf/export.sh && pip install -r requirements.txt && make dist ARCH=xtensawin V=1 - name: Archive dist artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: dist path: dist - name: Prepare release run: make release - name: Archive release artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: release path: emlearn-micropython-*.zip From 4b9b24e1c94789f5b9c88d7843d62ab942fdd94e Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Thu, 20 Mar 2025 19:33:07 +0100 Subject: [PATCH 31/80] CI: Use MicroPython with natmod static-linking --- .github/workflows/build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index c51eb98..8a26092 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -24,7 +24,7 @@ jobs: with: repository: jonnor/micropython path: micropython - ref: emlearn-micropython-v1.24-1 + ref: v1.25preview-linking - name: Install Python dependencies run: pip install -r requirements.txt - name: Setup MicroPython X86 From fddec71c5d36f7e0306363c98f3bfd31688b7e69 Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Thu, 20 Mar 2025 19:49:08 +0100 Subject: [PATCH 32/80] trees: Use new static linking --- src/emlearn_trees/Makefile | 33 +-------------------------------- src/emlearn_trees/trees.c | 6 ------ 2 files changed, 1 insertion(+), 38 deletions(-) diff --git a/src/emlearn_trees/Makefile b/src/emlearn_trees/Makefile index d6111c2..03378a7 100644 --- a/src/emlearn_trees/Makefile +++ b/src/emlearn_trees/Makefile @@ -10,37 +10,16 @@ MPY_ABI_VERSION := 6.3 # Location of emlearn library EMLEARN_DIR := $(shell python3 -c "import emlearn; print(emlearn.includedir)") +LINK_RUNTIME = 1 # enable linking of libm etc DIST_DIR := ../../dist/$(ARCH)_$(MPY_ABI_VERSION) # Name of module -# NOTE: cannot contain _ - functions do not register on the module then? MOD = emlearn_trees # Source files (.c or .py) SRC = trees.c trees.py -# Stuff to make soft-float work -# If symbols are undefined, use tools/find_symbols.py to locate object files to add -SOFTFP_O := _arm_cmpsf2.o lesf2.o _arm_fixsfsi.o fixsfsi.o eqsf2.o gesf2.o _arm_addsubsf3.o _arm_muldivsf3.o addsf3.o _clzsi2.o _udivsi3.o floatsisf.o divsf3.o _thumb1_case_uqi.o -XTENSA_SOFTFP_O := _divsf3.o -SOFTFP_ENABLE := 0 -ifeq ($(ARCH), armv6m) - SOFTFP_ENABLE=1 -endif -ifeq ($(ARCH), armv7m) - SOFTFP_ENABLE=1 -endif -ifeq ($(ARCH), xtensawin) - SRC_O += $(XTENSA_SOFTFP_O) - CLEAN_EXTRA += $(XTENSA_SOFTFP_O) -endif - -ifeq ($(SOFTFP_ENABLE), 1) - SRC_O += $(SOFTFP_O) - CLEAN_EXTRA += $(SOFTFP_O) -endif - # Releases DIST_FILE = $(DIST_DIR)/$(MOD).mpy $(DIST_DIR): @@ -52,16 +31,6 @@ $(DIST_FILE): $(MOD).mpy $(DIST_DIR) # Include to get the rules for compiling and linking the module include $(MPY_DIR)/py/dynruntime.mk -# CROSS is defined by the included -LIBGCC_FILENAME = $(shell $(CROSS)gcc $(CFLAGS) -print-libgcc-file-name) -$(info $(LIBGCC_FILENAME)) - -_arm_cmpsf2.o: - $(CROSS)ar -x $(LIBGCC_FILENAME) $(SOFTFP_O) - -_divsf3.o: - $(CROSS)ar -x $(LIBGCC_FILENAME) $(XTENSA_SOFTFP_O) - CFLAGS += -I$(EMLEARN_DIR) dist: $(DIST_FILE) diff --git a/src/emlearn_trees/trees.c b/src/emlearn_trees/trees.c index 3404f60..b98ac76 100644 --- a/src/emlearn_trees/trees.c +++ b/src/emlearn_trees/trees.c @@ -18,12 +18,6 @@ void *memset(void *s, int c, size_t n) { } #endif -// Softfloat -int -__aeabi_idiv0(int return_value) { - return return_value; -} - // For building up an EmlTrees structure typedef struct _EmlTreesBuilder { From d0352ce20df31d1d58d1c196388711a52bb81485 Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Thu, 20 Mar 2025 19:56:32 +0100 Subject: [PATCH 33/80] iir: Use linking --- src/emlearn_iir/Makefile | 26 ++---------------------- src/emlearn_iir/iir_filter.c | 11 ---------- src/emlearn_iir_q15/Makefile | 35 ++------------------------------ src/emlearn_iir_q15/iir_filter.c | 17 ---------------- 4 files changed, 4 insertions(+), 85 deletions(-) diff --git a/src/emlearn_iir/Makefile b/src/emlearn_iir/Makefile index 96b57a6..d884e95 100644 --- a/src/emlearn_iir/Makefile +++ b/src/emlearn_iir/Makefile @@ -10,6 +10,8 @@ MPY_ABI_VERSION := 6.3 # Location of emlearn library EMLEARN_DIR := $(shell python3 -c "import emlearn; print(emlearn.includedir)") +# enable linking of libm etc +LINK_RUNTIME=1 DIST_DIR := ../../dist/$(ARCH)_$(MPY_ABI_VERSION) @@ -19,23 +21,6 @@ MOD = emlearn_iir # Source files (.c or .py) SRC = iir_filter.c -# Stuff to make soft-float work -# If symbols are undefined, use tools/find_symbols.py to locate object files to add -SOFTFP_O := _arm_cmpsf2.o lesf2.o _arm_fixsfsi.o fixsfsi.o eqsf2.o gesf2.o addsf3.o mulsf3.o subsf3.o _clzsi2.o divdf3.o -SOFTFP_O += _dvmd_tls.o _aeabi_uldivmod.o _aeabi_ldivmod.o _divsi3.o _udivsi3.o _modsi3.o _umodsi3.o _thumb1_case_uhi.o _udivmoddi4.o bpabi.o _clzdi2.o _ashldi3.o _lshrdi3.o _muldi3.o _divdi3.o _arm_muldivsf3.o _arm_addsubsf3.o -SOFTFP_ENABLE := 0 -ifeq ($(ARCH), armv6m) - SOFTFP_ENABLE=1 -endif -ifeq ($(ARCH), armv7m) - SOFTFP_ENABLE=1 -endif - -ifeq ($(SOFTFP_ENABLE), 1) - SRC_O += $(SOFTFP_O) - #CLEAN_EXTRA += $(SOFTFP_O) -endif - # Releases DIST_FILE = $(DIST_DIR)/$(MOD).mpy $(DIST_DIR): @@ -47,13 +32,6 @@ $(DIST_FILE): $(MOD).mpy $(DIST_DIR) # Include to get the rules for compiling and linking the module include $(MPY_DIR)/py/dynruntime.mk -# CROSS is defined by the included -LIBGCC_FILENAME = $(shell $(CROSS)gcc $(CFLAGS) -print-libgcc-file-name) -$(info $(LIBGCC_FILENAME)) - -_arm_cmpsf2.o: - $(CROSS)ar -x $(LIBGCC_FILENAME) $(SOFTFP_O) - CFLAGS += -I$(EMLEARN_DIR) dist: $(DIST_FILE) diff --git a/src/emlearn_iir/iir_filter.c b/src/emlearn_iir/iir_filter.c index 7946675..ac62f31 100644 --- a/src/emlearn_iir/iir_filter.c +++ b/src/emlearn_iir/iir_filter.c @@ -19,17 +19,6 @@ void NORETURN abort() { ; } } - -int -__aeabi_idiv0(int return_value) { - return return_value; -} - -long long -__aeabi_ldiv0(long long return_value) { - return return_value; -} - #endif diff --git a/src/emlearn_iir_q15/Makefile b/src/emlearn_iir_q15/Makefile index d4dd712..7a8005f 100644 --- a/src/emlearn_iir_q15/Makefile +++ b/src/emlearn_iir_q15/Makefile @@ -22,33 +22,8 @@ SRC += \ ${CMSIS_DSP_DIR}/Source/FilteringFunctions/arm_biquad_cascade_df1_q15.c \ ${CMSIS_DSP_DIR}/Source/FilteringFunctions/arm_biquad_cascade_df1_init_q15.c -# Stuff to make soft-float work -# If symbols are undefined, use tools/find_symbols.py to locate object files to add - -SOFTFP_O := -SOFTFP_ENABLE := 0 - -ARM_SOFTFP_O := _arm_cmpsf2.o lesf2.o _arm_fixsfsi.o fixsfsi.o eqsf2.o gesf2.o addsf3.o mulsf3.o subsf3.o _clzsi2.o divdf3.o -ARM_SOFTFP_O += _dvmd_tls.o _aeabi_uldivmod.o _aeabi_ldivmod.o _divsi3.o _udivsi3.o _modsi3.o _umodsi3.o _thumb1_case_uhi.o _udivmoddi4.o bpabi.o _clzdi2.o _ashldi3.o _lshrdi3.o _muldi3.o _divdi3.o _arm_muldivsf3.o _arm_addsubsf3.o _ashrdi3.o - -ifeq ($(ARCH), xtensawin) - SOFTFP_O += _divsf3.o _ashrdi3.o - SOFTFP_ENABLE=1 -endif - -ifeq ($(ARCH), armv6m) - SOFTFP_O += $(ARM_SOFTFP_O) - SOFTFP_ENABLE=1 -endif -ifeq ($(ARCH), armv7m) - SOFTFP_O += $(ARM_SOFTFP_O) - SOFTFP_ENABLE=1 -endif - -ifeq ($(SOFTFP_ENABLE), 1) - SRC_O += $(SOFTFP_O) - #CLEAN_EXTRA += $(SOFTFP_O) -endif +# enable linking of libm etc +LINK_RUNTIME=1 # Releases DIST_FILE = $(DIST_DIR)/$(MOD).mpy @@ -67,12 +42,6 @@ $(info $(LIBGCC_FILENAME)) CFLAGS += -I$(CMSIS_DSP_DIR)/Include -_arm_cmpsf2.o: - $(CROSS)ar -x $(LIBGCC_FILENAME) $(SOFTFP_O) - -_divsf3.o: - $(CROSS)ar -x $(LIBGCC_FILENAME) $(SOFTFP_O) - $(CMSIS_DSP_DIR)/iir_q15.patched: cd $(CMSIS_DSP_DIR) && git apply -v ../df1_q15_disable_dsp.patch echo DONE > $@ diff --git a/src/emlearn_iir_q15/iir_filter.c b/src/emlearn_iir_q15/iir_filter.c index a83c88d..a918029 100644 --- a/src/emlearn_iir_q15/iir_filter.c +++ b/src/emlearn_iir_q15/iir_filter.c @@ -13,23 +13,6 @@ void *memcpy(void *dst, const void *src, size_t n) { void *memset(void *s, int c, size_t n) { return mp_fun_table.memset_(s, c, n); } - -void NORETURN abort() { - while (1) { - ; - } -} - -int -__aeabi_idiv0(int return_value) { - return return_value; -} - -long long -__aeabi_ldiv0(long long return_value) { - return return_value; -} - #endif From 33813a446c0ffd6d255bf9c66e35fe0055360010 Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Thu, 20 Mar 2025 20:04:02 +0100 Subject: [PATCH 34/80] kmeans: Use linking --- src/emlearn_kmeans/Makefile | 21 ++------------------- src/emlearn_kmeans/kmeans.c | 4 ---- 2 files changed, 2 insertions(+), 23 deletions(-) diff --git a/src/emlearn_kmeans/Makefile b/src/emlearn_kmeans/Makefile index d50e56a..64775fa 100644 --- a/src/emlearn_kmeans/Makefile +++ b/src/emlearn_kmeans/Makefile @@ -15,22 +15,8 @@ MOD = emlearn_kmeans # Source files (.c or .py) SRC = kmeans.c kmeans.py -# Stuff to make soft-float work -# If symbols are undefined, use tools/find_symbols.py to locate object files to add -SOFTFP_O := _divsi3.o - -SOFTFP_ENABLE := 0 -ifeq ($(ARCH), armv6m) - SOFTFP_ENABLE=1 -endif -ifeq ($(ARCH), armv7m) - SOFTFP_ENABLE=1 -endif - -ifeq ($(SOFTFP_ENABLE), 1) - SRC_O += $(SOFTFP_O) - #CLEAN_EXTRA += $(SOFTFP_O) -endif +# enable linking of libm etc +LINK_RUNTIME=1 # Releases DIST_FILE = $(DIST_DIR)/$(MOD).mpy @@ -47,7 +33,4 @@ include $(MPY_DIR)/py/dynruntime.mk LIBGCC_FILENAME = $(shell $(CROSS)gcc $(CFLAGS) -print-libgcc-file-name) $(info $(LIBGCC_FILENAME)) -_divsi3.o: - $(CROSS)ar -x $(LIBGCC_FILENAME) $(SOFTFP_O) - dist: $(DIST_FILE) diff --git a/src/emlearn_kmeans/kmeans.c b/src/emlearn_kmeans/kmeans.c index ab0fe62..72f6415 100644 --- a/src/emlearn_kmeans/kmeans.c +++ b/src/emlearn_kmeans/kmeans.c @@ -14,10 +14,6 @@ #define debug_printf(...) //(0) #endif -int -__aeabi_idiv0(int return_value) { - return return_value; -} // Find which vector in @vectors that @v is closes to uint16_t From 1fb0f4b944191decc411c1a259fe1ad4c5b96fb3 Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Thu, 20 Mar 2025 20:05:16 +0100 Subject: [PATCH 35/80] cnn: Use linking --- src/emlearn_trees/Makefile | 3 ++- src/tinymaix_cnn/Makefile | 42 +++----------------------------------- src/tinymaix_cnn/mod_cnn.c | 11 ---------- 3 files changed, 5 insertions(+), 51 deletions(-) diff --git a/src/emlearn_trees/Makefile b/src/emlearn_trees/Makefile index 03378a7..d1a71cf 100644 --- a/src/emlearn_trees/Makefile +++ b/src/emlearn_trees/Makefile @@ -10,7 +10,8 @@ MPY_ABI_VERSION := 6.3 # Location of emlearn library EMLEARN_DIR := $(shell python3 -c "import emlearn; print(emlearn.includedir)") -LINK_RUNTIME = 1 # enable linking of libm etc +# enable linking of libm etc +LINK_RUNTIME=1 DIST_DIR := ../../dist/$(ARCH)_$(MPY_ABI_VERSION) diff --git a/src/tinymaix_cnn/Makefile b/src/tinymaix_cnn/Makefile index affaba7..bfa45d2 100644 --- a/src/tinymaix_cnn/Makefile +++ b/src/tinymaix_cnn/Makefile @@ -16,38 +16,15 @@ DIST_DIR := ../../dist/$(ARCH)_$(MPY_ABI_VERSION) CONFIG_DIR := ./$(CONFIG) +# enable linking of libm etc +LINK_RUNTIME=1 + # Name of module MOD = emlearn_cnn # Source files (.c or .py) SRC = mod_cnn.c -# Stuff to make soft-float work -# If symbols are undefined, use tools/find_symbols.py to locate object files to add -SOFTFP_O := -SOFTFP_ENABLE := 0 -ifeq ($(ARCH), armv6m) - SOFTFP_ENABLE=1 - SOFTFP_O += _divsi3.o mulsf3.o subsf3.o subdf3.o _arm_cmpsf2.o lesf2.o _arm_fixsfsi.o _fixunssfsi.o fixsfsi.o extendsfdf2.o eqsf2.o gesf2.o _arm_addsubsf3.o _arm_muldivsf3.o addsf3.o _clzsi2.o _udivsi3.o floatsisf.o divsf3.o truncdfsf2.o _thumb1_case_uqi.o -endif -ifeq ($(ARCH), armv7m) - SOFTFP_ENABLE=1 - SOFTFP_O += _arm_addsubsf3.o _arm_muldivsf3.o _arm_fixsfsi.o _arm_cmpsf2.o _arm_fixunssfsi.o _arm_addsubdf3.o _arm_truncdfsf2.o -endif -ifeq ($(ARCH), armv7emsp) - SOFTFP_ENABLE=1 - SOFTFP_O += _arm_addsubdf3.o _arm_truncdfsf2.o -endif -ifeq ($(ARCH), xtensawin) - SOFTFP_ENABLE=1 - SOFTFP_O += _truncdfsf2.o _addsubdf3.o _extendsfdf2.o _divsf3.o -endif - -ifeq ($(SOFTFP_ENABLE), 1) - SRC_O += $(SOFTFP_O) - #CLEAN_EXTRA += $(SOFTFP_O) -endif - # Releases DIST_FILE = $(DIST_DIR)/$(MOD)_$(CONFIG).mpy $(DIST_DIR): @@ -59,19 +36,6 @@ $(DIST_FILE): $(MOD).mpy $(DIST_DIR) # Include to get the rules for compiling and linking the module include $(MPY_DIR)/py/dynruntime.mk -# CROSS is defined by the included -LIBGCC_FILENAME = $(shell $(CROSS)gcc $(CFLAGS) -print-libgcc-file-name) -$(info $(LIBGCC_FILENAME)) - -_arm_cmpsf2.o: - $(CROSS)ar -x $(LIBGCC_FILENAME) $(SOFTFP_O) - -_addsubdf3.o: - $(CROSS)ar -x $(LIBGCC_FILENAME) $(SOFTFP_O) - -_arm_addsubdf3.o: - $(CROSS)ar -x $(LIBGCC_FILENAME) $(SOFTFP_O) - CFLAGS += -I$(CONFIG_DIR) -I$(TINYMAIX_DIR)/include -I$(TINYMAIX_DIR)/src -Wno-error=unused-variable -Wno-error=multichar -Wdouble-promotion dist: $(DIST_FILE) diff --git a/src/tinymaix_cnn/mod_cnn.c b/src/tinymaix_cnn/mod_cnn.c index 135f1ce..04ea77e 100644 --- a/src/tinymaix_cnn/mod_cnn.c +++ b/src/tinymaix_cnn/mod_cnn.c @@ -24,17 +24,6 @@ void *memcpy(void *dst, const void *src, size_t n) { void *memset(void *s, int c, size_t n) { return mp_fun_table.memset_(s, c, n); } - -int -__aeabi_idiv0(int return_value) { - return return_value; -} - -void NORETURN abort() { - while (1) { - ; - } -} #endif // get model output shapes From c94aab601524142121ca038d7f03764ff4a0339a Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Thu, 20 Mar 2025 20:31:49 +0100 Subject: [PATCH 36/80] arrayutils: Use linking --- src/emlearn_arrayutils/Makefile | 41 ++------------------------ src/emlearn_arrayutils/modarrayutils.c | 17 ----------- 2 files changed, 3 insertions(+), 55 deletions(-) diff --git a/src/emlearn_arrayutils/Makefile b/src/emlearn_arrayutils/Makefile index f125341..4df8c14 100644 --- a/src/emlearn_arrayutils/Makefile +++ b/src/emlearn_arrayutils/Makefile @@ -9,39 +9,15 @@ MPY_ABI_VERSION := 6.3 DIST_DIR := ../../dist/$(ARCH)_$(MPY_ABI_VERSION) +# enable linking of libm etc +LINK_RUNTIME=1 + # Name of module MOD = emlearn_arrayutils # Source files (.c or .py) SRC = modarrayutils.c -# Stuff to make soft-float work -# If symbols are undefined, use tools/find_symbols.py to locate object files to add -SOFTFP_O := -SOFTFP_ENABLE := 0 - -ARM_SOFTFP_O := _arm_cmpsf2.o lesf2.o divsf3.o _thumb1_case_uqi.o _arm_fixsfsi.o floatsisf.o fixsfsi.o eqsf2.o gesf2.o addsf3.o mulsf3.o subsf3.o _clzsi2.o _udivsi3.o -ARM_SOFTFP_O += _arm_muldivsf3.o _arm_addsubsf3.o - -ifeq ($(ARCH), xtensawin) - SOFTFP_O += _divsf3.o _ashrdi3.o - SOFTFP_ENABLE=1 -endif - -ifeq ($(ARCH), armv6m) - SOFTFP_O += $(ARM_SOFTFP_O) - SOFTFP_ENABLE=1 -endif -ifeq ($(ARCH), armv7m) - SOFTFP_O += $(ARM_SOFTFP_O) - SOFTFP_ENABLE=1 -endif - -ifeq ($(SOFTFP_ENABLE), 1) - SRC_O += $(SOFTFP_O) - #CLEAN_EXTRA += $(SOFTFP_O) -endif - # Releases DIST_FILE = $(DIST_DIR)/$(MOD).mpy $(DIST_DIR): @@ -53,17 +29,6 @@ $(DIST_FILE): $(MOD).mpy $(DIST_DIR) # Include to get the rules for compiling and linking the module include $(MPY_DIR)/py/dynruntime.mk -# CROSS is defined by the included -LIBGCC_FILENAME = $(shell $(CROSS)gcc $(CFLAGS) -print-libgcc-file-name) -$(info $(LIBGCC_FILENAME)) - CFLAGS += -I$(CMSIS_DSP_DIR)/Include -_arm_cmpsf2.o: - $(CROSS)ar -x $(LIBGCC_FILENAME) $(SOFTFP_O) - -_divsf3.o: - $(CROSS)ar -x $(LIBGCC_FILENAME) $(SOFTFP_O) - - dist: $(DIST_FILE) diff --git a/src/emlearn_arrayutils/modarrayutils.c b/src/emlearn_arrayutils/modarrayutils.c index f6f2861..47c6e25 100644 --- a/src/emlearn_arrayutils/modarrayutils.c +++ b/src/emlearn_arrayutils/modarrayutils.c @@ -11,23 +11,6 @@ void *memcpy(void *dst, const void *src, size_t n) { void *memset(void *s, int c, size_t n) { return mp_fun_table.memset_(s, c, n); } - -void NORETURN abort() { - while (1) { - ; - } -} - -int -__aeabi_idiv0(int return_value) { - return return_value; -} - -long long -__aeabi_ldiv0(long long return_value) { - return return_value; -} - #endif From 0784ef9b14dc1c9faab70f2ea035db556bcd0b39 Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Thu, 20 Mar 2025 20:32:19 +0100 Subject: [PATCH 37/80] fft: Use linking --- src/emlearn_fft/Makefile | 74 ++-------------------------------------- src/emlearn_fft/fft.c | 11 ------ 2 files changed, 2 insertions(+), 83 deletions(-) diff --git a/src/emlearn_fft/Makefile b/src/emlearn_fft/Makefile index ed494d1..857868c 100644 --- a/src/emlearn_fft/Makefile +++ b/src/emlearn_fft/Makefile @@ -10,6 +10,8 @@ MPY_ABI_VERSION := 6.3 # Location of emlearn library EMLEARN_DIR := $(shell python3 -c "import emlearn; print(emlearn.includedir)") +# enable linking of libm etc +LINK_RUNTIME=1 DIST_DIR := ../../dist/$(ARCH)_$(MPY_ABI_VERSION) @@ -19,47 +21,6 @@ MOD = emlearn_fft # Source files (.c or .py) SRC = fft.c fft.py - -# Stuff to make soft-float work -# If symbols are undefined, use tools/find_symbols.py to locate object files to add -ifeq ($(ARCH), armv7emsp) - SOFTFP_O := _arm_addsubdf3.o _arm_muldivdf3.o _arm_muldivdf3.o _arm_truncdfsf2.o -endif - - -ifeq ($(ARCH), xtensawin) - SOFTFP_O := _floatsidf.o _muldf3.o _divdf3.o _truncdfsf2.o -endif - -ifeq ($(ARCH), armv6m) - SOFTFP_O := mulsf3.o addsf3.o subsf3.o _clzsi2.o _udivsi3.o -endif -ifeq ($(ARCH), armv7m) - SOFTFP_O := _arm_addsubsf3.o _arm_muldivsf3.o mulsf3.o addsf3.o subsf3.o _clzsi2.o _udivsi3.o -endif - - -SOFTFP_ENABLE := 0 -ifeq ($(ARCH), armv6m) - SOFTFP_ENABLE=1 -endif -ifeq ($(ARCH), armv7m) - SOFTFP_ENABLE=1 -endif - -ifeq ($(ARCH), xtensawin) - SOFTFP_ENABLE=1 -endif -ifeq ($(ARCH), armv7emsp) - SOFTFP_ENABLE=1 -endif - -ifeq ($(SOFTFP_ENABLE), 1) - SRC_O += $(SOFTFP_O) - SRC_O += $(LIBM_O) - #CLEAN_EXTRA += $(SOFTFP_O) -endif - # Releases DIST_FILE = $(DIST_DIR)/$(MOD).mpy $(DIST_DIR): @@ -71,37 +32,6 @@ $(DIST_FILE): $(MOD).mpy $(DIST_DIR) # Include to get the rules for compiling and linking the module include $(MPY_DIR)/py/dynruntime.mk -# CROSS is defined by the included -LIBGCC_FILENAME = $(shell $(CROSS)gcc $(CFLAGS) -print-libgcc-file-name) -$(info $(LIBGCC_FILENAME)) - -# FIXME: unhardcode these libm -ifeq ($(ARCH), xtensawin) -LIBM_FILENAME = /home/jon/.espressif/tools/xtensa-esp32-elf/esp-2022r1-11.2.0/xtensa-esp32-elf/xtensa-esp32-elf/lib/esp32-psram/no-rtti/libm.a -endif - -ifeq ($(ARCH), armv7emsp) - LIBM_FILENAME = /usr/arm-none-eabi/lib/thumb/v7e-m+fp/hard/libm.a -endif - -libm_a-sf_cos.o: - $(CROSS)ar -x $(LIBM_FILENAME) $(LIBM_O) - -lib_a-sf_cos.o: - $(CROSS)ar -x $(LIBM_FILENAME) $(LIBM_O) - -_arm_truncdfsf2.o: - $(CROSS)ar -x $(LIBGCC_FILENAME) $(SOFTFP_O) - -_truncdfsf2.o: - $(CROSS)ar -x $(LIBGCC_FILENAME) $(SOFTFP_O) - -_udivsi3.o: - $(CROSS)ar -x $(LIBGCC_FILENAME) $(SOFTFP_O) - -_arm_addsubsf3.o: - $(CROSS)ar -x $(LIBGCC_FILENAME) $(SOFTFP_O) - CFLAGS += -I$(EMLEARN_DIR) diff --git a/src/emlearn_fft/fft.c b/src/emlearn_fft/fft.c index 3f58ef2..9f79733 100644 --- a/src/emlearn_fft/fft.c +++ b/src/emlearn_fft/fft.c @@ -15,19 +15,8 @@ void *memcpy(void *dst, const void *src, size_t n) { void *memset(void *s, int c, size_t n) { return mp_fun_table.memset_(s, c, n); } - -void NORETURN abort() { - while (1) { - ; - } -} #endif -int -__aeabi_idiv0(int return_value) { - return return_value; -} - // Copy of eml_fft.h, without eml_fft_fill // - contains sin/cos that trips up mpy_ld.py (even if the function is not used) From 58780674c548b478b21b2d1ce2b1cfe41a07c0e1 Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Thu, 20 Mar 2025 20:34:02 +0100 Subject: [PATCH 38/80] neighbors: Use linking --- src/emlearn_neighbors/Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/emlearn_neighbors/Makefile b/src/emlearn_neighbors/Makefile index cd12854..53ee216 100644 --- a/src/emlearn_neighbors/Makefile +++ b/src/emlearn_neighbors/Makefile @@ -10,6 +10,9 @@ MPY_ABI_VERSION := 6.3 # Location of emlearn library EMLEARN_DIR := $(shell python3 -c "import emlearn; print(emlearn.includedir)") +# enable linking of libm etc +LINK_RUNTIME=1 + DIST_DIR := ../../dist/$(ARCH)_$(MPY_ABI_VERSION) # Name of module @@ -22,9 +25,6 @@ SRC = neighbors.c # Include to get the rules for compiling and linking the module include $(MPY_DIR)/py/dynruntime.mk -# for qsort -SRC_O += $(LIBC_O) - # Releases DIST_FILE = $(DIST_DIR)/$(MOD).mpy $(DIST_DIR): From 7e920fc79b5eba6740ff54395151f1f227885bac Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Thu, 20 Mar 2025 23:26:53 +0100 Subject: [PATCH 39/80] CI: Skip armv6m build --- .github/workflows/build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 8a26092..e989acb 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -36,7 +36,7 @@ jobs: working-directory: micropython run: source tools/ci.sh && ci_rp2_setup - name: Build module armv6m - run: make dist ARCH=armv6m V=1 + run: echo make dist ARCH=armv6m V=1 - name: Build module armv7m run: make dist ARCH=armv7m V=1 - name: Build module armv7emsp From 6b55b10dbf747831e40abc921a6a7b5c164e5a16 Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Fri, 28 Mar 2025 22:25:47 +0100 Subject: [PATCH 40/80] docs: Update getting started on host --- docs/getting_started_host.rst | 85 +++++++++++++++++--------------- docs/helloworld_xor/.gitignore | 3 ++ docs/helloworld_xor/xor_run.py | 26 ++++++++++ docs/helloworld_xor/xor_train.py | 31 ++++++++++++ docs/index.rst | 4 +- 5 files changed, 108 insertions(+), 41 deletions(-) create mode 100644 docs/helloworld_xor/.gitignore create mode 100644 docs/helloworld_xor/xor_run.py create mode 100644 docs/helloworld_xor/xor_train.py diff --git a/docs/getting_started_host.rst b/docs/getting_started_host.rst index 55322c9..0457834 100644 --- a/docs/getting_started_host.rst +++ b/docs/getting_started_host.rst @@ -11,19 +11,19 @@ Getting started on PC (Linux/MacOS/Windows) .. currentmodule:: emlearn-micropython -emlearn models work anywhere there is a C99 compiler available. -This includes common desktop platforms such as Linux, Mac OS, Windows, etc. +emlearn-micropython runs on most platforms that MicroPython does. +This includes common desktop platforms such as Linux, Mac OS, Windows, et.c. Since you need such a host platform to develop the Python machine-learning, it is convenient also to do the first tests of the model on the host. + Prerequisites =========================== -You need to have installed **Python** (version 3.6+), +You need to have installed **Python** (version 3.10+), and a **C99 compiler** for your platform (GCC/Clang/MSVC). -On Windows, the Windows Subsystem for Linux (WSL) is recommended, -but MSCV and cmd.exe/Powershell can also be used. +On Windows, the **Windows Subsystem for Linux (WSL)** is recommended. Install scikit-learn =========================== @@ -37,13 +37,33 @@ In this example, **scikit-learn** is used to train the models. Install emlearn =========================== -**emlearn** will be used to convert the scikit-learn models to C code. +The **emlearn** Python package will be used to convert the scikit-learn models +to something that can be loaded with emlearn-micropython. .. code-block:: console pip install emlearn +Install MicroPython Unix port +================================== + +We need to have the ``micropython`` interpreter installed. + +Install the Unix port of MicroPython by following the `unix port documentation `_ + + +Install emlearn-micropython modules +==================================== + +emlearn-micropython is distributed as a set of MicroPython native modules. +These are .npy file with native code, that can be installed at runtime using **mip**. +This example uses the ``emlearn_trees`` module, so that is what we will install. + +.. code-block:: console + + micropython -m mip install https://emlearn.github.io/emlearn-micropython/builds/master/x64_6.3/emlearn_trees.mpy + Create model in Python =========================== @@ -53,7 +73,7 @@ Copy and save this as file ``xor_train.py``. .. literalinclude:: helloworld_xor/xor_train.py :language: python - :emphasize-lines: 1,21-24 + :emphasize-lines: 3-4,26,30 :linenos: Run the script @@ -62,58 +82,45 @@ Run the script python xor_train.py -It will generate a file ``xor_model.h`` containing the C code for our model. +It will generate a file ``xor_model.csv`` containing the C code for our model. + -Use in C code +Use in MicroPython code ======================== -To run our model we use a simple C program that -takes data on the commandline, and prints out the detected class. +To run our model we use a simple MicroPython program. -Copy and save this as file ``xor_host.c``. +Copy and save this as file ``xor_run.py``. -.. literalinclude:: helloworld_xor/xor_host.c - :language: c - :emphasize-lines: 1,18-19 +.. literalinclude:: helloworld_xor/xor_run.py + :language: python + :emphasize-lines: 3,10,23 :linenos: -On Linux / MacOS / WSL with GCC - -.. code-block:: console - - export EMLEARN_INCLUDE_DIR=`python -c 'import emlearn; print(emlearn.includedir)'` - gcc -o xor_host xor_host.c -I${EMLEARN_INCLUDE_DIR} - -On Windows with cmd.exe - -.. code-block:: console - - python -c "import emlearn; print(emlearn.includedir)" - - set EMLEARN_INCLUDE_DIR= output from above command - - cl xor_host.c /I %EMLEARN_INCLUDE_DIR% /link /out:xor_host.exe Try it out ======================== -In our training data input values above ``0.5`` is considered "true". -So for the XOR function, if **one and only one** of the values is above ``0.5``, should get class **1** as output - else class **0**. +In our training data input values above ``2**14`` is considered "true". +So for the XOR function, if **one and only one** of the values is above this limit, should get class **1** as output - else class **0**. -The following should output 1 +Run the program using `micropython`: .. code-block:: console - ./xor_host 0.6 0.0 - ./xor_host 0.1 0.7 + micropython xor_host.py -The following should output 0 + +The output should be something like: .. code-block:: console - ./xor_host 0.8 0.7 - ./xor_host 0.0 0.0 + [0, 0] -> [1.0, 0.0] : False + [32767, 32767] -> [0.666, 0.333] : False + [0, 32767] -> [0.0, 1.0] : True + [32767, 0] -> [0.0, 1.0] : True + Next ======== diff --git a/docs/helloworld_xor/.gitignore b/docs/helloworld_xor/.gitignore new file mode 100644 index 0000000..5327144 --- /dev/null +++ b/docs/helloworld_xor/.gitignore @@ -0,0 +1,3 @@ +xor_browser.js +xor_browser.wasm +xor_model.h diff --git a/docs/helloworld_xor/xor_run.py b/docs/helloworld_xor/xor_run.py new file mode 100644 index 0000000..e916ad5 --- /dev/null +++ b/docs/helloworld_xor/xor_run.py @@ -0,0 +1,26 @@ +# device/micropython code + +import emlearn_trees +import array + +model = emlearn_trees.new(5, 30, 2) + +# Load a CSV file with the model +with open('xor_model.csv', 'r') as f: + emlearn_trees.load_model(model, f) + +# run it +max_val = (2**15-1) # 1.0 as int16 +examples = [ + array.array('h', [0, 0]), + array.array('h', [max_val, max_val]), + array.array('h', [0, max_val]), + array.array('h', [max_val, 0]), +] + +out = array.array('f', range(model.outputs())) +for ex in examples: + model.predict(ex, out) + result = out[1] > 0.5 + print(list(ex), '->', list(out), ':', result) + diff --git a/docs/helloworld_xor/xor_train.py b/docs/helloworld_xor/xor_train.py new file mode 100644 index 0000000..a9fff47 --- /dev/null +++ b/docs/helloworld_xor/xor_train.py @@ -0,0 +1,31 @@ +# python/host code + +import emlearn +from emlearn.preprocessing import Quantizer +import numpy +from sklearn.ensemble import RandomForestClassifier +from sklearn.metrics import get_scorer + +# Generate simple dataset +def make_xor(lower=0.0, upper=1.0, threshold=0.5, samples=100, seed=42): + rng = numpy.random.RandomState(seed) + X = rng.uniform(lower, upper, size=(samples, 2)) + y = numpy.logical_xor(X[:, 0] > threshold, X[:, 1] > threshold) + X = Quantizer(max_value=1.0).fit_transform(X) # convert to int16 + return X, y + +X, y = make_xor() + +# Train a model +estimator = RandomForestClassifier(n_estimators=3, max_depth=3, max_features=2, random_state=1) +estimator.fit(X, y) +score = get_scorer('f1')(estimator, X, y) +assert score > 0.90, score # verify that we learned the function + +# Convert model using emlearn +cmodel = emlearn.convert(estimator, method='inline') + +# Save as loadable .csv file +path = 'xor_model.csv' +cmodel.save(file=path, name='xor', format='csv') +print('Wrote model to', path) diff --git a/docs/index.rst b/docs/index.rst index afc4105..2347786 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -4,8 +4,8 @@ Welcome to emlearn-micropython's documentation! =================================== .. toctree:: - :maxdepth: 3 - :caption: Contents: + :maxdepth: 2 + :caption: Contents source/README.md user_guide From 9df687af4b8ee0542a74f8953badc42500b9d770 Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Fri, 28 Mar 2025 23:03:37 +0100 Subject: [PATCH 41/80] docs: Update getting started on device --- docs/getting_started_device.rst | 121 ++++++++++++++++++++++++++++---- docs/getting_started_host.rst | 1 - 2 files changed, 107 insertions(+), 15 deletions(-) diff --git a/docs/getting_started_device.rst b/docs/getting_started_device.rst index f6e6e17..685ef8d 100644 --- a/docs/getting_started_device.rst +++ b/docs/getting_started_device.rst @@ -3,25 +3,118 @@ :parenttoc: True -.. _getting_started_micropython: +.. _getting_started_device: -================================== -Getting started with MicroPython -================================== +================================================== +Getting started on device (ESP32/RP2/STM32/etc) +================================================== -.. currentmodule:: emlearn +.. currentmodule:: emlearn-micropython -emlearn has dedicated support for `MicroPython `_ -though `emlearn-micropython `_. +emlearn-micropython runs on most hardware platforms that MicroPython does. -It enables using emlearn to get efficient inference, -but by programming only in Python and not having to deal with the underlying C code. -MicroPython supports a wide range of microcontroller devices, -and enables interactive programming. -It also supports modern features such as file-system access, USB mass storage, etc. +Prerequisites +=========================== -Information about the supported ML models and -how to get started, can be found in the `emlearn-micropython README `_. +Ensure that you have **Python** and **emlearn** setup as per the :doc:`getting_started_host`. + +Ensure that you have MicroPython flashed onto your device. +Check the `Download section `_ for MicroPython. + +Install mpremote +=========================== + +``mpremote`` is used to run scripts and copy files to/from a hardware device running MicroPython. + +.. code-block:: console + + pip install mpremote + + +Install emlearn-micropython modules +==================================== + +emlearn-micropython is distributed as a set of MicroPython native modules. +These are .npy file with native code, that can be installed at runtime using **mip**. +This example uses the ``emlearn_trees`` module, so that is what we will install. + +For ESP32 use the ``xtensawin`` architecture. + +.. code-block:: console + + micropython -m mip install https://emlearn.github.io/emlearn-micropython/builds/master/xtensawin_6.3/emlearn_trees.mpy + +For ARM Cortex M4F/M33/M7 etc use the ``armv7emsp`` architecture. + +.. code-block:: console + + micropython -m mip install https://emlearn.github.io/emlearn-micropython/builds/master/armv7emsp_6.3/emlearn_trees.mpy + +For more details about architectures for native modules, see `MicroPython mpyfiles documentation `_ + + +Create model in Python +=========================== + +We will train a simple model to learn the XOR function. +The same steps will be used for model of any complexity. +Copy and save this as file ``xor_train.py``. + +.. literalinclude:: helloworld_xor/xor_train.py + :language: python + :emphasize-lines: 3-4,26,30 + :linenos: + +Run the script + +.. code-block:: console + + python xor_train.py + +It will generate a file ``xor_model.csv`` containing the C code for our model. + + +Use in MicroPython code +======================== + +To run our model we use a simple MicroPython program. + +Copy and save this as file ``xor_run.py``. + +.. literalinclude:: helloworld_xor/xor_run.py + :language: python + :emphasize-lines: 3,10,23 + :linenos: + + +Try it out +======================== + +In our training data input values above ``2**14`` is considered "true". +So for the XOR function, if **one and only one** of the values is above this limit, should get class **1** as output - else class **0**. + +Run the program on device using ``mpremote run``: + +.. code-block:: console + + mpremote run xor_host.py + + +The output should be something like: + +.. code-block:: console + + [0, 0] -> [1.0, 0.0] : False + [32767, 32767] -> [0.666, 0.333] : False + [0, 32767] -> [0.0, 1.0] : True + [32767, 0] -> [0.0, 1.0] : True + +Next +======== + +Now you have the emlearn-micropython on running hardware. +For information on practical usecases, see :doc:`examples`. +Otherwise check out :doc:`api_reference`. diff --git a/docs/getting_started_host.rst b/docs/getting_started_host.rst index 0457834..43b62cf 100644 --- a/docs/getting_started_host.rst +++ b/docs/getting_started_host.rst @@ -98,7 +98,6 @@ Copy and save this as file ``xor_run.py``. :linenos: - Try it out ======================== From 945db1612f9f4913b8b0ac176acb0a88c07ef1e5 Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Fri, 28 Mar 2025 23:09:36 +0100 Subject: [PATCH 42/80] readthedocs: Add required Sphinx key --- .readthedocs.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.readthedocs.yaml b/.readthedocs.yaml index ab53229..faf87db 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -14,3 +14,7 @@ python: install: - requirements: requirements.dev.txt +sphinx: + # Path to your Sphinx configuration file. + configuration: docs/conf.py + From f43fbe61eaa8bd4bd9abaadeabd23c23c5c2a798 Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Fri, 28 Mar 2025 23:44:02 +0100 Subject: [PATCH 43/80] docs: Fix wrong mip install command --- docs/getting_started_device.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/getting_started_device.rst b/docs/getting_started_device.rst index 685ef8d..9fbe469 100644 --- a/docs/getting_started_device.rst +++ b/docs/getting_started_device.rst @@ -43,13 +43,13 @@ For ESP32 use the ``xtensawin`` architecture. .. code-block:: console - micropython -m mip install https://emlearn.github.io/emlearn-micropython/builds/master/xtensawin_6.3/emlearn_trees.mpy + mpremote mip install https://emlearn.github.io/emlearn-micropython/builds/master/xtensawin_6.3/emlearn_trees.mpy For ARM Cortex M4F/M33/M7 etc use the ``armv7emsp`` architecture. .. code-block:: console - micropython -m mip install https://emlearn.github.io/emlearn-micropython/builds/master/armv7emsp_6.3/emlearn_trees.mpy + mpremote mip install https://emlearn.github.io/emlearn-micropython/builds/master/armv7emsp_6.3/emlearn_trees.mpy For more details about architectures for native modules, see `MicroPython mpyfiles documentation `_ From e5fbe78c9318d82620500bca11be942ef79036e9 Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Sat, 29 Mar 2025 00:03:42 +0100 Subject: [PATCH 44/80] har_trees: Fix typo in mpremote command Fixes #33 --- examples/har_trees/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/har_trees/README.md b/examples/har_trees/README.md index 2b79a5d..40b9caa 100644 --- a/examples/har_trees/README.md +++ b/examples/har_trees/README.md @@ -189,7 +189,7 @@ mpremote run har_record.py Alternatively: Copy the program to device. This way it will run even if there is no USB device connected. ``` -mpremote cp har_record.py main.py +mpremote cp har_record.py :main.py mpremote reset ``` From 6c6cc919e157067e8891595ca894736b366f2db5 Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Sat, 12 Apr 2025 21:46:44 +0200 Subject: [PATCH 45/80] README: Fix documentation link References #34 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c0a45b6..bba9fc8 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ Builds on [emlearn](https://emlearn.org), a C99 library for Machine Learning on ## Documentation -Complete [documentation on ReadTheDocs](https://emlearn-micropython.readthedocs.io/en/latest/user_guide.htmls). +Complete [documentation on ReadTheDocs](https://emlearn-micropython.readthedocs.io/en/latest/user_guide.html). ## Prerequisites From d53fbc76c164dbee1218289eb3e203bc139e8c26 Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Mon, 14 Apr 2025 11:10:33 +0200 Subject: [PATCH 46/80] README: Update hardware support --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index bba9fc8..8d5d6f1 100644 --- a/README.md +++ b/README.md @@ -17,8 +17,9 @@ Builds on [emlearn](https://emlearn.org), a C99 library for Machine Learning on ## Status **Minimally useful** -- Tested *working* on `x64` (Unix port) and `xtensawin` (ESP32). -- Currently *broken* on ARM `armv6m` (Cortex M0 / RP2040). [Issue](https://github.com/emlearn/emlearn-micropython/issues/19) +- Tested *working* on `x64` (Unix port) and `xtensawin` (ESP32/ESP32-S3/etc). +- Currently not supported: [armv6m/Cortex-M0/RP2040](https://github.com/emlearn/emlearn-micropython/issues/19) +and [RISC-V/ESP32-C3/ESP32-C6](https://github.com/emlearn/emlearn-micropython/issues/35) ## Features From f4cec366623c64d27b4afde705b961b1f220546e Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Mon, 14 Apr 2025 11:44:48 +0200 Subject: [PATCH 47/80] trees: Support building as user C module For example in micropython/ports/unix make -j4 USER_C_MODULES=/home/jon/projects/emlearn-micropython/src/ Note that right now there is a -Wdouble-promotion failure in eml_trees.h --- src/emlearn_trees/Makefile | 2 +- src/emlearn_trees/micropython.mk | 9 +++++ src/emlearn_trees/trees.c | 59 ++++++++++++++++++++++++++++---- 3 files changed, 63 insertions(+), 7 deletions(-) create mode 100644 src/emlearn_trees/micropython.mk diff --git a/src/emlearn_trees/Makefile b/src/emlearn_trees/Makefile index d1a71cf..01220c9 100644 --- a/src/emlearn_trees/Makefile +++ b/src/emlearn_trees/Makefile @@ -32,6 +32,6 @@ $(DIST_FILE): $(MOD).mpy $(DIST_DIR) # Include to get the rules for compiling and linking the module include $(MPY_DIR)/py/dynruntime.mk -CFLAGS += -I$(EMLEARN_DIR) +CFLAGS += -I$(EMLEARN_DIR) -DDYNAMIC_RUNTIME=1 dist: $(DIST_FILE) diff --git a/src/emlearn_trees/micropython.mk b/src/emlearn_trees/micropython.mk new file mode 100644 index 0000000..066419a --- /dev/null +++ b/src/emlearn_trees/micropython.mk @@ -0,0 +1,9 @@ +MOD_DIR := $(USERMOD_DIR) + +# Add all C files to SRC_USERMOD. +SRC_USERMOD_C += $(MOD_DIR)/trees.c + +EMLEARN_DIR := $(shell python3 -c "import emlearn; print(emlearn.includedir)") + +# We can add our module folder to include paths if needed +CFLAGS_USERMOD += -I$(EMLEARN_DIR) diff --git a/src/emlearn_trees/trees.c b/src/emlearn_trees/trees.c index b98ac76..8cb3f45 100644 --- a/src/emlearn_trees/trees.c +++ b/src/emlearn_trees/trees.c @@ -1,5 +1,10 @@ // Include the header file to get access to the MicroPython API + +#ifdef MICROPY_ENABLE_DYNRUNTIME #include "py/dynruntime.h" +#else +#include "py/runtime.h" +#endif #define EML_TREES_REGRESSION_ENABLE 0 #include @@ -33,7 +38,11 @@ typedef struct _mp_obj_trees_builder_t { EmlTreesBuilder builder; } mp_obj_trees_builder_t; +#if MICROPY_ENABLE_DYNRUNTIME mp_obj_full_type_t trees_builder_type; +#else +static const mp_obj_type_t trees_builder_type; +#endif // Create a new tree builder static mp_obj_t builder_new(mp_obj_t trees_obj, mp_obj_t nodes_obj, mp_obj_t leaves_obj) { @@ -58,9 +67,9 @@ static mp_obj_t builder_new(mp_obj_t trees_obj, mp_obj_t nodes_obj, mp_obj_t lea self->max_leaves = max_leaves; // create storage for trees - EmlTreesNode *nodes = (EmlTreesNode *)m_malloc(sizeof(EmlTreesNode)*max_nodes); - int32_t *roots = (int32_t *)m_malloc(sizeof(int32_t)*max_trees); - uint8_t *leaves = (uint8_t *)m_malloc(sizeof(uint8_t)*max_leaves); + EmlTreesNode *nodes = m_new(EmlTreesNode, self->max_nodes); + int32_t *roots = m_new(int32_t, self->max_trees); + uint8_t *leaves = m_new(uint8_t, self->max_leaves); #if EMLEARN_MICROPYTHON_DEBUG mp_printf(&mp_plat_print, "emltrees nodes=%p roots=%p builder=%p\n", nodes, roots, self); @@ -91,9 +100,9 @@ static mp_obj_t builder_del(mp_obj_t trees_obj) { EmlTreesBuilder *self = &o->builder; // free allocated data - m_free(self->trees.nodes); - m_free(self->trees.tree_roots); - m_free(self->trees.leaves); + m_del(EmlTreesNode, self->trees.nodes, self->max_nodes); + m_del(int32_t, self->trees.tree_roots, self->max_nodes); + m_del(uint8_t, self->trees.leaves, self->max_leaves); #if EMLEARN_MICROPYTHON_DEBUG mp_printf(&mp_plat_print, "emltrees del \n"); @@ -261,6 +270,7 @@ static mp_obj_t builder_predict(mp_obj_t self_obj, mp_obj_t features_obj, mp_obj static MP_DEFINE_CONST_FUN_OBJ_3(builder_predict_obj, builder_predict); +#ifdef MICROPY_ENABLE_DYNRUNTIME mp_map_elem_t trees_locals_dict_table[7]; static MP_DEFINE_CONST_DICT(trees_locals_dict, trees_locals_dict_table); @@ -289,3 +299,40 @@ mp_obj_t mpy_init(mp_obj_fun_bc_t *self, size_t n_args, size_t n_kw, mp_obj_t *a MP_DYNRUNTIME_INIT_EXIT } +#else + + +// Define the tree builder class +static const mp_rom_map_elem_t emlearn_trees_builder_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_predict), MP_ROM_PTR(&builder_predict_obj) }, + { MP_ROM_QSTR(MP_QSTR_addnode), MP_ROM_PTR(&builder_addnode_obj) }, + { MP_ROM_QSTR(MP_QSTR_addroot), MP_ROM_PTR(&builder_addroot_obj) }, + { MP_ROM_QSTR(MP_QSTR_addleaf), MP_ROM_PTR(&builder_addleaf_obj) }, + { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&builder_del_obj) }, + { MP_ROM_QSTR(MP_QSTR_setdata), MP_ROM_PTR(&builder_setdata_obj) }, + { MP_ROM_QSTR(MP_QSTR_outputs), MP_ROM_PTR(&builder_get_outputs_obj) }, +}; +static MP_DEFINE_CONST_DICT(emlearn_trees_builder_locals_dict, emlearn_trees_builder_locals_dict_table); + +static MP_DEFINE_CONST_OBJ_TYPE( + trees_builder_type, + MP_QSTR_emltrees, + MP_TYPE_FLAG_NONE, + locals_dict, &emlearn_trees_builder_locals_dict +); + +// Define module object. +static const mp_rom_map_elem_t emlearn_trees_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR_new), MP_ROM_PTR(&builder_new_obj) }, +}; +static MP_DEFINE_CONST_DICT(emlearn_trees_globals, emlearn_trees_globals_table); + +const mp_obj_module_t emlearn_trees_cmodule = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&emlearn_trees_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_emlearn_trees, emlearn_trees_cmodule); + +#endif + From b7c498a6004708f3bc8c0df0ab712f1b2fa64979 Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Tue, 15 Apr 2025 19:46:34 +0200 Subject: [PATCH 48/80] requirements: Bump emlearn to 0.21.1 Fixes some compile warnigs that are errors in MicroPython user C modules --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 5d13c4b..6ee4e56 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -emlearn>=0.21.0 +emlearn>=0.21.1 scikit-learn>=1.0.0 ar>=1.0.0 pyelftools>=0.31 From 5c1ac0de9001066e48abe2c73454c907076e7831 Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Tue, 15 Apr 2025 21:22:15 +0200 Subject: [PATCH 49/80] iir: Support building as user C module Right now there are some duplicated symbols from eml_common.h when built together with emlearn_trees --- src/emlearn_iir/iir_filter.c | 53 +++++++++++++++++++++++++++++----- src/emlearn_iir/micropython.mk | 9 ++++++ 2 files changed, 55 insertions(+), 7 deletions(-) create mode 100644 src/emlearn_iir/micropython.mk diff --git a/src/emlearn_iir/iir_filter.c b/src/emlearn_iir/iir_filter.c index ac62f31..0434ea6 100644 --- a/src/emlearn_iir/iir_filter.c +++ b/src/emlearn_iir/iir_filter.c @@ -1,5 +1,9 @@ // Include the header file to get access to the MicroPython API +#ifdef MICROPY_ENABLE_DYNRUNTIME #include "py/dynruntime.h" +#else +#include "py/runtime.h" +#endif #include @@ -29,7 +33,11 @@ typedef struct _mp_obj_iir_filter_t { EmlIIR filter; } mp_obj_iir_filter_t; +#if MICROPY_ENABLE_DYNRUNTIME mp_obj_full_type_t iir_filter_type; +#else +static const mp_obj_type_t iir_filter_type; +#endif // Create a new instance static mp_obj_t iir_filter_new(mp_obj_t array_obj) { @@ -55,17 +63,17 @@ static mp_obj_t iir_filter_new(mp_obj_t array_obj) { self->n_stages = n_values / 6; self->states_length = self->n_stages * 4; - self->states = (float *)m_malloc(sizeof(float)*self->states_length); + self->states = m_new(float, self->states_length); self->coefficients_length = n_values; - self->coefficients = (float *)m_malloc(sizeof(float)*self->coefficients_length); + self->coefficients = m_new(float, self->coefficients_length); memcpy((float *)self->coefficients, values, sizeof(float)*self->coefficients_length); const EmlError err = eml_iir_check(*self); if (err != EmlOk) { - m_free(self->states); - m_free((float *)self->coefficients); + m_del(float, self->states, self->states_length); + m_del(float, (float *)self->coefficients, self->coefficients_length); mp_raise_ValueError(MP_ERROR_TEXT("EmlError")); } @@ -80,9 +88,8 @@ static mp_obj_t iir_filter_del(mp_obj_t self_obj) { EmlIIR *self = &o->filter; // free allocated data - m_free(self->states); - m_free((float *)self->coefficients); - + m_del(float, self->states, self->states_length); + m_del(float, (float *)self->coefficients, self->coefficients_length); return mp_const_none; } @@ -114,6 +121,7 @@ static mp_obj_t iir_filter_run(mp_obj_t self_obj, mp_obj_t array_obj) { static MP_DEFINE_CONST_FUN_OBJ_2(iir_filter_run_obj, iir_filter_run); +#ifdef MICROPY_ENABLE_DYNRUNTIME mp_map_elem_t iir_locals_dict_table[2]; static MP_DEFINE_CONST_DICT(iir_locals_dict, iir_locals_dict_table); @@ -136,4 +144,35 @@ mp_obj_t mpy_init(mp_obj_fun_bc_t *self, size_t n_args, size_t n_kw, mp_obj_t *a // This must be last, it restores the globals dict MP_DYNRUNTIME_INIT_EXIT } +#else + + +// Define a class +static const mp_rom_map_elem_t emlearn_iir_filter_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_run), MP_ROM_PTR(&iir_filter_run_obj) }, + { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&iir_filter_del_obj) } +}; +static MP_DEFINE_CONST_DICT(emlearn_iir_filter_locals_dict, emlearn_iir_filter_locals_dict_table); + +static MP_DEFINE_CONST_OBJ_TYPE( + iir_filter_type, + MP_QSTR_emliir, + MP_TYPE_FLAG_NONE, + locals_dict, &emlearn_iir_filter_locals_dict +); +// Define module object. +static const mp_rom_map_elem_t emlearn_iir_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR_new), MP_ROM_PTR(&iir_filter_new_obj) }, +}; +static MP_DEFINE_CONST_DICT(emlearn_iir_globals, emlearn_iir_globals_table); + +const mp_obj_module_t emlearn_iir_cmodule = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&emlearn_iir_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_emlearn_iir, emlearn_iir_cmodule); + + +#endif diff --git a/src/emlearn_iir/micropython.mk b/src/emlearn_iir/micropython.mk new file mode 100644 index 0000000..40d8ad6 --- /dev/null +++ b/src/emlearn_iir/micropython.mk @@ -0,0 +1,9 @@ +MOD_DIR := $(USERMOD_DIR) + +# Add all C files to SRC_USERMOD. +SRC_USERMOD_C += $(MOD_DIR)/iir_filter.c + +EMLEARN_DIR := $(shell python3 -c "import emlearn; print(emlearn.includedir)") + +# We can add our module folder to include paths if needed +CFLAGS_USERMOD += -I$(EMLEARN_DIR) From d2731f687500186db5e5167cd8af9f134d116bc7 Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Tue, 15 Apr 2025 21:29:50 +0200 Subject: [PATCH 50/80] gitignore: Ignore mpy_ld cache --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 50f3249..5ca0c96 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ venv/ .ipynb_checkpoints/ __pycache__/ +.mpy_ld_cache/ From d7ac53790f12fa6e169ae5aa625662e95e97dd44 Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Sun, 20 Apr 2025 13:32:26 +0200 Subject: [PATCH 51/80] trees: Merge .py code also for USER_C_MODULES --- Makefile | 20 ++++++++++++++++++- src/emlearn_trees/Makefile | 2 +- .../{trees.py => emlearn_trees.py} | 7 +++++++ src/emlearn_trees/trees.c | 3 ++- src/manifest.py | 5 +++++ 5 files changed, 34 insertions(+), 3 deletions(-) rename src/emlearn_trees/{trees.py => emlearn_trees.py} (76%) create mode 100644 src/manifest.py diff --git a/Makefile b/Makefile index ec5dcf7..08b6f69 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,13 @@ VERSION := $(shell git describe --tags --always) MPY_DIR_ABS = $(abspath $(MPY_DIR)) +C_MODULES_SRC_PATH = $(abspath ./src) +MANIFEST_PATH = $(abspath ./src/manifest.py) + +PORT=unix MODULES_PATH = ./dist/$(ARCH)_$(MPY_ABI_VERSION) +PORT_DIR = ./dist/ports/$(PORT) +UNIX_MICROPYTHON = ./dist/ports/unix/micropython $(MODULES_PATH)/emlearn_trees.mpy: make -C src/emlearn_trees/ ARCH=$(ARCH) MPY_DIR=$(MPY_DIR_ABS) V=1 clean dist @@ -61,7 +67,19 @@ emlearn_iir_q15.results: $(MODULES_PATH)/emlearn_iir_q15.mpy emlearn_arrayutils.results: $(MODULES_PATH)/emlearn_arrayutils.mpy MICROPYPATH=$(MODULES_PATH) $(MICROPYTHON_BIN) tests/test_arrayutils.py -.PHONY: clean +$(PORT_DIR): + mkdir -p $@ + +$(UNIX_MICROPYTHON): $(PORT_DIR) + make -C $(MPY_DIR)/ports/unix USER_C_MODULES=$(C_MODULES_SRC_PATH) FROZEN_MANIFEST=$(MANIFEST_PATH) EXTRA_CFLAGS='-Wno-unused-function' -j4 + cp $(MPY_DIR)/ports/unix/build-standard/micropython $@ + +unix: $(UNIX_MICROPYTHON) + +check_unix: $(UNIX_MICROPYTHON) + $(UNIX_MICROPYTHON) tests/test_trees.py + +.PHONY: clean unix clean: make -C src/emlearn_trees/ ARCH=$(ARCH) MPY_DIR=$(MPY_DIR_ABS) V=1 clean diff --git a/src/emlearn_trees/Makefile b/src/emlearn_trees/Makefile index 01220c9..eab5596 100644 --- a/src/emlearn_trees/Makefile +++ b/src/emlearn_trees/Makefile @@ -19,7 +19,7 @@ DIST_DIR := ../../dist/$(ARCH)_$(MPY_ABI_VERSION) MOD = emlearn_trees # Source files (.c or .py) -SRC = trees.c trees.py +SRC = trees.c emlearn_trees.py # Releases DIST_FILE = $(DIST_DIR)/$(MOD).mpy diff --git a/src/emlearn_trees/trees.py b/src/emlearn_trees/emlearn_trees.py similarity index 76% rename from src/emlearn_trees/trees.py rename to src/emlearn_trees/emlearn_trees.py index a46b2b3..f652ec4 100644 --- a/src/emlearn_trees/trees.py +++ b/src/emlearn_trees/emlearn_trees.py @@ -1,4 +1,11 @@ +# When used as external C module, the .py is the top-level import, +# and we need to merge the native module symbols at import time +# When used as dynamic native modules (.mpy), .py and native code is merged at build time +try: + from emlearn_trees_c import * +except ImportError as e: + pass def load_model(builder, f): diff --git a/src/emlearn_trees/trees.c b/src/emlearn_trees/trees.c index 8cb3f45..bff1866 100644 --- a/src/emlearn_trees/trees.c +++ b/src/emlearn_trees/trees.c @@ -332,7 +332,8 @@ const mp_obj_module_t emlearn_trees_cmodule = { .globals = (mp_obj_dict_t *)&emlearn_trees_globals, }; -MP_REGISTER_MODULE(MP_QSTR_emlearn_trees, emlearn_trees_cmodule); +// External module name is XXX_c to allow .py file to be the entrypoint +MP_REGISTER_MODULE(MP_QSTR_emlearn_trees_c, emlearn_trees_cmodule); #endif diff --git a/src/manifest.py b/src/manifest.py new file mode 100644 index 0000000..c9d207e --- /dev/null +++ b/src/manifest.py @@ -0,0 +1,5 @@ + +# Manifest is used to include .py files for external C module build +# NOTE: this is a different mechanism than +# Ref https://docs.micropython.org/en/latest/reference/manifest.html +module("emlearn_trees.py", base_path='./emlearn_trees') From c3dfcadacd0d1f74cc2365a347623e22f28345c1 Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Sun, 20 Apr 2025 13:42:50 +0200 Subject: [PATCH 52/80] CI: Try run tests on Unix with user C modules --- .github/workflows/build.yaml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index e989acb..759e1a6 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-24.04 env: MPY_DIR: ./micropython - MICROPYTHON_BIN: ./micropython/ports/unix/build-standard/micropython + MICROPYTHON_BIN: ./micropython/ports/unix/build-nomodules/micropython steps: - uses: actions/checkout@v4 with: @@ -29,8 +29,12 @@ jobs: run: pip install -r requirements.txt - name: Setup MicroPython X86 working-directory: micropython - run: source tools/ci.sh && ci_unix_32bit_setup && ci_unix_standard_build - - name: Run test and build module x64 + run: | + source tools/ci.sh && ci_unix_32bit_setup && ci_unix_standard_build + mv ./ports/unix/build-standard/ ./ports/unix/build-nomodules/ + - name: Build custom firmware with user modules, and tests. Unix/x64 + run: make check_unix V=1 + - name: Build .mpy modules and run tests Unix/x64 run: make check ARCH=x64 V=1 - name: Setup MicroPython ARM working-directory: micropython From 9b893046bd34e753f554c11b3a367abe12a89df5 Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Sun, 20 Apr 2025 14:37:52 +0200 Subject: [PATCH 53/80] unix: Fix passing of extra CFLAGS --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 08b6f69..d84a91e 100644 --- a/Makefile +++ b/Makefile @@ -71,7 +71,7 @@ $(PORT_DIR): mkdir -p $@ $(UNIX_MICROPYTHON): $(PORT_DIR) - make -C $(MPY_DIR)/ports/unix USER_C_MODULES=$(C_MODULES_SRC_PATH) FROZEN_MANIFEST=$(MANIFEST_PATH) EXTRA_CFLAGS='-Wno-unused-function' -j4 + make -C $(MPY_DIR)/ports/unix V=1 USER_C_MODULES=$(C_MODULES_SRC_PATH) FROZEN_MANIFEST=$(MANIFEST_PATH) CFLAGS_EXTRA='-Wno-unused-function' -j4 cp $(MPY_DIR)/ports/unix/build-standard/micropython $@ unix: $(UNIX_MICROPYTHON) From 374a11248bfcb6f6217e163a9a786cb450b5b603 Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Sun, 20 Apr 2025 15:37:27 +0200 Subject: [PATCH 54/80] requirements: Bump emlearn to 0.21.2 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 6ee4e56..3a89384 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -emlearn>=0.21.1 +emlearn>=0.21.2 scikit-learn>=1.0.0 ar>=1.0.0 pyelftools>=0.31 From 762e6b46238a2ff10d61bcf4d3b0015fe5f29f01 Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Sun, 20 Apr 2025 15:52:34 +0200 Subject: [PATCH 55/80] unix: Allow unused function --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index d84a91e..2df1df8 100644 --- a/Makefile +++ b/Makefile @@ -71,7 +71,7 @@ $(PORT_DIR): mkdir -p $@ $(UNIX_MICROPYTHON): $(PORT_DIR) - make -C $(MPY_DIR)/ports/unix V=1 USER_C_MODULES=$(C_MODULES_SRC_PATH) FROZEN_MANIFEST=$(MANIFEST_PATH) CFLAGS_EXTRA='-Wno-unused-function' -j4 + make -C $(MPY_DIR)/ports/unix V=1 USER_C_MODULES=$(C_MODULES_SRC_PATH) FROZEN_MANIFEST=$(MANIFEST_PATH) CFLAGS_EXTRA='-Wno-unused-function -Wno-unused-function' -j4 cp $(MPY_DIR)/ports/unix/build-standard/micropython $@ unix: $(UNIX_MICROPYTHON) From 47788359186e8930dc822c5ac3a42272ca2d9348 Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Sun, 20 Apr 2025 16:10:02 +0200 Subject: [PATCH 56/80] unix: Also test emlearn_iir --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index 2df1df8..02bf1e8 100644 --- a/Makefile +++ b/Makefile @@ -78,6 +78,7 @@ unix: $(UNIX_MICROPYTHON) check_unix: $(UNIX_MICROPYTHON) $(UNIX_MICROPYTHON) tests/test_trees.py + $(UNIX_MICROPYTHON) tests/test_iir.py .PHONY: clean unix From 539a10eb8a7b2b6d3826c7624d6273f2dae879e3 Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Sun, 20 Apr 2025 16:10:27 +0200 Subject: [PATCH 57/80] Allow unused functions in native module builds --- src/emlearn_fft/Makefile | 2 +- src/emlearn_iir/Makefile | 2 +- src/emlearn_iir_q15/Makefile | 1 + src/emlearn_neighbors/Makefile | 2 +- src/emlearn_trees/Makefile | 2 +- 5 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/emlearn_fft/Makefile b/src/emlearn_fft/Makefile index 857868c..a9836ee 100644 --- a/src/emlearn_fft/Makefile +++ b/src/emlearn_fft/Makefile @@ -33,6 +33,6 @@ $(DIST_FILE): $(MOD).mpy $(DIST_DIR) include $(MPY_DIR)/py/dynruntime.mk -CFLAGS += -I$(EMLEARN_DIR) +CFLAGS += -I$(EMLEARN_DIR) -Wno-unused-function dist: $(DIST_FILE) diff --git a/src/emlearn_iir/Makefile b/src/emlearn_iir/Makefile index d884e95..5160c7d 100644 --- a/src/emlearn_iir/Makefile +++ b/src/emlearn_iir/Makefile @@ -32,6 +32,6 @@ $(DIST_FILE): $(MOD).mpy $(DIST_DIR) # Include to get the rules for compiling and linking the module include $(MPY_DIR)/py/dynruntime.mk -CFLAGS += -I$(EMLEARN_DIR) +CFLAGS += -I$(EMLEARN_DIR) -Wno-unused-function dist: $(DIST_FILE) diff --git a/src/emlearn_iir_q15/Makefile b/src/emlearn_iir_q15/Makefile index 7a8005f..e5f0faf 100644 --- a/src/emlearn_iir_q15/Makefile +++ b/src/emlearn_iir_q15/Makefile @@ -41,6 +41,7 @@ LIBGCC_FILENAME = $(shell $(CROSS)gcc $(CFLAGS) -print-libgcc-file-name) $(info $(LIBGCC_FILENAME)) CFLAGS += -I$(CMSIS_DSP_DIR)/Include +CFLAGS += -Wno-unused-function $(CMSIS_DSP_DIR)/iir_q15.patched: cd $(CMSIS_DSP_DIR) && git apply -v ../df1_q15_disable_dsp.patch diff --git a/src/emlearn_neighbors/Makefile b/src/emlearn_neighbors/Makefile index 53ee216..cadd861 100644 --- a/src/emlearn_neighbors/Makefile +++ b/src/emlearn_neighbors/Makefile @@ -33,6 +33,6 @@ $(DIST_DIR): $(DIST_FILE): $(MOD).mpy $(DIST_DIR) cp $< $@ -CFLAGS += -I$(EMLEARN_DIR) +CFLAGS += -I$(EMLEARN_DIR) -Wno-unused-function dist: $(DIST_FILE) diff --git a/src/emlearn_trees/Makefile b/src/emlearn_trees/Makefile index eab5596..cb30c04 100644 --- a/src/emlearn_trees/Makefile +++ b/src/emlearn_trees/Makefile @@ -32,6 +32,6 @@ $(DIST_FILE): $(MOD).mpy $(DIST_DIR) # Include to get the rules for compiling and linking the module include $(MPY_DIR)/py/dynruntime.mk -CFLAGS += -I$(EMLEARN_DIR) -DDYNAMIC_RUNTIME=1 +CFLAGS += -I$(EMLEARN_DIR) -Wno-unused-function dist: $(DIST_FILE) From f86c229aea6e34e2115e6274cd014467183d3644 Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Sun, 20 Apr 2025 17:03:25 +0200 Subject: [PATCH 58/80] fft: Enable build as extmod --- Makefile | 1 + src/emlearn_fft/Makefile | 2 +- src/emlearn_fft/{fft.py => emlearn_fft.py} | 9 +++- src/emlearn_fft/fft.c | 58 +++++++++++++++++++--- src/emlearn_fft/micropython.mk | 9 ++++ src/manifest.py | 1 + 6 files changed, 70 insertions(+), 10 deletions(-) rename src/emlearn_fft/{fft.py => emlearn_fft.py} (64%) create mode 100644 src/emlearn_fft/micropython.mk diff --git a/Makefile b/Makefile index 02bf1e8..a884119 100644 --- a/Makefile +++ b/Makefile @@ -79,6 +79,7 @@ unix: $(UNIX_MICROPYTHON) check_unix: $(UNIX_MICROPYTHON) $(UNIX_MICROPYTHON) tests/test_trees.py $(UNIX_MICROPYTHON) tests/test_iir.py + $(UNIX_MICROPYTHON) tests/test_fft.py .PHONY: clean unix diff --git a/src/emlearn_fft/Makefile b/src/emlearn_fft/Makefile index a9836ee..a27910f 100644 --- a/src/emlearn_fft/Makefile +++ b/src/emlearn_fft/Makefile @@ -19,7 +19,7 @@ DIST_DIR := ../../dist/$(ARCH)_$(MPY_ABI_VERSION) MOD = emlearn_fft # Source files (.c or .py) -SRC = fft.c fft.py +SRC = fft.c emlearn_fft.py # Releases DIST_FILE = $(DIST_DIR)/$(MOD).mpy diff --git a/src/emlearn_fft/fft.py b/src/emlearn_fft/emlearn_fft.py similarity index 64% rename from src/emlearn_fft/fft.py rename to src/emlearn_fft/emlearn_fft.py index 61026cb..7db8224 100644 --- a/src/emlearn_fft/fft.py +++ b/src/emlearn_fft/emlearn_fft.py @@ -1,9 +1,16 @@ +# When used as external C module, the .py is the top-level import, +# and we need to merge the native module symbols at import time +# When used as dynamic native modules (.mpy), .py and native code is merged at build time +try: + from emlearn_fft_c import * +except ImportError as e: + pass + import math import array import gc - def fill(fft, n): # pre-compute the trigonometrics needed for FFT computation diff --git a/src/emlearn_fft/fft.c b/src/emlearn_fft/fft.c index 9f79733..e5f42ab 100644 --- a/src/emlearn_fft/fft.c +++ b/src/emlearn_fft/fft.c @@ -1,5 +1,9 @@ // Include the header file to get access to the MicroPython API +#ifdef MICROPY_ENABLE_DYNRUNTIME #include "py/dynruntime.h" +#else +#include "py/runtime.h" +#endif #include @@ -72,9 +76,12 @@ fft_forward(float *table_sin, float *table_cos, float real[], float imag[], size } - // MicroPython type for EmlFFT -mp_obj_full_type_t mp_fft_type; +#if MICROPY_ENABLE_DYNRUNTIME +mp_obj_full_type_t fft_type; +#else +static const mp_obj_type_t fft_type; +#endif typedef struct _mp_obj_fft_t { mp_obj_base_t base; @@ -228,6 +235,8 @@ static mp_obj_t fft_run(mp_obj_t self_obj, mp_obj_t real_obj, mp_obj_t imag_obj) static MP_DEFINE_CONST_FUN_OBJ_3(fft_run_obj, fft_run); + +#ifdef MICROPY_ENABLE_DYNRUNTIME mp_map_elem_t mod_locals_dit_table[3]; static MP_DEFINE_CONST_DICT(mod_locals_dit, mod_locals_dit_table); @@ -240,21 +249,54 @@ mp_obj_t mpy_init(mp_obj_fun_bc_t *self, size_t n_args, size_t n_kw, mp_obj_t *a mp_printf(&mp_plat_print, "fft-mpy-init\n"); #endif - mp_fft_type.base.type = (void*)&mp_type_type; - mp_fft_type.flags = MP_TYPE_FLAG_NONE; - mp_fft_type.name = MP_QSTR_FFT; - MP_OBJ_TYPE_SET_SLOT(&mp_fft_type, make_new, fft_new, 0); + fft_type.base.type = (void*)&mp_type_type; + fft_type.flags = MP_TYPE_FLAG_NONE; + fft_type.name = MP_QSTR_FFT; + MP_OBJ_TYPE_SET_SLOT(&fft_type, make_new, fft_new, 0); // methods mod_locals_dit_table[0] = (mp_map_elem_t){ MP_OBJ_NEW_QSTR(MP_QSTR_run), MP_OBJ_FROM_PTR(&fft_run_obj) }; mod_locals_dit_table[1] = (mp_map_elem_t){ MP_OBJ_NEW_QSTR(MP_QSTR___del__), MP_OBJ_FROM_PTR(&fft_del_obj) }; mod_locals_dit_table[2] = (mp_map_elem_t){ MP_OBJ_NEW_QSTR(MP_QSTR_fill), MP_OBJ_FROM_PTR(&fft_fill_obj) }; - MP_OBJ_TYPE_SET_SLOT(&mp_fft_type, locals_dict, (void*)&mod_locals_dit, 3); + MP_OBJ_TYPE_SET_SLOT(&fft_type, locals_dict, (void*)&mod_locals_dit, 3); // Make the Factorial type available on the module. - mp_store_global(MP_QSTR_FFT, MP_OBJ_FROM_PTR(&mp_fft_type)); + mp_store_global(MP_QSTR_FFT, MP_OBJ_FROM_PTR(&fft_type)); // This must be last, it restores the globals dict MP_DYNRUNTIME_INIT_EXIT } +#else // extmod + +// Define a class +static const mp_rom_map_elem_t emlearn_fft_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_run), MP_ROM_PTR(&fft_run_obj) }, + { MP_ROM_QSTR(MP_QSTR_fill), MP_ROM_PTR(&fft_fill_obj) }, + { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&fft_del_obj) } +}; +static MP_DEFINE_CONST_DICT(emlearn_fft_locals_dict, emlearn_fft_locals_dict_table); + + +static MP_DEFINE_CONST_OBJ_TYPE( + fft_type, + MP_QSTR_emlfft, + MP_TYPE_FLAG_NONE, + make_new, fft_new, + locals_dict, &emlearn_fft_locals_dict +); + +// Define module object. +static const mp_rom_map_elem_t emlearn_fft_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR_FFT), MP_ROM_PTR(&fft_type) } +}; +static MP_DEFINE_CONST_DICT(emlearn_fft_globals, emlearn_fft_globals_table); + +const mp_obj_module_t emlearn_fft_cmodule = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&emlearn_fft_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_emlearn_fft_c, emlearn_fft_cmodule); +#endif + diff --git a/src/emlearn_fft/micropython.mk b/src/emlearn_fft/micropython.mk new file mode 100644 index 0000000..c1bfd9a --- /dev/null +++ b/src/emlearn_fft/micropython.mk @@ -0,0 +1,9 @@ +MOD_DIR := $(USERMOD_DIR) + +# Add all C files to SRC_USERMOD. +SRC_USERMOD_C += $(MOD_DIR)/fft.c + +EMLEARN_DIR := $(shell python3 -c "import emlearn; print(emlearn.includedir)") + +# We can add our module folder to include paths if needed +CFLAGS_USERMOD += -I$(EMLEARN_DIR) diff --git a/src/manifest.py b/src/manifest.py index c9d207e..d79520e 100644 --- a/src/manifest.py +++ b/src/manifest.py @@ -3,3 +3,4 @@ # NOTE: this is a different mechanism than # Ref https://docs.micropython.org/en/latest/reference/manifest.html module("emlearn_trees.py", base_path='./emlearn_trees') +module("emlearn_fft.py", base_path='./emlearn_fft') From 3cedc9bd81b9ce5a703ee2531767e5950b31edce Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Sun, 20 Apr 2025 17:33:25 +0200 Subject: [PATCH 59/80] arrayutils: Support build as extmod --- Makefile | 1 + src/emlearn_arrayutils/micropython.mk | 9 +++++++++ src/emlearn_arrayutils/modarrayutils.c | 27 +++++++++++++++++++++++++- 3 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 src/emlearn_arrayutils/micropython.mk diff --git a/Makefile b/Makefile index a884119..e208fab 100644 --- a/Makefile +++ b/Makefile @@ -80,6 +80,7 @@ check_unix: $(UNIX_MICROPYTHON) $(UNIX_MICROPYTHON) tests/test_trees.py $(UNIX_MICROPYTHON) tests/test_iir.py $(UNIX_MICROPYTHON) tests/test_fft.py + $(UNIX_MICROPYTHON) tests/test_arrayutils.py .PHONY: clean unix diff --git a/src/emlearn_arrayutils/micropython.mk b/src/emlearn_arrayutils/micropython.mk new file mode 100644 index 0000000..03746a2 --- /dev/null +++ b/src/emlearn_arrayutils/micropython.mk @@ -0,0 +1,9 @@ +MOD_DIR := $(USERMOD_DIR) + +# Add all C files to SRC_USERMOD. +SRC_USERMOD_C += $(MOD_DIR)/modarrayutils.c + +EMLEARN_DIR := $(shell python3 -c "import emlearn; print(emlearn.includedir)") + +# We can add our module folder to include paths if needed +# CFLAGS_USERMOD += -I$(EMLEARN_DIR) diff --git a/src/emlearn_arrayutils/modarrayutils.c b/src/emlearn_arrayutils/modarrayutils.c index 47c6e25..55bde77 100644 --- a/src/emlearn_arrayutils/modarrayutils.c +++ b/src/emlearn_arrayutils/modarrayutils.c @@ -1,5 +1,9 @@ // Include the header file to get access to the MicroPython API +#ifdef MICROPY_ENABLE_DYNRUNTIME #include "py/dynruntime.h" +#else +#include "py/runtime.h" +#endif #include @@ -98,7 +102,7 @@ arrayutils_linear_map(size_t n_args, const mp_obj_t *args) { const float *in = in_bufinfo.buf; int16_t *out = out_bufinfo.buf; for (int i=0; i Date: Sun, 20 Apr 2025 18:14:59 +0200 Subject: [PATCH 60/80] doc: Update notes on native modules --- doc/TODO.md | 4 +- doc/micropython-tinyml-status.md | 30 ++++++++++ doc/native-modules-future.md | 94 ++++++++++++++++++++++++++++++++ 3 files changed, 127 insertions(+), 1 deletion(-) create mode 100644 doc/native-modules-future.md diff --git a/doc/TODO.md b/doc/TODO.md index 7da2de8..b0471d8 100644 --- a/doc/TODO.md +++ b/doc/TODO.md @@ -16,7 +16,8 @@ - Complete example for full flow. Data collect, train, deploy. DONE, HAR - First course held - Can run on-device training example -- Can run example in browser +- Can run inference example in browser +- Can do data collection, training in browser, deploy to device # TODO @@ -51,3 +52,4 @@ Requires user/external C module build support. https://github.com/emlearn/emlearn-micropython/issues/18 - Test getting audio input into MicroPython Webassembly - Test getting IMU data (ie on phone), in browser +- Test running scikit-learn and/or Keras training in browser. With Pyiodine diff --git a/doc/micropython-tinyml-status.md b/doc/micropython-tinyml-status.md index 62f17b6..a8d32af 100644 --- a/doc/micropython-tinyml-status.md +++ b/doc/micropython-tinyml-status.md @@ -21,6 +21,30 @@ https://github.com/micropython/micropython/issues/6769 Need to manually resolve all symbols. https://github.com/micropython/micropython/issues/5629 +Supporting both natmod and extmod builds requires duplicating class and module definitions, +because they are slightly different. +Maybe an "unified API" could be introduced using some C macros? + +Supporting both natmod and extmod builds requires 3 build systems setup. +natmod requires a `Makefile` which includes `dynruntime.mk`, +whereas extmod requires `micropython.mk` for Makefile-based ports, +and also `micropython.cmake` for cmake-based ports. + +Native modules with relocations does not work on armv6m/RP2040. + +Native modules with relocations do not work on RISC-V/ESP32-C3/ESP32-C6. + +Native modules does not work on Webassembly target. + +To install native modules with + +Question. Does native modules work on Zephyr port? + +Question. Can native modules be distributed in a ROMFS? + +Native modules do not support Execute-in-Place, consuming RAM for all the code. +Questiion. Can this be solved using ROMFS? + ## Efficient data processing #### Using specialized code emitters @@ -154,6 +178,12 @@ https://github.com/micropython/micropython/pull/8318 Missing! Support for Bluetooth Audio. Useful for collecting raw data by sending to a phone or computer. +#### USB +Good support for USB device on many targets. +USB devices can be created dynamically at runtime, without needing to rebuild firmware. + +USB host not supported on any target. + #### LoRa Unknown/not investigated. diff --git a/doc/native-modules-future.md b/doc/native-modules-future.md new file mode 100644 index 0000000..67b9c95 --- /dev/null +++ b/doc/native-modules-future.md @@ -0,0 +1,94 @@ + +# Dynamic native modules are the future + +WIP on a post for MicroPython Discussion forum, +advocating for dynamic native modules - their potential and how they can be further improved. + +Dynamic modules are .mpy files with native code that can be installed at runtime. +This enables to "mip install" modules that extend the + +For computationally intensive uses C modules are very useful, +because they can be 10-100x faster than Python (even when using native and Viper emitters). +This includes things like data processing, signal processing, machine learning, compression, cryptography, and much more. + +The facilities for dynamic native modules for several years already, +but it has not seen that much usage or testing. +However in MicroPython 1.24 (October 2024) critical issues where fixed, +and now in MicroPython 1.25 (April 2025) there is support for linking symbols, +which means that it is getting ready for wider usage. + +## What are dynamic native modules + +natmod, https://docs.micropython.org/en/latest/develop/natmod.html +extmod, https://docs.micropython.org/en/latest/develop/cmodules.html + +## Advantages of native modules + +User benefits, user-experience +Library developer benefits +Hardware manufacturer benefits + +Enables having C module extensions without requiring custom builds + +## Drawbacks of native modules + +RAM usage. +Code dupliation. + +## What to use native modules for (and not) + + + +## Painpoints with native modules + +TODO: import from other notes + +These are all things which can be improved, and it is up to us all to help out. +I believe that this can significantly improve the MicroPython ecosystem. + +## Building + +TODO: file issues around these things, at least the most obvious/clear ones + +Ideas + +- Support for natmod in micropython-lib +- Distribute the natmods in micropython. btree +- mpremote. Resolve correct architecture for native .mpy files +- awesome-micropython. Include standard marker for native modules? +- mpbuild. Support for natmod building +- docs. Document better how to support both extmod and natmod with same codebase + + +## Call to Action + +Library developers. +Consider supporting native modules for your C-based library modules. +Try to develop new modules as natmod-first, with extmod as the backup +(for ports without natmod or uses where ). + +MicroPython contributors. +Help out to improve + +## Examples of native modules in use + +Libraries + +- [BrianPugh/tamp](https://github.com/BrianPugh/tamp) - a low-memory, DEFLATE-inspired lossless compression library. +- emlearn-micropython - Efficient and convenient Machine Learning engine +- **Your library here!!** + +Tools + +- ViperIDE. +- **Your tools here!** + + +# Thanks to + +vhymassky +agatti +damien +BrianPugh + + From fa53fc7e56942e6bfe365b351a5b080a7b399d18 Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Sun, 20 Apr 2025 19:03:48 +0200 Subject: [PATCH 61/80] cnn: Start support for building as extmod Currently only builds int8 configuration, so tests fail --- Makefile | 1 + src/tinymaix_cnn/Makefile | 3 +- src/tinymaix_cnn/fp32/tm_port.h | 2 +- src/tinymaix_cnn/int8/tm_port.h | 2 +- src/tinymaix_cnn/mod_cnn.c | 67 +++++++++++++++++++++++++++++++-- 5 files changed, 67 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index e208fab..099b14c 100644 --- a/Makefile +++ b/Makefile @@ -81,6 +81,7 @@ check_unix: $(UNIX_MICROPYTHON) $(UNIX_MICROPYTHON) tests/test_iir.py $(UNIX_MICROPYTHON) tests/test_fft.py $(UNIX_MICROPYTHON) tests/test_arrayutils.py + echo SKIP $(UNIX_MICROPYTHON) tests/test_cnn.py .PHONY: clean unix diff --git a/src/tinymaix_cnn/Makefile b/src/tinymaix_cnn/Makefile index bfa45d2..3129668 100644 --- a/src/tinymaix_cnn/Makefile +++ b/src/tinymaix_cnn/Makefile @@ -11,11 +11,10 @@ MPY_ABI_VERSION := 6.3 # Location of deps TINYMAIX_DIR := ../../dependencies/TinyMaix +CONFIG_DIR := ./$(CONFIG) DIST_DIR := ../../dist/$(ARCH)_$(MPY_ABI_VERSION) -CONFIG_DIR := ./$(CONFIG) - # enable linking of libm etc LINK_RUNTIME=1 diff --git a/src/tinymaix_cnn/fp32/tm_port.h b/src/tinymaix_cnn/fp32/tm_port.h index 1868177..4ef6777 100644 --- a/src/tinymaix_cnn/fp32/tm_port.h +++ b/src/tinymaix_cnn/fp32/tm_port.h @@ -48,7 +48,7 @@ limitations under the License. // Use MicroPython for dynamic allocation #define tm_malloc(x) m_malloc(x) -#define tm_free(x) m_free(x) +#define tm_free(x) mod_cnn_free(x) // FIXME: set theese to use MicroPython primitives diff --git a/src/tinymaix_cnn/int8/tm_port.h b/src/tinymaix_cnn/int8/tm_port.h index f908f61..ec5cf44 100644 --- a/src/tinymaix_cnn/int8/tm_port.h +++ b/src/tinymaix_cnn/int8/tm_port.h @@ -48,7 +48,7 @@ limitations under the License. // Use MicroPython for dynamic allocation #define tm_malloc(x) m_malloc(x) -#define tm_free(x) m_free(x) +#define tm_free(x) mod_cnn_free(x) // FIXME: set theese to use MicroPython primitives diff --git a/src/tinymaix_cnn/mod_cnn.c b/src/tinymaix_cnn/mod_cnn.c index 04ea77e..83ad069 100644 --- a/src/tinymaix_cnn/mod_cnn.c +++ b/src/tinymaix_cnn/mod_cnn.c @@ -1,5 +1,12 @@ // Include the header file to get access to the MicroPython API +#ifdef MICROPY_ENABLE_DYNRUNTIME #include "py/dynruntime.h" +#else +#include "py/runtime.h" +#endif + + +void mod_cnn_free(void *ptr); // TinyMaix config #include "./tm_port.h" @@ -65,11 +72,27 @@ typedef struct _mp_obj_mod_cnn_t { tm_mdl_t model; tm_mat_t input; uint8_t *model_buffer; + size_t model_buffer_length; uint8_t *data_buffer; + size_t data_buffer_length; uint16_t out_dims[4]; } mp_obj_mod_cnn_t; +#if MICROPY_ENABLE_DYNRUNTIME mp_obj_full_type_t mod_cnn_type; +#else +static const mp_obj_type_t mod_cnn_type; +#endif + + +void mod_cnn_free(void *ptr) +{ +#if MICROPY_ENABLE_DYNRUNTIME + return m_free(ptr); +#else + return m_del(void *, ptr, 0); // XXX: not sure if safe +#endif +} // TODO: add function for getting the shape of expected input. As a tuple @@ -95,12 +118,14 @@ static mp_obj_t mod_cnn_new(mp_obj_t model_data_obj) { tm_mdl_t *model = &o->model; // Copy the model data - o->model_buffer = m_malloc(model_data_length); + o->model_buffer_length = model_data_length; + o->model_buffer = m_new(uint8_t, o->model_buffer_length); memcpy(o->model_buffer, model_data_buffer, model_data_length); // Allocate temporary buffer // TODO: this can possibly be smaller? Might want to use TinyMaix internal alloc - o->data_buffer = m_malloc(model_data_length); + o->data_buffer_length = model_data_length; + o->data_buffer = m_new(uint8_t, o->data_buffer_length); // loading model // will set the dimensions of the input matrix @@ -139,8 +164,8 @@ static mp_obj_t mod_cnn_del(mp_obj_t self_obj) { mp_obj_mod_cnn_t *o = MP_OBJ_TO_PTR(self_obj); tm_mdl_t *model = &o->model; - m_free(o->model_buffer); - m_free(o->data_buffer); + m_del(uint8_t, o->model_buffer, o->model_buffer_length); + m_del(uint8_t, o->data_buffer, o->data_buffer_length); tm_unload(model); return mp_const_none; @@ -245,6 +270,7 @@ static mp_obj_t mod_cnn_output_dimensions(mp_obj_t self_obj) { static MP_DEFINE_CONST_FUN_OBJ_1(mod_cnn_output_dimensions_obj, mod_cnn_output_dimensions); +#ifdef MICROPY_ENABLE_DYNRUNTIME // natmod mp_map_elem_t mod_locals_dict_table[3]; static MP_DEFINE_CONST_DICT(mod_locals_dict, mod_locals_dict_table); @@ -268,4 +294,37 @@ mp_obj_t mpy_init(mp_obj_fun_bc_t *self, size_t n_args, size_t n_kw, mp_obj_t *a // This must be last, it restores the globals dict MP_DYNRUNTIME_INIT_EXIT } +#else // extmod + +// Define a class +static const mp_rom_map_elem_t mod_cnn_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_run), MP_ROM_PTR(&mod_cnn_run_obj) }, + { MP_ROM_QSTR(MP_QSTR_output_dimensions), MP_ROM_PTR(&mod_cnn_del_obj) }, + { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&mod_cnn_output_dimensions_obj) } +}; +static MP_DEFINE_CONST_DICT(mod_cnn_locals_dict, mod_cnn_locals_dict_table); + + +static MP_DEFINE_CONST_OBJ_TYPE( + mod_cnn_type, + MP_QSTR_tinymaix_cnn, + MP_TYPE_FLAG_NONE, + locals_dict, &mod_cnn_locals_dict +); + +// Define module object. +static const mp_rom_map_elem_t mod_cnn_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR_new), MP_ROM_PTR(&mod_cnn_new_obj) } +}; +static MP_DEFINE_CONST_DICT(mod_cnn_globals, mod_cnn_globals_table); + +const mp_obj_module_t mod_cnn_cmodule = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&mod_cnn_globals, +}; + +// FIXME: unhardcode config part of module name +MP_REGISTER_MODULE(MP_QSTR_emlearn_cnn_int8, mod_cnn_cmodule); +#endif + From bf559997a4742e7e802d83afbc8234a5de408239 Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Sun, 20 Apr 2025 23:33:12 +0200 Subject: [PATCH 62/80] extmod: Add support for cmake/rp2 for trees --- Makefile | 5 +++++ src/emlearn_trees/micropython.cmake | 17 +++++++++++++++++ src/micropython.cmake | 2 ++ 3 files changed, 24 insertions(+) create mode 100644 src/emlearn_trees/micropython.cmake create mode 100644 src/micropython.cmake diff --git a/Makefile b/Makefile index 099b14c..625578e 100644 --- a/Makefile +++ b/Makefile @@ -83,6 +83,11 @@ check_unix: $(UNIX_MICROPYTHON) $(UNIX_MICROPYTHON) tests/test_arrayutils.py echo SKIP $(UNIX_MICROPYTHON) tests/test_cnn.py +rp2: $(PORT_DIR) + make -C $(MPY_DIR)/ports/rp2 V=1 USER_C_MODULES=$(C_MODULES_SRC_PATH)/micropython.cmake FROZEN_MANIFEST=$(MANIFEST_PATH) CFLAGS_EXTRA='-Wno-unused-function -Wno-unused-function' -j4 + mkdir -p ./dist/ports/rp2/RPI_PICO + cp -r $(MPY_DIR)/ports/rp2/build-RPI_PICO/firmware* ./dist/ports/rp2/RPI_PICO/ + .PHONY: clean unix clean: diff --git a/src/emlearn_trees/micropython.cmake b/src/emlearn_trees/micropython.cmake new file mode 100644 index 0000000..6ad928a --- /dev/null +++ b/src/emlearn_trees/micropython.cmake @@ -0,0 +1,17 @@ +add_library(usermod_emlearn_trees INTERFACE) + +execute_process( + COMMAND python3 -c "import emlearn; print(emlearn.includedir)" + OUTPUT_VARIABLE EMLEARN_DIR + OUTPUT_STRIP_TRAILING_WHITESPACE +) + +target_sources(usermod_emlearn_trees INTERFACE + ${CMAKE_CURRENT_LIST_DIR}/trees.c +) + +target_include_directories(usermod_emlearn_trees INTERFACE + ${EMLEARN_DIR} +) + +target_link_libraries(usermod INTERFACE usermod_emlearn_trees) diff --git a/src/micropython.cmake b/src/micropython.cmake new file mode 100644 index 0000000..a159e67 --- /dev/null +++ b/src/micropython.cmake @@ -0,0 +1,2 @@ + +include(${CMAKE_CURRENT_LIST_DIR}/emlearn_trees/micropython.cmake) From e95c38760f8af6537164db1e73a8fadcbfdc7c74 Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Sun, 20 Apr 2025 23:34:41 +0200 Subject: [PATCH 63/80] CI: Try build rp2 --- .github/workflows/build.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 759e1a6..533fb3d 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -39,6 +39,8 @@ jobs: - name: Setup MicroPython ARM working-directory: micropython run: source tools/ci.sh && ci_rp2_setup + - name: Build custom firmware with extmod, RP2 + run: make rp2 V=1 - name: Build module armv6m run: echo make dist ARCH=armv6m V=1 - name: Build module armv7m From ca0426e290bfa7b58c5ceff7d275799a35f1e0fa Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Sun, 20 Apr 2025 23:51:57 +0200 Subject: [PATCH 64/80] CI: Pull submodules for RP2 --- .github/workflows/build.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 533fb3d..d790513 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -39,6 +39,9 @@ jobs: - name: Setup MicroPython ARM working-directory: micropython run: source tools/ci.sh && ci_rp2_setup + - name: Setup MicroPython RP2 port + working-directory: micropython/ports/rp2 + run: make submodules - name: Build custom firmware with extmod, RP2 run: make rp2 V=1 - name: Build module armv6m From cbe765bf5915740fa0147e1cf96192988fddc576 Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Sun, 20 Apr 2025 23:58:46 +0200 Subject: [PATCH 65/80] emlearn_trees: Only use memset/memcpy for natmod --- src/emlearn_trees/trees.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/emlearn_trees/trees.c b/src/emlearn_trees/trees.c index bff1866..6e13a30 100644 --- a/src/emlearn_trees/trees.c +++ b/src/emlearn_trees/trees.c @@ -13,6 +13,7 @@ #define EMLEARN_MICROPYTHON_DEBUG 0 +#ifdef MICROPY_ENABLE_DYNRUNTIME // memset is used by some standard C constructs #if !defined(__linux__) void *memcpy(void *dst, const void *src, size_t n) { @@ -22,7 +23,7 @@ void *memset(void *s, int c, size_t n) { return mp_fun_table.memset_(s, c, n); } #endif - +#endif // For building up an EmlTrees structure typedef struct _EmlTreesBuilder { From f317279635840abdf306011db920ae507f54f753 Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Mon, 21 Apr 2025 00:35:56 +0200 Subject: [PATCH 66/80] xor_run: Fix example after emlearn_trees predict() change Must pass output array --- examples/xor_trees/xor_run.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/examples/xor_trees/xor_run.py b/examples/xor_trees/xor_run.py index 5d220cf..c9e630f 100644 --- a/examples/xor_trees/xor_run.py +++ b/examples/xor_trees/xor_run.py @@ -3,6 +3,16 @@ import emlearn_trees import array +def argmax(arr): + idx_max = 0 + value_max = arr[0] + for i in range(1, len(arr)): + if arr[i] > value_max: + value_max = arr[i] + idx_max = i + + return idx_max + model = emlearn_trees.new(5, 30, 2) # Load a CSV file with the model @@ -18,7 +28,9 @@ array.array('h', [max_val, 0]), ] -for ex in examples: - result = model.predict(ex) +out = array.array('f', range(model.outputs())) +for ex in examples: + model.predict(ex, out) + result = argmax(out) print(ex, result) From a7e1a485570790258dfc777a51444b6b8bd9d68f Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Mon, 21 Apr 2025 00:43:04 +0200 Subject: [PATCH 67/80] har_trees: Fix wrong dataset used by default --- examples/har_trees/har_run.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/har_trees/har_run.py b/examples/har_trees/har_run.py index efdaa70..ec476f8 100644 --- a/examples/har_trees/har_run.py +++ b/examples/har_trees/har_run.py @@ -65,7 +65,7 @@ def main(): model = emlearn_trees.new(10, 1000, 10) dataset = 'har_uci' - dataset = 'har_exercise_1' + #dataset = 'har_exercise_1' model_path = f'{dataset}_trees.csv' From a4e1b34622c6a7747a954f570107ea23c34116d5 Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Mon, 21 Apr 2025 01:40:25 +0200 Subject: [PATCH 68/80] CI: Bump to MicroPython 1.25.0 release tag --- .github/workflows/build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index d790513..8a675f9 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -24,7 +24,7 @@ jobs: with: repository: jonnor/micropython path: micropython - ref: v1.25preview-linking + ref: v1.25.0 - name: Install Python dependencies run: pip install -r requirements.txt - name: Setup MicroPython X86 From e3582512ff1542b1046a7a6c6cf9b655ee299179 Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Mon, 21 Apr 2025 11:11:11 +0200 Subject: [PATCH 69/80] doc: Link mpbuild natmod support issue --- doc/native-modules-future.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/doc/native-modules-future.md b/doc/native-modules-future.md index 67b9c95..90e154a 100644 --- a/doc/native-modules-future.md +++ b/doc/native-modules-future.md @@ -52,11 +52,12 @@ TODO: file issues around these things, at least the most obvious/clear ones Ideas -- Support for natmod in micropython-lib -- Distribute the natmods in micropython. btree +- Support for natmod in micropython-lib. Automatically built in CI and published. +- Build and publish the natmods that are part of MicroPython. btree, deflate - mpremote. Resolve correct architecture for native .mpy files - awesome-micropython. Include standard marker for native modules? -- mpbuild. Support for natmod building +- mpbuild. Support for natmod building. +https://github.com/mattytrentini/mpbuild/issues/76 - docs. Document better how to support both extmod and natmod with same codebase From fa9325c31bc55314e550e00c494600281173e691 Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Mon, 21 Apr 2025 13:12:18 +0200 Subject: [PATCH 70/80] extmod: Also support ESP32 build --- Makefile | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Makefile b/Makefile index 625578e..bfa3990 100644 --- a/Makefile +++ b/Makefile @@ -12,8 +12,13 @@ C_MODULES_SRC_PATH = $(abspath ./src) MANIFEST_PATH = $(abspath ./src/manifest.py) PORT=unix +BOARD=ESP32_GENERIC_S3 + MODULES_PATH = ./dist/$(ARCH)_$(MPY_ABI_VERSION) PORT_DIR = ./dist/ports/$(PORT) +PORT_BUILD_DIR=$(MPY_DIR)/ports/$(PORT)/build-$(BOARD) +PORT_DIST_DIR=./dist/ports/$(PORT)/$(BOARD) + UNIX_MICROPYTHON = ./dist/ports/unix/micropython $(MODULES_PATH)/emlearn_trees.mpy: @@ -88,6 +93,14 @@ rp2: $(PORT_DIR) mkdir -p ./dist/ports/rp2/RPI_PICO cp -r $(MPY_DIR)/ports/rp2/build-RPI_PICO/firmware* ./dist/ports/rp2/RPI_PICO/ + +extmod: + make -C $(MPY_DIR)/ports/esp32 V=1 BOARD=$(BOARD) USER_C_MODULES=$(C_MODULES_SRC_PATH)/micropython.cmake FROZEN_MANIFEST=$(MANIFEST_PATH) CFLAGS_EXTRA='-Wno-unused-function -Wno-unused-function' -j4 + mkdir -p $(PORT_DIST_DIR) + cp -r $(PORT_BUILD_DIR)/firmware* $(PORT_DIST_DIR) + cp -r $(PORT_BUILD_DIR)/micropython* $(PORT_DIST_DIR) + + .PHONY: clean unix clean: From 516110568de7b1ec2db00e534bdf93a2a0abdeed Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Mon, 21 Apr 2025 13:12:52 +0200 Subject: [PATCH 71/80] doc: Some notes on MicroPython documentation --- doc/micropython-documentation.md | 35 ++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 doc/micropython-documentation.md diff --git a/doc/micropython-documentation.md b/doc/micropython-documentation.md new file mode 100644 index 0000000..4a19043 --- /dev/null +++ b/doc/micropython-documentation.md @@ -0,0 +1,35 @@ + +## Background +At the moment the MicroPython documentation is mostly missing a *user-guide*. +The existing documentation is mostly a *reference* (to ports/internals/tools). +A user in this context is an application/firmware developer: +Someone who wants to develop programs for a particular usecase, +and less interested in MicroPython internals etc. + +Here are some things I would like to see, that helps people in more quickly learning how to build with MicroPython. + +## User guide + +- Getting started. zero to hero, simplified flow (mentioned earlier). +- Structuring programs +Project layout. Program flow, asyncio/blocking/nonblocking/interrupts. +- Development tools. +mpremote/commandline-based. IDEs and IDE extensions/plugins. +- Debugging MicroPython programs. +Tools, techniques. logging +- Selecting hardware +Limited scope: different capabilities of ports +- Using external libraries +Where to find. micropython-lib. Awesome MicroPython. How to install. +- Developing libraries +Project layout. Publishing. +- Automated testing +Host-based. On-device. Simulation with qemu. Hardware-in-the-loop +- Type checking. +stubs +- Continious integration/deployment +Building and publishing applications using CI + + +## More learning resources +Pointers to other places to learn about developing with MicroPython. From b384681d91babb93cad9b78ce01c627f676e712b Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Mon, 21 Apr 2025 13:27:33 +0200 Subject: [PATCH 72/80] extmod: Make sure to include port manifest Fixes issue where mpremote could not access filesystem xor_run with emlearn_trees now tested working on RPI-Pico/RP2040 and ESP32_S3_GENERIC (M5Stack AtomSU3) --- src/manifest.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/manifest.py b/src/manifest.py index d79520e..85621ff 100644 --- a/src/manifest.py +++ b/src/manifest.py @@ -4,3 +4,5 @@ # Ref https://docs.micropython.org/en/latest/reference/manifest.html module("emlearn_trees.py", base_path='./emlearn_trees') module("emlearn_fft.py", base_path='./emlearn_fft') + +include("$(PORT_DIR)/boards/manifest.py") From ab1a0f70bacac50f55a81a5937b00ec7b76c0dd6 Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Mon, 21 Apr 2025 13:32:53 +0200 Subject: [PATCH 73/80] CI: Add ESP32 extmod build --- .github/workflows/build.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 8a675f9..6ae5de4 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -53,6 +53,10 @@ jobs: - name: Setup MicroPython ESP32 working-directory: micropython run: source tools/ci.sh && ci_esp32_idf_setup + - name: Build custom firmware with extmod, RP2 + run: | + make extmod PORT=esp32 BOARD=ESP32_GENERIC_S3 + make extmod PORT=esp32 BOARD=ESP32_GENERIC - name: Build module xtensawin run: source micropython/esp-idf/export.sh && pip install -r requirements.txt && make dist ARCH=xtensawin V=1 - name: Archive dist artifacts From 9789044ec9a164ac80db519a211ed534a26fe74c Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Mon, 21 Apr 2025 13:45:29 +0200 Subject: [PATCH 74/80] extmod: Create dedicated manifest for Unix Unix does not have manifest at PORT level, which is different from the hardware ones... --- Makefile | 13 ++++++++++--- src/manifest_unix.py | 8 ++++++++ 2 files changed, 18 insertions(+), 3 deletions(-) create mode 100644 src/manifest_unix.py diff --git a/Makefile b/Makefile index bfa3990..0b6ff72 100644 --- a/Makefile +++ b/Makefile @@ -4,15 +4,22 @@ MPY_ABI_VERSION ?= 6.3 MPY_DIR ?= ../micropython MICROPYTHON_BIN ?= micropython +# extmod settings +PORT=unix +BOARD=ESP32_GENERIC_S3 + VERSION := $(shell git describe --tags --always) MPY_DIR_ABS = $(abspath $(MPY_DIR)) C_MODULES_SRC_PATH = $(abspath ./src) -MANIFEST_PATH = $(abspath ./src/manifest.py) -PORT=unix -BOARD=ESP32_GENERIC_S3 +ifeq ($(PORT),unix) + MANIFEST_PATH=$(abspath ./src/manifest_unix.py) +else + MANIFEST_PATH=$(abspath ./src/manifest.py) +endif + MODULES_PATH = ./dist/$(ARCH)_$(MPY_ABI_VERSION) PORT_DIR = ./dist/ports/$(PORT) diff --git a/src/manifest_unix.py b/src/manifest_unix.py new file mode 100644 index 0000000..2a52a37 --- /dev/null +++ b/src/manifest_unix.py @@ -0,0 +1,8 @@ + +# Manifest is used to include .py files for external C module build +# NOTE: this is a different mechanism than +# Ref https://docs.micropython.org/en/latest/reference/manifest.html +module("emlearn_trees.py", base_path='./emlearn_trees') +module("emlearn_fft.py", base_path='./emlearn_fft') + +#include("$(PORT_DIR)/boards/manifest.py") From 89a71a971630b18456c5884ddf650d5d92e4f085 Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Mon, 21 Apr 2025 14:16:13 +0200 Subject: [PATCH 75/80] CI: Source esp-idf before building ESP32 extmod --- .github/workflows/build.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 6ae5de4..4c42e68 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -55,6 +55,7 @@ jobs: run: source tools/ci.sh && ci_esp32_idf_setup - name: Build custom firmware with extmod, RP2 run: | + source micropython/esp-idf/export.sh && pip install -r requirements.txt make extmod PORT=esp32 BOARD=ESP32_GENERIC_S3 make extmod PORT=esp32 BOARD=ESP32_GENERIC - name: Build module xtensawin From 39801dfe49b7f80cb326f22dcc9744cea4d0a0a1 Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Mon, 21 Apr 2025 14:58:21 +0200 Subject: [PATCH 76/80] fft: Enable extmod for cmake ports --- src/emlearn_fft/fft.c | 3 ++- src/emlearn_fft/micropython.cmake | 17 +++++++++++++++++ src/micropython.cmake | 1 + 3 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 src/emlearn_fft/micropython.cmake diff --git a/src/emlearn_fft/fft.c b/src/emlearn_fft/fft.c index e5f42ab..ca2a13c 100644 --- a/src/emlearn_fft/fft.c +++ b/src/emlearn_fft/fft.c @@ -11,6 +11,7 @@ #define DEBUG (0) +#ifdef MICROPY_ENABLE_DYNRUNTIME // memset is used by some standard C constructs #if !defined(__linux__) void *memcpy(void *dst, const void *src, size_t n) { @@ -20,7 +21,7 @@ void *memset(void *s, int c, size_t n) { return mp_fun_table.memset_(s, c, n); } #endif - +#endif // Copy of eml_fft.h, without eml_fft_fill // - contains sin/cos that trips up mpy_ld.py (even if the function is not used) diff --git a/src/emlearn_fft/micropython.cmake b/src/emlearn_fft/micropython.cmake new file mode 100644 index 0000000..404c559 --- /dev/null +++ b/src/emlearn_fft/micropython.cmake @@ -0,0 +1,17 @@ +add_library(usermod_emlearn_fft INTERFACE) + +execute_process( + COMMAND python3 -c "import emlearn; print(emlearn.includedir)" + OUTPUT_VARIABLE EMLEARN_DIR + OUTPUT_STRIP_TRAILING_WHITESPACE +) + +target_sources(usermod_emlearn_fft INTERFACE + ${CMAKE_CURRENT_LIST_DIR}/fft.c +) + +target_include_directories(usermod_emlearn_fft INTERFACE + ${EMLEARN_DIR} +) + +target_link_libraries(usermod INTERFACE usermod_emlearn_fft) diff --git a/src/micropython.cmake b/src/micropython.cmake index a159e67..cb00382 100644 --- a/src/micropython.cmake +++ b/src/micropython.cmake @@ -1,2 +1,3 @@ include(${CMAKE_CURRENT_LIST_DIR}/emlearn_trees/micropython.cmake) +include(${CMAKE_CURRENT_LIST_DIR}/emlearn_fft/micropython.cmake) From d53b5639ef4dec8457a213a3d2813faca86ab338 Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Mon, 21 Apr 2025 16:17:35 +0200 Subject: [PATCH 77/80] iir: Enable cmake extmod build --- src/emlearn_iir/iir_filter.c | 3 ++- src/emlearn_iir/micropython.cmake | 17 +++++++++++++++++ src/micropython.cmake | 1 + 3 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 src/emlearn_iir/micropython.cmake diff --git a/src/emlearn_iir/iir_filter.c b/src/emlearn_iir/iir_filter.c index 0434ea6..bf67f00 100644 --- a/src/emlearn_iir/iir_filter.c +++ b/src/emlearn_iir/iir_filter.c @@ -9,6 +9,7 @@ #include +#ifdef MICROPY_ENABLE_DYNRUNTIME // memset is used by some standard C constructs #if !defined(__linux__) void *memcpy(void *dst, const void *src, size_t n) { @@ -24,7 +25,7 @@ void NORETURN abort() { } } #endif - +#endif // MicroPython type for EmlIIR diff --git a/src/emlearn_iir/micropython.cmake b/src/emlearn_iir/micropython.cmake new file mode 100644 index 0000000..9b49e71 --- /dev/null +++ b/src/emlearn_iir/micropython.cmake @@ -0,0 +1,17 @@ +add_library(usermod_emlearn_iir INTERFACE) + +execute_process( + COMMAND python3 -c "import emlearn; print(emlearn.includedir)" + OUTPUT_VARIABLE EMLEARN_DIR + OUTPUT_STRIP_TRAILING_WHITESPACE +) + +target_sources(usermod_emlearn_iir INTERFACE + ${CMAKE_CURRENT_LIST_DIR}/iir_filter.c +) + +target_include_directories(usermod_emlearn_iir INTERFACE + ${EMLEARN_DIR} +) + +target_link_libraries(usermod INTERFACE usermod_emlearn_iir) diff --git a/src/micropython.cmake b/src/micropython.cmake index cb00382..3480c05 100644 --- a/src/micropython.cmake +++ b/src/micropython.cmake @@ -1,3 +1,4 @@ include(${CMAKE_CURRENT_LIST_DIR}/emlearn_trees/micropython.cmake) include(${CMAKE_CURRENT_LIST_DIR}/emlearn_fft/micropython.cmake) +include(${CMAKE_CURRENT_LIST_DIR}/emlearn_iir/micropython.cmake) From cc6036b437ce02bb57d2818fee1fcf91a2fb1889 Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Mon, 21 Apr 2025 16:23:12 +0200 Subject: [PATCH 78/80] arrayutils: Enable extmod cmake build --- src/emlearn_arrayutils/micropython.cmake | 17 +++++++++++++++++ src/emlearn_arrayutils/modarrayutils.c | 3 ++- src/micropython.cmake | 1 + 3 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 src/emlearn_arrayutils/micropython.cmake diff --git a/src/emlearn_arrayutils/micropython.cmake b/src/emlearn_arrayutils/micropython.cmake new file mode 100644 index 0000000..8d76791 --- /dev/null +++ b/src/emlearn_arrayutils/micropython.cmake @@ -0,0 +1,17 @@ +add_library(usermod_emlearn_arrayutils INTERFACE) + +execute_process( + COMMAND python3 -c "import emlearn; print(emlearn.includedir)" + OUTPUT_VARIABLE EMLEARN_DIR + OUTPUT_STRIP_TRAILING_WHITESPACE +) + +target_sources(usermod_emlearn_arrayutils INTERFACE + ${CMAKE_CURRENT_LIST_DIR}/modarrayutils.c +) + +target_include_directories(usermod_emlearn_arrayutils INTERFACE + ${EMLEARN_DIR} +) + +target_link_libraries(usermod INTERFACE usermod_emlearn_arrayutils) diff --git a/src/emlearn_arrayutils/modarrayutils.c b/src/emlearn_arrayutils/modarrayutils.c index 55bde77..b472ffa 100644 --- a/src/emlearn_arrayutils/modarrayutils.c +++ b/src/emlearn_arrayutils/modarrayutils.c @@ -7,6 +7,7 @@ #include +#ifdef MICROPY_ENABLE_DYNRUNTIME // memset is used by some standard C constructs #if !defined(__linux__) void *memcpy(void *dst, const void *src, size_t n) { @@ -16,7 +17,7 @@ void *memset(void *s, int c, size_t n) { return mp_fun_table.memset_(s, c, n); } #endif - +#endif #define MAP_LINEAR(x, in_min, in_max, out_min, out_max) do { \ diff --git a/src/micropython.cmake b/src/micropython.cmake index 3480c05..d974464 100644 --- a/src/micropython.cmake +++ b/src/micropython.cmake @@ -2,3 +2,4 @@ include(${CMAKE_CURRENT_LIST_DIR}/emlearn_trees/micropython.cmake) include(${CMAKE_CURRENT_LIST_DIR}/emlearn_fft/micropython.cmake) include(${CMAKE_CURRENT_LIST_DIR}/emlearn_iir/micropython.cmake) +include(${CMAKE_CURRENT_LIST_DIR}/emlearn_arrayutils/micropython.cmake) From a45e599f4202a1e8a317aca13b26fbb7be6eabfd Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Mon, 21 Apr 2025 17:28:26 +0200 Subject: [PATCH 79/80] cnn: Enable extmod build on cmake --- src/micropython.cmake | 1 + src/tinymaix_cnn/micropython.cmake | 22 ++++++++++++++++++++++ src/tinymaix_cnn/mod_cnn.c | 2 ++ 3 files changed, 25 insertions(+) create mode 100644 src/tinymaix_cnn/micropython.cmake diff --git a/src/micropython.cmake b/src/micropython.cmake index d974464..4a72323 100644 --- a/src/micropython.cmake +++ b/src/micropython.cmake @@ -3,3 +3,4 @@ include(${CMAKE_CURRENT_LIST_DIR}/emlearn_trees/micropython.cmake) include(${CMAKE_CURRENT_LIST_DIR}/emlearn_fft/micropython.cmake) include(${CMAKE_CURRENT_LIST_DIR}/emlearn_iir/micropython.cmake) include(${CMAKE_CURRENT_LIST_DIR}/emlearn_arrayutils/micropython.cmake) +include(${CMAKE_CURRENT_LIST_DIR}/tinymaix_cnn/micropython.cmake) diff --git a/src/tinymaix_cnn/micropython.cmake b/src/tinymaix_cnn/micropython.cmake new file mode 100644 index 0000000..9b80e78 --- /dev/null +++ b/src/tinymaix_cnn/micropython.cmake @@ -0,0 +1,22 @@ +add_library(usermod_cnn INTERFACE) + +execute_process( + COMMAND python3 -c "import emlearn; print(emlearn.includedir)" + OUTPUT_VARIABLE EMLEARN_DIR + OUTPUT_STRIP_TRAILING_WHITESPACE +) + +target_sources(usermod_cnn INTERFACE + ${CMAKE_CURRENT_LIST_DIR}/mod_cnn.c +) + +target_include_directories(usermod_cnn INTERFACE + ${CMAKE_CURRENT_LIST_DIR}/int8 + ${CMAKE_CURRENT_LIST_DIR}/../../dependencies/TinyMaix/include + ${CMAKE_CURRENT_LIST_DIR}/../../dependencies/TinyMaix/src +) + +target_compile_options(usermod_cnn INTERFACE + -Wno-error=unused-variable -Wno-error=multichar -Wdouble-promotion +) +target_link_libraries(usermod INTERFACE usermod_cnn) diff --git a/src/tinymaix_cnn/mod_cnn.c b/src/tinymaix_cnn/mod_cnn.c index 83ad069..adee99f 100644 --- a/src/tinymaix_cnn/mod_cnn.c +++ b/src/tinymaix_cnn/mod_cnn.c @@ -23,6 +23,7 @@ void mod_cnn_free(void *ptr); #define DEBUG (0) +#ifdef MICROPY_ENABLE_DYNRUNTIME // memset is used by some standard C constructs #if !defined(__linux__) void *memcpy(void *dst, const void *src, size_t n) { @@ -32,6 +33,7 @@ void *memset(void *s, int c, size_t n) { return mp_fun_table.memset_(s, c, n); } #endif +#endif // get model output shapes //mdl: model handle; in: input mat; out: output mat From 4c8991b382b65d176d8e06a5e87295c4365c1a82 Mon Sep 17 00:00:00 2001 From: Jon Nordby Date: Mon, 21 Apr 2025 18:01:16 +0200 Subject: [PATCH 80/80] docs: Move more into Sphinx, add external modules, mention MicroPython 1.25 --- README.md | 139 ++++++++------------------------------ docs/external_modules.rst | 80 ++++++++++++++++++++++ docs/more.rst | 40 +++++++++++ docs/native_modules.rst | 109 ++++++++++++++++++++++++++++++ docs/support.rst | 37 ++++++++++ docs/user_guide.rst | 3 + 6 files changed, 296 insertions(+), 112 deletions(-) create mode 100644 docs/external_modules.rst create mode 100644 docs/native_modules.rst create mode 100644 docs/support.rst diff --git a/README.md b/README.md index 8d5d6f1..0237bd2 100644 --- a/README.md +++ b/README.md @@ -17,9 +17,11 @@ Builds on [emlearn](https://emlearn.org), a C99 library for Machine Learning on ## Status **Minimally useful** -- Tested *working* on `x64` (Unix port) and `xtensawin` (ESP32/ESP32-S3/etc). -- Currently not supported: [armv6m/Cortex-M0/RP2040](https://github.com/emlearn/emlearn-micropython/issues/19) -and [RISC-V/ESP32-C3/ESP32-C6](https://github.com/emlearn/emlearn-micropython/issues/35) +- Initial set of Machine Learning and Digital Signal Processing modules available, including example projects. +- Supports most MicroPython ports using runtime installable native modules +- Primarily tested on `x64` (Unix port) and `xtensawin` (ESP32/ESP32-S3/etc). +- Some devices only supported using external, notably armv6m/Cortex-M0/RP2040 +- Not yet supported: [RISC-V/ESP32-C3/ESP32-C6](https://github.com/emlearn/emlearn-micropython/issues/35) ## Features @@ -35,7 +37,7 @@ and [RISC-V/ESP32-C3/ESP32-C6](https://github.com/emlearn/emlearn-micropython/is - Operates on standard `array.array` data structures - Models can be loaded at runtime from a file in disk/flash - Highly efficient. Inference times down to 100 microseconds, RAM usage <2 kB, FLASH usage <2 kB -- Pre-built binaries available for most architectures. +- Pre-built native modules available for most common architectures `xtensawin`. ## Examples @@ -46,109 +48,36 @@ and [RISC-V/ESP32-C3/ESP32-C6](https://github.com/emlearn/emlearn-micropython/is ## Documentation -Complete [documentation on ReadTheDocs](https://emlearn-micropython.readthedocs.io/en/latest/user_guide.html). +Complete usage [documentation on ReadTheDocs](https://emlearn-micropython.readthedocs.io/en/latest/user_guide.html). -## Prerequisites -Minimally you will need - -- Python 3.10+ on host -- MicroPython 1.24+ running onto your device +## Citations -#### Download repository +If you use `emlearn-micropython` in an academic work, please reference it using: -Download the repository with examples etc -``` -git clone https://github.com/emlearn/emlearn-micropython +```tex +@misc{emlearn_micropython, + author = {Jon Nordby}, + title = {{emlearn-micropython: Efficient Machine Learning engine for MicroPython}}, + month = aug, + year = 2023, + doi = {10.5281/zenodo.8212731}, + url = {https://doi.org/10.5281/zenodo.8212731} +} ``` -## Usage - -Start with the instructions in [XOR example](./examples/xor_trees/). - - -## Supported versions - -At any given point in time, emlearn-micropython only provides pre-built binaries for one MicroPython version. -In general we strongly encourage people to use the latest version. -There are no long-term-support or bugfix versions, at this point. -If you build from source, the current version of emlearn-micropython might also work on a couple of MicroPython versions around the time, but this is not guaranteed. - -| MicroPython | emlearn-micropython | -|------------------| ------------------ | -| 1.24.x | master | -| 1.24.x | 0.7.0 | -| 1.23.x | 0.6.0 | - -#### Find architecture and .mpy version - -The correct .mpy files to use depend on the CPU architecture of your microcontroller, -as well as the MicroPython version. - -| MicroPython version | .mpy version | -|---------------------| ------------- | -| 1.23.x | 6.3 | -| 1.24.x | 6.3 | - - -Identify which CPU architecture your device uses. -You need to specify `ARCH` to install the correct module version. - -| ARCH | Description | Examples | -|---------------|-----------------------------------|---------------------- | -| x64 | x86 64 bit | PC | -| x86 | x86 32 bit | | -| armv6m | ARM Thumb (1) | Cortex-M0 | -| armv7m | ARM Thumb 2 | Cortex-M3 | -| armv7emsp | ARM Thumb 2, single float | Cortex-M4F, Cortex-M7 | -| armv7emdp | ARM Thumb 2, double floats | Cortex-M7 | -| xtensa | non-windowed | ESP8266 | -| xtensawin | windowed with window size 8 | ESP32 | - -Information is also available in the official documentation: -[MicroPython: .mpy files](https://docs.micropython.org/en/latest/reference/mpyfiles.html#versioning-and-compatibility-of-mpy-files) -## More learning resources +## Developing -emlearn-micropython and emlearn has been covered in the following presentations. +For those that wish to hack on emlearn-micropython itself. -- Microcontrollers + Machine Learning in 1-2-3 (PyData Global 2024). -[Slides etc](https://github.com/jonnor/embeddedml/tree/master/presentations/PyDataGlobal2024) -- Sensor data processing on microcontrollers with MicroPython and emlearn (PyConZA 2024). -[Slides etc](https://github.com/jonnor/embeddedml/tree/master/presentations/PyConZA2024) -- 6 years of open source TinyML with emlearn - a scikit-learn for microcontrollers (TinyML EMEA 2024) -[YouTube video](https://www.youtube.com/watch?v=oG7PjPMA3Is) | -[Slides etc](https://github.com/jonnor/embeddedml/tree/master/presentations/TinymlEMEA2024) -- emlearn - Machine Learning for Tiny Embedded Systems (Embedded Online Conference 2024). -[Youtube video](https://www.youtube.com/watch?v=qamVWmcBdmI) | -[Slides etc](https://github.com/jonnor/embeddedml/tree/master/presentations/EmbeddedOnlineConference2024) -- Machine Learning on microcontrollers using MicroPython and emlearn (PyCon DE & PyData Berlin 2024). -[Slides etc](https://github.com/jonnor/embeddedml/tree/master/presentations/PyDataBerlin2024) | -[YouTube video](https://www.youtube.com/watch?v=_MGm8sctqjg&t=1311s&pp=ygUSZW1sZWFybiBtaWNyb3B5dGhv). +#### Download the code -Here is an overview of resources for [TinyML in general](https://tinyml.seas.harvard.edu/courses/). - -## Benchmarks - -#### UCI handwriting digits - -UCI ML hand-written digits datasets dataset from -[sklearn.datasets.load_digits](https://scikit-learn.org/stable/modules/generated/sklearn.datasets.load_digits.html). -8x8 image, 64 features. Values are 4-bit integers (16 levels). 10 classes. - -Running with a very simple RandomForest, 7 trees. -Reaches approx 86% accuracy. -Tested on Raspberry PI Pico, with RP2040 microcontroller (ARM Cortex M0 @ 133 MHz). - -![Inferences per second](./benchmarks/digits_bench.png) - -NOTE: over half of the time for emlearn case, -is spent on converting the Python lists of integers into a float array. -Removing that bottleneck would speed up things considerably. - - -## Developing locally +Clone the repository using git +``` +git clone https://github.com/emlearn/emlearn-micropython +``` #### Prerequisites These come in addition to the prequisites described above. @@ -159,13 +88,13 @@ See [MicroPython: Building native modules](https://docs.micropython.org/en/lates We assume that micropython is installed in the same place as this repository. If using another location, adjust `MPY_DIR` accordingly. -You should be using MicroPython 1.24 (or newer). +You should be using MicroPython 1.25 (or newer). #### Build Build the .mpy native module ``` -make dist ARCH=armv6m MPY_DIR=../micropython +make dist ARCH=xtensawin MPY_DIR=../micropython ``` Install it on device @@ -180,18 +109,4 @@ To build and run tests on host make check ``` -## Citations - -If you use `emlearn-micropython` in an academic work, please reference it using: - -```tex -@misc{emlearn_micropython, - author = {Jon Nordby}, - title = {{emlearn-micropython: Efficient Machine Learning engine for MicroPython}}, - month = aug, - year = 2023, - doi = {10.5281/zenodo.8212731}, - url = {https://doi.org/10.5281/zenodo.8212731} -} -``` diff --git a/docs/external_modules.rst b/docs/external_modules.rst new file mode 100644 index 0000000..dd5ba07 --- /dev/null +++ b/docs/external_modules.rst @@ -0,0 +1,80 @@ + +.. Places parent toc into the sidebar +:parenttoc: True + +.. _external_modules: + +=============================== +External modules +=============================== + +An alternative, much simpler, installation method is as :ref:`native_modules` (recommended). + +This method is more complicated, as it requires building MicroPython from scratch. +Therefore it is only recommended for 1) platforms which do not support native modules, +and 2) advanced users. + +Supported versions +=========================== + +For general information about supported MicroPython versions, see :ref:`support`. +In principle, external modules should work on any hardware/port of MicroPython. + + +Prerequisites +=========================== + +External modules are included into the build process of MicroPython itself. +That means that you must have a MicroPython build environment set up, +including neccesary build toolchains et.c. +This process is specific for each MicroPython port. +Refer to the relevant port/X/README.md in the `micropython git repository `_ for the setup. + +Always make sure that you can succesfully build and run the vanilla firmware +(no external modules), before you add in emlearn-micropython. + +You need a git checkout of emlearn-micropython. +For example as a git submodule in your project. + +:: + git clone https://github.com/emlearn/emlearn-micropython.git + + +Include external modules in build +=================================== + +emlearn-micropython contains both C modules, as well as Python modules. +Therefore it is neccesary to use both the options *USER_C_MODULES* and *FROZEN_MANIFEST*. + +For details on this process, see official MicroPython documentation on +`building with external modules `_. + + +Example build +---------------- + +For platforms that use a cmake-based build system. + +:: + make USER_C_MODULES=./emlearn-micropython/src/micropython.cmake \ + FROZEN_MANIFEST=./emlearn-micropython/src/manifest.py \ + CFLAGS_EXTRA='-Wno-unused-function -Wno-unused-function' + + +For platforms that use a Makefile-based build. + +:: + + make USER_C_MODULES=./emlearn-micropython/src/ \ + FROZEN_MANIFEST=./emlearn-micropython/src/manifest.py \ + CFLAGS_EXTRA='-Wno-unused-function -Wno-unused-function' + + +Combining multiple modules +--------------------------- + +If you want to add more C modules and/or frozen manifest, +you will need to create your own `manifest.py` and `micropython.cmake` files. +Use the ones in emlearn-micropython as a starting point. + + diff --git a/docs/more.rst b/docs/more.rst index b8661c6..461cd92 100644 --- a/docs/more.rst +++ b/docs/more.rst @@ -24,6 +24,39 @@ This includes: Presentations ==================== +emlearn-micropython has been covered in the following presentations. + + +Microcontrollers + Machine Learning in 1-2-3 +------------------------------------------------------------------ + +Presented at PyData Global 2024, by Jon Nordby + +`Video recording (YouTube) `_. + +`Slides and notes `_. + + +MicroPython - Python for microcontrollers and embedded linux +------------------------------------------------------------------------- + +Presented at FOSDEM 2025, by Jon Nordby. +Has a section on emlearn-micropython. + +`Video recording (YouTube) `_. + +`Slides and notes `_. + + +Sensor data processing on microcontrollers with MicroPython and emlearn +------------------------------------------------------------------------- + +Presented at PyConZA 2024, by Jon Nordby. + +Video recording: coming... + +`Slides and notes `_. + Machine Learning on microcontrollers using MicroPython and emlearn ------------------------------------------------------------------ @@ -33,3 +66,10 @@ Presented at PyCon DE & PyData Berlin 2024, by Jon Nordby. `Video recording (YouTube) `_. `Slides and notes `_. + + +Other resources +==================== +Here is an overview of resources for `TinyML in general `_. + + diff --git a/docs/native_modules.rst b/docs/native_modules.rst new file mode 100644 index 0000000..0d23af2 --- /dev/null +++ b/docs/native_modules.rst @@ -0,0 +1,109 @@ + +.. Places parent toc into the sidebar +:parenttoc: True + +.. _native_modules: + +========================= +Native modules +========================= + +This is the recommended installation method on the majority of hardware platform, +especially when getting started. +An alternative, more complicated, installation method is documented in :ref:`external_modules`. + + +Supported versions +=========================== + +The correct .mpy files to use depends on the CPU architecture of your microcontroller, as well as the MicroPython version. +Information is also available in the official documentation: `MicroPython: .mpy files `_. + +The following are **supported** as native modules at this time: +``x64``, ``armv7m``, ``armv7emsp``, ``xtensawin``. + +The following are **not supported**: ``rv32imc``, ``armv6m``, ``xtensa``, ``x86``. +For these you will instead need to use :ref:`external_modules`. + +We hope to support ``arm6vm`` and ``rv32imc`` better in the future. +There are no plans to support ``xtensa`` (ESP8266) or ``x86`` as native modules, +as these are very old platforms. + + +Find .mpy ABI version +------------------------- + +The following is an overview of .mpy ABI version for MicroPython releases. + ++---------------------+---------------+ +| MicroPython version | .mpy version | ++=====================+===============+ +| 1.25.x | 6.3 | ++---------------------+---------------+ +| 1.24.x | 6.3 | ++---------------------+---------------+ +| 1.23.x | 6.3 | ++---------------------+---------------+ + + +Find .mpy architecture +------------------------- + +Identify which CPU architecture your device uses. +You need to specify `ARCH` to install the correct module version. + ++--------------+------------------------------------+------------------------+ +| ARCH | Description | Examples | ++==============+====================================+========================+ +| x64 | x86 64 bit | PC | ++--------------+------------------------------------+------------------------+ +| x86 | x86 32 bit | | ++--------------+------------------------------------+------------------------+ +| armv6m | ARM Thumb (1) | Cortex-M0 | ++--------------+------------------------------------+------------------------+ +| armv7m | ARM Thumb 2 | Cortex-M3 | ++--------------+------------------------------------+------------------------+ +| armv7emsp | ARM Thumb 2, single float | Cortex-M4F, Cortex-M7 | ++--------------+------------------------------------+------------------------+ +| armv7emdp | ARM Thumb 2, double floats | Cortex-M7 | ++--------------+------------------------------------+------------------------+ +| xtensa | non-windowed | ESP8266 | ++--------------+------------------------------------+------------------------+ +| xtensawin | windowed with window size 8 | ESP32, ESP32-S3 | ++--------------+------------------------------------+------------------------+ +| rv32imc | RISC-V | ESP32-C3, ESP32-C6 | ++--------------+------------------------------------+------------------------+ + + + +Prebuilt native modules +=========================== + +All the modules in emlearn-micropython for supported architectures are available pre-built. +You can `browse them on Github `_. +And they are available over HTTPS. +The directory structure is as follows: + +:: + + https://emlearn.github.io/emlearn-micropython/builds/$VERSION/$ARCH_$ABI/$MODULE.mpy + + where: + VERSION=master|0.8.0 + MODULE=emlearn_trees + ARCH=xtensawin + ABI=6.3 + + +Installing using mip +=========================== + +Native modules can be installed using the `mip package manager `_. + + +For example, to install ``emlearn_trees`` for MicroPython 1.25 (ABI 6.3) on ESP32 (xtensawin), use: + +:: + + mpremote mip install https://emlearn.github.io/emlearn-micropython/builds/master/xtensawin_6.3/emlearn_trees.mpy + diff --git a/docs/support.rst b/docs/support.rst new file mode 100644 index 0000000..e9b8c9c --- /dev/null +++ b/docs/support.rst @@ -0,0 +1,37 @@ + +.. Places parent toc into the sidebar +:parenttoc: True + +.. _support: + +=============================== +Supported versions +=============================== + + +Supported MicroPython versions +============================== + +At any given point in time, emlearn-micropython only supports one MicroPython version. +In general we strongly encourage people to use the latest version. +There are no long-term-support or bugfix versions of emlearn-micropython, at this point. + +================== ====================== +MicroPython emlearn-micropython +================== ====================== +1.25.x master +1.25.x 0.8.x +1.24.x 0.7.0 +1.23.x 0.6.0 +================== ====================== + +If you build emlearn-micropython from source, +it might also work on a couple of MicroPython versions around the time of the commit, but this is not guaranteed. + + +Supported hardware +=========================== + +Due to limitations in MicroPython support for C modules, +the hardware support is a bit different for :ref:`native_modules` (recommended) +versus :ref:`external_modules` (advanced). diff --git a/docs/user_guide.rst b/docs/user_guide.rst index 551dd96..6681c6d 100644 --- a/docs/user_guide.rst +++ b/docs/user_guide.rst @@ -16,4 +16,7 @@ User Guide getting_started_host.rst getting_started_device.rst + support.rst + native_modules.rst + external_modules.rst