diff --git a/APP_Framework/Applications/Kconfig b/APP_Framework/Applications/Kconfig index 7639f0449..04130d0ed 100644 --- a/APP_Framework/Applications/Kconfig +++ b/APP_Framework/Applications/Kconfig @@ -18,5 +18,5 @@ menu "Applications" source "$APP_DIR/Applications/control_app/Kconfig" source "$APP_DIR/Applications/knowing_app/Kconfig" source "$APP_DIR/Applications/sensor_app/Kconfig" - + source "$APP_DIR/Applications/embedded_database_app/Kconfig" endmenu diff --git a/APP_Framework/Applications/embedded_database_app/Kconfig b/APP_Framework/Applications/embedded_database_app/Kconfig new file mode 100644 index 000000000..271aa0165 --- /dev/null +++ b/APP_Framework/Applications/embedded_database_app/Kconfig @@ -0,0 +1,6 @@ +menuconfig USING_EMBEDDED_DATABASE_APP + bool "embedded database app" + default n +if USING_EMBEDDED_DATABASE_APP + source "$APP_DIR/Applications/embedded_database_app/flashdb_app/Kconfig" +endif diff --git a/APP_Framework/Applications/embedded_database_app/SConscript b/APP_Framework/Applications/embedded_database_app/SConscript new file mode 100644 index 000000000..f307e3f70 --- /dev/null +++ b/APP_Framework/Applications/embedded_database_app/SConscript @@ -0,0 +1,14 @@ +import os +Import('RTT_ROOT') +from building import * + +cwd = GetCurrentDir() +objs = [] +list = os.listdir(cwd) + +for d in list: + path = os.path.join(cwd, d) + if os.path.isfile(os.path.join(path, 'SConscript')): + objs = objs + SConscript(os.path.join(path, 'SConscript')) + +Return('objs') diff --git a/APP_Framework/Applications/embedded_database_app/flashdb_app/Kconfig b/APP_Framework/Applications/embedded_database_app/flashdb_app/Kconfig new file mode 100644 index 000000000..ae7bfb6a2 --- /dev/null +++ b/APP_Framework/Applications/embedded_database_app/flashdb_app/Kconfig @@ -0,0 +1,5 @@ +config EMBEDDED_DATABASE_FLASHDB_APP + bool "embedded database apps/flashdb(example)" + select USING_EMBEDDED_DATABASE + select USING_EMBEDDED_DATABASE_FLASHDB + default n diff --git a/APP_Framework/Applications/embedded_database_app/flashdb_app/SConscript b/APP_Framework/Applications/embedded_database_app/flashdb_app/SConscript new file mode 100644 index 000000000..5929b2676 --- /dev/null +++ b/APP_Framework/Applications/embedded_database_app/flashdb_app/SConscript @@ -0,0 +1,9 @@ +from building import * + +cwd = GetCurrentDir() +src = Glob('*.c') + Glob('*.cpp') +CPPPATH = [cwd] + +group = DefineGroup('flashdb(example)', src, depend = ['EMBEDDED_DATABASE_FLASHDB_APP'], LOCAL_CPPPATH = CPPPATH) + +Return('group') diff --git a/APP_Framework/Applications/embedded_database_app/flashdb_app/flashdb_example.c b/APP_Framework/Applications/embedded_database_app/flashdb_app/flashdb_example.c new file mode 100644 index 000000000..7c89822a3 --- /dev/null +++ b/APP_Framework/Applications/embedded_database_app/flashdb_app/flashdb_example.c @@ -0,0 +1,129 @@ +#include +#include +#define FDB_LOG_TAG "[flashdb_app]" +static pthread_mutex_t kv_locker, ts_locker; +static uint32_t boot_count = 0; +static time_t boot_time[10] = {0, 1, 2, 3}; +/* default KV nodes */ +static struct fdb_default_kv_node default_kv_table[] = { + {"username", "armink", 0}, /* string KV */ + {"password", "123456", 0}, /* string KV */ + {"boot_count", &boot_count, sizeof(boot_count)}, /* int type KV */ + {"boot_time", &boot_time, sizeof(boot_time)}, /* int array type KV */ +}; +/* KVDB object */ +static struct fdb_kvdb kvdb = { 0 }; +/* TSDB object */ +struct fdb_tsdb tsdb = { 0 }; +/* counts for simulated timestamp */ +static int counts = 0; + +extern void kvdb_basic_sample(fdb_kvdb_t kvdb); +extern void kvdb_type_string_sample(fdb_kvdb_t kvdb); +extern void kvdb_type_blob_sample(fdb_kvdb_t kvdb); +extern void tsdb_sample(fdb_tsdb_t tsdb); + +static void lock(fdb_db_t db) +{ + pthread_mutex_lock((pthread_mutex_t *)db->user_data); +} + +static void unlock(fdb_db_t db) +{ + pthread_mutex_unlock((pthread_mutex_t *)db->user_data); +} + +static fdb_time_t get_time(void) +{ + return time(NULL); +} + +int flashdb_app(void) +{ + fdb_err_t result; + bool file_mode = true; + uint32_t sec_size = 4096, db_size = sec_size * 4; + +#ifdef FDB_USING_KVDB + { /* KVDB Sample */ + struct fdb_default_kv default_kv; + + default_kv.kvs = default_kv_table; + default_kv.num = sizeof(default_kv_table) / sizeof(default_kv_table[0]); + /* set the lock and unlock function if you want */ + pthread_mutex_init(&kv_locker, NULL); + fdb_kvdb_control(&kvdb, FDB_KVDB_CTRL_SET_LOCK, (void *)lock); + fdb_kvdb_control(&kvdb, FDB_KVDB_CTRL_SET_UNLOCK, (void *)unlock); + /* set the sector and database max size */ + fdb_kvdb_control(&kvdb, FDB_KVDB_CTRL_SET_SEC_SIZE, &sec_size); + fdb_kvdb_control(&kvdb, FDB_KVDB_CTRL_SET_MAX_SIZE, &db_size); + /* enable file mode */ + fdb_kvdb_control(&kvdb, FDB_KVDB_CTRL_SET_FILE_MODE, &file_mode); + /* create database directory */ + mkdir("fdb_kvdb1", 0777); + /* Key-Value database initialization + * + * &kvdb: database object + * "env": database name + * "fdb_kvdb1": The flash partition name base on FAL. Please make sure it's in FAL partition table. + * Please change to YOUR partition name. + * &default_kv: The default KV nodes. It will auto add to KVDB when first initialize successfully. + * &kv_locker: The locker object. + */ + result = fdb_kvdb_init(&kvdb, "env", "fdb_kvdb1", &default_kv, &kv_locker); + + if (result != FDB_NO_ERR) { + return -1; + } + + /* run basic KV samples */ + kvdb_basic_sample(&kvdb); + /* run string KV samples */ + kvdb_type_string_sample(&kvdb); + /* run blob KV samples */ + kvdb_type_blob_sample(&kvdb); + } +#endif /* FDB_USING_KVDB */ + +#ifdef FDB_USING_TSDB + { /* TSDB Sample */ + /* set the lock and unlock function if you want */ + pthread_mutex_init(&ts_locker, NULL); + fdb_tsdb_control(&tsdb, FDB_TSDB_CTRL_SET_LOCK, (void *)lock); + fdb_tsdb_control(&tsdb, FDB_TSDB_CTRL_SET_UNLOCK, (void *)unlock); + /* set the sector and database max size */ + fdb_tsdb_control(&tsdb, FDB_TSDB_CTRL_SET_SEC_SIZE, &sec_size); + fdb_tsdb_control(&tsdb, FDB_TSDB_CTRL_SET_MAX_SIZE, &db_size); + /* enable file mode */ + fdb_tsdb_control(&tsdb, FDB_TSDB_CTRL_SET_FILE_MODE, &file_mode); + /* create database directory */ + mkdir("fdb_tsdb1", 0777); + /* Time series database initialization + * + * &tsdb: database object + * "log": database name + * "fdb_tsdb1": The flash partition name base on FAL. Please make sure it's in FAL partition table. + * Please change to YOUR partition name. + * get_time: The get current timestamp function. + * 128: maximum length of each log + * ts_locker: The locker object. + */ + result = fdb_tsdb_init(&tsdb, "log", "fdb_tsdb1", get_time, 128, &ts_locker); + /* read last saved time for simulated timestamp */ + fdb_tsdb_control(&tsdb, FDB_TSDB_CTRL_GET_LAST_TIME, &counts); + + if (result != FDB_NO_ERR) { + return -1; + } + + /* run TSDB sample */ + tsdb_sample(&tsdb); + } +#endif /* FDB_USING_TSDB */ + + return 0; +} + +#ifdef __RT_THREAD_H__ +MSH_CMD_EXPORT(flashdb_app, flashdb test); +#endif diff --git a/APP_Framework/Applications/embedded_database_app/flashdb_app/kvdb_basic_sample.c b/APP_Framework/Applications/embedded_database_app/flashdb_app/kvdb_basic_sample.c new file mode 100644 index 000000000..01e1c0f79 --- /dev/null +++ b/APP_Framework/Applications/embedded_database_app/flashdb_app/kvdb_basic_sample.c @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2020, Armink, + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief basic KV samples. + * + * basic Key-Value Database KV feature samples + * get and show currnet boot counts + */ + +#include + +#ifdef FDB_USING_KVDB + +#define FDB_LOG_TAG "[sample][kvdb][basic]" + +void kvdb_basic_sample(fdb_kvdb_t kvdb) +{ + struct fdb_blob blob; + int boot_count = 0; + + FDB_INFO("==================== kvdb_basic_sample ====================\n"); + + { /* GET the KV value */ + /* get the "boot_count" KV value */ + fdb_kv_get_blob(kvdb, "boot_count", fdb_blob_make(&blob, &boot_count, sizeof(boot_count))); + /* the blob.saved.len is more than 0 when get the value successful */ + if (blob.saved.len > 0) { + FDB_INFO("get the 'boot_count' value is %d\n", boot_count); + } else { + FDB_INFO("get the 'boot_count' failed\n"); + } + } + + { /* CHANGE the KV value */ + /* increase the boot count */ + boot_count ++; + /* change the "boot_count" KV's value */ + fdb_kv_set_blob(kvdb, "boot_count", fdb_blob_make(&blob, &boot_count, sizeof(boot_count))); + FDB_INFO("set the 'boot_count' value to %d\n", boot_count); + } + + FDB_INFO("===========================================================\n"); +} + +#endif /* FDB_USING_KVDB */ diff --git a/APP_Framework/Applications/embedded_database_app/flashdb_app/kvdb_type_blob_sample.c b/APP_Framework/Applications/embedded_database_app/flashdb_app/kvdb_type_blob_sample.c new file mode 100644 index 000000000..f37983c91 --- /dev/null +++ b/APP_Framework/Applications/embedded_database_app/flashdb_app/kvdb_type_blob_sample.c @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2020, Armink, + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief blob KV samples. + * + * Key-Value Database blob type KV feature samples + */ + +#include + +#ifdef FDB_USING_KVDB + +#define FDB_LOG_TAG "[sample][kvdb][blob]" + +void kvdb_type_blob_sample(fdb_kvdb_t kvdb) +{ + struct fdb_blob blob; + + FDB_INFO("==================== kvdb_type_blob_sample ====================\n"); + + { /* CREATE new Key-Value */ + int temp_data = 36; + + /* It will create new KV node when "temp" KV not in database. + * fdb_blob_make: It's a blob make function, and it will return the blob when make finish. + */ + fdb_kv_set_blob(kvdb, "temp", fdb_blob_make(&blob, &temp_data, sizeof(temp_data))); + FDB_INFO("create the 'temp' blob KV, value is: %d\n", temp_data); + } + + { /* GET the KV value */ + int temp_data = 0; + + /* get the "temp" KV value */ + fdb_kv_get_blob(kvdb, "temp", fdb_blob_make(&blob, &temp_data, sizeof(temp_data))); + /* the blob.saved.len is more than 0 when get the value successful */ + if (blob.saved.len > 0) { + FDB_INFO("get the 'temp' value is: %d\n", temp_data); + } + } + + { /* CHANGE the KV value */ + int temp_data = 38; + + /* change the "temp" KV's value to 38 */ + fdb_kv_set_blob(kvdb, "temp", fdb_blob_make(&blob, &temp_data, sizeof(temp_data))); + FDB_INFO("set 'temp' value to %d\n", temp_data); + } + + { /* DELETE the KV by name */ + fdb_kv_del(kvdb, "temp"); + FDB_INFO("delete the 'temp' finish\n"); + } + + FDB_INFO("===========================================================\n"); +} + +#endif /* FDB_USING_KVDB */ diff --git a/APP_Framework/Applications/embedded_database_app/flashdb_app/kvdb_type_string_sample.c b/APP_Framework/Applications/embedded_database_app/flashdb_app/kvdb_type_string_sample.c new file mode 100644 index 000000000..90e6598fe --- /dev/null +++ b/APP_Framework/Applications/embedded_database_app/flashdb_app/kvdb_type_string_sample.c @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2020, Armink, + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief string KV samples. + * + * Key-Value Database string type KV feature samples source file. + */ + +#include +#include + +#ifdef FDB_USING_KVDB + +#define FDB_LOG_TAG "[sample][kvdb][string]" + +void kvdb_type_string_sample(fdb_kvdb_t kvdb) +{ + FDB_INFO("==================== kvdb_type_string_sample ====================\n"); + + { /* CREATE new Key-Value */ + char temp_data[10] = "36C"; + + /* It will create new KV node when "temp" KV not in database. */ + fdb_kv_set(kvdb, "temp", temp_data); + FDB_INFO("create the 'temp' string KV, value is: %s\n", temp_data); + } + + { /* GET the KV value */ + char *return_value, temp_data[10] = { 0 }; + + /* Get the "temp" KV value. + * NOTE: The return value saved in fdb_kv_get's buffer. Please copy away as soon as possible. + */ + return_value = fdb_kv_get(kvdb, "temp"); + /* the return value is NULL when get the value failed */ + if (return_value != NULL) { + strncpy(temp_data, return_value, sizeof(temp_data)); + FDB_INFO("get the 'temp' value is: %s\n", temp_data); + } + } + + { /* CHANGE the KV value */ + char temp_data[10] = "38C"; + + /* change the "temp" KV's value to "38.1" */ + fdb_kv_set(kvdb, "temp", temp_data); + FDB_INFO("set 'temp' value to %s\n", temp_data); + } + + { /* DELETE the KV by name */ + fdb_kv_del(kvdb, "temp"); + FDB_INFO("delete the 'temp' finish\n"); + } + + FDB_INFO("===========================================================\n"); +} + +#endif /* FDB_USING_KVDB */ diff --git a/APP_Framework/Applications/embedded_database_app/flashdb_app/tsdb_sample.c b/APP_Framework/Applications/embedded_database_app/flashdb_app/tsdb_sample.c new file mode 100644 index 000000000..644b96d16 --- /dev/null +++ b/APP_Framework/Applications/embedded_database_app/flashdb_app/tsdb_sample.c @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2020, Armink, + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief TSDB samples. + * + * Time series log (like TSDB) feature samples source file. + * + * TSL is time series log, the TSDB saved many TSLs. + */ + +#include +#include + +#ifdef FDB_USING_TSDB + +#define FDB_LOG_TAG "[sample][tsdb]" + +struct env_status { + int temp; + int humi; +}; + +static bool query_cb(fdb_tsl_t tsl, void *arg); +static bool query_by_time_cb(fdb_tsl_t tsl, void *arg); +static bool set_status_cb(fdb_tsl_t tsl, void *arg); + +void tsdb_sample(fdb_tsdb_t tsdb) +{ + struct fdb_blob blob; + + FDB_INFO("==================== tsdb_sample ====================\n"); + + { /* APPEND new TSL (time series log) */ + struct env_status status; + + /* append new log to TSDB */ + status.temp = 36; + status.humi = 85; + fdb_tsl_append(tsdb, fdb_blob_make(&blob, &status, sizeof(status))); + FDB_INFO("append the new status.temp (%d) and status.humi (%d)\n", status.temp, status.humi); + + status.temp = 38; + status.humi = 90; + fdb_tsl_append(tsdb, fdb_blob_make(&blob, &status, sizeof(status))); + FDB_INFO("append the new status.temp (%d) and status.humi (%d)\n", status.temp, status.humi); + } + + { /* QUERY the TSDB */ + /* query all TSL in TSDB by iterator */ + fdb_tsl_iter(tsdb, query_cb, tsdb); + } + + { /* QUERY the TSDB by time */ + /* prepare query time (from 1970-01-01 00:00:00 to 2020-05-05 00:00:00) */ + struct tm tm_from = { .tm_year = 1970 - 1900, .tm_mon = 0, .tm_mday = 1, .tm_hour = 0, .tm_min = 0, .tm_sec = 0 }; + struct tm tm_to = { .tm_year = 2020 - 1900, .tm_mon = 4, .tm_mday = 5, .tm_hour = 0, .tm_min = 0, .tm_sec = 0 }; + time_t from_time = mktime(&tm_from), to_time = mktime(&tm_to); + size_t count; + /* query all TSL in TSDB by time */ + fdb_tsl_iter_by_time(tsdb, from_time, to_time, query_by_time_cb, tsdb); + /* query all FDB_TSL_WRITE status TSL's count in TSDB by time */ + count = fdb_tsl_query_count(tsdb, from_time, to_time, FDB_TSL_WRITE); + FDB_INFO("query count is: %zu\n", count); + } + + { /* SET the TSL status */ + /* Change the TSL status by iterator or time iterator + * set_status_cb: the change operation will in this callback + * + * NOTE: The actions to modify the state must be in order. + * like: FDB_TSL_WRITE -> FDB_TSL_USER_STATUS1 -> FDB_TSL_DELETED -> FDB_TSL_USER_STATUS2 + * The intermediate states can also be ignored. + * such as: FDB_TSL_WRITE -> FDB_TSL_DELETED + */ + fdb_tsl_iter(tsdb, set_status_cb, tsdb); + } + + FDB_INFO("===========================================================\n"); +} + +static bool query_cb(fdb_tsl_t tsl, void *arg) +{ + struct fdb_blob blob; + struct env_status status; + fdb_tsdb_t db = arg; + + fdb_blob_read((fdb_db_t) db, fdb_tsl_to_blob(tsl, fdb_blob_make(&blob, &status, sizeof(status)))); + FDB_INFO("[query_cb] queried a TSL: time: %ld, temp: %d, humi: %d\n", tsl->time, status.temp, status.humi); + + return false; +} + +static bool query_by_time_cb(fdb_tsl_t tsl, void *arg) +{ + struct fdb_blob blob; + struct env_status status; + fdb_tsdb_t db = arg; + + fdb_blob_read((fdb_db_t) db, fdb_tsl_to_blob(tsl, fdb_blob_make(&blob, &status, sizeof(status)))); + FDB_INFO("[query_by_time_cb] queried a TSL: time: %ld, temp: %d, humi: %d\n", tsl->time, status.temp, status.humi); + + return false; +} + +static bool set_status_cb(fdb_tsl_t tsl, void *arg) +{ + fdb_tsdb_t db = arg; + + FDB_INFO("set the TSL (time %ld) status from %d to %d\n", tsl->time, tsl->status, FDB_TSL_USER_STATUS1); + fdb_tsl_set_status(db, tsl, FDB_TSL_USER_STATUS1); + + return false; +} + +#endif /* FDB_USING_TSDB */ diff --git a/APP_Framework/Applications/knowing_app/cmsis_5_demo/Kconfig b/APP_Framework/Applications/knowing_app/cmsis_5_demo/Kconfig index dd09e11d9..e9aa30cea 100644 --- a/APP_Framework/Applications/knowing_app/cmsis_5_demo/Kconfig +++ b/APP_Framework/Applications/knowing_app/cmsis_5_demo/Kconfig @@ -11,6 +11,11 @@ menuconfig USING_CMSIS_5_DEMOAPP select IMAGE_PROCESSING_USING_TJPGD default n + config USING_CMSIS_5_NN_DEMOAPP_VEG_CLASSIFY + bool "Using CMSIS-5 NN demo app vegetable classify" + select USING_IMAGE_PROCESSING + select IMAGE_PROCESSING_USING_TJPGD + default n endif diff --git a/APP_Framework/Applications/knowing_app/cmsis_5_demo/cmsisnn_vegetable_classify/README.md b/APP_Framework/Applications/knowing_app/cmsis_5_demo/cmsisnn_vegetable_classify/README.md new file mode 100644 index 000000000..f267b9cb1 --- /dev/null +++ b/APP_Framework/Applications/knowing_app/cmsis_5_demo/cmsisnn_vegetable_classify/README.md @@ -0,0 +1,37 @@ +# CMSIS-NN vegetable classify example + +This example uses CMSIS-NN to classify vegetable in real time under certain circumstances . + +## Requirements: +- CMSIS-NN in Framework/knowing/cmsis_5 +- **ov2640 need to be configured** in menuconfig "More Drivers->ov2640 driver" as follows + - Output format (RGB565 mode) + - (256) X direction resolution of outputimage + - (256) Y direction resolution of outputimage + - (512) X direction WINDOWS SIZE + - (512) Y direction WINDOWS SIZE + +## To run this demo: +- Set up and configure the corresponding hardware environment. + +- Run demo by type the command + ``` + cmsisnn_vegetable_classify + +## Results + +- **tomato** + +![tomato](https://www.gitlink.org.cn/repo/WentaoWong/xiuos/raw/branch/dev/APP_Framework/Applications/knowing_app/cmsis_5_demo/cmsisnn_vegetable_classify/doc/tomato.jpg) + +- **potato** + +![potato](https://www.gitlink.org.cn/repo/WentaoWong/xiuos/raw/branch/dev/APP_Framework/Applications/knowing_app/cmsis_5_demo/cmsisnn_vegetable_classify/doc/potato.jpg) + +- **pepper** + +![pepper](https://www.gitlink.org.cn/repo/WentaoWong/xiuos/raw/branch/dev/APP_Framework/Applications/knowing_app/cmsis_5_demo/cmsisnn_vegetable_classify/doc/pepper.jpg) + +- **mushroom** + +![mushroom](https://www.gitlink.org.cn/repo/WentaoWong/xiuos/raw/branch/dev/APP_Framework/Applications/knowing_app/cmsis_5_demo/cmsisnn_vegetable_classify/doc/mushroom.jpg) \ No newline at end of file diff --git a/APP_Framework/Applications/knowing_app/cmsis_5_demo/cmsisnn_vegetable_classify/SConscript b/APP_Framework/Applications/knowing_app/cmsis_5_demo/cmsisnn_vegetable_classify/SConscript new file mode 100644 index 000000000..891eff9df --- /dev/null +++ b/APP_Framework/Applications/knowing_app/cmsis_5_demo/cmsisnn_vegetable_classify/SConscript @@ -0,0 +1,18 @@ +from building import * +import os + +cwd = GetCurrentDir() + +src = Split(''' +model/nn_vegetable_classify.c +cmsisnn_vegetable_classify.c +''') + +path = [ + cwd + '/model', + cwd + '/demo' + ] + +group = DefineGroup('CMSISNN vegetable classify application', src, depend = ['USING_CMSIS_5_NN_DEMOAPP_VEG_CLASSIFY'], CPPPATH = path) + +Return('group') diff --git a/APP_Framework/Applications/knowing_app/cmsis_5_demo/cmsisnn_vegetable_classify/cmsisnn_vegetable_classify.c b/APP_Framework/Applications/knowing_app/cmsis_5_demo/cmsisnn_vegetable_classify/cmsisnn_vegetable_classify.c new file mode 100644 index 000000000..8bc48a076 --- /dev/null +++ b/APP_Framework/Applications/knowing_app/cmsis_5_demo/cmsisnn_vegetable_classify/cmsisnn_vegetable_classify.c @@ -0,0 +1,188 @@ +#include +#include +#include "stdio.h" +#include "string.h" + +#ifdef OV2640_RGB565_MODE +#ifdef RT_USING_POSIX +#include +#include +#ifdef RT_USING_POSIX_TERMIOS +#include +#endif +#endif + +#include +#include +#include "nn_vegetable_classify.h" +#define JPEG_BUF_SIZE (2 * OV2640_X_RESOLUTION_IMAGE_OUTSIZE * OV2640_Y_RESOLUTION_IMAGE_OUTSIZE) +#define IOCTL_ERROR 1 + +static int fd = 0; +static int infer_times = 0; +static int photo_times = 0; +static int height = OV2640_X_RESOLUTION_IMAGE_OUTSIZE; +static int width = OV2640_Y_RESOLUTION_IMAGE_OUTSIZE; +static _ioctl_shoot_para shoot_para_t = {0}; +const char *vegetable_label[] = {"mushroom", "pepper", "potato", "tomato"}; + +uint8_t *resized_buffer = NULL; +uint8_t *in_buffer = NULL; + +int get_top_prediction_detection(q7_t *predictions) +{ + int max_ind = 0; + int max_val = -128; + for (int i = 0; i < 10; i++) + { + if (max_val < predictions[i]) + { + max_val = predictions[i]; + max_ind = i; + } + } + return max_ind; +} + +int cmsisnn_inference_vegetable_classify(uint8_t *input_data) +{ + int8_t output_data[4]; + char output[50] = {0}; + char outputPrediction[50] = {0}; + memset(output, 0, 50); + memset(outputPrediction, 0, 50); + + run_nn_sn_classify((int8_t *)input_data, output_data); + arm_softmax_q7(output_data, 4, output_data); + + infer_times++; + int top_ind = get_top_prediction_detection(output_data); + printf("times:%d Prediction:%s \r\n", infer_times, vegetable_label[top_ind]); + sprintf(outputPrediction, "times:%d Prediction:%s \r\n", infer_times, vegetable_label[top_ind]); + lcd_show_string(1, 280, 240, 16, 16, outputPrediction, RED); + return top_ind; +} + +void resize_rgb888in_rgb565out(uint8_t *camera_image, uint16_t *resize_image) +{ + uint8_t *psrc_temp = (uint8_t *)camera_image; + uint16_t *pdst_temp = (uint16_t *)resize_image; + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + *pdst_temp++ = (*psrc_temp++ & 0xF8) << 8 | (*psrc_temp++ & 0xFC) << 3 | *psrc_temp++ >> 3; + } + } +} + +void resize_rgb565in_rgb888out(uint8_t *camera_image, uint8_t *resize_image) +{ + uint8_t *psrc_temp = (uint8_t *)camera_image; + uint8_t *pdst_temp = (uint8_t *)resize_image; + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + uint8_t pixel_lo = *psrc_temp++; + uint8_t pixel_hi = *psrc_temp++; + *pdst_temp++ = (0xF8 & pixel_hi); + *pdst_temp++ = ((0x07 & pixel_hi) << 5) | ((0xE0 & pixel_lo) >> 3); + *pdst_temp++ = (0x1F & pixel_lo) << 3; + } + } +} + +void lcd_show_ov2640_thread_detection(uint8_t *rgbbuffer) +{ + int32_t ret = 0; + while (1) + { + ret = ioctl(fd, IOCTRL_CAMERA_START_SHOT, &shoot_para_t); + if (ret == IOCTL_ERROR) + { + printf("ov2640 can't wait event flag"); + free(rgbbuffer); + return; + } + lcd_fill_array(0, 0, OV2640_X_RESOLUTION_IMAGE_OUTSIZE, OV2640_Y_RESOLUTION_IMAGE_OUTSIZE, rgbbuffer); + + if (photo_times % 20 == 0) + { + resize_rgb565in_rgb888out(rgbbuffer, resized_buffer); + int pixel = 0; + for (int i = 0; i < 3 * width; i += 3 * width / CONV1_IN_DIM) + { + for (int j = 0; j < 3 * height; j += 3 * height / CONV1_IN_DIM) + { + for (int k = 0; k < 3; k++, pixel++) + { + *(in_buffer + pixel) = *(resized_buffer + 256 * i + j + k); + } + } + } + cmsisnn_inference_vegetable_classify(in_buffer); + } + photo_times++; + } +} + +void cmsisnn_vegetable_classify() +{ + fd = open("/dev/ov2640", O_RDONLY); + if (fd < 0) + { + printf("open ov2640 fail !!"); + return; + } + printf("memory_init \n\r"); + uint8_t *JpegBuffer = malloc(JPEG_BUF_SIZE); + if (JpegBuffer == NULL) + { + printf("JpegBuffer senddata buf malloc error!\n"); + return; + } + resized_buffer = malloc(3 * width * height); + if (resized_buffer == NULL) + { + printf("Resized_buffer buf malloc error!\n"); + return; + } + in_buffer = malloc(CONV1_IN_CH * CONV1_IN_DIM * CONV1_IN_DIM); + if (in_buffer == NULL) + { + printf("In_buffer buf malloc error!\n"); + return; + } + memory_init(); + printf("memory_init success\n\r"); + + shoot_para_t.pdata = (uint32_t)JpegBuffer; + shoot_para_t.length = JPEG_BUF_SIZE / 2; + + int result = 0; + pthread_t tid = 0; + pthread_attr_t attr; + struct sched_param prio; + prio.sched_priority = 8; + size_t stack_size = 1024 * 11; + pthread_attr_init(&attr); + pthread_attr_setschedparam(&attr, &prio); + pthread_attr_setstacksize(&attr, stack_size); + + result = pthread_create(&tid, &attr, lcd_show_ov2640_thread_detection, JpegBuffer); + if (0 == result) { + printf("thread_detect_entry successfully!\n"); + } else { + printf("thread_detect_entry failed! error code is %d.\n", result); + close(fd); + } + + return; +} + +#ifdef __RT_THREAD_H__ +MSH_CMD_EXPORT(cmsisnn_vegetable_classify, classify vegetable using cmsis-nn); +#endif + +#endif \ No newline at end of file diff --git a/APP_Framework/Applications/knowing_app/cmsis_5_demo/cmsisnn_vegetable_classify/doc/mushroom.jpg b/APP_Framework/Applications/knowing_app/cmsis_5_demo/cmsisnn_vegetable_classify/doc/mushroom.jpg new file mode 100644 index 000000000..79d85de2d Binary files /dev/null and b/APP_Framework/Applications/knowing_app/cmsis_5_demo/cmsisnn_vegetable_classify/doc/mushroom.jpg differ diff --git a/APP_Framework/Applications/knowing_app/cmsis_5_demo/cmsisnn_vegetable_classify/doc/pepper.jpg b/APP_Framework/Applications/knowing_app/cmsis_5_demo/cmsisnn_vegetable_classify/doc/pepper.jpg new file mode 100644 index 000000000..254c734c9 Binary files /dev/null and b/APP_Framework/Applications/knowing_app/cmsis_5_demo/cmsisnn_vegetable_classify/doc/pepper.jpg differ diff --git a/APP_Framework/Applications/knowing_app/cmsis_5_demo/cmsisnn_vegetable_classify/doc/potato.jpg b/APP_Framework/Applications/knowing_app/cmsis_5_demo/cmsisnn_vegetable_classify/doc/potato.jpg new file mode 100644 index 000000000..b9ad7bff6 Binary files /dev/null and b/APP_Framework/Applications/knowing_app/cmsis_5_demo/cmsisnn_vegetable_classify/doc/potato.jpg differ diff --git a/APP_Framework/Applications/knowing_app/cmsis_5_demo/cmsisnn_vegetable_classify/doc/tomato.jpg b/APP_Framework/Applications/knowing_app/cmsis_5_demo/cmsisnn_vegetable_classify/doc/tomato.jpg new file mode 100644 index 000000000..13865cf64 Binary files /dev/null and b/APP_Framework/Applications/knowing_app/cmsis_5_demo/cmsisnn_vegetable_classify/doc/tomato.jpg differ diff --git a/APP_Framework/Applications/knowing_app/cmsis_5_demo/cmsisnn_vegetable_classify/model/nn_vegetable_classify.c b/APP_Framework/Applications/knowing_app/cmsis_5_demo/cmsisnn_vegetable_classify/model/nn_vegetable_classify.c new file mode 100644 index 000000000..1afeca19f --- /dev/null +++ b/APP_Framework/Applications/knowing_app/cmsis_5_demo/cmsisnn_vegetable_classify/model/nn_vegetable_classify.c @@ -0,0 +1,108 @@ +#include "nn_vegetable_classify.h" + +static const q7_t conv1_w[CONV1_WT_SHAPE] = CONV1_WT; +static const q7_t conv1_b[CONV1_BIAS_SHAPE] = CONV1_BIAS; +static const q7_t conv2_w[CONV2_WT_SHAPE] = CONV2_WT; +static const q7_t conv2_b[CONV2_BIAS_SHAPE] = CONV2_BIAS; +// static const q7_t conv3_w[CONV3_WT_SHAPE] = CONV3_WT; +// static const q7_t conv3_b[CONV3_BIAS_SHAPE] = CONV3_BIAS; +static const q7_t interface_w[INTERFACE_WT_SHAPE] = INTERFACE_WT; +static const q7_t interface_b[INTERFACE_BIAS_SHAPE] = INTERFACE_BIAS; +static const q7_t linear_w[LINEAR_WT_SHAPE] = LINEAR_WT; +static const q7_t linear_b[LINEAR_BIAS_SHAPE] = LINEAR_BIAS; + +q7_t *conv1_out = NULL; +q7_t *pool1_out = NULL; +q7_t *conv2_out = NULL; +q7_t *pool2_out = NULL; +// q7_t *conv3_out = NULL ; +q7_t *interface_out = NULL; +q7_t *linear_out = NULL; +q7_t *y_out = NULL; +q7_t *conv_buffer = NULL; +q7_t *fc_buffer = NULL; + +void memory_init() +{ + static int flag = 0; + if (flag == 0) + { + conv1_out = malloc(CONV1_OUT_CH * CONV1_OUT_DIM * CONV1_OUT_DIM); + if (conv1_out == NULL) + { + printf("conv1_out malloc failed...\n"); + return; + } + pool1_out = malloc(CONV1_OUT_CH * POOL1_OUT_DIM * POOL1_OUT_DIM); + if (pool1_out == NULL) + { + printf("pool1_out malloc failed...\n"); + return; + } + conv2_out = malloc(CONV2_OUT_CH * CONV2_OUT_DIM * CONV2_OUT_DIM); + if (conv2_out == NULL) + { + printf("conv2_out malloc failed...\n"); + return; + } + pool2_out = malloc(CONV2_OUT_CH * POOL2_OUT_DIM * POOL2_OUT_DIM); + if (pool2_out == NULL) + { + printf("pool2_out malloc failed...\n"); + return; + } + interface_out = malloc(INTERFACE_OUT_DIM); + if (interface_out == NULL) + { + printf("interface_out malloc failed...\n"); + return; + } + linear_out = malloc(LINEAR_OUT_DIM); + if (linear_out == NULL) + { + printf("linear_out malloc failed...\n"); + return; + } + y_out = malloc(LINEAR_OUT_DIM); + if (y_out == NULL) + { + printf("y_out malloc failed...\n"); + return; + } + conv_buffer = malloc(MAX_CONV_BUFFER_SIZE); + if (conv_buffer == NULL) + { + printf("conv_buffer malloc failed...\n"); + return; + } + fc_buffer = malloc(MAX_FC_BUFFER); + if (fc_buffer == NULL) + { + printf("fc_buffer malloc failed...\n"); + return; + } + } +} + +void run_nn_sn_classify(q7_t *input_data, q7_t *output_data) +{ + for (int i = 0; i < CONV1_IN_CH * CONV1_IN_DIM * CONV1_IN_DIM; i++) + { + input_data[i] = input_data[i] - 127; + } + arm_convolve_HWC_q7_basic(input_data, CONV1_IN_DIM, CONV1_IN_CH, conv1_w, CONV1_OUT_CH, CONV1_KER_DIM, CONV1_PAD, CONV1_STRIDE, conv1_b, CONV1_BIAS_LSHIFT, CONV1_OUT_RSHIFT, conv1_out, CONV1_OUT_DIM, (q15_t *)conv_buffer, fc_buffer); + arm_maxpool_q7_HWC(conv1_out, POOL1_IN_DIM, POOL1_IN_CH, POOL1_KER_DIM, POOL1_PAD, POOL1_STRIDE, POOL1_OUT_DIM, NULL, pool1_out); + arm_relu_q7(pool1_out, POOL1_OUT_DIM * POOL1_OUT_DIM * CONV1_OUT_CH); + arm_convolve_HWC_q7_basic(pool1_out, CONV2_IN_DIM, CONV2_IN_CH, conv2_w, CONV2_OUT_CH, CONV2_KER_DIM, CONV2_PAD, CONV2_STRIDE, conv2_b, CONV2_BIAS_LSHIFT, CONV2_OUT_RSHIFT, conv2_out, CONV2_OUT_DIM, (q15_t *)conv_buffer, NULL); + arm_maxpool_q7_HWC(conv2_out, POOL2_IN_DIM, POOL2_IN_CH, POOL2_KER_DIM, POOL2_PAD, POOL2_STRIDE, POOL2_OUT_DIM, NULL, pool2_out); + arm_relu_q7(pool2_out, POOL2_OUT_DIM * POOL2_OUT_DIM * CONV2_OUT_CH); + // printf("1\n"); + // arm_convolve_HWC_q7_basic(pool2_out, CONV3_IN_DIM, CONV3_IN_CH, conv3_w, CONV3_OUT_CH, CONV3_KER_DIM, + // CONV3_PAD, CONV3_STRIDE, conv3_b, CONV3_BIAS_LSHIFT, CONV3_OUT_RSHIFT, conv3_out, + // CONV3_OUT_DIM, (q15_t *) conv_buffer, NULL); + // arm_relu_q7(conv3_out, CONV3_OUT_DIM * CONV3_OUT_DIM * CONV3_OUT_CH); + // printf("2\n"); + arm_fully_connected_q7_opt(pool2_out, interface_w, INTERFACE_IN_DIM, INTERFACE_OUT_DIM, INTERFACE_BIAS_LSHIFT, INTERFACE_OUT_RSHIFT, interface_b, interface_out, (q15_t *)fc_buffer); + arm_relu_q7(interface_out, INTERFACE_OUT_DIM); + arm_fully_connected_q7_opt(interface_out, linear_w, LINEAR_IN_DIM, LINEAR_OUT_DIM, LINEAR_BIAS_LSHIFT, LINEAR_OUT_RSHIFT, linear_b, output_data, (q15_t *)fc_buffer); +} diff --git a/APP_Framework/Applications/knowing_app/cmsis_5_demo/cmsisnn_vegetable_classify/model/nn_vegetable_classify.h b/APP_Framework/Applications/knowing_app/cmsis_5_demo/cmsisnn_vegetable_classify/model/nn_vegetable_classify.h new file mode 100644 index 000000000..7c46c8fb4 --- /dev/null +++ b/APP_Framework/Applications/knowing_app/cmsis_5_demo/cmsisnn_vegetable_classify/model/nn_vegetable_classify.h @@ -0,0 +1,13 @@ +#ifndef __NN_H__ +#define __NN_H__ + +#include +#include "arm_math.h" +#include "arm_nnfunctions.h" +#include "parameter_vegetable_classify.h" +#include "weights_vegetable_classify.h" + +void run_nn_detection(q7_t* input_data, q7_t* output_data); +void memory_init(); + +#endif diff --git a/APP_Framework/Applications/knowing_app/cmsis_5_demo/cmsisnn_vegetable_classify/model/parameter_vegetable_classify.h b/APP_Framework/Applications/knowing_app/cmsis_5_demo/cmsisnn_vegetable_classify/model/parameter_vegetable_classify.h new file mode 100644 index 000000000..910b2ca6a --- /dev/null +++ b/APP_Framework/Applications/knowing_app/cmsis_5_demo/cmsisnn_vegetable_classify/model/parameter_vegetable_classify.h @@ -0,0 +1,56 @@ +#define CONV1_IN_CH 3 +#define CONV1_OUT_CH 32 +#define CONV1_KER_DIM 3 +#define CONV1_PAD 0 +#define CONV1_STRIDE 1 +#define CONV1_IN_DIM 32 +#define CONV1_OUT_DIM 30 +#define MAX_CONV_BUFFER_SIZE 3096 +#define POOL1_IN_CH 32 +#define POOL1_KER_DIM 2 +#define POOL1_PAD 0 +#define POOL1_STRIDE 2 +#define POOL1_IN_DIM 30 +#define POOL1_OUT_DIM 15 +#define CONV2_IN_CH 32 +#define CONV2_OUT_CH 32 +#define CONV2_KER_DIM 3 +#define CONV2_PAD 0 +#define CONV2_STRIDE 1 +#define CONV2_IN_DIM 15 +#define CONV2_OUT_DIM 13 +#define POOL2_IN_CH 32 +#define POOL2_KER_DIM 2 +#define POOL2_PAD 0 +#define POOL2_STRIDE 2 +#define POOL2_IN_DIM 13 +#define POOL2_OUT_DIM 6 +#define INTERFACE_OUT_DIM 32 +#define INTERFACE_IN_DIM 1152 +#define MAX_FC_BUFFER 3096 +#define LINEAR_OUT_DIM 4 +#define LINEAR_IN_DIM 32 +#define CONV1_BIAS_LSHIFT 6 +#define CONV1_OUT_RSHIFT 9 +#define CONV1_WEIGHT_Q 8 +#define CONV1_BIAS_Q 8 +#define CONV1_INPUT_Q 6 +#define CONV1_OUT_Q 5 +#define CONV2_BIAS_LSHIFT 4 +#define CONV2_OUT_RSHIFT 10 +#define CONV2_WEIGHT_Q 9 +#define CONV2_BIAS_Q 10 +#define CONV2_INPUT_Q 5 +#define CONV2_OUT_Q 4 +#define INTERFACE_BIAS_LSHIFT 3 +#define INTERFACE_OUT_RSHIFT 11 +#define INTERFACE_WEIGHT_Q 9 +#define INTERFACE_BIAS_Q 10 +#define INTERFACE_INPUT_Q 4 +#define INTERFACE_OUT_Q 2 +#define LINEAR_BIAS_LSHIFT 0 +#define LINEAR_OUT_RSHIFT 7 +#define LINEAR_WEIGHT_Q 7 +#define LINEAR_BIAS_Q 9 +#define LINEAR_INPUT_Q 2 +#define LINEAR_OUT_Q 2 diff --git a/APP_Framework/Applications/knowing_app/cmsis_5_demo/cmsisnn_vegetable_classify/model/weights_vegetable_classify.h b/APP_Framework/Applications/knowing_app/cmsis_5_demo/cmsisnn_vegetable_classify/model/weights_vegetable_classify.h new file mode 100644 index 000000000..114146a21 --- /dev/null +++ b/APP_Framework/Applications/knowing_app/cmsis_5_demo/cmsisnn_vegetable_classify/model/weights_vegetable_classify.h @@ -0,0 +1,16 @@ +#define CONV1_WT {-39,14,4,-16,-18,-38,-58,36,-14,36,-26,41,-13,46,-37,-21,8,-17,-56,-29,-44,32,-17,41,-30,-39,-3,-10,47,-35,-21,-49,29,61,-23,-19,-27,44,-35,61,49,15,44,-42,-16,-38,-10,35,-28,-7,-35,46,-49,7,55,2,-57,6,-34,-3,-18,-25,-38,27,-7,24,30,-44,-49,71,-17,30,9,-11,-29,61,40,-25,70,-4,-26,-35,40,49,28,-18,11,-37,-36,20,-27,38,30,-40,-3,16,5,9,17,43,-14,40,-20,29,-7,23,27,-6,-12,-26,-29,21,45,-15,-39,26,-22,-13,8,-21,-37,42,7,-3,12,37,-23,-20,-35,-9,49,-43,-58,50,12,53,-30,35,-13,-36,1,28,24,-52,-9,46,-16,49,-40,-21,4,-44,34,-12,46,-38,-30,-6,7,0,-2,-32,3,-20,47,20,-24,-49,-42,45,-10,-26,9,1,3,-27,20,44,-4,21,31,-35,42,-15,5,12,40,9,-38,-51,46,-5,-12,55,30,-19,-18,-20,20,23,-22,-65,-35,-37,-55,52,-4,16,13,-50,-11,33,-21,-13,-39,43,-22,-44,-21,-15,-48,-12,-32,-22,22,-20,47,-34,-27,-19,7,23,14,-11,39,-36,-25,-19,-13,8,28,39,-54,-6,-2,-3,-25,-24,-22,24,37,9,-40,-39,20,-42,12,27,-43,49,28,36,47,8,-36,-42,44,1,-21,12,-52,-26,25,-71,-29,-33,20,28,-44,-47,34,-19,-8,9,13,-7,60,-52,-72,20,-33,-47,56,-14,-75,-2,37,23,33,36,45,-10,-41,37,-7,9,3,-10,-57,28,17,-54,-18,-26,-30,45,-41,-10,11,11,-43,33,29,33,5,14,47,-17,-22,38,-6,22,-6,-21,-14,32,37,2,1,4,26,-21,20,-23,-3,25,5,40,0,49,-2,42,-27,11,-37,-23,19,-10,-43,27,-37,-37,-14,35,34,-28,-36,44,22,-34,4,40,-11,20,44,-24,-5,21,8,36,-17,-13,-5,6,3,-14,35,-13,45,-60,15,5,18,-15,7,-15,-26,47,-27,-50,53,8,-34,31,-48,54,58,-45,48,58,-18,45,61,34,21,-10,-2,65,-12,1,16,-9,23,44,48,10,-20,18,42,-20,49,-30,33,44,16,43,47,-40,45,19,-8,52,59,5,75,29,22,23,20,28,71,63,-60,50,-25,-16,67,-1,-35,12,5,-20,31,2,-24,-9,-26,-33,13,29,11,20,-38,-3,-52,17,-24,-23,22,-53,-36,-27,-42,26,27,-47,44,-1,-39,-25,19,29,47,-44,49,20,-2,41,12,49,28,39,15,40,-2,11,32,-30,8,-26,-46,-36,-24,-14,-18,-16,-23,17,21,-41,-3,15,41,31,-58,27,18,38,-53,15,10,-50,41,-28,16,25,9,-44,1,-9,-43,-45,-20,44,3,-57,-34,36,-12,38,3,-22,-19,15,33,-5,-21,-40,-4,22,-25,9,-38,-50,-14,-48,11,-41,10,22,3,-47,-22,35,-48,8,11,34,-27,-6,-33,11,-18,37,27,14,-36,-25,41,40,29,36,-39,1,52,-53,-5,70,-8,5,26,44,-19,53,-13,-51,67,-33,-12,15,-51,13,24,-38,-45,-18,-49,42,22,-44,-9,55,-27,18,64,41,-39,16,53,-14,-19,27,-12,12,61,23,20,-1,-52,49,26,-47,1,43,-22,40,-19,-11,20,35,-43,30,39,-1,49,23,-15,-36,50,-59,7,23,8,-42,23,-58,4,-22,-68,-21,43,-37,46,23,-23,100,-5,-49,49,-9,-36,110,-34,0,96,-46,-60,38,-22,-49,23,-45,-63,84,-23,-2,69,-10,-38,13,25,-55,23,-50,-39,50,-19,31,-18,16,15,13,19,26,-4,-39,22,45,43,9,-10,-14,-26,-19,-28,15,-33,-46,45,-52,39,31,-49,-31,-34,30,4,16,-1,-34,-24,-59,-3,54,-4,38,11,-11,2,-3,6,32,18,-30,4,-26,25,-25,-26,-24,-52,34,-3,-47,-6,44,-42,8,-32,32,-5,-27,-38,-25,27,0,17,57,36,10,-11,-16,-34,-37,10,-2,-62,29,-14,-32,37,0,-62,-21,-29,-2,43,-9,-58,31,-54,-1,2,-46,-71,-10,27,10,54,-28,-43,-13,10,18,44,46,0,25,-3,-28,23,48,-48,28,-3,-33,27,-12,-23,-17,25,34,-39,2,-1,29,18,-37,28,-46,2,59,20,-46,-25,-52,-37,11,21,-59,-33,-22,-65,51,-31,-52,-18,-7,-58,39,3,-1,20,-26} +#define CONV1_WT_SHAPE 864 +#define CONV1_BIAS {-19,-7,-36,-14,-38,25,-44,-11,-26,-8,-13,-34,46,71,-11,-55,41,-8,-19,7,-14,35,-39,-33,-3,-42,-14,53,-5,-44,43,-18} +#define CONV1_BIAS_SHAPE 32 +#define CONV2_WT {-16,-22,-20,-18,-1,3,4,-31,19,-25,-12,-8,-21,6,-25,26,31,-7,11,2,14,-19,23,24,-23,-8,-29,13,-10,-30,-8,3,-8,-1,-5,25,11,20,6,-16,-15,24,14,1,32,26,-10,8,31,-15,26,0,24,26,-8,38,22,-29,5,17,4,7,-8,-18,8,9,14,17,28,-20,-11,23,18,-24,-13,-28,17,-12,-10,19,50,-7,21,18,-25,3,6,-4,36,-5,-15,2,7,-24,-12,-10,-20,-25,-14,-16,-2,15,-24,-2,-5,8,-6,-21,-27,10,19,-19,31,-11,12,-2,13,9,23,2,25,-26,-11,-5,20,26,-12,-27,-7,-11,-20,-6,8,18,30,12,-28,16,-8,-17,2,0,12,46,35,19,34,-18,8,-29,-23,20,-11,-18,-13,-27,-14,12,13,-27,21,18,22,9,10,-29,-12,-17,27,7,-31,-6,27,-6,28,45,-2,3,0,-22,26,-8,17,8,-13,-18,-15,-13,-4,26,20,10,6,-1,14,17,5,-18,12,-11,16,1,22,5,-18,26,-5,22,-14,17,24,-9,15,-3,-2,17,-23,5,4,-25,19,-18,24,0,17,-7,22,17,-18,3,0,-8,14,24,-31,8,36,19,12,-6,4,-30,-3,-26,-14,4,-22,14,-9,-7,22,-13,13,-5,-8,-31,14,9,-24,25,-28,-2,-15,18,23,24,-17,-28,27,22,19,16,1,12,30,-21,14,12,16,14,-10,-5,-16,21,-5,-15,1,-10,29,25,9,-17,24,3,24,-3,-5,17,-29,17,29,-27,5,-9,12,-38,5,6,-9,24,-5,25,-7,-29,-4,17,-6,23,-19,4,20,1,16,31,-31,3,-15,-7,26,9,17,17,22,1,12,39,6,-21,32,-28,-14,27,-2,4,-3,-39,-9,-26,-15,-11,5,-2,6,-15,-27,-10,-20,-14,10,-21,-7,-24,-12,-10,36,2,20,49,23,-6,-6,-12,5,1,23,-3,21,-8,-5,10,3,-8,31,9,-30,22,-24,-3,18,-11,-22,-8,5,-18,-16,-28,40,-18,-31,12,35,-18,-12,23,9,21,13,12,-33,-3,26,-12,3,5,7,5,24,-12,2,10,-6,3,-10,19,-19,-17,-23,7,-3,-7,21,-6,8,17,-13,-9,9,-19,-3,-4,-13,-38,-22,-31,-4,-27,17,23,15,9,-21,13,-13,-24,-26,-31,-24,-3,24,3,6,24,8,10,28,24,27,-27,-2,-4,5,0,11,10,-23,-1,-16,-20,-21,-20,-6,-22,-3,19,-5,-9,0,-9,-9,6,6,-5,32,-7,20,31,9,9,26,11,17,-2,-31,6,1,-38,18,23,-12,5,-15,21,0,-18,16,12,-11,-4,-24,2,9,3,-9,23,23,15,13,24,25,-34,29,-15,-7,28,-2,6,-33,-13,6,-4,24,18,23,16,-23,21,-7,31,25,3,-8,-21,-24,-24,-31,27,27,15,25,-10,30,2,-8,1,-20,-11,-18,21,-34,-16,-19,-18,-21,20,4,24,-24,0,0,-21,-7,7,21,28,0,4,17,8,-12,-26,1,-8,-14,-3,25,27,-16,-9,-12,-3,34,17,30,-30,11,-9,14,2,-21,-4,49,-12,23,28,-12,17,-5,23,-35,26,13,-23,-16,-1,36,-4,-4,9,22,13,-8,20,16,38,-18,-10,-30,-18,14,-30,-26,33,44,-13,-16,11,8,4,22,0,13,26,-30,-24,8,27,1,34,-9,19,13,20,27,22,-15,34,-23,-19,4,-13,-17,-12,-32,34,34,2,-7,12,-18,28,-9,17,-11,-14,-22,-14,-9,23,23,-5,2,25,27,-14,-14,0,26,29,-13,10,-1,-13,-10,-28,-30,13,32,-28,31,31,27,-5,-20,-19,-12,41,18,-35,19,-19,29,39,9,-30,-26,15,-5,17,41,58,-11,-12,-14,-19,19,-26,5,39,4,-17,-9,15,15,-22,-11,-3,-22,-12,4,2,0,-1,30,1,-20,-3,22,8,21,-10,40,63,21,-17,-9,-27,-14,-11,-25,-7,26,29,5,11,-10,6,1,3,11,-3,-18,7,8,12,-21,-18,-19,17,4,-29,-6,9,29,-4,2,24,-1,4,8,-4,-16,38,-3,0,19,7,-11,13,-27,26,8,1,-30,-9,20,-17,-15,10,23,-1,-25,5,26,2,11,34,17,-6,11,20,10,10,-25,12,34,-29,-10,32,-1,17,-13,-23,11,-3,-7,16,20,-28,-20,3,14,-3,8,-1,17,18,29,51,-26,-36,21,-1,-9,16,-26,0,-7,19,1,14,18,5,3,-8,16,-16,-10,0,-4,0,20,13,-18,3,-16,-20,11,22,8,14,7,13,-19,-25,-25,-28,-26,-6,35,0,13,-15,6,1,-1,-1,-28,-6,2,-23,-16,20,-21,18,-30,3,5,26,-13,14,28,-20,19,-11,-14,1,5,15,-5,-13,7,-27,11,26,-26,15,-11,26,-25,2,-27,-12,7,19,-7,14,18,30,7,-4,23,7,7,21,-7,7,-10,19,-24,7,-21,-26,16,3,-32,30,0,5,-22,23,-28,18,-24,8,15,3,27,11,-10,-25,-5,-6,-28,-17,-2,-13,29,22,-17,-7,-35,-8,22,23,19,-4,-29,-26,-3,-30,16,-25,-28,-16,12,-13,-15,20,-13,20,6,-8,-11,29,6,26,36,5,-8,4,24,-21,-30,-18,17,17,-14,-26,-3,-4,-12,-20,20,-26,21,8,15,-15,17,24,27,15,-22,21,-2,29,12,1,21,13,4,24,-20,-1,-17,-31,3,-14,13,25,-12,22,-21,7,-23,12,11,12,-12,-14,-4,20,18,-10,-29,20,18,-9,-17,-7,22,2,-8,23,27,8,-37,-6,-28,12,-1,22,-12,-16,6,-16,17,30,-8,19,-3,-12,27,-9,-9,-8,-28,26,14,6,-29,12,30,13,-1,4,30,17,-21,-25,-27,23,7,-2,17,9,-7,-29,6,15,-27,2,-25,9,33,8,-9,29,-8,6,-18,7,-14,-1,1,17,14,-3,11,-21,-18,-12,-4,-3,34,6,-21,7,-10,-10,20,-20,17,15,-10,10,-16,10,-1,22,-27,-8,-11,-2,39,5,-9,2,-17,-12,25,2,2,19,23,17,-18,-3,9,6,2,3,9,-10,9,2,-15,22,26,5,-21,13,-6,0,30,15,20,-22,-5,14,31,14,2,20,22,32,21,3,-3,12,-3,-10,25,-8,12,14,28,18,9,4,13,-9,-20,-10,24,4,18,29,-6,-23,-26,11,12,-3,-9,28,18,27,-17,26,40,7,36,-6,29,23,34,-1,39,24,12,-14,-22,9,25,-19,27,-9,39,-6,2,-2,4,31,21,-4,-3,10,2,-4,18,30,15,18,34,29,-6,-10,23,6,36,-18,27,-25,-12,9,3,16,1,-20,37,-28,2,-16,-11,15,30,-9,19,-10,-26,38,-1,24,26,27,17,7,-19,5,-7,28,23,-5,-21,30,-9,-22,-5,-16,-25,-7,-11,19,31,4,-15,24,-22,-24,25,18,-17,23,14,28,-16,24,-13,6,11,11,7,1,34,0,-12,-3,-4,-28,-7,6,6,5,-6,-17,38,-27,-11,39,29,-7,7,22,-15,14,28,11,0,1,-12,-21,-2,18,17,18,40,-9,15,23,35,6,-26,3,-24,-10,-6,-2,10,16,9,34,30,-19,-4,23,-1,2,6,27,-21,10,34,22,-28,33,-12,18,35,-19,-19,9,7,21,-28,23,-15,13,33,11,38,-27,-25,44,-21,21,14,30,33,10,11,3,-15,-21,-1,-2,27,16,4,-14,-27,26,-7,0,18,-30,16,6,-13,15,7,-14,23,-13,6,-26,-11,-19,-3,-19,26,-32,27,24,4,3,15,27,7,26,7,13,-25,19,-29,6,-14,27,-20,-15,-11,5,-21,-24,-6,13,27,2,-15,16,-29,17,21,-3,-17,-2,-23,5,-21,17,-23,-26,-24,17,0,10,26,-2,-19,-15,4,-21,-7,-5,19,-10,5,26,5,-8,16,-29,0,5,21,16,28,28,-1,17,-22,-27,26,6,-27,-19,3,-18,27,-24,26,-23,-31,8,-3,24,18,24,-18,-19,-18,-16,1,15,26,19,-12,6,-10,8,-3,-19,27,25,-2,9,-26,18,-34,-22,-23,-27,-18,12,-16,32,-21,-25,8,6,-6,26,-5,6,-12,21,-17,-15,15,-13,-7,-21,25,11,-17,24,1,-7,-25,12,16,-5,13,-18,-3,23,23,27,-21,23,1,31,10,22,37,34,-17,-29,18,-24,-35,-27,27,-12,22,22,5,23,3,-28,25,-11,-20,3,-29,2,16,-17,30,-6,0,-20,9,3,30,17,15,18,-14,12,-28,0,-3,-7,4,-14,-9,-7,6,-22,-16,28,-17,17,11,6,7,-34,10,26,18,-9,4,-7,-8,18,25,1,30,-1,4,-13,16,5,-23,-28,3,2,-2,-32,-19,-14,16,-6,-10,-28,24,-19,10,-29,19,15,-4,33,0,21,-18,19,14,28,33,42,-18,6,18,-7,15,7,-20,10,5,27,-16,26,25,-22,13,29,-10,-19,-4,-9,-5,-9,-14,-10,-19,19,-11,7,-32,7,-29,-1,-24,3,22,-22,-26,-6,-3,-20,16,-9,14,-19,22,28,11,19,15,-21,-1,13,-29,-5,6,-16,-22,25,21,1,-1,11,29,-16,3,1,-17,7,26,-18,-15,26,23,21,1,7,3,-9,5,16,11,-16,23,-10,-12,26,-24,-6,16,21,10,17,-20,5,-10,-8,-12,-22,-12,-27,-14,12,-25,21,4,30,-1,8,-21,-7,-16,11,-12,-2,17,-9,11,-19,-7,-3,-13,-21,7,-1,28,-5,-6,-1,-1,32,-17,-22,2,10,27,-13,23,22,-7,1,-26,3,2,-11,26,17,-24,-3,24,-4,-32,11,11,-3,-18,20,10,28,-20,23,15,6,13,27,16,5,-9,-14,-6,-23,-23,14,-4,11,-29,-2,5,27,21,24,14,-18,27,14,4,13,-28,-10,-25,-22,-8,30,-4,2,17,15,17,13,11,20,-22,2,9,12,27,-4,21,16,-13,2,-24,31,-11,-7,-5,-9,-3,6,0,-22,5,15,13,-31,-9,12,17,11,13,-28,6,-13,14,2,16,-4,30,25,-5,18,20,-4,-24,35,-28,-13,-13,-26,-28,24,-23,-28,-11,9,23,-2,13,21,-28,-2,-24,-12,-5,31,-22,-12,-24,-8,18,-7,-10,-7,26,-28,12,24,11,17,-11,-7,-29,-10,23,15,-8,-10,-16,-4,8,-12,3,-24,17,6,9,38,27,-27,39,20,-8,-2,-27,13,-17,6,-22,-3,35,-23,-7,22,-11,-20,20,-13,32,-7,-17,21,-19,-8,-6,6,21,21,13,-15,51,9,34,12,-20,12,26,-7,-10,30,-27,-18,-3,-19,40,-18,-24,15,17,24,21,2,-11,38,29,-14,10,-15,15,16,-5,-20,24,10,-3,28,-25,6,26,24,-25,24,22,-28,-16,-8,17,0,-11,31,21,3,15,7,-1,47,10,-19,29,-17,20,23,9,18,34,-8,33,14,-5,-24,-26,28,13,-3,3,-14,-4,-3,23,6,-9,-13,-8,10,45,12,30,23,-17,-23,-22,16,17,10,24,3,24,17,32,14,-7,17,17,27,30,1,-10,22,32,1,13,-13,13,22,-22,-20,31,29,11,12,31,0,24,-13,-27,20,20,12,45,16,-3,10,-26,-9,15,2,15,20,-26,2,-23,-22,-14,1,-7,2,-1,18,16,-4,11,64,-25,-20,19,10,-13,18,-18,11,-5,-28,5,32,-23,-2,21,2,19,38,10,5,11,-13,-1,17,-23,27,24,-6,47,27,-4,17,11,-29,13,17,27,26,0,-2,34,-29,28,21,-12,13,23,-3,11,14,-27,-31,31,3,-21,34,-3,-2,19,-18,50,16,-15,46,-21,-15,16,24,-11,16,-22,-7,50,12,7,26,-19,-19,6,-14,-21,7,-19,-25,25,22,26,16,27,-16,8,11,31,-25,-29,25,5,-24,31,12,3,-12,-24,18,-8,-37,-39,33,-15,21,21,19,-32,-7,30,11,-5,24,64,-15,30,18,-8,-13,3,20,63,-16,-17,-31,-12,-18,-2,-2,-7,25,-26,-11,-28,-27,31,-16,14,3,8,2,26,-27,13,36,69,-1,-20,7,-22,-24,-24,40,29,11,15,10,-22,14,-14,1,14,2,-13,-14,-34,9,-27,-22,-15,-29,0,30,21,-10,33,32,87,-23,33,24,-31,8,-36,56,39,-31,-17,-20,0,-23,-5,-34,-4,5,-22,10,1,11,-1,-34,-19,-20,3,17,-14,-32,-9,33,78,17,-19,-20,11,-23,-49,48,38,0,12,-29,-27,5,10,-7,-19,-33,4,5,6,19,20,-12,15,-8,-43,20,-1,9,10,69,92,-19,-15,-15,-8,-9,-23,44,25,-30,-10,-29,26,6,3,0,-27,-9,-10,18,17,-18,11,-27,-7,1,-32,-6,19,-5,24,52,97,12,5,14,-16,25,-15,39,49,18,-25,-3,26,-9,-30,-14,-10,-26,-1,4,-13,-1,-7,-17,-38,-15,-33,26,-9,-17,-19,7,72,-2,-21,-9,2,-17,-12,48,49,-28,-30,-36,22,6,-39,-6,24,-11,-2,-7,-33,24,-5,-36,12,8,-39,13,20,-4,33,61,65,-16,23,-25,-38,5,1,27,25,-36,-6,-22,-23,-38,-12,-31,8,19,-29,-22,9,-27,-28,23,-9,-26,-28,15,13,-24,25,25,84,0,6,4,-27,20,-33,43,7,25,5,-15,1,-14,8,16,3,-10,-21,0,24,11,29,16,11,-12,18,26,-25,9,11,26,13,30,-7,16,22,-1,-6,-25,32,-26,15,7,1,26,-14,32,-2,-25,-10,5,11,26,3,26,-9,-30,-15,27,-8,-26,-20,24,-7,18,-6,19,21,18,-33,6,31,-29,-1,4,2,8,-16,15,-10,-24,-4,1,-4,16,-28,32,2,7,31,16,-8,-20,-7,4,-3,-8,26,3,-18,0,-4,-12,18,14,-18,14,-10,17,27,18,20,-19,-8,13,11,31,30,-10,3,0,4,-23,-12,-27,-10,19,-36,30,16,26,21,6,-34,-24,10,13,26,5,20,13,-26,4,-19,-8,9,-19,23,-22,16,6,9,23,39,21,25,26,30,8,18,24,26,-1,-20,8,11,-26,-10,-7,25,17,-28,-13,-6,43,3,-5,10,-18,7,30,-8,-14,17,16,-8,-33,24,19,2,-16,-12,-25,8,-12,-12,17,-12,22,-6,3,2,37,-16,-19,-8,42,-16,-25,-25,-12,27,2,-13,23,-3,26,15,-31,-27,4,-20,1,18,1,-8,26,33,-7,-15,5,5,-20,-24,-13,11,18,-20,32,-7,19,14,-5,-20,23,-9,-17,28,16,6,6,-17,-10,-23,-26,-5,0,-22,1,0,-10,14,-30,-1,-10,22,12,21,-8,28,-8,34,-33,-25,14,2,-23,0,10,-27,-10,-12,-5,14,20,15,-13,-6,26,10,35,-5,13,-30,-18,18,-9,24,37,14,29,5,12,1,-4,52,12,34,17,24,29,16,14,-8,23,26,-12,35,-9,16,50,30,37,22,-9,41,26,22,90,10,-21,46,15,24,15,6,17,44,-19,-10,19,-1,19,26,-10,40,39,-9,-33,45,-16,23,25,-26,32,42,-10,73,2,-12,100,2,-31,7,46,-30,18,6,27,20,16,33,11,-1,37,34,-13,-18,55,-32,-13,-3,-20,9,1,-9,30,-13,-24,51,31,31,91,33,-27,15,18,-24,42,3,5,14,6,44,22,9,-8,7,8,-6,50,9,-37,19,7,-2,11,-29,-9,22,-17,76,-16,3,67,20,4,11,41,13,28,-11,50,67,-30,-4,-21,-6,-4,9,-5,39,48,-22,2,40,7,9,21,28,-12,31,22,51,31,-13,62,-11,20,34,37,-18,-10,6,46,27,21,-9,20,-18,-12,-17,-27,44,-1,-37,-24,4,-8,18,-5,-24,6,44,19,35,34,23,63,-2,-4,29,41,7,11,42,20,26,-28,14,32,-28,16,14,31,3,34,-18,0,2,-16,4,-4,-27,17,9,8,65,-5,-1,32,26,13,-14,18,-14,12,30,22,65,11,40,2,-14,22,1,24,46,11,-30,16,12,17,14,43,4,-6,38,-2,10,-13,9,88,-13,-31,41,41,-18,6,36,21,37,-21,20,-4,26,40,24,19,5,-5,-16,-36,3,-5,13,47,3,50,30,20,23,-3,38,86,-2,-13,39,36,-13,30,-20,21,13,17,-18,1,-29,21,-4,-29,14,-7,-17,-11,26,3,-26,17,2,10,-11,16,12,4,-23,31,-17,-6,13,-26,13,22,0,-15,14,-17,-29,15,-1,-16,0,-13,-14,1,9,25,23,-25,28,-17,-28,-20,-8,-24,-3,-28,-5,-19,-24,-23,20,-23,-19,24,2,-20,-24,24,25,-12,5,-9,-4,16,2,30,-22,-3,13,5,-18,-15,-20,-22,-16,30,-6,8,-20,-6,19,23,-6,9,23,26,-16,14,-4,-11,-18,18,5,1,-16,22,-3,-12,-29,23,18,9,-14,-23,25,20,-11,-24,-15,30,-21,-17,-11,-27,-17,4,0,-25,-15,-26,-17,30,14,-23,17,-20,-1,-8,-30,-13,-23,-1,3,13,-29,-9,-14,-25,-10,-8,3,22,-18,-22,-16,2,24,7,23,-26,-9,16,19,-7,-14,20,-16,24,0,-26,-17,11,-27,22,-11,13,10,28,9,16,3,-3,28,11,10,-8,2,30,30,11,29,-20,-12,14,-28,-19,-5,-19,-9,-21,3,17,-2,3,24,-10,-27,20,-10,10,-13,-16,-12,-16,-17,27,26,24,29,5,21,5,-10,20,-15,-10,14,-5,29,24,-2,-13,-1,-19,-22,8,-3,-17,-7,-14,15,-27,28,23,7,-8,-7,-25,19,23,-24,5,-8,-1,22,-24,-15,-27,20,-14,-6,-27,17,14,25,-14,-26,-21,-3,20,20,-22,-26,14,1,3,4,-6,-15,4,29,-16,-27,30,-11,21,19,-16,-25,-30,1,-13,-6,-26,-21,-26,-25,10,12,-32,1,-1,14,15,8,-1,-12,-8,11,31,-30,15,-28,-11,18,10,-1,-4,-32,-13,26,1,-2,29,9,-14,6,21,12,4,-15,7,15,21,-10,46,52,-11,-11,13,7,32,-28,38,-9,1,2,-11,-17,18,-23,-18,13,7,1,-1,-11,14,11,-23,17,24,-6,-27,-6,-16,22,44,41,-8,-10,17,23,-10,-17,-1,10,-31,-24,2,21,3,0,22,2,-17,-8,28,-27,-13,8,-19,17,-3,-6,-14,-11,-33,-7,61,58,-18,4,-26,24,11,11,2,-7,-22,-14,-27,-5,0,16,17,27,-33,14,-12,18,-6,-17,-31,6,-30,-33,-29,32,21,-10,53,9,-10,15,1,-31,18,-28,14,30,-16,-30,-28,-5,5,12,-16,24,-16,25,-4,23,-29,22,-28,-25,20,-28,-7,7,-6,-8,63,58,-29,14,1,-32,-19,25,9,-25,8,-27,-29,-14,-1,33,6,3,-17,-38,2,-27,-16,-20,-13,-13,-31,16,-5,26,-6,28,12,45,21,35,22,21,-6,21,7,10,-29,29,10,10,-30,26,-16,18,-23,-2,26,26,2,-18,-14,10,23,-32,-27,-5,7,0,60,31,-7,12,10,1,13,-16,32,-3,-40,25,-7,-12,-30,31,21,28,22,2,24,7,14,5,15,16,-16,-17,-9,-2,22,7,22,70,-19,38,17,-3,-13,-4,1,-25,-31,-15,-16,-29,-23,20,-1,-10,23,27,10,44,25,26,3,34,29,6,6,-17,14,26,5,-25,19,-25,41,37,-3,-7,-19,24,34,-17,-6,-12,47,9,52,6,-13,19,-12,2,-13,16,23,15,22,0,13,-19,-24,29,31,-14,37,-23,31,7,-30,-5,22,13,-3,27,26,3,3,-22,49,23,-26,-23,24,22,-28,-20,40,22,-3,24,-4,-26,-2,28,18,22,32,-1,21,-7,-12,30,10,-18,31,-26,15,12,11,22,1,7,-21,-3,-17,41,-10,-20,6,24,4,4,-7,7,13,15,-5,-3,26,-16,12,19,19,-12,1,33,30,-16,-9,0,54,18,10,-9,-8,-12,28,27,15,0,43,-10,-17,46,-7,9,8,8,11,-10,10,29,30,4,-7,30,-20,2,-30,28,-19,29,34,22,46,13,-6,-36,-5,-12,18,20,14,2,-7,41,-14,-22,-20,-18,-20,-32,-26,-14,17,2,-11,12,-25,-25,-15,-23,-19,33,32,17,50,30,-16,-17,5,6,-10,24,16,13,-18,27,-6,-25,-10,37,26,-23,26,30,-3,13,-3,27,-9,-7,14,-16,21,4,52,20,58,24,19,-23,-22,45,-10,9,6,18,-5,39,4,-14,21,12,19,-35,32,5,20,-17,-22,-4,-28,18,-10,13,36,12,4,32,4,1,5,-22,21,40,0,-7,10,-15,-7,46,18,20,16,41,10,16,-10,-16,-4,5,25,26,-38,24,-32,28,5,-26,14,19,11,17,23,28,27,31,-1,-20,15,-3,27,4,-7,1,-1,6,-5,-11,1,19,-11,25,-17,0,-3,-4,-20,-25,-5,-13,-22,11,21,29,9,32,19,-23,-8,-15,28,29,5,8,-3,-24,-26,-19,25,-14,5,11,-7,7,29,-11,13,9,-26,23,-14,7,-20,8,-6,31,-27,23,-8,-1,-14,19,-25,25,5,18,30,12,-14,10,21,2,-20,-27,-15,-25,-8,-4,1,-15,-26,-2,6,-16,-21,-8,19,-15,-9,-18,24,-17,-28,-22,15,29,0,-27,31,-21,-21,7,25,-7,12,6,27,10,-5,8,-28,36,36,23,16,10,9,-21,-10,-21,-19,13,30,-2,19,5,-25,15,20,-2,-24,8,6,-16,25,-4,13,-11,-7,-13,1,-27,-21,7,-10,-23,-12,-20,-7,-22,-21,15,-7,-6,30,-24,-24,-1,14,21,6,-5,22,5,-19,5,20,-19,-5,-26,21,12,25,8,-24,24,12,25,-24,-15,2,0,26,20,-19,28,-19,-3,-6,27,-9,-8,-22,29,31,10,-4,24,-2,16,32,-10,9,-2,25,14,3,-6,39,-10,10,21,0,-31,18,8,-8,28,-25,0,-4,29,0,-22,27,14,16,18,-5,23,29,26,20,-20,21,-14,-5,28,-26,33,15,19,-25,31,-7,-1,25,-26,-9,21,28,32,-21,-4,18,-14,3,11,-12,8,-16,-4,-26,2,4,-3,-20,-6,-28,1,-28,3,-23,20,20,-28,31,-20,20,7,33,23,-7,-15,1,27,10,16,12,1,46,-29,-18,26,-17,7,3,17,-33,-23,-23,27,20,-19,43,-15,-2,-22,19,-24,-30,-11,35,4,-4,-8,2,8,5,-20,20,2,4,-7,11,17,-22,6,36,-5,-2,-12,12,20,-1,16,48,-17,-3,14,-23,-4,-14,-14,8,48,-21,-14,-15,5,-25,5,-17,9,41,-7,16,19,-24,4,-14,-29,23,-9,23,-9,18,11,71,-27,-28,27,6,13,5,-27,-1,35,2,28,-6,-24,-24,32,9,-28,10,-20,-40,-2,-19,-1,12,-11,-29,32,27,18,21,23,41,-22,7,25,14,-16,-28,-17,22,54,-8,-18,-14,13,6,5,15,-32,1,-13,-38,28,21,20,-9,-21,25,35,10,4,12,23,34,-29,-32,-16,-16,15,-42,-31,41,9,-7,-10,36,-11,-26,5,-2,-29,26,-12,-36,28,-3,9,6,-6,-9,2,29,16,-3,41,80,-28,-40,-25,-26,-30,-37,-12,-8,3,13,27,-8,26,7,-4,21,-32,20,-10,-12,-8,13,24,0,17,-11,23,28,44,2,22,79,3,16,16,-29,-6,8,-19,-7,50,-30,-5,-2,-18,-7,25,-16,1,40,-19,16,15,6,32,33,-32,-3,11,24,44,-8,22,66,-21,4,1,-36,-39,-17,20,15,29,-33,-4,0,19,-24,3,12,-35,2,-18,-37,-14,15,41,-2,1,-15,-11,0,-6,26,28,81,28,-24,0,22,1,-31,1,18,-7,5,20,22,-19,13,7,-19,44,31,21,-9,27,-19,-5,-3,9,-6,7,-33,-12,-11,11,18,9,9,-24,40,3,-8,-8,-24,7,-2,14,15,19,18,11,-13,11,20,18,-19,26,5,-20,-8,-15,9,33,16,-3,-11,24,-2,15,5,14,33,-11,10,37,10,0,-13,25,-18,17,11,-2,-29,28,-28,-20,-19,-9,-18,22,7,31,39,29,0,-14,24,35,-12,10,30,-26,39,-23,49,30,-21,-29,24,17,4,-19,23,-15,-19,20,25,21,11,18,-15,-14,-20,20,20,32,-18,-17,6,-10,-1,-15,-13,1,20,-4,46,6,-21,-4,-4,16,23,18,6,19,-16,38,15,-23,-21,9,1,26,21,28,-8,4,20,-6,16,31,-26,-2,16,-11,11,-5,18,22,-16,16,0,2,-9,24,12,15,2,0,-2,21,20,-16,-19,-36,33,-16,-4,32,-31,-18,-17,-13,-11,-3,34,18,45,28,47,-11,8,19,1,-4,7,-2,25,-24,-6,7,-28,4,-14,22,10,14,14,-18,22,31,-6,-5,-13,18,-18,21,29,-3,0,29,22,32,5,-36,24,31,21,19,18,-8,16,11,-31,-16,25,23,-25,-30,-19,-28,11,-10,11,17,19,-7,-36,15,4,17,30,-4,51,-14,24,-40,30,8,-4,-25,28,11,19,31,-22,-19,-12,39,-23,-23,21,2,8,33,-28,-11,3,36,-28,-26,-8,2,13,-14,31,24,23,-26,25,4,-21,18,-13,18,-12,6,-17,-23,-1,18,11,-19,-1,11,3,6,29,30,15,-3,-12,26,-21,-14,-29,-16,3,12,-24,-21,-26,-28,29,17,-27,-15,5,1,-28,-6,-9,-7,-15,16,-29,16,-10,21,-15,-7,-24,-24,9,1,23,16,12,13,-26,26,11,30,-10,4,-9,-16,19,15,15,16,12,-29,-13,13,26,25,-2,8,20,-7,-30,-1,8,-1,19,-18,3,-16,26,2,14,22,18,-2,-1,27,0,-14,-7,-20,-10,-4,-20,29,-17,-10,-3,-22,-27,14,-12,-6,-18,17,-9,-28,13,8,22,-4,27,8,5,2,-11,-29,30,-16,-12,29,1,22,-14,-19,4,19,9,-18,21,-17,-3,-20,-27,30,-21,1,21,-18,22,14,2,-29,-10,-2,12,-28,22,-14,16,22,-27,-10,29,15,30,11,28,1,-28,15,-17,-26,-10,-20,-14,24,28,-3,-22,-24,-24,-14,-15,3,8,25,8,1,30,3,0,12,-28,24,-13,7,5,-14,-26,18,11,27,-22,-8,-6,-27,24,10,-9,-1,-30,-7,-9,4,-13,10,-13,-26,-27,30,-27,13,20,12,6,20,6,-25,-6,-11,-27,24,-8,-19,22,-2,-24,0,0,-10,8,1,-2,-12,-7,14,-19,22,24,15,3,19,3,-2,7,15,-19,13,-17,14,29,30,-17,5,29,-17,24,1,27,-28,-27,-18,-5,12,-29,-20,10,4,9,-10,2,27,-25,-21,26,62,1,-20,33,17,14,13,-10,-34,27,-32,-39,-22,4,-3,16,-31,-39,31,21,-8,23,46,67,-13,-37,-11,-40,-23,-31,2,-7,16,17,19,30,-7,0,-20,-29,-46,43,11,-9,-27,-5,9,18,-22,-20,29,33,36,28,42,90,-2,-35,24,-35,-5,-49,3,28,8,-24,-29,-8,-4,-34,6,14,-45,25,-27,8,31,1,3,30,-33,-27,-15,-11,19,22,33,52,-7,-42,7,-10,-49,-5,-28,3,6,-23,16,16,-4,-37,27,21,-1,30,5,-15,19,1,18,26,4,-10,36,-7,23,32,18,86,-15,-20,-10,-7,-30,-45,-9,-8,8,-8,4,27,-1,-6,21,17,-45,37,12,4,13,0,10,15,-32,14,2,4,11,23,55,63,12,-19,29,10,-36,3,-31,-3,43,10,23,0,-20,18,-16,-29,16,47,2,-4,-21,-2,46,31,15,28,-12,-13,38,50,11,57,-13,-3,-6,-29,-3,-42,-4,6,-1,-19,10,-19,10,20,-10,27,-34,44,6,9,-27,17,27,-3,-7,-17,-14,28,29,29,47,75,21,4,-25,14,-42,-19,10,20,8,-13,-17,13,-23,-1,-28,22,-21,39,0,-13,-1,7,33,-10,-9,11,-23,17,25,30,51,30,28,6,13,-6,-21,-2,-21,-7,2,-36,-26,14,-18,6,19,-10,-33,12,-28,7,11,-7,-9,11,-29,-20,-13,21,39,-3,38,54,15,-20,-3,-36,-21,-39,30,31,-6,-26,-14,-7,-8,-19,13,-16,-19,13,-10,-5,21,17,24,2,16,6,-14,-32,-5,-22,26,45,14,28,-21,13,-23,3,-14,-8,21,11,3,30,28,34,23,-27,30,24,14,12,29,17,-6,1,17,28,37,-22,25,-24,-12,45,10,-27,-2,-10,22,42,3,19,26,28,0,22,-28,14,2,23,13,-15,13,-18,14,1,-6,5,-6,1,-6,-30,35,-26,31,33,14,3,-4,9,32,3,14,-10,16,26,-2,7,-29,29,14,-5,-1,-5,-20,-16,-10,-6,2,48,6,5,17,-4,38,-10,3,55,31,8,30,47,-29,-8,29,-21,-16,-5,-5,18,30,13,18,11,31,24,9,-3,21,-7,8,19,8,17,21,-18,-5,-13,16,60,25,-8,24,-9,-6,8,-5,-12,38,14,19,27,24,41,35,2,23,-15,-26,12,10,-23,-1,2,11,-16,-13,-27,7,-10,8,39,25,18,2,-7,-1,-8,19,5,35,2,29,-24,-14,13,26,-21,20,25,-20,-33,28,-6,21,22,-29,22,17,-27,14,-14,34,38,5,3,10,18,6,21,27,4,-13,-30,24,-23,14,-8,13,7,8,-12,7,11,16,14,8,36,-16,37,5,-24,29,-4,-15,1,29,11,4,27,10,7,39,-4,-2,23,11,14,-7,32,28,17,33,18,3,20,14,-19,-39,25,8,29,40,-2,9,-14,11,-5,-15,15,18,41,-27,20,1,-26,-7,-8,-17,-23,11,-4,30,-28,-28,18,29,28,4,-20,-15,16,13,25,-14,2,-1,-20,-13,-6,-25,-9,-7,-9,26,20,13,-18,-23,-6,12,0,-5,6,-24,15,-25,-16,-12,15,-10,8,25,-9,10,-26,-27,-2,-10,25,-4,-10,24,-19,-19,18,-14,24,-16,-20,3,26,-7,4,25,-6,28,17,-22,2,-30,-3,15,8,-10,14,-22,17,-11,25,-22,-29,5,-24,-13,-7,-29,13,14,13,-12,-3,4,-14,-27,8,11,17,-3,6,-2,-3,-2,13,-19,11,25,6,-9,15,2,-3,-15,27,-20,-11,3,19,-19,-7,20,5,-22,-7,-10,-13,12,-9,-21,-16,-13,3,-8,3,-29,25,18,4,-5,9,25,-25,31,-6,17,15,-25,-13,-6,17,9,11,-27,-21,18,18,21,29,2,-20,26,-15,0,27,-9,17,4,-26,-14,4,27,-12,21,27,1,27,-23,30,-5,-1,22,6,-1,-15,14,-16,-25,-16,21,19,-13,-18,6,17,24,24,-18,6,9,-4,0,12,-24,0,-27,9,24,-22,4,-20,-18,-22,-20,29,-13,24,-23,-26,-19,26,2,8,-18,4,25,-20,-30,-10,21,29,-21,-16,1,-14,-4,-18,-29,28,-8,18,2,3,-5,-8,-27,-29,8,-28,8,14,18,-28,-7,24,-12,-6,-5,7,-20,-6,-9,26,1,-10,11,23,-19,-24,-22,-3,26,-23,-7,-18,25,14,17,10,-5,-16,-1,18,25,-8,-22,2,8,2,-1,16,10,-9,46,12,-14,-6,3,-14,31,3,17,-7,-11,8,1,-21,-14,-34,-2,1,-11,4,27,32,4,-15,5,-29,37,20,22,16,6,21,31,-12,7,26,14,28,4,31,-20,3,-23,8,-11,-9,0,-40,-27,7,13,27,26,-1,35,19,-35,30,30,2,12,22,17,11,25,2,32,1,27,38,15,-24,-3,17,24,-29,-10,-8,-6,-32,19,-15,-20,18,-25,35,-19,-16,-25,-15,7,13,-10,21,-3,-4,36,22,1,19,29,16,-4,27,30,19,33,-21,11,27,15,-37,-15,-16,29,47,18,51,39,14,-36,7,-5,2,-2,24,-23,8,46,-25,9,-10,32,33,-5,-23,2,7,2,-6,16,22,9,4,-15,9,18,48,-7,29,-19,-16,-18,26,24,-1,18,19,-10,8,38,-15,-9,9,-15,-18,-3,16,-23,0,6,3,21,17,-6,-28,-23,31,21,45,8,2,-6,-22,-33,-2,33,-1,0,31,23,13,33,-31,26,-19,19,-18,-3,13,-14,-13,14,11,-13,12,-20,-9,-15,21,4,47,24,38,31,-26,6,13,-9,-22,-11,18,9,-10,12,21,-19,1,37,33,29,-7,13,30,-4,15,-2,-16,23,-22,16,17,-27,47,-15,15,-13,7,-7,-8,19,-21,22,39,4,-22,48,-35,3,30,20,3,-15,15,-19,15,8,-15,-26,-18,28,-4,-27,15,-26,23,-1,44,18,-5,4,-13,8,-10,13,11,-28,29,-14,-7,16,-28,-23,19,30,-25,-17,17,5,-4,6,9,-26,11,5,19,19,7,-11,3,20,-24,17,13,-25,17,10,-12,-13,9,3,10,-6,-22,-18,-14,11,-13,24,8,16,22,-18,-26,-13,-10,5,-25,0,3,-2,23,20,9,28,-26,20,-23,26,15,-16,-10,-6,22,-7,19,21,19,14,11,19,-6,25,-10,13,13,10,-17,-10,26,-25,-27,17,-20,-2,-15,-21,-20,13,28,4,-18,2,-14,27,6,-19,-4,-10,31,-2,-9,-26,-14,-20,-23,-23,19,-21,-26,1,23,-20,-27,11,18,-10,-29,-18,-12,-1,26,13,28,19,-1,-25,-22,14,14,17,-21,-12,-19,-19,23,-14,28,8,-22,-8,-13,-20,0,-22,15,13,-14,-24,-4,-20,21,10,-6,8,5,19,-13,-24,-14,-9,26,12,17,16,-17,16,-15,-11,-20,-29,15,8,24,18,20,-4,-25,-19,26,-7,0,-21,-19,-4,-11,-28,-28,-11,-19,-16,14,-6,-29,-22,8,-20,27,-4,-28,-30,4,20,-17,-16,23,27,8,-22,5,13,-8,-22,-19,12,4,23,17,-22,30,3,8,-24,19,-3,27,30,-1,-9,8,-12,-12,-17,19,-16,-15,26,-18,-4,-2,16,25,3,7,26,24,-14,-16,-24,13,-22,-29,-22,27,22,27,-2,-22,-22,-8,9,18,-3,7,-21,2,22,-21,-13,-16,-22,20,-14,29,14,-25,-9,-20,-30,-1,4,-12,-3,-9,32,-7,24,-6,-30,-34,-1,-14,-14,-8,13,-8,32,13,-34,19,57,-26,7,37,-4,60,25,20,-15,2,22,-11,3,15,14,6,28,0,16,-7,23,-11,53,18,2,20,11,7,-22,13,21,18,51,-25,-3,16,-27,56,10,23,28,10,-3,-16,30,-15,9,12,17,8,5,34,26,23,51,-21,-27,30,5,7,26,15,-29,22,41,-37,-12,22,-12,47,30,13,21,-21,-27,24,51,24,-20,34,38,-26,18,26,-5,-7,21,27,-15,16,-6,-11,27,-39,-27,-22,7,-21,-10,2,-16,62,-20,67,11,9,-25,-13,55,-14,-21,51,12,-1,51,-22,4,-1,20,12,-6,40,4,53,-13,4,9,-20,54,14,5,-16,-31,14,29,35,-6,-9,-30,32,47,-1,5,19,-14,-6,54,-3,-31,1,37,-22,-37,16,-24,37,29,-33,5,-20,0,26,29,-5,16,62,12,43,39,-1,-29,-19,46,-6,-6,48,-8,-10,45,30,-13,-5,32,-8,-41,22,-21,-11,40,-11,26,-32,2,-27,12,-7,24,25,2,59,-8,17,-29,-14,37,5,1,15,42,-15,50,-9,-18,-32,-6,-19,-21,6,19,46,14,-2,-4,5,37,20,-19,40,-3,28,32,44,12,25,9,-19,27,18,-24,45,35,1,44,16,-23,24,25,-4,-66,53,12,22,20,-7,-25,12,37,32,4,5,2,66,5,14,3,-27,-15,19,-10,27,-4,-3,21,2,-14,14,19,14,28,-1,3,10,19,-12,15,-23,-26,-11,-24,16,-20,21,15,1,-18,-29,22,24,7,-13,-17,-3,18,-23,20,23,14,-21,8,8,-24,-18,0,-25,-1,-3,3,-21,-21,9,22,-22,-10,-16,-24,13,-27,21,-3,10,-10,14,-1,22,6,1,6,1,-8,18,17,-20,20,-10,21,22,21,-27,2,-24,-13,-24,-13,-17,-12,3,-15,-24,4,-8,-9,-10,21,-7,8,-30,12,-13,-28,-24,3,-1,-7,-15,-16,-23,-16,-13,23,-28,-18,-9,30,-2,16,2,3,-17,-13,-19,-29,-2,-27,-2,23,-22,-15,4,12,16,-11,22,-2,-21,-11,22,-27,-16,-26,-4,-28,-9,-6,-16,-12,3,11,15,23,9,2,-2,1,30,27,-1,-19,18,11,8,-17,-11,27,23,9,0,12,-4,-21,12,-11,2,2,16,11,-28,28,2,-25,-23,7,-3,4,-21,-26,15,3,3,-20,26,25,3,15,-1,4,-21,17,-18,-28,0,-4,-18,-19,-13,-20,14,-11,-2,-3,-18,-1,23,19,-14,2,9,29,-9,25,26,-21,-7,-7,24,-26,-8,-1,-2,-27,-29,25,8,-10,-5,-17,20,-21,13,5,-10,27,-21,-27,13,29,-18,-6,-2,20,15,-19,-23,-5,27,-20,-14,13,-27,10,-16,-3,-29,7,30,27,-27,29,0,-7,-8,-8,23,-21,9,-15,-1,5,-26,-19,-22,-24,13,-14,13,-1,-27,18,13,22,-26,-13,11,-24,16,14,0,-15,37,34,-12,-7,-10,15,30,17,37,34,-17,-17,-16,-17,6,-4,-9,-3,5,26,24,10,-9,22,1,-10,-26,-17,23,28,-12,-14,9,63,13,-8,-2,-11,-20,-22,36,21,-24,-24,-24,19,16,25,15,-20,-1,-9,10,23,13,-27,-9,7,26,-28,-13,-7,24,36,44,36,22,8,29,23,11,-27,52,19,-22,-15,12,3,23,-24,-23,8,-35,-24,-9,16,17,16,-10,-17,-18,-29,-12,22,-20,-28,0,34,16,18,-26,2,11,-24,20,18,-14,1,24,-15,-9,28,7,18,5,8,-31,7,-1,-22,-26,-2,-25,-32,-28,-8,13,-15,29,35,-31,4,-16,-8,-21,-9,24,40,15,11,24,18,-13,-18,-39,23,-9,-23,4,6,14,2,24,-27,13,5,-5,-5,-6,19,51,81,11,-16,-6,4,18,5,39,13,17,-1,-16,-27,9,0,-6,-16,20,14,-7,0,28,-29,-16,-26,-16,-25,-2,-11,-18,-15,20,16,24,-11,18,17,6,-20,-9,25,1,-14,20,12,-17,16,-16,18,1,-20,-14,-28,24,-15,23,14,-27,-32,-23,38,20,26,8,48,9,-22,-1,-27,14,-17,38,24,-8,-9,-5,-23,17,25,-8,27,21,11,13,-25,25,15,24,-29,6,-35,11,30,-28,-23,7,77,7,15,-30,14,-15,-26,10,39,2,5,-12,20,-21,-21,-16,25,27,-2,4,-2,10,28,-17,-21,-30,-6,-8,-15,12,-1,26,13,20,-29,17,34,0,-2,-12,16,8,4,-4,10,3,6,8,9,25,8,12,-9,-21,13,-20,17,19,15,-22,-20,9,-25,29,18,21,-19,28,33,-18,-10,14,6,1,-16,19,-20,14,-27,33,15,-17,-8,-22,11,0,4,1,-2,-8,6,-28,2,29,-7,26,-10,5,-23,2,-1,12,7,11,-21,-12,26,31,-12,-1,-25,29,8,-23,-30,-24,1,-11,7,18,9,-25,19,-7,1,13,-20,-20,27,-14,-28,-26,13,11,-19,-12,31,13,6,9,-3,12,-8,26,34,-21,-18,-16,-1,-27,8,29,-10,-11,-16,-2,-24,-13,-13,-29,-27,36,13,13,-17,-24,-9,-22,-16,20,2,23,-23,-5,-9,0,6,25,-26,7,0,-18,-21,26,10,-19,40,21,3,-4,-9,6,-30,24,21,33,-25,2,-35,-4,-13,1,3,1,-14,17,26,41,17,-17,8,1,8,-12,-15,35,-1,22,19,-19,-28,-16,-18,-1,-18,25,-23,-25,35,-24,-7,29,-29,12,-22,-17,0,40,-5,25,31,-19,-14,-16,13,-25,-5,33,29,-26,-2,5,-24,22,5,2,27,25,-13,27,14,-25,-25,4,31,12,-28,-22,-10,35,-20,14,27,-15,-13,0,-20,15,-7,-4,-11,3,-8,20,-14,28,0,-22,-12,14,10,-5,-23,-16,-6,-25,-14,4,8,33,-12,-3,14,2,1,13,7,25,-22,15,-26,2,8,2,-33,46,0,-15,27,-29,-11,-7,1,-8,-22,13,51,-6,-3,62,12,-17,-2,8,23,10,-21,7,23,22,12,-14,-13,8,32,-7,11,14,-18,-35,9,5,13,4,23,29,4,31,0,8,14,62,-7,-24,4,-32,-14,-5,15,8,8,5,-17,-2,25,-6,20,15,-3,-12,9,-14,4,18,-2,-18,19,28,-6,-2,36,20,-19,25,3,-30,14,12,-4,19,15,2,51,-31,10,22,28,6,10,-9,1,37,7,6,14,9,15,-6,16,-9,7,25,19,-5,33,64,-22,11,16,0,-35,-11,-21,-7,38,11,19,29,-15,5,8,28,-13,27,-17,-15,-25,-19,21,2,22,21,15,-22,-2,-11,30,33,4,-1,-14,3,1,-22,-32,16,51,-24,-17,23,13,-29,19,9,-41,-10,-7,-28,-15,-4,-2,-21,-23,-3,29,23,40,-21,1,22,28,-30,-11,10,-34,-5,-31,-17,10,-32,17,30,-13,3,29,16,-14,27,-20,11,-21,-4,-25,-18,13,1,-17,-25,28,-12,1,49,-9,13,8,19,2,-1,-22,-19,-9,20,3,-2,20,-30,-11,13,-33,13,21,-13,0,27,35,-14,15,-8,27,-23,37,29,-3,39,25,22,-13,-13,-5,-10,-8,20,50,-32,6,34,-3,16,-22,-25,-4,20,-30,-12,7,4,35,23,19,-25,-27,10,25,39,-10,45,-2,-18,-16,23,-30,-18,-3,2,-23,24,30,-9,-25,-11,8,5,33,25,20,24,-23,17,-3,-2,11,7,16,-27,-15,13,16,-29,28,3,23,29,17,22,4,1,-19,14,30,23,15,8,0,-25,21,3,-21,-1,25,-6,19,-22,-30,11,-5,6,27,23,9,-25,-1,-3,-10,21,-23,15,11,22,5,-23,5,-1,14,17,17,-6,-3,15,21,-1,29,-4,24,8,-7,22,17,0,18,17,-5,18,-19,-17,-28,-21,5,2,-4,-12,-27,-13,17,-5,9,31,-3,-22,29,-23,15,-15,19,-27,18,6,4,2,2,-33,-17,28,-5,-27,-9,6,-31,20,-6,28,-25,-30,-1,-30,28,4,7,26,4,-17,31,-17,-17,-5,34,7,-9,10,-23,7,-23,-26,20,-11,-3,-29,8,-8,-10,-16,4,-9,-21,22,-21,7,-12,-18,-19,15,28,-12,-7,-21,6,-4,18,-21,3,22,14,20,-22,18,-3,30,36,-24,-2,17,-24,32,-18,32,4,21,-11,-16,28,-28,25,-13,-14,10,35,-26,-31,-14,23,22,26,20,-27,30,14,-23,20,-26,30,-26,12,23,-4,0,-21,4,15,19,18,-17,-14,-12,2,26,13,15,-16,-8,-17,-26,24,-3,-30,32,-22,1,-14,-10,-10,4,39,-17,13,30,-7,12,-23,-11,-15,19,10,-23,22,25,3,32,3,3,25,0,10,-24,-10,17,1,11,-16,3,-27,-13,8,-26,-23,-30,4,-1,-10,-2,2,9,7,6,-19,19,3,4,-14,9,-31,19,25,-8,30,-10,27,-15,19,-2,13,8,28,17,-19,-1,17,11,-8,14,-26,-9,-1,-5,-21,25,-25,-27,12,14,26,13,-31,-15,-5,5,-16,23,33,-23,2,-19,14,14,-7,-20,-22,-18,10,15,5,9,-20,-15,24,2,-26,-12,-13,25,13,4,30,24,1,16,-13,-26,0,31,34,-11,-2,-33,7,31,-29,6,-7,19,-21,6,22,-5,-31,22,-9,-25,20,-18,-25,-27,21,-17,24,-16,-17,-9,22,18,-2,8,-5,20,22,3,26,31,-5,-10,-17,28,-9,-6,12,23,-21,10,-14,30,22,6,-12,2,-6,-32,27,26,-2,29,-21,11,12,-9,1,9,11,23,-2,26,-7,0,10,-18,20,-2,24,-9,9,35,25,9,7,-12,-11,-26,9,26,31,5,21,21,30,-23,6,16,-22,-3,0,12,-3,8,8,12,-28,29,-20,11,-5,1,-3,16,10,-5,19,12,-15,8,30,-5,24,9,10,11,5,14,-12,1,20,-1,-6,-6,0,-5,-27,2,-6,25,32,-35,-26,8,2,-19,-23,12,3,-33,16,28,21,29,-12,-8,5,8,19,-2,26,17,2,15,27,-4,5,24,-21,4,-28,24,25,22,9,5,21,28,-18,-15,1,-13,9,1,13,0,-26,20,-2,6,4,-2,34,16,-16,30,9,-23,-14,26,-19,-13,-32,-12,-19,12,4,32,-23,24,0,2,-20,1,-17,22,-10,27,10,-2,1,-22,-19,-4,2,29,3,22,17,6,-19,-2,-7,-21,22,-24,-22,6,-16,28,-7,17,-14,-5,22,29,-28,-23,-2,4,29,18,-9,-12,3,-17,8,-4,15,-13,-5,13,-20,-12,-28,-15,23,0,22,21,30,-10,-1,5,12,29,2,-17,30,28,-29,4,-7,-2,-5,29,15,25,-6,-16,13,5,18,-25,-14,-17,-21,-27,-5,20,-3,19,-29,1,-10,5,24,-21,-4,-27,-22,-28,-3,-17,7,-21,-13,5,-19,-1,-25,7,30,22,-15,0,4,-5,19,-18,31,1,-8,29,-15,-14,-14,24,6,28,4,-8,2,30,-23,-29,-9,18,25,-1,-27,-21,-9,-18,-11,-2,5,-25,24,-4,24,-10,-15,-29,20,-24,-26,11,0,18,-26,-9,-8,-13,15,-11,19,16,19,-14,-1,10,1,-27,9,2,20,-9,-7,-9,-12,-22,15,-10,28,-27,-3,-2,-3,11,-19,-24,-21,18,-19,8,8,21,-19,18,19,24,31,-20,-28,-28,-12,23,-10,-2,-16,-5,21,-22,-15,-11,-25,9,-6,20,-2,-28,24,-11,-28,7,20,-4,-25,-10,-26,-2,-26,-2,23,-11,25,-7,-28,-10,-28,-9,-3,-13,0,-10,5,-23,-2,15,23,6,11,26,1,23,-14,-15,-27,27,1,30,-28,24,-20,-27,6,-6,5,7,-11,13,6,-25,21,-27,-29,-23,0,4,-19,6,-20,-19,-24,26,9,14,-11,29,-26,-14,-16,-12,-12,11,-15,-24,-24,-12,4,26,-5,16,17,-24,-1,-1,10,-2,-28,1,-24,-6,19,-17,25,-3,-17,28,9,-12,25,-21,25,20,6,25,-4,-20,18,-1,12,7,-13,0,8,25,21,1,-15,20,21,20,-1,4,-14,-19,23,1,-3,3,-1,18,-2,27,0,27,-6,1,-20,10,-22,-9,-28,-23,-20,14,-22,12,-12,-2,-8,-16,1,-28,-29,0,9,21,16,2,30,22,-26,11,-26,-10,-8,-29,18,-24,9,-5,-20,25,-3,21,17,24,30,-18,21,26,-22,24,-26,0,-17,-2,34,-15,-28,14,8,-22,-25,-3,3,3,-14,17,-23,-17,-12,-7,-13,-27,-17,11,-5,-31,16,-33,2,-3,25,-6,8,26,8,17,-7,-8,2,-9,3,-11,-4,-8,29,30,22,-20,-21,-14,8,20,11,-30,-26,13,-15,6,22,1,-25,-4,5,24,-18,-19,24,-15,-6,26,-18,-10,-8,-20,7,6,23,-5,14,20,14,20,-23,-16,2,-12,22,9,-1,-35,-25,16,9,34,23,18,28,-24,10,23,31,22,-27,-31,27,17,1,19,-14,16,-17,-25,-13,17,-28,16,-28,20,25,12,-9,-27,7,2,21,11,1,-1,12,-18,7,17,7,14,30,-26,-11,-4,-14,-12,-9,27,-6,-29,-26,-24,11,4,30,21,-18,-17,-16,-11,18,8,10,8,22,-23,-3,19,-10,-8,24,37,4,-22,-18,-24,-9,16} +#define CONV2_WT_SHAPE 9216 +#define CONV2_BIAS {38,-86,8,80,-99,-14,13,-74,126,-38,-127,2,40,-82,-1,18,0,-26,-57,-61,-38,20,-55,-54,-25,20,-65,-51,-45,28,-27,58} +#define CONV2_BIAS_SHAPE 32 +#define INTERFACE_WT {19,-7,5,11,1,-8,8,17,1,-12,-14,12,16,-11,2,4,-26,-14,14,10,8,-1,-12,-7,-11,-2,-19,-6,-10,7,16,7,-11,-10,-48,-9,15,5,58,23,-10,-15,12,-12,12,1,11,-8,-11,-11,-14,0,-11,-9,10,-5,-14,2,-23,-1,15,23,1,5,-8,-20,0,4,19,17,10,12,15,-4,-36,-16,2,-7,33,9,16,4,-6,-2,14,1,4,7,10,0,-19,1,2,6,35,13,5,-10,-1,-15,12,-14,12,20,17,8,3,10,-7,1,23,16,0,-2,-1,9,15,-3,14,-4,-6,6,-7,-9,-10,16,-5,-12,-2,6,-2,-1,8,-8,17,11,3,5,-12,-5,-13,-6,-11,-2,-5,2,-8,5,33,15,-7,-9,-3,-9,-12,-3,11,-10,11,1,0,-3,-22,-21,7,-2,49,22,8,10,3,-8,15,1,15,13,-10,13,-12,-9,4,10,5,-1,-18,-1,-14,-10,21,-3,3,12,-18,-7,6,-9,18,20,9,-7,-9,9,-19,-20,-5,7,30,22,15,0,11,7,3,-1,9,-5,-6,-3,-12,1,5,6,48,21,-12,-9,-4,-1,6,2,11,11,10,0,10,-3,5,-5,16,-3,-5,-5,-1,-5,7,-6,6,15,0,-2,5,-10,-7,-10,-1,-9,-2,9,-8,11,-4,10,13,-6,-1,2,11,-12,-11,13,5,-7,7,8,4,15,12,-5,6,1,-1,-14,6,-12,0,-9,21,-12,6,9,-27,9,10,6,45,-2,4,-2,-12,8,-1,14,-3,-9,1,18,-5,7,0,8,9,8,-7,10,-8,6,20,-12,19,-7,-14,2,10,-3,12,12,10,-17,-11,1,3,0,6,10,19,5,1,-4,3,9,-14,-5,-9,4,-5,13,4,-19,8,6,26,8,-2,11,-14,11,1,-7,0,15,7,-12,11,2,16,16,21,-4,-9,9,-3,7,10,6,2,-13,11,-10,-11,-7,4,-2,6,2,14,-12,-3,-6,12,8,12,-15,2,-11,4,4,0,-9,-8,-6,0,20,11,14,27,11,12,-8,1,15,14,4,1,5,-1,-4,6,16,8,21,-3,0,25,-6,3,9,-6,-5,11,10,-7,0,0,11,16,5,5,12,-4,6,-14,11,7,-9,30,-1,7,-14,-11,-7,6,-4,28,-3,17,6,1,1,-9,-1,-14,7,13,3,9,-14,8,14,-4,2,9,1,-6,7,-7,-4,5,13,3,8,-9,11,-12,-1,-11,0,13,13,14,6,-12,4,7,11,15,4,11,-11,-6,-2,10,-9,-5,7,9,0,-9,-10,9,4,2,10,6,11,1,1,11,-6,8,-7,-4,7,-2,-1,9,1,-15,4,-1,12,5,4,29,6,13,-2,-8,-4,11,26,-2,-1,34,13,7,-9,6,33,1,-9,66,12,-5,6,-7,14,13,18,-2,-11,1,8,-3,0,-5,6,-5,4,1,9,-6,14,25,14,9,-9,-5,1,0,22,11,25,11,2,3,14,0,5,0,10,36,17,5,-14,15,-9,15,-14,4,-4,3,-3,-12,-4,14,22,33,14,14,11,4,9,4,10,2,-4,2,-11,-1,23,-1,-11,10,3,4,12,-12,-1,-7,-9,4,-1,-2,-2,13,7,-10,14,10,13,16,-4,-16,-1,-6,-1,0,11,13,12,-7,4,-18,2,0,8,-5,12,8,-14,23,22,-2,7,-8,-9,-13,26,3,4,27,-10,10,-13,-11,22,-20,-16,41,14,-7,3,1,-9,19,3,10,-11,6,12,15,1,-34,8,2,-2,-11,5,-6,11,15,5,-1,-2,8,9,14,15,28,13,-14,-16,3,-12,3,19,12,6,35,13,14,-1,6,-1,-7,-11,15,0,7,3,-2,-1,8,21,54,27,11,3,-4,-15,11,7,24,-5,19,5,2,21,-11,-11,0,-15,10,4,11,-14,6,14,-12,2,5,14,8,-14,-3,11,2,13,17,5,-1,-3,13,-1,-1,-10,-5,-13,5,-7,16,13,15,-13,-14,4,-9,9,19,6,-1,9,-6,0,-20,3,14,2,1,-7,7,3,-54,-31,15,12,20,25,-8,2,-4,0,19,6,12,10,0,-2,-1,-12,-6,12,1,13,-23,-14,4,-9,3,7,3,-17,-2,-3,4,-17,4,25,12,-13,0,10,-35,-22,-5,-1,16,11,11,14,9,-5,14,8,-9,2,6,16,-20,3,20,1,13,2,5,5,-3,-12,-12,12,21,-1,11,9,-14,-7,9,-5,-4,13,-4,10,-5,13,13,15,6,-8,3,15,15,2,19,-2,3,-1,4,7,3,-15,9,-11,7,-11,-12,-2,-5,-6,3,6,-11,4,-22,-21,-6,12,3,13,-3,-8,-3,9,-1,-20,11,-6,26,7,12,3,-32,-6,-18,-14,26,27,4,1,-5,1,0,10,-1,-7,11,-7,14,-11,-19,3,-3,-12,-18,-17,-6,-19,27,14,7,-6,-4,-5,0,-14,16,25,5,9,-7,-7,-1,-1,6,5,34,26,-10,0,-1,11,9,-8,-3,-4,-9,12,-7,-8,-2,23,38,11,-5,-1,-18,5,12,1,22,1,-6,-7,-10,-16,-4,-6,-4,7,-11,7,-5,-5,13,-5,12,0,7,10,13,1,4,-10,5,2,10,2,6,3,-1,13,12,-16,9,-7,-10,9,-5,5,6,-2,10,-5,2,-1,7,13,-2,7,12,-5,1,-6,2,12,17,-11,0,-6,3,-8,-3,-11,21,-12,-11,5,6,-6,5,5,6,-12,17,5,-11,9,3,-6,-4,16,2,-12,-6,-13,11,1,-6,-5,-4,-1,12,-19,21,12,3,7,3,-1,-16,4,-7,1,12,12,-11,13,1,-12,-10,10,15,0,-11,3,6,-5,11,-7,-5,2,-9,-4,4,15,13,13,19,-2,19,13,-6,-5,-11,12,0,0,2,3,4,2,0,-11,-14,-8,-3,13,-1,-6,4,15,-15,-6,-2,8,12,-5,-7,-5,2,10,19,5,5,5,-7,10,1,-9,-14,4,-13,14,-9,-16,-13,0,-4,13,-7,7,-2,-1,15,-12,15,-6,17,17,-8,8,19,3,7,-13,-3,-11,5,11,-10,15,-3,-9,7,1,16,1,2,15,-10,9,-9,8,7,-6,-6,-12,-10,12,19,-8,22,2,-4,-7,-9,13,-7,2,-4,-9,-7,-9,5,-7,14,-2,11,-15,5,-3,6,5,3,6,24,18,3,4,-11,-6,7,2,1,7,5,7,1,-10,16,0,3,8,3,-20,-4,-1,-5,8,-2,-2,5,6,5,5,-3,12,12,8,12,0,-11,4,14,16,-13,-12,-1,6,-3,1,-6,-9,6,9,1,-12,-4,10,-15,10,21,3,-8,-11,8,10,-1,9,-14,6,25,-8,15,6,0,13,-22,-15,34,-17,10,11,7,15,22,-3,5,-1,10,-15,6,-10,-9,14,-12,6,-5,-7,4,15,34,21,-22,-6,8,-1,16,14,17,10,-1,-11,-14,-9,17,11,-14,-9,0,-9,-3,4,-13,-5,10,-1,4,-5,7,-4,20,14,24,14,12,0,2,11,9,-16,2,4,17,19,-6,1,-2,23,-3,-17,-4,-13,16,-7,6,1,13,5,-3,6,-9,-16,-1,-11,12,1,-2,-13,10,14,23,1,-2,13,-3,-15,9,-7,17,-5,0,-11,-22,-1,3,-4,-7,0,9,9,10,10,-9,12,13,8,-20,-5,15,6,2,-15,18,37,-44,-4,26,0,-15,-1,-2,-2,20,13,-13,-9,9,-10,-6,-2,-31,-10,-12,13,-15,-11,1,19,21,1,-4,-21,15,8,-5,28,12,14,-21,-15,14,12,19,6,4,12,26,16,4,10,-14,7,5,11,-2,0,-14,-13,-6,9,-2,16,30,14,8,13,-15,-12,10,5,-2,7,1,2,6,27,-36,-18,-6,7,-13,-6,13,7,0,18,6,-2,4,2,-1,-10,-24,-10,6,1,7,-2,-7,-14,-12,9,10,7,-9,-3,12,-10,13,-14,8,14,-25,3,10,17,10,-4,-11,15,3,-8,1,-18,-4,-13,-6,-4,17,-6,-37,-17,12,-3,11,8,4,4,9,-11,-8,-7,-6,-9,1,-10,6,0,5,3,-14,-12,-12,12,7,-7,13,-4,-6,-13,-15,-4,-18,-14,21,-2,20,8,1,3,-4,-11,11,-7,-8,9,1,-5,11,-1,-10,-14,-2,-9,-10,11,-7,-12,14,-7,-10,4,3,7,1,4,-1,-4,-11,-7,2,-14,4,2,10,-6,16,-8,-13,5,10,-5,12,7,-5,-4,9,-1,-12,0,-3,-3,-9,7,14,6,-10,-7,-6,-5,13,10,5,-4,-6,13,14,-13,9,-14,4,-1,-9,13,14,4,14,2,1,-1,-15,5,2,6,2,-16,1,4,-23,-22,7,6,12,-7,4,-10,-6,-9,7,-2,9,14,1,-12,12,3,1,-1,-1,8,-17,-17,-11,6,6,11,18,-6,11,-7,3,5,4,6,4,-21,3,-8,-20,-4,5,6,19,4,4,-13,-11,2,11,-8,-5,13,-1,-7,10,-10,10,-7,19,7,3,4,-17,8,1,-7,6,11,15,5,-11,13,5,-13,1,-2,14,12,-4,0,3,15,-4,11,1,13,8,-3,14,-9,2,-5,13,-10,-1,-4,-10,1,13,-2,11,9,16,11,8,5,5,10,7,12,13,13,4,-12,13,3,1,2,11,6,13,-7,27,-11,-3,7,6,15,-2,14,16,-5,-17,-15,2,10,-1,9,13,-1,1,6,-11,-11,11,15,12,6,-1,10,20,7,6,8,-1,-17,-15,2,-5,-10,19,9,-7,-13,-1,0,-9,8,1,-8,6,-7,2,5,13,-14,-15,-5,-14,5,1,4,6,13,0,1,13,-7,14,7,-11,7,-11,-13,-3,7,0,8,8,1,15,8,14,-6,-7,-3,-12,-7,0,-1,10,-12,11,0,17,7,18,10,1,10,-2,-5,9,-11,15,12,11,-14,6,9,7,-5,11,1,-12,-11,-2,12,2,13,27,9,7,8,2,8,11,1,-5,5,6,-1,14,-28,10,15,-8,-3,15,-21,3,-13,2,-4,0,6,-12,0,3,4,-11,-10,19,-4,18,6,-8,5,5,9,-1,-3,-6,-20,-2,-14,7,13,3,12,-11,-19,3,-10,6,4,-12,11,22,-3,-10,-6,1,8,3,-10,-7,14,-6,-16,3,15,30,15,33,14,-10,10,9,3,-4,9,-5,-8,-6,8,0,-2,7,8,-2,8,3,-11,14,-11,22,-5,-11,9,10,-4,2,-1,1,-8,15,-12,-8,5,7,21,-15,-3,-6,8,3,-14,12,1,8,-5,-26,-12,15,-1,15,-4,9,6,-3,8,12,5,7,22,2,-5,10,-8,3,-31,2,37,-28,-7,14,0,13,-9,10,7,10,14,11,-14,-19,-3,16,11,-23,-5,-5,-1,1,20,22,7,17,-2,-7,-10,8,17,6,-5,12,19,-6,5,-12,6,15,1,10,9,6,11,13,-2,5,15,-5,10,-7,1,3,8,16,11,25,15,18,28,-1,2,-3,-16,4,-3,17,4,-9,-21,7,-1,-12,-6,-12,-2,13,-8,6,-13,15,11,-8,16,2,-15,13,-8,-5,-2,-4,-8,-11,2,19,15,-23,-3,-9,5,-2,-16,-3,3,5,-5,-11,-15,11,9,-1,-5,7,-4,4,8,10,-5,19,13,-2,-9,17,-14,9,-4,12,24,-61,0,4,12,14,10,15,2,-6,-1,5,-9,4,-2,2,11,-17,-6,9,4,-4,6,6,2,12,14,-3,-10,-10,1,-1,19,9,8,-25,-1,1,3,3,7,-1,-8,-2,-1,9,-5,-3,15,-1,2,6,-5,5,-5,-10,-11,-1,13,20,3,1,-12,-3,-3,12,14,-1,-6,10,-5,16,6,-16,0,8,-9,-10,-17,-9,-9,-9,-7,-7,-13,-10,4,8,-4,-6,10,-7,-2,-6,1,-13,4,-2,11,-2,7,-8,-2,0,2,17,8,-3,-14,-3,12,-9,16,4,-4,9,7,-10,4,1,-16,15,14,-2,-14,-15,14,0,2,15,15,-1,-6,-11,6,11,8,-10,-1,-9,6,-11,-7,5,10,-1,-6,10,0,3,7,-2,11,-6,-5,10,0,-15,5,-2,-16,-10,-6,18,-20,-11,-10,6,-9,-13,-3,-11,0,7,0,13,8,-9,12,3,5,-10,20,13,6,-6,11,2,3,4,-1,6,13,-5,-3,-11,13,7,-7,10,12,-5,-4,2,10,0,-3,-9,-8,0,12,-14,9,1,11,1,-2,23,10,8,-8,0,13,-4,12,5,15,0,-18,-4,-12,0,0,-12,6,2,-9,-5,0,-5,12,3,9,15,5,-5,12,3,-6,4,-2,10,6,13,-4,-3,-3,0,1,19,1,-1,-13,-7,5,2,4,-6,3,-5,-15,0,-4,14,-6,12,6,-2,2,10,4,7,-4,14,2,-3,8,9,-8,0,13,17,-11,11,-9,-6,10,-10,-11,33,8,-13,-11,-3,-9,-5,-14,12,-5,16,21,5,7,3,-1,30,-3,-7,-6,-6,-9,-14,9,5,20,-5,10,6,5,-8,7,18,-13,11,-4,-10,5,25,14,-11,-9,-9,9,-9,-10,12,-5,4,10,-9,11,-1,14,-2,-1,0,-17,-3,10,-5,-4,-13,-5,-17,7,12,23,15,9,36,3,8,10,18,-4,2,26,7,-15,26,-13,15,-3,6,6,-20,11,44,-8,5,12,2,5,22,18,13,7,-2,3,7,5,-12,1,12,8,14,-3,4,22,12,0,16,-25,13,16,26,19,21,23,19,-17,13,1,3,1,-12,10,18,19,3,-3,11,-13,-14,5,11,14,-4,-10,4,18,34,22,36,16,0,-10,-15,-10,13,-2,8,-2,-9,-15,-8,15,-4,9,25,0,1,14,4,-7,-2,-4,-4,15,-13,2,15,5,17,4,-11,-3,-3,-17,-3,20,-11,-7,13,9,8,-13,0,-4,-6,9,3,9,-11,14,3,-1,18,-4,2,-6,-9,-12,14,6,-15,-16,37,10,-3,-32,23,30,-19,-2,50,4,-3,7,-4,-4,16,22,0,14,-4,-27,14,5,-12,3,-8,-3,0,-7,26,22,28,14,-12,-20,-4,2,5,8,28,12,14,-16,-11,-13,1,29,-14,-3,46,13,10,3,6,-11,14,-11,-10,9,-14,-6,-3,-4,27,4,37,13,-12,11,10,-3,0,-1,16,3,11,-17,5,5,1,-10,4,-2,-8,13,7,12,1,-5,4,6,4,-10,-12,6,19,0,-7,-1,-17,6,-1,23,7,-6,-7,-2,-20,-12,10,-10,-7,14,1,-3,-12,1,-11,-13,-1,-2,-6,11,13,0,17,13,-9,-8,14,9,2,-34,4,40,-39,8,16,-11,-4,0,9,-1,1,19,4,15,-21,-3,15,6,-13,17,2,-5,-9,-10,19,21,17,-1,11,-11,-10,3,13,10,29,19,-5,4,8,6,-2,25,13,3,17,18,2,11,-1,9,11,-8,-3,0,-2,-6,-7,9,13,11,37,4,-3,-1,6,4,9,-6,-10,-2,7,-11,-9,18,-20,-1,13,-5,-6,-4,-4,-7,11,8,1,3,5,-10,3,13,0,12,11,8,-2,-13,20,19,-20,6,17,8,-10,4,1,-10,3,16,-7,6,0,-5,-11,-7,-4,13,2,-3,1,2,3,19,-3,11,-3,-10,13,-8,18,25,-41,3,31,10,-8,5,-8,9,9,9,-14,-5,8,-1,13,2,-29,-11,13,-6,-16,5,5,6,13,14,13,-10,6,1,20,27,3,9,-3,-1,6,-9,3,4,9,1,26,6,-3,11,-11,11,1,10,4,-5,-7,-21,1,-1,-8,-7,37,3,13,-14,-2,-7,10,0,15,-8,1,-21,20,24,-26,0,-13,-16,7,-11,14,-7,-10,3,-11,-10,8,4,8,9,5,5,10,13,2,1,-1,11,-1,-4,-5,-15,-3,2,2,6,-12,-14,11,-5,17,-6,10,5,4,13,-7,-15,-7,4,1,-5,-11,-10,20,-3,0,8,27,-7,20,-13,-5,6,9,-8,-6,-1,2,2,15,8,0,-6,14,-10,-6,1,17,7,-11,-3,15,5,-2,4,-1,-15,3,7,-7,-12,12,21,8,-9,-1,8,14,12,-1,-12,-7,1,-13,-10,-7,3,13,8,-15,12,4,16,7,18,27,5,11,3,1,1,4,10,11,2,-1,10,-1,6,15,-10,14,9,1,12,13,12,3,12,-8,0,-2,-13,-5,-1,-3,-8,5,17,-8,-1,0,12,10,13,16,11,6,-4,13,16,-4,-7,-9,-3,5,-7,10,12,9,-8,4,14,9,-3,14,5,0,-10,-9,-9,22,7,9,13,14,-3,25,3,32,-3,-8,-8,12,-9,7,16,-10,-5,-11,-8,-3,8,9,4,-6,11,6,13,18,13,29,-2,3,0,9,-11,-3,-6,2,21,18,2,1,-9,-2,10,-10,10,15,12,11,3,8,-6,-10,3,14,7,-5,-4,19,8,34,-1,21,20,6,3,2,3,-14,-4,0,-1,-2,-11,5,-10,16,22,21,-10,9,4,-7,8,3,-1,-8,14,-1,18,18,-5,9,-9,11,1,-16,-14,31,2,11,6,-2,-11,-12,-4,19,-11,18,-3,9,5,-12,5,11,-5,33,-4,1,3,11,0,11,18,11,-12,14,-1,22,-18,22,-7,5,20,37,-3,5,-7,7,5,21,18,-4,-5,2,-12,3,-7,-2,6,5,7,-9,12,22,-11,22,5,-14,-15,10,-4,33,12,29,17,-17,-11,5,4,14,-3,-8,-1,31,8,-1,13,2,3,-1,2,-11,-3,12,-9,18,9,23,12,32,9,-5,-12,-7,-12,-13,4,21,16,6,-15,-1,10,4,1,6,5,9,3,-7,-10,21,10,-8,0,3,-11,15,-12,23,16,-3,-11,-19,-3,17,-12,-9,-1,1,8,-21,8,19,-2,6,5,-3,-9,5,5,-14,2,11,-7,8,-7,4,-2,28,23,3,0,15,-5,-10,-12,26,-1,-12,12,29,10,-9,3,-7,-5,16,20,11,3,2,-6,-12,-12,1,7,9,12,8,25,30,1,18,5,-9,-3,-3,18,29,15,25,13,-24,-4,-3,4,-2,-9,11,15,28,20,-5,-1,-4,7,-14,9,-7,15,8,13,6,16,26,24,50,29,4,11,-7,10,15,-4,15,11,11,-1,16,8,-1,11,-13,-2,-3,0,-14,7,-1,-3,7,5,6,-11,15,5,4,-4,-10,-13,8,2,20,19,-11,-14,11,15,3,6,15,-8,-4,0,-20,-14,-8,6,-9,-1,16,-10,-9,2,-2,-15,13,2,3,-12,5,-6,2,-24,1,29,-23,-7,34,-2,-9,11,-7,-10,10,18,11,-13,-16,1,-8,-6,-23,-2,-8,6,1,10,23,26,9,1,-7,-19,-13,6,18,5,14,20,-10,-24,-7,14,-9,12,8,5,26,0,4,3,-12,10,-10,-4,9,-7,3,-15,7,6,21,-4,43,33,-9,7,10,-14,-8,1,13,-7,7,-12,22,4,-24,0,5,10,10,3,-11,-1,9,-7,3,-7,15,-11,5,-10,3,13,13,-11,-6,-12,22,18,-1,-15,14,-10,-6,-22,-8,12,4,-10,-20,-6,2,16,1,0,12,-9,0,-13,15,9,2,20,1,-4,16,-12,8,-15,22,29,-15,-10,9,13,9,14,-5,-1,13,10,-1,3,-18,-1,7,-3,-5,0,10,7,-12,12,0,22,2,22,-14,-6,7,-10,3,10,17,13,1,-17,14,-8,10,4,4,-7,16,20,-2,-11,-7,1,-10,11,8,10,3,-20,-11,10,-4,14,21,22,4,-12,-8,-14,4,-5,-2,12,12,-12,10,24,-16,1,2,-3,0,-8,2,10,4,18,5,3,14,2,-2,11,-14,0,-2,-13,3,-7,8,18,-10,-5,16,2,-5,7,11,2,-9,5,9,-7,-7,-7,14,4,24,21,4,-5,17,-13,2,17,9,-3,20,-6,5,-9,13,3,17,13,46,1,8,-9,11,-1,12,5,3,2,8,-4,11,9,-5,17,5,-1,12,8,27,3,17,18,15,-8,7,14,18,15,18,17,-4,5,-1,-3,15,16,-3,5,15,0,-14,-10,15,-7,14,-2,-1,3,5,8,17,2,17,7,22,12,-1,8,-14,15,15,-6,16,16,-9,-11,5,5,21,-7,-4,-13,12,4,9,-6,19,-4,8,5,-14,13,16,-8,19,-6,6,11,4,10,28,3,11,14,19,13,11,-8,-10,-8,15,-11,11,6,3,20,15,-12,19,11,-7,11,-7,15,15,-2,10,-6,36,1,4,-5,38,5,7,-1,51,15,-10,-2,14,-2,10,-4,-12,-10,-5,0,7,10,17,4,18,13,9,13,9,-5,21,27,4,-13,-1,5,29,11,14,27,11,7,8,15,23,-6,4,5,48,14,12,11,12,5,-3,-13,-7,5,-12,6,19,13,17,11,42,17,4,12,7,12,8,9,16,0,6,12,12,6,-6,17,0,3,3,-14,-6,3,27,18,-12,6,0,4,0,11,1,3,-11,12,8,-6,13,20,7,-4,14,-8,1,-9,0,-1,4,-2,5,2,13,-1,15,-4,21,10,10,10,8,-10,23,10,-16,9,31,9,23,-28,44,19,-3,13,59,16,-10,6,-9,9,18,6,10,-9,-13,-3,1,4,17,15,2,-1,5,0,29,12,44,21,-11,2,15,-4,19,9,23,6,8,5,0,-9,25,23,7,8,35,7,-2,15,-2,3,-11,-3,0,-5,3,-3,-3,22,21,15,52,8,13,-12,6,1,3,-11,18,13,0,-10,26,10,-9,3,15,-3,-5,15,-8,-7,1,18,1,9,-14,-8,7,-13,14,9,-11,-4,8,10,15,12,0,-9,-6,-8,-19,4,-6,0,-1,-11,7,-6,5,6,-2,-4,31,4,-9,-11,-1,-14,20,1,-12,-6,4,-3,23,-15,43,18,-12,3,40,22,2,-6,4,-14,21,5,0,13,-15,-3,8,-5,-10,-3,-1,14,5,7,15,18,43,8,-3,-3,13,7,29,21,22,24,-14,-3,10,-13,26,-3,12,-4,33,8,11,-10,0,-7,-13,-9,-2,-13,-7,10,2,-3,19,1,56,11,11,-5,-13,2,5,6,22,17,-6,-2,12,15,0,-6,-2,-16,6,8,-8,9,2,-12,2,13,-5,-8,10,-7,-4,-2,9,-11,-11,-17,7,9,-12,-8,3,13,-18,8,21,8,-5,10,-10,2,0,7,-2,-7,17,3,-7,6,-5,-13,2,2,-7,5,26,-3,-1,-22,35,27,-14,-4,36,20,10,12,-6,15,14,14,15,13,-16,3,18,9,-8,-14,-7,-8,-16,-6,26,17,21,27,0,-21,13,10,33,-1,37,10,-20,-23,-12,4,1,12,-7,-8,14,6,12,3,4,11,-12,6,5,-1,-6,-5,-1,7,13,-6,51,23,4,15,-4,-8,9,14,-5,13,-4,3,17,13,-21,11,-7,-9,4,-7,-7,15,8,-1,1,11,1,-7,12,10,3,0,-12,8,3,2,7,8,2,0,17,17,-11,-15,7,-2,-9,3,0,-9,6,2,-5,6,11,10,11,-4,0,11,1,-3,6,4,-3,-11,6,-11,16,9,-22,10,4,11,8,10,-13,-6,24,14,12,-9,-2,1,-5,-3,-20,-7,-2,7,-3,9,21,-3,27,-5,9,-11,11,4,25,-1,12,17,-4,0,-12,11,10,18,-12,5,6,5,5,-12,11,-7,-5,-7,-1,1,-15,2,-10,2,14,-1,32,21,3,-5,1,1,11,3,0,-9,-10,-2,6,18,-3,-8,-5,-15,7,5,-1,0,-7,-7,12,8,-12,10,-6,5,-12,6,-12,11,-13,6,-15,-14,-6,4,4,5,-13,-11,1,1,1,7,-7,11,-2,-8,0,8,2,-9,15,15,6,-3,-10,-7,-3,2,-21,-3,17,10,-5,-8,13,-1,-40,-6,-13,-15,-7,-4,13,11,0,4,12,9,-13,7,-3,15,6,2,-14,6,4,13,-1,-3,-17,5,-8,-10,2,11,-17,5,-16,13,1,11,5,-13,13,-3,-17,5,5,-11,10,-4,10,13,13,0,12,14,-12,1,4,3,-17,13,13,10,9,-13,15,8,5,-14,12,9,12,-14,1,5,-9,-4,11,13,6,0,4,1,-5,-8,8,-15,-13,-7,3,-4,15,0,5,-3,-12,-10,6,12,-12,10,4,15,14,-10,16,13,6,9,-4,2,12,4,-8,3,19,8,11,-3,2,1,8,-8,0,1,-8,1,-13,6,-7,10,-29,12,-15,-9,-13,-9,12,-13,8,-12,-4,13,-12,11,2,-11,-4,-11,-13,7,10,1,-8,-11,-17,18,-2,1,-7,6,8,-16,2,13,2,6,-5,1,3,9,-16,3,2,-5,0,5,6,0,14,15,2,7,5,11,4,-8,-1,-1,-4,-7,9,10,10,-2,-18,-12,12,2,-11,7,8,0,-5,5,-2,-14,6,5,-2,1,-8,-6,-5,13,9,6,4,3,13,-3,-1,9,11,6,17,17,6,9,9,-8,7,1,-9,-8,0,6,11,-12,4,8,-5,1,16,14,16,7,-6,-3,-7,2,-8,20,4,-3,1,12,12,12,-25,19,8,3,15,11,0,0,-14,-11,2,15,2,3,-8,0,4,-1,-11,-11,7,-3,4,2,11,20,-1,-10,-14,-1,-8,-1,-1,32,-14,2,-13,11,-1,-11,4,-6,-1,15,-13,8,-13,8,-11,-14,11,-10,-1,11,12,6,-12,3,2,11,9,-10,-8,5,5,-8,8,0,-12,9,5,10,-8,13,4,12,-8,13,16,-8,-4,2,-12,2,-11,-15,7,-2,1,7,-5,-4,-7,-13,-5,2,-9,8,7,11,2,2,-5,-5,4,8,-2,1,-3,7,-3,14,9,-3,-6,8,2,3,16,14,11,29,0,-14,-5,15,2,7,-11,32,-8,-10,-2,7,3,-3,14,7,0,-14,4,15,4,-9,-9,-5,-11,-1,1,-3,12,-7,2,26,11,10,10,-7,0,-9,-8,19,-4,-7,5,8,6,5,-15,15,12,12,3,14,5,-14,13,3,18,-10,5,-14,18,4,7,-7,-6,2,9,-5,14,8,-1,-12,16,9,-1,8,7,0,-4,12,-13,-2,-10,11,-4,3,-6,6,0,-10,9,12,15,9,8,-2,4,4,0,8,-4,11,10,16,-1,-11,15,15,9,10,-12,9,17,1,3,13,3,13,7,-2,6,-9,19,5,14,2,-6,24,-1,3,7,-9,15,1,-2,46,-6,-13,13,-6,1,-13,8,-3,-5,-14,13,-1,16,-1,5,4,13,11,-1,3,14,-4,12,14,-10,4,-7,0,-13,2,8,29,3,10,-7,6,14,0,10,7,-14,-11,10,-3,15,2,2,-3,-8,-7,5,13,8,-8,-10,-3,-10,-1,-8,14,4,12,-10,11,-5,-6,-7,13,5,-7,8,20,12,-10,-5,-9,-6,8,2,3,17,-8,2,-14,14,15,12,-2,7,6,13,-9,10,14,9,15,3,-10,15,1,-10,-13,-1,13,-7,-14,5,8,0,5,0,-4,4,-11,-3,12,-4,-1,14,15,22,4,16,12,-2,-2,-7,28,-7,-1,5,4,-2,-4,-6,2,16,8,15,1,13,-19,13,-7,-2,9,5,-1,13,-7,16,13,-3,1,6,-2,0,-8,-6,2,-14,4,11,1,-8,-6,16,18,-11,1,8,-2,6,-9,-12,5,16,1,5,11,6,-16,10,-10,8,-9,-12,5,-8,-13,6,8,0,-12,-7,15,16,7,5,0,-7,12,11,-5,15,11,5,1,-3,-14,-10,-5,-7,-11,-5,-12,-4,-3,-10,-3,-11,13,8,8,10,-1,11,-13,-11,0,15,-5,3,7,3,10,-16,-10,15,-10,14,5,-9,-8,-6,5,-26,13,-4,0,-3,-4,8,7,-38,2,2,-3,4,-8,-12,4,9,-11,10,5,3,5,-8,-7,-6,7,-10,-3,-13,13,-10,-6,3,24,-10,8,-16,-12,-1,12,4,5,-14,-9,-19,6,-6,9,-17,2,12,13,-12,1,-14,-5,13,-6,3,8,-17,-13,8,7,-15,-9,-3,-8,6,0,0,14,-7,-8,-12,1,-16,8,-10,1,5,7,15,-9,-7,-13,9,11,-11,7,16,15,3,5,16,-2,12,-2,8,-11,-17,-13,-1,-13,5,1,4,-9,2,1,-8,-7,12,3,-9,-6,-5,-2,-17,4,8,-3,-8,13,-17,13,2,11,-15,16,14,10,-26,4,19,2,-40,24,11,-6,2,-10,-6,-6,13,-10,6,-7,15,-3,-6,-3,7,2,4,-5,-16,-12,-5,2,-19,12,-2,-13,8,-8,3,-6,2,34,-11,4,-21,11,8,-5,1,20,-13,15,14,10,11,6,-1,-13,-6,-2,-17,1,-2,13,-4,8,12,-1,-7,15,14,-12,7,9,-2,-6,13,4,9,16,-9,15,5,14,-14,-3,13,-5,-10,-1,6,-10,2,14,7,10,7,7,-2,11,-6,9,-8,-2,-4,3,-5,2,14,-5,3,-9,-11,10,-15,9,-7,9,-7,13,-1,-9,-13,1,-9,3,6,-1,-20,16,14,-7,-18,9,20,2,-26,21,-9,2,8,7,-5,-8,-6,-8,9,9,9,-10,-1,15,-7,11,4,-13,8,0,-16,-5,-9,21,-13,6,-9,-6,-10,-14,-9,35,15,4,1,-5,4,8,-15,13,0,-4,-7,0,-10,4,12,-6,12,3,-15,4,13,-9,2,8,-10,5,-12,11,-11,1,-10,12,3,-8,3,5,12,17,-14,20,15,1,4,8,2,-3,-6,5,12,-7,-8,-10,15,-2,13,-3,-11,8,-6,6,-5,9,-14,23,-3,-1,4,-11,15,9,-8,-9,-8,-5,15,12,1,4,-10,9,9,-6,-9,7,-4,8,11,16,-13,-9,-3,-15,17,7,-13,26,4,-5,-10,-2,-11,-1,-8,-12,-1,7,4,-13,15,-14,-14,-4,16,5,-17,-2,-14,11,-8,5,-7,7,2,-13,4,-6,-14,26,-11,-13,-10,4,-5,-6,10,5,14,-5,-1,-14,2,-5,-12,-7,-9,0,0,14,4,7,-7,12,12,-9,-10,8,-6,-4,-5,-6,-9,-13,15,-11,-5,15,-9,26,-5,7,0,-8,-1,0,-10,3,6,-14,12,12,-1,-11,-12,9,7,11,20,-11,3,3,24,11,-3,1,-8,-12,3,-6,4,-4,13,-11,16,-11,11,-1,16,-4,3,5,8,-4,3,1,18,24,-8,8,20,12,5,-8,17,50,-13,-15,2,1,-8,0,6,-7,-5,-12,10,6,15,-13,-13,8,10,-1,13,-4,-2,-11,-1,32,-8,11,17,-6,-3,16,1,12,-14,10,7,14,12,-5,-3,14,-9,15,13,-1,-12,6,-15,13,13,-11,-5,-12,-10,-14,-3,-4,-1,-10,13,14,14,8,3,3,12,11,18,1,-9,-6,-5,23,10,6,-9,9,13,3,-8,-8,-5,13,-12,6,8,-2,-9,1,-1,-6,14,-3,13,5,24,5,2,-1,-10,-10,5,-23,-2,11,16,8,-2,0,16,5,-8,5,8,-7,22,6,-5,-6,-2,15,22,16,31,-7,-4,-2,23,12,10,-5,-9,-6,10,-9,-2,-9,16,-12,10,6,-11,-25,15,12,13,9,24,-3,4,7,20,30,6,-15,3,10,-7,10,7,16,1,3,8,-15,3,-13,24,14,-3,6,11,13,13,10,1,5,-11,-9,-10,-7,6,-7,6,-5,3,8,-5,11,-13,8,9,-11,9,0,21,-2,22,-6,12,10,9,-1,-5,10,3,7,9,6,12,2,-12,-3,-10,-5,9,-15,-10,-11,-9,12,0,14,-1,11,8,-13,7,14,-7,-8,-8,-2,-7,-9,-8,0,0,8,10,13,-13,13,-6,5,-2,11,3,10,-13,-12,-7,-10,-4,0,-27,-9,15,13,11,4,13,1,-9,-12,14,6,13,-10,-4,11,-10,5,4,-12,-13,2,3,-1,3,21,-4,2,0,-8,-15,-10,-16,23,12,-10,-14,12,4,-6,-21,10,-6,-12,7,14,-4,14,-12,8,16,-15,-10,5,-11,-13,-1,2,-10,6,3,8,-12,-2,-11,9,-8,-13,-3,11,17,11,2,-6,2,8,-9,-14,-1,5,7,-2,-8,4,-6,-10,18,-10,-9,16,1,-7,-8,-4,4,11,16,34,5,-3,9,-12,6,4,-13,4,-5,8,-12,-8,6,6,14,13,10,8,-13,-3,2,2,-15,11,-4,-1,-17,13,12,-8,-12,36,3,-13,3,10,-3,-13,0,-6,3,5,7,-5,4,-4,12,5,-1,7,-10,-7,-10,10,5,9,14,-1,1,2,-2,5,-1,26,-12,6,-14,5,10,-3,-21,20,-12,3,-12,13,-12,-14,0,9,16,3,-11,-4,17,-7,-8,-3,13,-6,3,3,5,-3,-17,-12,-4,8,10,12,3,0,7,9,12,9,4,-8,11,11,9,-5,1,-7,17,-15,-1,-10,3,17,-7,8,0,11,-2,4,10,8,-12,7,-13,-6,-9,0,13,-5,-11,13,12,2,-2,14,3,14,17,-12,12,3,3,17,7,34,-13,4,-12,-2,-5,17,14,29,-5,-10,-5,-6,-10,-6,12,8,-2,4,1,-1,8,-12,0,17,-12,17,9,4,0,-6,9,38,-1,11,12,11,-3,11,3,34,4,12,3,-8,-11,2,10,12,14,-12,6,8,-6,7,3,-15,-7,4,13,-1,-15,-12,7,-13,-13,-11,-11,6,-7,15,9,9,14,11,14,12,19,13,14,25,-4,-3,3,7,-6,6,10,-14,1,-1,-4,-7,0,-10,15,-3,-12,15,3,-3,18,-3,-11,24,8,-13,-3,14,3,-11,12,-1,2,-13,-8,-2,-9,16,-11,-14,12,-6,-8,14,9,-6,18,27,-7,-12,18,3,2,15,7,36,4,1,-1,-9,12,0,11,-13,7,9,11,0,8,6,-8,2,-10,1,20,8,-1,-7,16,29,-9,6,-4,-1,-15,-2,1,27,-6,-11,-1,7,13,12,7,14,0,-9,-2,-9,15,-7,10,13,3,-15,6,7,6,-18,5,10,8,-12,10,9,5,11,2,-10,12,9,-7,13,-5,1,6,3,-1,13,12,2,-5,-1,9,-5,9,-11,16,-8,-10,5,-3,15,0,2,1,15,-8,-8,7,2,14,-14,8,2,14,-9,9,-1,1,14,-2,-11,10,-13,-1,3,-1,-1,-6,-2,14,10,11,13,9,-13,17,-5,5,-11,22,6,-10,-7,6,11,-13,13,6,14,-2,-7,-12,-12,-9,-13,-11,-6,7,-7,11,4,11,-3,3,23,7,11,19,0,0,-12,11,13,-13,4,4,-4,-13,3,-2,23,8,-8,13,13,15,9,-10,0,-4,13,13,9,-11,-14,9,6,13,-2,8,7,-3,-3,-2,-13,-5,-12,4,8,12,-24,15,8,-6,6,14,3,15,-7,14,-13,-6,10,0,-3,11,-7,12,15,11,0,4,6,0,1,13,11,14,-11,12,10,8,-19,6,5,-8,-13,-13,15,-9,12,4,-1,-12,12,-4,15,-6,13,7,8,-9,-9,16,-1,10,-17,17,27,-4,-13,-1,8,-5,11,6,-8,4,-5,6,-9,5,-28,-12,9,3,8,16,-3,4,-5,16,5,16,-15,16,6,17,-9,12,16,-4,-11,-8,-5,2,12,-7,1,15,-11,3,8,13,14,15,-3,-10,-7,-2,-12,-13,-6,-3,5,-5,12,-4,5,3,-5,-3,-14,-2,-7,2,9,4,-6,9,-4,-1,-10,4,-10,13,-5,14,-7,5,-5,-6,-3,16,-15,-12,-9,10,10,-5,-13,2,-6,12,18,3,-9,10,0,-4,14,8,-6,15,-2,-2,-7,1,-4,10,-4,14,-13,-2,7,-3,17,1,14,9,-13,11,-11,-12,-3,15,14,0,3,-10,-2,17,17,-5,1,12,-5,9,-4,11,5,16,-4,-3,10,-11,5,-1,-13,8,12,6,14,-12,-5,0,-7,-1,17,15,-4,11,-13,-9,-12,-8,16,6,5,-8,-4,-7,-12,-12,2,13,9,19,-16,-8,17,15,13,-7,-6,12,-9,9,1,-6,6,5,-4,-4,12,9,-13,3,0,-7,-10,-2,9,9,11,13,-13,-8,5,-14,4,5,20,8,-3,2,10,-6,-12,3,-15,15,36,12,-14,2,6,-5,-3,-13,6,10,4,-3,5,-1,-8,5,1,-5,14,13,-10,1,16,9,13,-18,-10,-4,-17,-2,0,10,41,12,-3,7,-5,15,3,7,14,5,15,-2,-14,-10,-14,13,16,4,0,-12,-16,-13,14,16,27,13,3,-3,-5,-11,-3,13,15,12,-7,7,3,-6,0,0,16,8,9,-8,-6,-13,12,-10,2,16,8,6,-17,-1,-14,11,23,-6,-6,14,3,7,-2,-12,-15,8,14,9,1,11,1,13,26,3,-2,-1,9,0,-10,7,10,-2,-6,4,2,10,-9,15,16,0,13,5,9,-6,-1,17,14,-11,3,-8,-12,-13,-3,11,18,12,-15,15,4,7,-11,-9,-11,-11,3,14,12,-7,-3,18,16,1,-5,11,0,-11,12,34,36,8,15,7,8,5,7,2,-7,-1,8,-12,-12,-13,-7,14,8,-8,9,14,7,6,-9,7,15,5,-10,12,-5,10,14,20,46,-8,-9,13,2,4,2,3,23,14,0,1,8,-9,8,14,-9,13,5,16,13,6,-10,-7,-5,7,-2,14,5,10,6,8,-4,-4,-1,3,8,-9,2,2,23,-1,5,-4,0,3,18,-1,-5,10,-11,-12,-4,6,12,13,-2,9,15,16,12,-9,-11,6,1,6,8,-14,16,-18,0,14,-7,21,-14,-11,15,-6,11,-10,9,5,-7,4,11,-12,-6,21,5,-24,10,7,11,-4,-12,34,37,-6,8,-8,-13,6,1,10,-4,-23,-2,4,0,7,-14,5,9,-12,7,22,12,-3,6,16,12,8,-11,10,-7,-8,16,31,40,-5,-3,14,-11,10,14,15,27,-8,6,6,8,13,-7,-9,-8,-9,-11,-10,-5,-4,-9,-10,-12,13,9,-3,9,0,-8,-8,3,10,-5,4,-13,-15,12,12,6,-5,-1,2,-5,-3,9,-1,9,-18,5,2,2,-6,-10,1,10,10,-14,-9,-6,-6,-18,-4,-1,5,-11,12,14,-6,-4,0,7,14,8,3,-5,10,7,-4,-9,16,-2,16,-7,-2,9,8,3,-11,10,26,-12,-3,-17,11,19,-11,-8,11,14,7,8,3,-4,-4,13,-6,13,-14,-29,-8,-1,-1,4,20,-7,-14,-17,2,5,-13,0,2,-10,1,7,1,2,11,5,9,9,-12,10,2,-11,-8,10,4,3,-14,1,2,-11,10,2,8,-12,7,6,17,-10,-2,3,0,-6,6,0,-12,-7,-1,-14,23,-8,-1,-13,23,24,-12,10,-10,12,2,-8,0,5,-10,-7,9,-11,-7,-1,10,7,-6,-2,0,-6,2,-17,3,0,1,4,15,-13,9,-1,-2,0,-6,-12,15,12,-9,-2,1,14,6,15,4,-12,16,14,2,-3,9,-10,2,-13,9,-16,19,25,4,0,-15,11,-7,-1,15,-8,6,0,13,-10,-6,-13,12,16,4,14,11,0,-3,-2,11,5,-7,13,4,3,-6,9,15,8,-8,4,-4,2,5,11,6,-2,15,0,13,-1,2,-7,9,5,-11,5,15,0,-3,-20,3,9,-7,6,11,3,14,-14,10,-3,1,0,4,11,8,-13,25,8,-12,-13,-10,-1,8,-4,0,-1,-15,7,-13,-13,14,4,6,12,2,-10,-3,5,-10,-6,-7,21,5,5,-4,9,-8,-6,10,12,0,-2,-11,0,16,-1,5,10,-7,-2,-3,3,7,5,3,-2,-10,-13,13,-10,1,1,24,20,5,4,11,-7,-3,16,14,16,-9,-14,4,-14,14,1,-7,-1,6,11,4,-15,-9,-12,-1,5,-3,-13,11,12,2,14,12,7,8,-8,-11,6,-11,-13,23,0,14,7,3,1,-3,-7,9,-5,-11,-14,12,0,11,1,7,0,4,14,0,0,2,-1,3,-13,13,-5,-12,12,-13,-10,-2,-1,10,14,-10,14,-10,-11,-4,2,14,5,-1,6,14,8,-14,5,12,13,-13,-3,-2,4,20,8,7,14,12,-1,12,-1,-10,14,-1,-4,-14,-7,15,-5,-3,11,3,-9,13,-14,-3,11,-2,25,7,6,-3,-9,9,8,-4,31,12,-12,-4,-9,-6,2,-9,11,12,6,15,-1,3,7,-6,1,15,10,2,-7,-9,20,24,25,-10,-3,11,11,0,11,2,14,15,-8,-10,-14,5,-11,12,3,-9,13,0,6,-12,-5,-12,10,-5,5,0,6,9,0,10,0,10,0,-8,8,-11,13,4,6,-4,10,-11,4,10,5,9,11,18,9,-13,-4,-12,15,9,5,6,12,14,-5,-3,7,-4,17,-10,-12,-1,-7,-10,0,20,23,6,5,14,-9,8,15,17,8,2,0,-6,-1,15,9,-9,-3,-2,13,16,13,11,17,19,24,-17,-12,7,-9,9,18,30,24,-10,1,-3,-2,-13,-8,12,2,8,12,13,7,-9,-1,9,5,-7,-9,5,-12,-7,11,10,28,5,5,1,-12,-10,-3,30,31,-1,14,-1,-4,13,-6,4,-1,10,-11,-10,3,11,-12,-8,-11,3,-12,19,-13,-12,12,-1,-1,8,2,5,12,-10,0,-7,5,-1,-4,-9,8,-8,12,11,11,-7,-12,-2,-5,-4,-1,10,-6,-9,-6,-6,7,-10,11,-7,10,-9,-4,-1,-5,-5,3,5,19,11,14,5,9,-5,-10,-1,-10,18,-9,-9,3,7,-15,-12,-11,14,7,-3,15,-3,5,5,-2,-19,-1,12,-9,10,-11,15,19,-5,-8,9,-5,7,-5,6,0,7,9,2,-2,11,-6,-3,-7,0,-9,17,0,13,6,14,3,0,-13,-6,5,-4,-8,12,34,0,-11,0,-1,10,14,15,11,8,13,0,11,-2,6,-10,-15,13,7,6,-5,11,2,-1,21,4,14,-16,-2,-12,4,0,4,-10,-2,0,15,1,15,0,14,2,-11,10,-13,-9,18,-12,-10,-4,4,13,-4,3,8,2,6,-14,7,-7,15,-12,2,20,0,-14,1,9,6,8,-15,-3,21,18,-12,-11,-12,-11,-10,9,-11,13,-10,15,13,9,15,2,-5,-16,-14,24,-3,7,-20,32,-4,6,11,15,-10,-11,10,-3,11,-15,7,-14,14,-9,-8,6,5,10,-6,2,-2,-12,-10,16,16,2,12,12,-5,-13,15,21,15,5,-6,13,5,-10,-13,-6,-5,-13,-6,12,-3,14,12,-14,-4,-8,-12,-12,-3,9,-4,13,-12,3,-10,5,2,-1,9,-3,7,-6,-8,7,-3,-11,-7,18,16,-1,8,7,13,-1,-9,1,8,-2,-12,-6,-12,1,7,7,16,7,-14,17,-4,-10,6,15,5,1,14,-4,13,-14,-13,-7,14,-2,-9,-3,-3,8,10,8,3,11,12,16,13,-11,4,24,3,1,-4,9,11,-5,11,37,12,-1,5,-3,-9,-7,-8,-8,9,-17,3,-2,-4,-4,-4,15,4,5,-13,-1,11,14,2,22,-3,3,8,13,5,-5,-8,22,21,-8,-14,1,-12,2,-13,12,9,-6,-12,2,-11,4,-1,-9,-11,2,10,5,4,-3,7,-5,5,-1,11,0,0,0,-14,-3,12,-4,3,-2,-15,12,-3,9,8,-10,1,-13,15,4,-9,-11,13,-7,-3,-11,-10,1,-2,10,4,11,14,-10,-11,0,-2,6,5,-5,12,15,12,-10,0,11,-9,12,10,-13,-13,17,9,16,-3,-5,-3,14,-13,10,-5,8,-10,2,-10,-6,-3,-11,1,23,-13,3,-1,7,-10,17,12,11,6,-12,10,-10,-8,11,17,10,7,2,8,0,-2,20,2,15,3,-5,9,-12,0,-1,-8,3,13,-12,5,4,14,14,-1,23,-18,-1,12,11,13,2,8,-6,11,16,5,-1,14,-8,6,7,2,14,-3,12,-8,3,-11,3,1,9,0,4,-7,-14,12,-6,16,-9,11,6,-4,14,-5,10,10,-1,-4,7,-7,-3,13,-8,15,3,-8,-10,8,5,-11,18,13,-6,-6,6,-1,-11,16,13,0,12,8,-6,4,18,13,-7,0,-4,12,-8,13,-1,-1,16,15,-4,13,15,-11,9,3,15,23,1,-10,2,15,-12,-8,-10,-11,-2,-1,-1,2,-14,-7,-10,-2,0,-11,-8,9,-9,-3,10,30,-8,-10,-5,-14,0,0,8,32,2,5,-10,-13,13,-13,21,-7,-13,-6,5,-3,-2,-10,2,14,-4,-4,-4,-10,4,14,8,-5,-4,3,-6,-2,-3,-10,-7,-8,8,-6,12,10,-10,19,15,8,-13,0,-8,-4,-14,-9,5,-11,14,8,9,13,1,15,9,11,-4,13,11,-11,-11,-4,30,11,0,-3,12,14,-13,-8,14,-8,16,10,-9,-14,-3,17,3,11,5,0,11,3,-4,-10,8,27,-10,8,1,1,-15,25,44,48,-6,7,-13,15,5,-8,-10,-8,-15,-15,14,-3,1,4,13,12,12,0,-1,-13,2,0,33,19,15,0,12,12,-4,10,6,33,-13,8,2,3,13,15,16,7,15,-8,10,6,9,6,5,-10,2,0,17,-12,11,6,27,4,8,10,-2,-8,1,-2,-2,12,6,0,1,5,-6,22,7,14,5,-6,-4,13,-14,17,-8,-11,-2,-5,15,10,-14,15,-6,-7,-15,11,-6,15,-12,6,20,17,10,11,-1,14,-8,-12,9,8,19,-4,7,-6,-1,1,-11,9,14,-5,23,5,15,6,12,31,5,12,26,13,-11,7,22,36,4,3,3,12,-13,7,3,1,7,6,-2,-1,-7,-5,-11,13,10,-10,20,4,9,10,27,41,12,12,-5,-15,-7,14,23,37,6,-15,13,12,5,-6,18,24,7,1,-7,15,-10,-5,10,10,9,-13,0,-8,-3,-11,6,20,-2,-6,6,-3,-3,-13,-1,8,-9,-5,23,6,4,19,4,26,12,15,9,15,17,10,11,12,-1,11,-7,1,1,7,2,4,6,-8,-2,8,-3,3,14,27,4,-11,15,0,2,9,7,16,21,-14,12,11,7,10,-1,1,12,-7,29,3,17,-9,13,10,-13,10,29,-13,0,-12,44,4,3,6,6,-10,7,-1,-2,-14,-12,-2,3,-12,-11,-10,16,-6,10,-3,24,-11,-9,-7,7,32,-2,-9,8,7,1,9,22,9,12,2,25,9,-9,15,1,7,3,13,-6,-11,-2,2,-7,-8,-2,-11,21,-4,1,5,24,1,10,12,12,-7,12,-3,8,12,-6,-13,-7,8,-8,4,20,15,10,0,-3,2,4,12,5,-12,-16,1,17,3,-10,-5,16,7,4,13,-6,-1,1,15,17,2,5,-8,-2,-8,-6,2,15,-1,16,8,-14,-12,-8,5,-11,-12,8,-14,15,-4,2,12,19,-10,-17,-14,19,-14,-1,0,26,4,16,4,-13,-11,-1,-4,-1,3,-4,8,11,5,-5,10,-5,-4,-1,6,7,16,16,-4,1,-6,-5,2,3,3,8,-7,13,6,0,7,2,11,-9,-10,23,-1,-9,5,-11,-6,-10,-4,-7,-3,9,-7,-1,-11,-9,-4,-4,9,-6,10,7,-4,-7,10,14,1,2,-1,5,3,-14,8,-6,12,-7,6,13,15,-1,-1,-5,14,13,11,14,-5,-12,-7,14,-13,-12,-2,3,7,-8,-1,5,11,-8,-5,8,11,-9,13,10,15,3,11,5,15,10,13,2,-12,-10,-8,10,-18,-15,-14,5,-15,8,-13,-14,-12,9,-13,39,-4,12,10,14,-4,11,-1,8,6,-9,0,8,13,1,-3,-13,13,14,12,4,-3,17,9,-5,3,-7,-7,7,0,17,0,11,6,-8,13,-3,7,13,-9,6,2,10,-11,-5,15,11,-7,2,1,-1,15,6,2,2,-10,-1,11,8,6,-8,11,-7,3,4,9,-4,13,-3,-19,-5,-12,-4,7,1,-13,4,-8,5,-14,13,1,4,-5,-4,11,2,-12,-15,3,-13,-8,1,-13,-14,14,-7,-14,-13,11,12,10,16,-10,11,1,11,-16,10,-10,20,0,-4,1,15,7,8,-13,-12,-6,8,8,-7,6,10,-12,-8,-7,19,-6,7,-1,9,3,-10,6,-6,14,8,-12,-7,-8,15,-4,1,13,-9,-17,-7,-13,7,-14,14,-9,-9,-7,2,-9,10,-5,5,13,6,3,-8,-16,-1,6,21,3,-2,6,-9,10,-12,0,-6,-4,0,11,3,-6,-5,-6,8,-9,12,-10,-8,-14,4,-4,2,-13,7,-6,-10,3,3,-1,12,14,-1,2,-8,8,5,-6,13,-14,-6,-9,6,8,-13,9,-16,13,15,7,-5,-2,-7,4,-2,-5,-13,3,-5,9,8,-5,11,11,-13,-13,5,0,-2,-12,10,-6,-2,14,12,7,-9,6,-2,-8,-4,12,-12,-23,-10,-2,27,11,7,9,1,-3,10,9,-9,5,-13,6,-13,0,12,0,-11,-4,4,-15,8,6,-1,6,2,11,-4,-1,9,-9,11,14,12,-14,15,15,1,-13,12,3,11,-10,8,-1,-4,-6,-4,8,7,-13,12,12,2,13,-5,-13,4,15,0,2,4,-4,-4,9,-11,-2,0,12,-6,-20,-3,3,7,-9,4,-8,-3,2,8,-3,6,8,-4,-2,-1,4,-14,0,-4,15,8,7,8,5,-11,15,3,9,-14,-2,7,9,-11,-8,-1,14,-1,-15,2,10,-8,-2,8,-13,7,-14,-9,-3,-13,14,-14,-8,-11,-6,15,-25,-11,8,-8,7,0,-10,-10,10,17,4,2,-8,-6,13,8,-9,-10,10,14,-13,4,-6,5,-13,7,-1,-7,12,4,-12,2,-2,-2,14,2,6,11,-13,-1,-9,12,0,10,-1,4,0,-1,3,-7,-1,-10,13,-3,7,-8,-11,15,7,-1,10,-5,4,-13,8,0,-7,8,-8,11,7,-2,3,11,-6,10,-2,-9,-4,-4,3,-2,2,6,11,-5,16,4,5,-5,-11,-17,13,12,-1,11,9,12,-2,-8,2,13,2,15,-9,-5,6,-6,3,-4,-17,-7,14,16,-3,6,-12,1,0,-4,-10,15,-3,-4,-2,14,-6,-2,-24,4,1,1,10,12,13,-5,2,-3,-2,-2,6,-9,-3,15,-9,6,-8,-6,9,-10,-3,2,-6,12,8,-16,-11,6,3,0,-11,3,13,-11,-4,-3,4,1,-9,15,-10,-16,9,7,6,13,9,13,-11,12,-8,-4,4,9,-5,-5,-8,3,-4,-6,3,11,-5,-11,2,14,-2,-2,20,15,0,16,14,3,7,15,1,1,13,-4,9,-2,-6,2,-2,0,-6,-10,-7,-14,-2,13,6,10,8,-2,7,-17,14,-1,12,5,10,3,-8,6,-13,3,5,-14,-8,9,-5,-10,-8,-11,0,5,11,0,-2,9,2,5,18,-3,-10,11,-8,1,-9,-14,11,-7,14,14,7,4,15,3,2,5,-8,4,-6,7,-6,8,-7,-5,-11,10,-4,-19,9,2,-11,1,-8,8,-7,7,0,10,-14,5,-11,-6,-8,9,0,0,-2,1,-9,3,12,-8,-7,-9,-11,-2,-2,8,2,-9,2,-1,11,-12,-7,6,-13,-3,-5,-2,11,-12,-12,-6,10,5,2,0,9,3,8,4,0,-13,-3,-13,13,-10,11,12,-2,-1,-10,7,-14,-13,6,-6,-11,-12,12,-11,-1,3,-4,-14,-12,-4,5,-6,1,-10,11,12,-15,-12,-7,10,6,2,7,-3,-8,14,-2,11,-2,-8,3,-4,-8,24,9,-9,0,-2,7,-7,14,-9,-6,5,14,-9,-14,10,10,-5,-9,-6,7,-7,-10,10,-4,4,10,-11,13,-13,2,13,-14,-7,-7,-9,3,2,-4,-11,-11,1,-16,-11,-13,-14,11,10,-11,3,-4,-2,6,9,5,6,-12,17,-10,-13,4,12,-3,6,8,5,9,7,-8,1,7,1,-13,3,1,14,3,-4,1,15,9,9,8,8,-5,-4,-4,-8,-9,6,2,-3,4,-11,-10,-4,9,8,-10,-8,10,-12,15,-3,0,10,-8,2,-15,13,15,2,6,11,-12,10,-11,5,-18,1,-13,-10,1,-3,11,-10,4,7,-8,11,-11,5,-2,6,2,8,-8,4,-5,14,15,10,3,-13,14,11,9,-1,12,13,-12,13,-8,1,-10,-5,0,5,-6,-4,-11,7,7,11,-14,1,10,-9,-14,-6,-7,-1,13,4,5,-7,10,3,-13,-4,-6,12,-1,14,10,-7,10,-13,11,2,-15,-12,3,19,-12,-14,5,2,-14,-19,-8,2,-12,-8,-2,8,3,-4,-1,-12,13,0,12,-12,-7,-4,-15,2,5,-5,-7,-3,-1,5,14,-12,6,-8,-9,-14,-6,-3,-1,1,-10,-7,-4,0,-11,16,8,-14,11,-12,11,10,-16,-14,-10,-12,8,11,13,-1,5,-1,-12,5,4,-8,13,-3,0,17,4,-6,-12,-10,13,13,13,-6,-9,4,1,-2,5,2,9,18,13,-6,5,12,-6,-14,3,4,-3,-10,-14,9,-13,-12,11,1,9,0,6,7,-14,1,-3,-12,-9,-2,-14,8,7,-6,-5,-3,0,-9,5,12,-7,-14,-12,3,12,7,1,6,-6,-2,8,-17,6,5,2,13,11,9,-8,9,-4,7,-15,12,-8,-10,15,11,4,-12,-3,6,3,14,10,-2,-10,7,1,-9,3,7,-8,-1,10,-7,2,-6,-1,-1,-13,-2,-10,14,13,5,0,10,-8,-15,-10,-6,-7,9,-1,-5,-6,1,11,-17,-6,13,5,9,-13,6,8,-8,4,-15,3,-10,6,7,-11,6,-6,2,-5,-4,-2,5,-13,-20,5,3,6,13,-2,-5,11,-5,4,-14,6,8,-11,-11,-14,3,9,-13,-9,-9,-7,13,6,4,-5,6,10,-9,-10,-4,-3,-11,-12,9,6,8,7,-9,14,3,3,2,-4,2,6,-3,11,-19,8,4,-4,8,14,15,8,-14,14,5,17,7,-9,4,-5,-15,-11,-3,5,-1,4,20,3,0,-3,3,20,13,1,19,2,0,-6,-10,10,-13,-12,12,-7,-10,4,-12,8,1,-11,7,-7,-14,-6,-8,20,7,-21,10,-1,-12,-5,14,2,-5,1,-10,-15,8,-11,8,6,3,3,26,8,3,0,-3,-11,-1,1,-4,-3,-6,1,-11,1,-4,-7,9,-4,-5,12,9,12,13,5,-9,12,-6,-7,0,5,-10,15,-4,-8,-13,-14,-5,16,14,-4,15,1,16,-11,-6,-1,-6,3,6,-6,-3,-2,6,7,-1,-5,4,1,-15,-13,-6,-7,2,4,-1,8,-4,7,-2,14,7,-1,15,11,-13,-6,-2,1,4,-4,-4,-9,-13,7,1,15,-3,12,1,-2,8,5,14,-13,9,-2,3,-12,14,-4,3,15,-13,-12,7,-4,-7,-9,10,21,-4,2,5,-9,-4,12,-11,6,-4,8,-3,10,-10,-13,12,20,-13,12,8,-1,-15,7,-1,-2,2,17,17,9,-13,12,-13,-3,9,1,-7,-5,-20,13,-10,11,-5,-16,5,0,-14,-3,-7,-12,1,-3,-4,15,-14,8,3,1,-8,7,3,-12,2,7,-4,8,-2,12,-7,5,2,13,1,16,-5,13,-4,-14,2,-13,-8,11,-1,3,-15,11,1,9,-6,20,15,9,9,-11,12,13,9,-11,-11,-5,0,10,-10,-11,0,-3,8,-14,2,3,-13,7,-10,-1,11,2,10,7,5,11,1,-11,4,1,2,0,13,2,15,14,-3,6,-9,15,20,-3,13,0,-10,12,-8,-15,14,9,12,-9,-8,-1,5,-4,10,-5,-11,12,-15,-7,6,1,5,-6,8,10,0,8,7,3,1,-4,8,-6,-14,13,-2,13,3,-11,-3,0,5,-12,-10,-1,-4,-11,9,15,-5,11,18,13,0,6,12,8,3,9,1,13,-3,-12,-6,3,-13,14,0,10,16,-12,14,-9,13,-1,10,-14,-1,6,-5,1,1,-11,-5,2,-2,-3,13,11,-14,-15,-2,-12,-9,6,9,5,10,15,-11,-11,-18,6,-11,12,12,0,16,3,3,13,5,10,-9,-2,1,6,-6,10,-13,4,-2,7,-14,-2,9,4,12,13,10,15,-10,1,3,8,12,10,8,-14,-17,2,-15,-6,-13,-11,5,-11,13,-13,-10,-14,-8,4,-5,-2,-5,10,-19,3,-3,9,-1,10,15,-7,-5,5,9,-8,-4,-5,-1,12,11,8,-2,-4,11,-7,13,7,-1,-15,-3,-8,13,15,2,-8,0,-14,-4,15,-1,-4,-3,-1,-9,-8,6,-18,-13,-5,5,-14,10,-11,-10,1,-10,-11,2,7,8,8,6,8,-8,7,-5,9,6,-15,-4,15,-10,-12,-5,7,3,9,-5,3,11,-5,10,-9,15,9,-6,7,7,1,-14,13,-9,7,-14,11,-10,7,-29,0,-5,-1,-2,0,-8,15,15,14,-12,2,10,24,-2,5,1,-10,-13,-3,-11,7,13,-7,3,3,0,-8,-7,-2,12,6,19,14,-1,3,13,2,-10,11,-7,12,-2,2,-5,16,12,-9,-8,-9,-9,-14,11,7,14,-11,9,11,-1,3,-12,-4,15,3,0,-9,1,12,6,-5,-10,-7,-5,14,-20,3,-5,-4,-4,7,13,5,-2,7,2,14,-2,-1,15,-2,10,5,4,16,-13,-15,-7,10,-6,6,-14,10,13,-4,-8,2,-1,-2,6,-16,-4,9,-8,11,6,-4,0,4,5,4,1,2,-3,4,2,40,-10,-2,-1,15,2,0,15,0,10,-2,7,12,-13,5,4,28,1,-7,14,13,3,7,-10,-18,4,-11,21,-13,0,-12,-8,-1,1,-5,15,12,-21,11,15,15,3,-3,-4,8,4,4,-8,16,15,-7,15,7,-5,-9,3,12,8,-23,3,0,10,4,-1,-14,-7,4,12,11,14,-13,-13,7,-8,3,-6,3,0,6,-9,-7,-3,2,5,-1,12,-13,14,-8,12,-3,3,3,-12,7,7,-3,6,14,11,-9,8,6,-4,4,-14,-4,23,11,1,6,3,9,-1,11,4,6,14,-11,-2,8,-7,-9,7,14,1,-6,14,43,6,-11,14,-3,-22,10,8,6,-9,10,2,14,-8,15,8,34,12,3,15,1,15,1,9,-12,-14,-6,7,12,4,10,-11,-9,3,19,-12,5,-9,-9,-3,2,4,0,2,-3,-4,3,13,-2,5,4,-8,13,10,-8,-3,10,11,0,0,1,3,1,10,10,9,2,-10,3,-10,-11,-1,10,-6,-13,13,1,-18,-2,3,8,4,6,1,11,-7,9,-14,-7,16,-11,-10,-9,-6,-4,-8,17,1,-7,15,4,0,-5,8,25,11,10,22,5,3,-4,10,-11,-2,-1,7,6,7,-4,5,3,-3,15,-4,2,-4,-7,-10,17,12,5,-6,0,3,3,7,5,-14,10,8,8,14,-4,6,8,-5,-8,0,14,9,9,-15,-6,-9,-6,13,7,-15,5,-11,15,-13,19,9,-6,0,12,-9,7,-5,16,6,-9,-1,-8,14,11,-3,-4,-12,6,12,11,12,12,-10,-9,-1,13,20,-15,-3,-5,-2,9,-2,-5,-2,5,0,18,-2,-8,-3,0,-12,-1,14,14,2,-6,1,13,5,14,8,20,-14,-10,-3,-2,-11,12,7,16,5,10,-10,6,-15,-2,-12,5,-5,15,9,13,-15,1,-11,-11,11,-10,-1,7,1,15,1,-15,-12,-9,1,3,9,-11,-1,1,-5,-2,-3,14,13,-7,-14,-5,15,7,-10,-1,5,4,0,-9,9,6,-10,-4,1,-12,-10,-2,7,-11,7,-8,-3,12,-3,10,0,15,11,-5,8,9,5,-11,11,-13,-12,-11,17,-14,2,-13,2,14,-4,2,-3,10,-15,12,10,-16,21,0,8,1,-8,7,1,10,13,13,1,-9,3,-8,-10,13,-2,-13,6,-4,-7,-11,12,-12,5,10,9,-11,5,-6,7,3,4,-9,6,7,1,10,12,-19,12,2,-13,-11,-8,-5,10,-5,-14,15,5,3,12,-13,-10,8,15,-15,-10,6,3,-2,7,-13,-7,2,6,-10,-4,24,0,-23,-13,6,11,-13,11,-1,12,-2,16,-11,14,-12,-7,2,-4,15,7,12,9,0,12,3,14,-16,-2,5,0,6,-8,-4,6,-15,12,6,-16,-10,11,-8,0,-11,9,12,20,-8,7,-5,15,3,-5,-12,-10,13,-9,-15,-9,-13,24,-5,11,-7,8,0,2,7,2,11,1,5,-12,-4,-13,-3,-4,-7,-15,2,-1,8,-2,-9,15,-4,6,10,10,-9,0,-2,7,-14,-13,2,-8,0,6,-18,-1,-8,-6,3,9,20,6,9,10,5,5,-2,15,-2,-2,-7,9,-1,-3,-12,-14,11,9,-25,0,-6,12,-6,-7,15,6,-14,-35,-6,13,12,-14,12,-8,1,-8,3,-10,6,-4,23,2,-6,-12,6,-6,-5,3,-20,4,-12,9,3,-5,-14,-12,-12,-6,7,4,-13,-19,-8,15,-2,-10,-13,-14,-4,2,14,-1,12,-5,9,4,6,-11,-12,0,-18,-8,-24,-1,-12,0,-4,-10,-2,-9,7,-4,4,6,6,-6,14,3,-5,-28,-12,-15,-6,-5,-13,4,-14,4,8,8,-8,0,-5,15,5,9,11,11,-13,-5,25,6,-9,-12,12,-7,-13,-9,3,0,-3,-9,0,-15,10,-7,5,8,4,-5,6,-7,-6,-2,-11,2,-5,4,11,-2,-6,-2,35,9,-14,-18,-4,3,-1,-2,4,9,2,10,-11,12,1,-7,10,-6,-15,-8,-15,-8,7,11,-15,-4,-8,18,10,11,-6,8,-19,7,6,2,-12,8,7,12,2,0,-26,-7,6,2,3,13,-2,0,0,7,-8,-8,3,3,6,3,-10,-2,4,14,-11,4,-4,9,12,11,2,15,-2,-6,29,15,6,-13,-2,4,0,3,-6,-2,8,14,11,-4,11,-3,1,-1,15,4,-5,-17,9,-1,0,4,-5,17,-11,-7,4,-3,9,-1,-12,21,11,5,0,1,-5,9,5,2,-8,-5,15,-4,6,1,-15,-10,-10,7,-14,-12,17,-8,-12,9,-7,-1,-2,-6,-11,-7,2,-8,-4,-12,-6,-2,8,7,6,-3,-3,6,2,-7,-1,-10,-11,16,12,-6,7,-7,10,-7,5,4,-5,-14,-11,-7,-1,14,2,-8,2,13,0,-14,-1,9,0,0,-5,5,-8,-12,8,5,-6,12,13,4,-13,7,6,-4,8,-13,-6,5,0,0,26,-11,-7,10,-12,4,9,13,9,2,-1,-8,-4,13,10,5,11,-6,-12,12,5,-1,8,9,12,-1,-13,-6,1,4,6,5,10,13,1,19,-4,-13,5,-12,-2,15,-10,8,7,-9,-8,9,16,-2,-3,-11,8,-2,9,-14,21,-13,-16,22,-6,-2,-13,0,11,-11,2,-6,15,14,2,-7,28,-6,1,22,12,-12,-12,15,-12,-13,-2,9,-10,1,-5,1,-6,-4,-3,1,-6,-3,-10,3,6,-1,-11,11,7,-11,0,-9,-10,-6,-6,-4,10,10,-1,-9,-5,6,-6,-4,-11,20,13,-7,-11,4,-8,7,-12,0,-2,-13,25,-4,-6,-6,9,5,-13,1,-6,-6,-3,-17,1,-4,12,-13,-3,-1,11,12,3,2,-5,-11,-2,8,8,-2,-14,-3,-4,15,3,4,-4,4,13,-5,6,13,13,-2,-10,15,-2,2,7,7,-13,-8,-4,-10,-3,15,4,6,1,8,-2,12,13,29,1,4,7,-5,-6,-6,-3,-4,-7,-10,6,-1,4,3,-5,10,1,-10,12,-4,15,-1,-4,9,-6,-3,-3,-1,-3,4,7,12,3,-7,7,-12,6,0,-11,10,-8,6,1,13,-5,14,12,-7,7,-12,-12,-15,13,9,-12,4,-11,-10,-1,-5,-11,-2,2,15,7,5,7,12,2,-5,11,9,-9,-9,4,11,15,12,-7,14,14,3,4,-3,5,4,5,-6,10,2,15,5,12,-16,-3,-10,7,-2,10,12,-6,2,-2,-12,8,11,-14,-11,7,22,-6,-5,14,10,-1,-14,-18,-1,-4,4,0,-10,-7,-7,-12,-24,-15,18,2,14,-2,1,4,-4,1,-14,12,-13,14,-13,2,-13,-2,-6,3,12,-3,-14,9,4,10,-3,-2,14,-13,5,5,-5,-11,-8,-1,8,12,4,-6,11,-10,0,-4,-1,2,7,2,-9,4,15,3,-13,1,5,-19,13,-7,22,6,-8,10,13,-4,13,5,0,-2,-13,4,-9,-1,-16,-3,-5,-12,-10,-7,-4,3,-1,13,-10,-11,10,-6,2,-2,-12,-1,-4,-7,2,-5,1,17,-10,-13,-7,5,-4,-11,-6,-11,13,-14,-12,-14,-5,4,-8,1,20,-4,-14,-9,-12,-3,-12,-9,2,-5,10,2,14,16,6,-13,-31,-14,10,-12,6,7,-8,-4,22,-7,9,14,4,15,12,-12,-11,-10,-13,9,-1,-14,5,15,6,12,-8,-10,-5,-16,7,-3,-4,-6,-21,7,-1,-13,0,-11,-8,-8,5,1,-13,-5,13,13,11,3,7,13,1,-14,-5,-2,13,6,11,15,8,10,4,9,1,6,12,2,3,0,5,6,1,-6,6,-3,-7,1,9,0,5,-1,15,15,-9,13,-12,12,-6,8,-1,11,-5,4,9,18,-5,-8,7,4,11,6,6,9,-4,8,4,7,-5,12,14,-7,3,12,6,2,-14,-8,3,5,-8,3,-10,-1,0,37,-16,-18,-7,-7,-2,-4,5,-15,7,-2,15,12,15,14,1,17,-3,-13,-11,12,-3,0,-1,1,10,-2,-4,-7,-23,15,-10,-12,13,3,6,12,-29,-4,-10,7,2,-5,-1,-14,2,-4,-13,-12,13,3,-2,9,8,12,-7,1,-4,-11,26,2,2,-15,-14,1,-9,9,-1,-5,5,6,3,28,8,-1,-5,12,-19,-6,11,-9,4,5,11,-3,11,0,4,4,-12,3,-13,10,4,2,-6,1,4,-3,6,-9,-2,-11,-1,14,0,7,19,7,-14,-7,-4,7,-7,4,-9,5,12,4,-6,15,9,12,1,-13,11,11,-4,17,-8,5,-3,-1,-5,1,-11,-16,11,-4,1,10,-9,9,-9,21,-11,-12,-1,4,9,14,14,1,15,-11,13,-1,9,-14,10,11,-3,15,16,5,-17,12,1,-14,13,-2,11,-7,11,5,-3,-3,1,-3,-12,14,0,8,2,1,5,-20,16,4,26,2,-8,12,-13,13,8,4,5,-13,-12,28,-4,-13,-6,11,-4,8,13,0,-14,8,2,-11,7,-6,-4,5,1,-7,-4,3,-12,4,14,18,2,-9,7,6,16,11,13,14,15,0,-2,3,1,-3,-2,12,-3,1,12,2,6,-5,-9,-10,1,-4,-6,-15,17,-10,14,38,14,-11,5,3,7,6,-13,2,-3,-8,-12,11,4,-8,14,13,-10,13,19,-2,15,14,-13,1,3,-6,15,4,-4,-3,9,-5,7,-4,0,15,8,-11,1,-4,12,-3,1,5,6,0,-5,5,-2,15,2,9,-4,3,-2,16,5,6,-2,2,13,10,2,-13,4,5,-8,14,1,-5,7,4,2,11,20,-6,-3,-5,13,17,7,-4,-3,-7,-3,1,9,10,-9,-8,4,-10,-10,14,0,-12,7,-9,-11,15,10,15,-10,7,1,-11,4,-2,13,-1,-11,-18,13,15,13,-6,-2,-6,10,-7,4,8,-5,10,22,9,-3,-11,-10,-12,8,14,46,10,-1,-4,15,7,2,6,-9,4,15,12,3,-8,3,-1,-3,11,5,2,4,2,27,2,-10,-2,7,-8,2,-3,12,-13,-3,-13,-1,-5,-3,-8,-13,-13,20,9,4,-8,-11,11,-10,12,5,-9,13,5,-3,2,-1,10,25,-14,-8,-2,-2,-4,11,8,6,2,6,13,-9,5,-5,14,14,-8,2,6,9,-4,8,-14,-6,-6,11,4,-12,2,-16,6,-4,-10,-8,15,7,11,2,13,-2,11,0,15,8,-11,7,-14,3,6,14,-6,15,-4,8,14,-10,-11,2,11,-3,-9,10,-4,21,-9,-1,21,9,-20,3,-7,37,-2,-15,3,-3,15,1,6,12,5,-8,11,-8,-5,-4,-7,8,-4,-4,-17,7,-3,27,13,-10,4,4,5,10,-6,24,9,13,-1,-13,-6,11,-19,11,11,14,-4,13,0,1,9,13,8,-9,15,-9,2,14,-6,-4,-13,18,-6,14,-7,11,-3,2,11,-4,-6,-13,21,-13,0,-16,4,0,5,-5,-7,5,-4,5,15,-5,3,3,2,5,4,15,-4,10,-14,1,19,14,-2,-9,6,1,12,11,2,-1,-8,-7,6,3,-6,-15,-19,13,-6,6,7,-7,-5,10,-2,-9,2,12,15,3,3,-5,26,11,1,-24,-2,-11,4,-8,-14,-11,-3,10,10,-4,15,11,7,-6,-9,5,13,1,8,-5,-10,-11,-5,-1,9,1,-3,-1,-9,-6,-4,5,1,-10,8,-2,3,-2,-23,12,-6,5,-4,12,12,-8,-14,4,-10,-6,12,11,-2,-14,-12,22,-5,24,-12,9,-5,5,-4,11,-10,14,-9,7,18,12,-16,-22,1,-9,-3,-4,-17,4,-5,-3,-6,0,-3,13,14,5,-1,4,-7,-3,-14,5,1,-13,6,-4,12,-9,-13,-13,19,12,1,5,1,-16,-5,-1,-12,7,-12,0,2,-11,12,-3,-1,-8,-17,-11,3,11,5,-2,36,10,-21,-7,4,-16,-4,-8,-4,-2,5,3,-3,-4,-2,7,9,-13,14,-6,-2,13,8,-14,-8,-11,6,20,0,-16,-3,4,-16,-9,5,21,-12,-8,-11,7,12,4,-17,-6,6,-7,-10,8,-12,-4,9,-4,-11,-6,1,-6,10,10,-21,-2,-9,3,4,-11,-11,8,-15,9,-11,4,1,-12,2,13,-10,-9,9,10,13,4,11,-8,-5,8,-4,-6,-7,11,1,-1,11,10,-12,-11,-8,-15,0,-9,-12,10,-2,-3,-12,14,3,-9,1,9,14,-5,5,12,-10,-12,11,12,8,9,14,-6,2,13,-3,-10,-7,-7,-9,-9,21,1,5,-5,-9,-1,-11,2,-4,-3,5,7,1,8,8,-14,29,-3,-6,16,-10,-3,-3,-6,8,4,-13,3,-14,-5,11,-1,-12,10,-3,21,1,7,-14,-14,-10,-11,1,8,12,3,3,-10,2,-9,7,15,-13,-7,-7,-4,6,6,5,5,0,16,-9,-14,5,-4,-11,-2,9,3,0,-3,27,1,2,0,-7,-8,-9,12,-5,10,-2,1,-4,2,7,-11,8,8,12,0,-13,2,-6,-2,-2,4,9,-11,0,9,-5,-14,5,4,-6,5,-4,2,-9,6,-13,0,10,1,-11,-2,3,-12,8,-15,6,-4,13,6,4,-4,20,-9,5,-10,7,21,4,-9,2,8,6,11,-12,-6,-3,-4,7,2,0,-15,0,-4,14,-14,8,2,-5,6,2,-9,-10,-2,8,10,-10,18,12,-8,15,1,1,13,5,4,8,3,-1,-1,8,-11,15,-11,1,9,13,-2,16,-13,12,12,12,4,2,8,6,-6,3,-5,-6,-8,-2,-12,23,-1,6,15,-7,-3,12,-6,-11,7,0,-4,6,-10,12,-11,14,-9,-13,12,2,-4,-10,-7,14,-2,5,5,18,-1,-20,-8,8,13,3,3,-2,-6,8,7,3,5,1,-28,-10,4,5,-10,-6,-13,-8,5,2,-20,-9,5,9,-4,3,4,8,-53,-41,2,-12,6,-9,-5,-10,14,4,-12,9,7,15,-12,0,-3,-13,-7,12,11,6,-26,-2,-4,-6,0,10,-10,-1,-3,-6,-10,-16,4,14,7,9,-13,5,-44,-37,-14,-1,4,5,12,-5,-4,-12,8,-5,0,8,-17,6,-19,-20,-14,-14,9,-4,-14,-3,-24,-21,14,4,1,-14,1,10,5,-11,2,-4,-9,-3,1,-18,-8,8,13,-3,-1,-11,-5,13,0,9,3,-10,15,-7,-8,-9,-2,-15,4,-7,8,6,8,-2,10,9,-8,3,4,0,-17,-28,17,8,15,11,-10,-5,-4,-9,-15,-13,8,7,-16,-5,1,15,-40,-49,-8,-3,1,6,4,7,-4,14,-10,1,-5,-2,6,7,1,5,-4,-7,10,9,-24,-15,-14,-6,-12,2,13,-4,-2,-11,-7,-17,7,13,7,-13,-11,-4,-26,-13,-2,3,14,-14,-8,-1,11,12,-13,-8,-14,12,-17,1,-16,-23,8,-9,13,6,3,4,-18,-1,-3,0,5,-14,12,12,0,-16,-12,0,0,0,-5,2,-14,10,-10,-7,6,9,8,9,-3,-7,3,-7,-11,-11,-9,0,-8,-6,-6,-6,14,3,1,2,13,9,6,10,6,14,10,-21,-7,14,-8,1,14,10,18,-7,12,-19,-15,3,-11,-16,17,7,7,-27,1,3,-9,-2,-11,-16,6,-8,16,-10,-14,9,9,7,13,-12,12,-12,-10,0,-20,-13,5,6,0,-13,-5,-12,12,0,13,-9,7,-4,-2,9,12,-15,8,-26,15,-12,-14,9,-3,2,-10,-6,-4,17,1,0,0,4,14,8,-11,3,4,-13,15,-11,9,11,-8,-11,-10,10,2,10,13,-17,15,2,4,3,-12,9,-13,-7,-13,14,6,1,-1,2,9,18,-2,8,-3,9,-8,2,11,9,6,2,-12,13,0,1,6,18,-3,-8,5,7,12,-8,-1,13,12,12,14,10,17,9,10,-13,9,-2,5,-5,0,-13,3,3,1,3,-3,2,7,7,13,-1,-5,3,7,-4,11,-5,0,-3,13,1,5,-10,-7,-8,13,-5,5,-8,1,-9,-13,-10,3,5,-6,12,12,-15,-12,-9,17,-2,-3,5,-7,-4,-8,15,-6,15,3,3,-2,15,2,-15,8,2,-7,-6,-2,5,13,4,-7,-5,5,5,10,-9,16,-2,1,-12,5,-6,2,1,-12,4,-7,13,5,-11,3,0,-5,12,17,4,-14,-10,14,9,-8,1,4,4,-12,-7,11,-8,-14,0,13,3,0,-3,-8,9,-14,-4,14,-11,14,-13,0,-7,3,-10,-13,6,-13,-11,-12,6,13,6,-8,-6,15,-4,9,9,-9,7,-3,11,4,-4,-12,-9,9,9,-10,-1,4,-11,-3,1,-23,6,10,3,8,2,6,-1,0,-15,12,1,-5,5,-8,13,4,-11,-5,-13,4,4,-1,-3,-1,7,-10,8,13,16,14,2,-8,6,1,-10,12,-12,6,-11,-1,-7,-10,-3,-5,1,10,-16,23,-5,13,-10,-14,-4,-3,5,-18,-8,-1,-6,3,15,12,-2,-1,-7,9,1,-13,13,9,9,-2,11,-6,3,3,10,0,11,15,-12,10,-1,-14,-1,-12,11,-17,6,-9,17,2,-6,0,3,19,2,-8,2,-10,-1,-10,9,14,13,-21,-1,-13,14,14,-7,-17,8,-8,-8,-12,-2,10,10,26,-11,9,8,10,4,2,-5,-30,3,23,3,-10,15,0,0,-3,-3,19,16,7,5,-9,-8,5,-10,-7,-14,11,-14,-4,12,-12,14,11,-13,-10,4,11,-13,0,-14,-33,-22,-14,-2,13,-14,-9,6,-15,-2,15,10,6,-9,25,17,11,4,11,-9,-1,12,5,-15,12,-11,10,-4,2,-15,25,0,3,-12,13,-7,1,4,-4,14,8,-12,0,-10,-7,-7,0,-14,14,16,2,1,-12,10,-24,-22,-5,8,-8,5,2,-16,5,15,-17,2,11,6,6,-13,-2,9,-36,-19,6,6,5,13,1,3,-8,-11,8,-13,-13,3,-12,13,-11,-3,-7,12,-7,-1,-21,-21,9,-14,13,-14,-4,9,-23,-6,-2,1,5,-4,-15,-5,8,-1,-30,-26,14,15,3,-7,-13,-10,15,2,-9,6,9,10,-14,3,-22,-11,0,-8,-12,-13,-3,1,-10,1,-6,10,-11,5,16,5,2,-8,-14,-12,-4,15,0,0,3,8,13,-7,-11,-6,14,10,14,-3,9,11,13,-7,2,16,7,13,-6,10,-13,-2,-12,10,7,6,-9,-6,12,3,-17,-15,4,0,7,14,14,-10,12,8,-13,-2,-1,2,10,-14,27,12,-1,-3,-4,-4,14,-6,4,-10,0,-9,5,11,-7,-5,-3,-3,6,-15,-5,8,3,-6,-23,-26,16,17,7,11,-14,-14,-18,-12,9,-12,14,10,-3,0,11,-6,-21,-8,-2,-2,-6,1,3,-8,-8,9,8,1,-15,16,-4,3,-8,-13,5,-2,-6,7,13,12,-1,6,-9,-9,-9,-1,-1,18,12,-13,-12,-15,-11,6,-5,-3,5,4,2,9,3,7,11,10,9,15,-8,10,-6,8,1,-3,27,25,11,1,-3,8,-10,-6,20,-3,14,-9,5,-7,-5,4,0,6,-6,-9,-3,3,18,16,7,11,-5,9,10,-11,16,-5,40,21,2,6,-11,6,-7,-7,12,5,14,7,7,-9,-11,1,14,4,-2,4,-9,-2,-17,-19,13,5,11,-1,-2,-10,-12,-4,12,0,3,9,-14,2,-4,11,8,19,-8,-10,10,9,6,1,3,15,0,7,7,8,5,7,19,10,-1,2,-6,-14,8,-8,-16,2,12,-5,16,-12,16,-2,-4,4,-3,14,-7,10,-11,-3,-11,-7,-6,15,4,-6,6,-7,18,7,-7,-12,-5,8,-3,-15,-1,3,4,-4,-10,-10,2,-3,-9,-2,-6,-16,9,-6,15,-15,-13,-8,12,16,4,-5,12,-4,14,0,1,5,-9,1,17,12,42,28,9,-11,-4,-3,-11,5,-3,14,11,-2,11,-12,-19,-17,17,1,-8,13,-2,-6,-9,11,29,19,-6,4,7,-1,3,-14,5,15,-9,-11,13,3,-6,13,34,18,4,-8,10,-12,-7,1,7,-4,-15,-14,-12,7,6,2,10,-6,4,3,15,0,4,-5,-8,9,9,15,0,2,-4,9,25,2,14,-14,12,0,-4,-15,-14,6,13,17,-1,-3,11,17,9,8,2,1,11,4,13,5,14,-7,0,-3,-11,7,-5,6,8,17,-9,12,-5,-9,-15,-17,12,1,-3,-7,-13,-5,17,-2,19,-1,7,14,-3,-10,24,-6,6,-8,14,4,14,0,-9,11,0,-3,-5,16,-8,-9,-1,2,0,15,6,2,-10,7,-2,-12,33,19,13,1,7,-10,-3,11,30,2,12,15,7,2,-7,1,-4,18,-9,-4,15,7,-1,-4,10,-2,-8,3,-15,1,7,-20,-13,0,12,6,9,8,1,1,-2,10,9,17,-5,-13,27,18,5,6,-11,-14,-8,12,-5,6,14,8,8,-6,12,-11,19,14,16,-10,10,0,2,-12,-8,13,-1,-7,-12,-6,-2,-9,3,7,8,5,-7,4,6,0,-3,-12,4,-13,9,11,-11,-15,17,12,-5,2,6,17,2,10,56,25,-13,-2,7,9,0,1,-17,-1,-13,12,-6,-7,-7,-14,11,15,-7,5,-6,-6,-2,-11,-7,-5,25,20,-5,1,15,14,-8,-17,28,11,-9,-12,-3,-2,10,1,4,-10,-3,-8,-2,5,-12,8,-14,9,-10,0,3,-7,-2,-14,-25,0,7,6,-2,-11,15,-5,-10,-7,13,-2,5,9,19,8,17,6,2,9,13,9,-7,1,-1,12,9,15,1,-12,2,10,15,9,-14,6,-10,-5,-7,-9,-4,-9,-4,11,-1,11,9,8,1,-7,-5,10,-2,2,3,2,14,-6,4,4,-15,-15,-1,6,1,-2,-13,3,11,-2,10,5,-11,-1,-13,9,9,-3,12,-5,13,14,0,7,-6,-8,-12,5,15,14,-15,-9,-12,16,-3,-3,20,15,13,-9,-2,-6,-7,-6,15,14,-2,3,-9,-2,3,0,-4,-19,-7,8,2,9,-10,13,12,-4,-7,-8,-8,6,1,-7,14,7,0,-12,5,-15,-7,14,-15,3,4,-3,12,-8,17,13,7,-15,4,0,-1,0,-5,-9,0,8,10,3,-11,-2,15,-10,9,8,0,-5,6,7,2,13,3,17,5,10,12,-1,15,2,-8,12,11,1,13,-9,-11,-11,17,-9,-12,0,10,-6,0,-9,19,14,-13,6,3,3,-6,24,13,-3,-1,6,-12,-2,6,-17,0,1,-6,-4,0,7,-1,12,8,6,-2,-13,4,11,3,-18,1,16,-1,-12,12,14,1,-12,15,-7,4,0,10,5,11,-11,20,-6,-8,-8,-4,-12,-6,-14,13,-4,-3,9,14,-14,-2,-20,13,10,2,-4,-15,10,2,-3,6,-8,-13,4,12,-12,-4,14,8,-2,13,-15,-2,-13,0,-3,15,-14,7,-7,-12,-13,-5,-9,-6,-14,5,4,14,11,1,0,21,22,5,12,-5,1,5,-1,15,20,-1,-9,10,12,2,2,-13,-8,0,8,-8,9,18,-4,20,-9,3,23,-1,0,11,5,21,27,14,0,16,3,6,2,6,-5,3,5,9,-2,12,-10,-10,17,-14,-11,-13,-9,-6,-16,24,24,0,-10,-3,-10,6,-1,32,6,-11,5,-3,9,12,-7,20,-2,4,-13,15,-5,-5,-12,1,5,-13,5,0,-7,-20,2,6,-12,-15,-12,-2,-14,-1,-6,-12,-16,-13,14,-14,6,27,24,22,12,-5,12,5,4,-2,-17,-10,-10,-4,6,-9,-1,-14,-1,18,-1,8,5,1,6,-15,-5,13,1,5,12,-10,3,-2,4,2,13,10,-9,-11,-13,-10,0,11,9,7,-3,3,-18,-3,16,-7,-13,5,5,7,9,31,19,12,-5,-7,14,-14,15,3,2,-14,-6,0,16,-9,-3,1,11,6,10,8,7,-12,-5,-8,-2,24,21,14,0,3,13,-6,-13,29,5,-10,15,0,-6,9,0,-4,10,-7,-5,6,5,-12,14,1,13,-14,0,-4,-9,-19,-1,-18,-18,-11,7,-9,10,13,-5,-3,-11,-14,19,-3,8,8,2,16,9,3,-7,-12,-2,-11,-9,-10,2,11,1,1,-13,-11,-4,-8,8,4,-5,-12,1,-9,2,25,7,8,5,-8,11,5,6,15,5,10,-3,13,-6,-22,-8,5,5,2,12,-1,10,18,13,-4,-2,-1,19,6,-12,30,7,10,-3,-9,-10,-10,-7,-9,-3,-6,-2,-10,9,1,-5,-10,-7,0,-14,0,2,14,1,-16,-5,9,-1,-11,-12,6,17,-8,-3,24,23,3,-13,-7,-5,-14,-3,3,7,6,1,-2,8,-5,-14,13,13,9,-6,2,-13,-12,-8,-27,-6,10,13,3,4,4,-13,4,9,11,6,1,-13,25,11,-2,8,0,-1,-5,15,4,5,14,5,3,-13,-2,0,-6,-9,20,14,10,6,-6,17,-6,-3,16,-2,11,9,9,-6,-1,-14,2,9,14,2,12,2,-3,8,-12,4,7,-11,-10,-5,0,20,3,-9,7,25,12,3,63,38,4,9,-5,9,0,-15,-15,10,1,-6,-3,-6,8,-14,20,16,9,-10,-11,10,-2,12,-23,-8,30,20,13,3,-13,-1,-14,6,43,18,-5,-1,12,-4,-8,10,-3,-5,-12,-12,-11,0,-9,7,-1,12,14,-2,6,-10,13,-11,-13,2,-5,-3,13,2,-4,-10,-12,-5,-7,10,14,-3,36,12,6,-2,-9,2,12,2,-6,10,6,3,-2,4,13,0,2,12,10,9,0,-13,-1,-6,-14,-11,-2,17,-6,-8,15,4,-9,-2,6,-11,-2,1,-10,-13,16,-18,2,-1,-6,-4,-9,5,10,2,11,11,7,-9,-7,-9,1,4,6,4,-11,-13,0,12,6,-2,-7,-9,-13,-8,11,-13,4,2,-7,1,-2,-15,6,9,5,2,14,2,9,-13,-3,3,-11,-10,23,14,11,-4,-7,3,-13,6,23,-17,-11,12,-6,-8,-2,8,-3,-10,12,-8,-12,-4,-16,3,16,7,-6,-9,-6,7,15,-8,-11,7,-11,0,4,2,3,12,5,-3,-15,5,-11,3,-7,-13,7,2,-14,3,11,-10,-2,1,11,-10,10,10,6,-1,13,1,8,17,-4,7,-14,11,-6,7,-8,12,-9,-3,5,-7,10,-17,13,14,4,-5,10,-15,17,3,12,-3,-13,20,-6,-2,-2,3,26,-6,-7,12,-1,12,10,-9,15,4,9,18,-6,5,-12,13,13,-11,0,10,-2,12,4,-13,18,8,-6,2,-4,-10,-10,3,28,7,-6,9,-10,4,7,-11,7,5,-8,-10,3,8,-1,-4,-1,-12,-1,5,-3,-10,-22,-30,7,1,-12,10,12,11,10,-9,-17,-4,-10,5,2,14,10,5,-7,8,6,-17,-8,-12,-7,-12,5,1,3,13,-1,14,-19,5,11,2,9,18,-14,-11,7,19,24,10,10,-3,-2,12,-11,-6,10,7,12,5,-2,-14,-31,-27,-4,5,-4,9,6,9,12,0,-6,-2,8,21,4,7,40,32,9,9,-4,-12,-6,8,5,-19,7,-1,3,17,-13,9,10,24,17,4,-10,0,7,13,-16,-15,7,-5,-11,-13,-9,-11,-9,4,39,12,1,-3,11,5,-14,6,0,-6,-15,-8,-14,15,2,-12,-1,-4,10,-6,2,-3,-15,-16,-1,-1,-6,5,-13,11,-3,-7,-12,-14,0,25,5,12,30,34,-4,4,-1,3,0,-3,-8,-14,6,5,-14,3,3,-12,11,0,-2,-7,-14,5,-4,11,-11,13,17,-7,13,-7,-7,2,-2,8,17,24,1,-17,5,-6,-4,-22,11,-8,6,15,-4,-5,2,-8,-12,-4,-6,9,7,-26,24,38,-13,-32,3,-16,-11,-9,-9,-16,2,9,0,10,5,13,10,1,-5,1,-2,-1,-2,-1,-5,-18,4,8,14,-13,-14,7,0,-19,33,-6,-3,15,-11,2,6,4,-13,-17,-11,14,-14,-6,2,-4,4,10,12,3,6,-11,-24,-5,-25,-25,-1,0,-2,-6,-1,3,8,10,8,12,10,2,15,14,-11,-13,8,12,12,15,-14,-14,-4,-14,5,-10,-2,-8,11,-12,12,17,14,-11,0,12,-5,14,-5,7,10,-6,12,1,-2,3,14,10,5,4,6,1,-6,-7,9,10,2,4,-1,11,-3,-7,2,5,-11,23,12,-20,33,21,3,-9,-1,13,-14,11,-8,-5,-10,2,-9,-2,4,9,8,-1,8,10,-10,-15,-12,11,-8,-1,-4,6,2,8,-6,-12,-18,-8,20,23,1,-13,14,10,12,7,6,-8,15,-1,9,5,11,2,-1,12,-11,0,-12,-4,-3,3,-18,-27,9,0,2,2,-14,7,6,12,-8,16,9,-5,21,23,-12,-7,11,-4,-1,-12,1,3,-9,-10,13,-8,12,14,11,-8,14,10,1,16,4,-1,-7,14,10,9,-14,5,13,-3,0,14,10,16,7,13,8,-14,8,-8,-4,9,-14,1,5,-11,2,11,5,9,-2,-2,0,-8,42,35,4,-14,7,8,8,9,-3,-14,6,13,2,8,-11,-5,10,5,17,-14,9,13,-11,7,-17,-14,11,6,-12,7,9,-11,-2,-7,28,-2,4,-6,-11,-12,-12,14,9,0,-3,8,0,-10,12,8,-9,2,3,11,-11,0,16,15,-20,-21,6,-3,-8,0,0,-5,2,12,14,19,8,-15,32,2,0,5,-10,-5,6,4,20,18,14,7,-2,5,0,5,19,-7,-7,1,-8,-7,-10,9,2,-7,18,-8,6,-8,10,-9,-14,-6,-9,10,-2,1,-2,3,-13,-9,-6,-1,0,15,-2,12,1,-13,10,4,-8,-12,11,0,-4,-15,13,5,6,-13,13,-14,8,2,12,10,2,-11,-10,-4,-4,6,-7,-16,-8,14,0,4,1,-15,-4,8,-11,9,-2,-1,8,-15,7,-8,-6,3,7,-7,-3,-10,10,-6,7,2,10,-12,14,5,14,-2,8,-9,1,-5,-4,-20,-2,-3,10,7,-8,0,2,-5,-18,7,15,-4,-12,9,1,-9,-9,-16,7,-4,11,9,-18,-6,-2,-6,5,-5,11,-13,12,6,-8,13,-11,12,3,3,10,-14,4,22,3,-8,11,-4,-1,-12,-5,16,1,4,-14,14,-18,-15,13,14,-13,4,-4,0,14,15,8,3,12,-9,11,1,-9,-15,16,12,3,-9,-2,0,-15,-5,11,-14,15,7,-5,0,-17,-14,2,-16,0,8,-6,12,-18,-6,22,6,12,-9,-11,12,-2,-14,28,26,15,-9,4,12,-8,-8,3,11,3,4,-9,-14,14,7,1,7,-2,-15,-3,6,-32,-5,-9,-4,4,-8,7,1,3,-11,-17,-3,-1,-5,3,-9,-13,-6,-2,-3,-11,2,9,-1,-19,-6,-6,-7,-4,-3,1,6,-7,8,-7,7,-8,13,-5,2,-1,-9,30,10,-6,8,13,-10,-16,-12,21,-5,7,10,-12,-10,-12,-21,-6,6,15,4,-11,15,5,-1,-1,19,-6,-1,-5,-7,27,17,8,5,2,4,-8,-10,-6,-2,-13,14,-5,-7,11,11,5,16,10,-5,4,-18,-2,6,-19,-25,18,17,3,-11,-4,2,6,0,35,22,-4,0,-6,-14,-9,5,18,14,13,-5,-2,-3,7,-8,-14,-13,3,0,-4,-12,-25,-16,0,-11,-9,10,-3,11,-14,-8,-15,-13,-8,20,-11,13,19,22,29,14,6,-9,11,1,-4,2,-6,14,7,8,-7,16,-18,7,-5,6,9,-1,11,8,-11,11,19,21,15,9,-8,2,1,-8,25,20,-9,-7,15,0,6,-9,2,2,15,2,1,-11,-3,3,9,7,10,15,-1,13,40,34,9,-2,14,-12,5,8,-15,2,-10,-1,-3,-5,-1,0,-10,-3,0,12,-13,-14,5,15,-8,-19,11,17,9,-2,-8,11,-19,-19,28,32,-3,11,-1,-10,-4,-2,-14,1,14,4,13,11,8,15,-9,1,-8,9,-9,-3,-18,-11,-24,-3,1,-1,-4,2,9,5,4,-7,-4,19,3,-10,20,25,9,1,-13,-13,-10,5,-17,-17,4,1,-11,-8,15,2,-14,-10,7,10,4,14,6,3,-8,7,-1,10,5,-11,-2,-12,4,1,25,1,-6,0,14,15,-6,5,-11,14,-2,10,-13,6,14,15,-12,-4,-2,-2,1,-4,28,17,-11,11,-6,-14,-5,-7,8,-14,-2,-11,-3,1,13,-12,3,-6,-5,5,-7,10,0,-5,-25,-20,17,-8,3,-3,5,14,-19,4,22,18,12,-2,2,-2,-8,12,-11,6,3,3,-12,15,0,8,13,-14,0,9,12,-4,-8,1,-21,-27,12,15,2,-7,-5,7,9,13,15,14,-13,-13,30,24,-7,13,10,-8,10,-11,16,13,-11,11,8,3,-13,11,3,16,-3,17,13,7,3,6,8,-2,-6,0,-6,16,10,9,8,-6,2,-4,9,2,9,10,-6,11,-4,7,-10,-5,-5,8,21,-7,7,-5,10,10,-10,11,41,37,10,-1,3,1,-1,6,0,-6,15,1,-13,2,2,14,13,28,7,-5,15,-3,6,16,8,4,-1,-5,-14,4,5,8,-5,0,5,-4,-14,-2,-14,5,12,-11,8,-9,14,-4,11,14,11,-2,-2,-10,-5,17,-14,9,2,12,-17,-8,14,15,7,8,11,-5,-11,-6,8,7,2,1,19,22,-9,-4,-13,3,-5,9,-7,0,12,-9,3,15,-10,10,24,10,2,10,-15,-7,5,0,4,-6,6,10,-1,13,5,-6,14,7,-9,14,-14,7,10,-4,-11,-16,2,18,0,4,5,-9,0,-6,1,-18,-1,-2,8,-13,-15,-3,-46,-19,-4,1,4,-2,-19,3,11,3,-1,3,9,-10,-1,0,-15,-7,9,-13,14,13,-3,-3,2,5,-8,-10,-8,10,-12,-4,6,4,1,-11,-10,-18,-8,8,-10,-18,-9,2,-3,-4,2,-11,-10,-2,5,-4,-13,7,-17,0,-19,-13,1,-11,1,8,-2,4,-21,5,-2,-5,2,-5,13,-8,-7,-8,4,8,-12,7,4,-17,-4,-11,6,1,-14,-2,13,8,5,-8,0,1,11,9,-4,12,1,-4,-1,14,-10,14,16,-6,0,-2,4,-10,-7,12,-29,-8,18,15,15,6,-12,12,6,4,-17,-23,5,9,6,-21,-1,0,-19,-21,-1,10,13,-1,-18,0,14,-5,7,-10,-10,5,17,17,6,-9,-13,10,-11,6,-41,-24,11,-3,-10,-13,17,4,-20,-13,8,9,-12,12,0,-7,-9,2,-32,-24,-12,-4,3,-2,1,-1,-8,-2,-10,-8,-3,-9,-32,-11,-37,-21,-11,-14,-10,-10,9,15,-5,-7,12,9,-10,-10,-2,1,1,-13,-7,-6,-13,6,-4,1,-11,-3,6,-8,13,-14,4,1,9,13,2,10,17,9,18,6,20,29,6,11,-2,5,-1,14,19,5,2,-8,-13,5,-10,-24,6,10,5,11,8,-3,-4,15,-12,-3,4,13,-3,-10,19,23,16,2,-2,8,3,-1,-22,-17,10,5,6,-1,-2,5,9,20,-14,10,-1,-15,-8,17,-32,-5,14,31,-12,-3,10,19,-3,-20,28,26,-2,1,-5,8,13,4,-16,-18,3,6,15,4,4,8,1,5,2,8,-6,-9,-19,-2,-31,-19,-5,-7,-5,12,3,-12,2,-18,-6,19,12,-9,11,7,7,0,12,6,6,4,3,-20,11,-5,12,11,3,4,9,-6,7,5,-10,11,15,14,19,17,22,11,4,-11,-7,-9,-7,-3,7,21,-7,3,11,8,5,-11,-9,11,12,1,-5,-10,4,3,-8,11,6,23,13,-12,27,36,15,14,-13,0,3,-5,6,-11,-4,1,14,5,-5,-9,1,24,-5,5,9,-10,17,-4,-22,1,36,15,-12,-4,10,-8,-6,-6,52,18,-9,7,10,1,8,-9,-20,-16,14,8,-2,1,7,-9,-11,12,-5,-10,5,-1,-5,-11,-14,-10,15,6,-10,5,-1,-14,-6,-18,3,-3,12,-2,34,10,8,20,2,2,-1,11,11,-13,-5,0,1,7,1,8,8,14,15,-8,6,-4,16,-2,-2,19,6,8,5,3,7,-8,-2,19,16,22,-10,-11,-14,10,-16,-10,-13,-10,15,8,16,-16,-8,-7,-5,4,-4,24,15,2,49,35,-11,-2,-14,-4,-11,-10,-17,-2,-5,-7,10,12,14,-6,15,23,1,6,-4,5,-12,0,-11,-4,14,-1,2,9,12,4,-4,-14,33,25,11,-9,4,7,-13,-1,-8,4,14,-10,6,13,0,-2,2,9,1,7,-2,0,8,-18,-32,-13,8,9,9,9,3,4,10,0,0,-4,1,14,17,10,23,5,6,12,-10,1,-8,7,-6,2,-11,10,-8,9,7,14,-4,-11,5,14,-11,-2,21,22,-5,-8,8,-1,-6,-10,26,19,17,-8,0,-11,-7,10,-13,-4,-6,-2,0,-12,-4,2,-4,-6,-9,-8,9,19,6,6,28,22,-6,-6,9,3,-14,3,-12,-8,-11,-2,2,12,12,-9,18,30,-9,-7,-10,-9,-3,8,1,-8,5,-9,-1,-5,1,-1,-13,-22,30,26,-14,-10,-1,7,-12,0,-7,-8,-3,-10,10,-13,-8,-4,-3,-10,-2,2,10,11,11,5,-17,-24,15,-8,-11,-14,-9,-8,-13,12,2,4,3,0,15,9,17,12,-13,5,2,-11,1,10,-8,0,-4,13,8,12,10,-7,16,-1,3,16,-11,5,-3,-8,11,11,10,0,1,-11,-7,7,6,7,5,8,8,4,-16,5,-11,-4,-9,0,-2,-12,14,-4,3,8,12,13,-19,11,-8,10,-14,7,10,-4,-6,13,-12,11,8,14,7,-5,-9,-9,11,5,-5,5,2,0,2,-9,4,-14,9,-5,6,15,-15,-5,9,1,-14,-8,-13,3,-15,5,-10,-14,1,2,8,2,-9,-5,2,5,12,-13,-2,-2,-15,-6,-4,-15,8,3,10,-6,0,2,15,-2,-1,4,15,-14,-6,1,9,13,12,11,14,-11,1,4,-4,2,-1,10,7,9,2,-4,13,2,6,0,-5,-3,3,3,15,-10,11,-10,-6,-7,8,-10,-12,-9,3,-2,8,9,2,11,4,-9,17,11,-6,8,-15,9,-10,-10,-10,0,12,0,9,15,-7,-14,3,-2,-14,-7,-7,0,-13,-2,11,-7,9,-15,-13,-6,17,-4,9,13,-12,9,-6,11,11,10,12,9,-6,4,-3,-13,-5,-10,12,15,-4,-14,8,-3,10,3,-9,16,11,-12,-5,-4,-8,4,8,15,11,-5,-2,5,-7,10,2,-4,9,11,-12,3,10,-5,6,0,-4,-11,-9,-6,-5,15,3,-9,-14,0,6,-8,10,-4,14,3,6,-13,4,-12,9,-8,7,9,5,12,3,-9,5,-8,10,7,-1,-9,-5,4,-9,10,2,-11,-1,14,-12,-12,15,10,17,-2,-13,-13,-13,-7,10,9,-1,14,7,-3,-6,7,-3,0,6,-7,-6,13,-11,13,-3,-15,-7,11,-4,-13,-10,-11,-6,8,4,-8,-3,15,5,-3,-5,-15,15,3,-10,15,-6,-13,-1,-11,1,-1,10,-13,-1,17,-11,-9,7,6,2,0,-7,13,1,7,-2,-5,10,17,8,18,-1,9,4,13,6,-6,-1,5,4,-6,5,-1,15,-11,-1,-13,14,-3,-4,10,7,5,-1,11,-13,9,4,-3,14,8,13,-4,16,2,14,0,-13,-7,11,14,14,-14,8,-7,13,4,13,2,-1,-8,-1,-5,12,-8,14,1,-1,13,14,15,-4,0,-5,-4,3,-5,1,12,-1,-13,-8,-2,5,0,19,3,-8,15,-3,-10,-6,5,15,-14,2,-14,1,3,-4,6,-12,-6,9,16,-10,16,10,-14,-4,11,-8,6,1,1,13,5,14,-7,15,-8,-14,5,8,-5,-9,6,10,5,-3,-4,4,-1,11,-7,10,8,2,4,5,-13,0,-2,5,5,1,-10,14,13,-8,-11,7,14,-10,-12,0,-2,9,9,-7,11,0,-6,10,14,7,9,2,6,-11,12,7,7,9,13,-8,12,-5,15,12,2,8,5,15,-8,8,-4,13,-4,12,5,9,21,0,9,-4,-4,15,3,-2,-4,5,20,5,-9,-1,0,10,-13,-9,24,11,12,1,-13,13,15,14,14,-10,-4,9,-15,13,-7,-9,-12,-5,2,1,-10,6,-4,6,7,-4,-13,1,-13,-6,13,6,-2,4,-1,-7,1,-13,15,-5,11,11,9,16,2,-11,7,-3,-11,8,-10,5,13,-11,15,3,11,-16,-2,-1,0,2,12,-7,-5,8,11,-10,4,15,-12,-3,11,5,9,13,8,14,-1,-3,-7,5,12,-7,12,16,-10,-7,3,10,-5,6,-10,13,-2,0,10,12,4,1,5,-12,4,4,16,-13,0,4,0,4,-1,-1,-8,5,6,4,11,-11,-4,16,18,8,-1,-3,15,3,10,16,26,-5,1,20,7,-4,-1,2,-12,-15,-12,13,10,13,-1,-8,7,-3,15,-13,13,12,-4,16,8,-14,6,7,-1,4,17,-7,-10,-11,-9,8,13,13,0,11,-5,15,-12,5,-13,11,12,-13,0,-4,12,11,-9,-7,-7,-6,21,-10,-5,-8,-13,-10,-12,-11,-11,-13,10,-3,-2,-7,-13,-3,-11,4,-12,1,-12,10,-7,-5,5,3,13,6,3,-2,-4,-1,-13,10,-9,-4,12,-13,-10,-14,1,10,2,-12,16,-6,-8,13,-3,5,-1,-10,9,6,-3,-4,14,-2,7,-9,1,1,9,-12,-4,-4,3,-4,-12,7,1,11,-8,7,-3,-13,-8,7,-5,4,-13,1,-13,7,-13,14,13,-12,15,-4,-13,-2,-2,9,-14,-5,9,9,14,-11,0,-11,11,-5,-4,12,12,2,11,1,8,8,-1,13,13,-13,-1,-12,-12,-12,-6,9,5,5,6,-10,7,9,10,15,-6,-8,12,-12,4,13,7,-9,-10,-12,13,7,7,-9,-6,14,-7,16,9,-6,-9,-8,9,-6,-15,1,8,2,8,2,-11,-13,12,-5,0,4,0,-11,-2,0,-2,6,2,2,7,3,9,-8,11,10,12,13,-3,4,6,1,11,10,-8,15,12,-6,16,7,8,-14,-12,-12,2,3,-7,-9,8,-19,13,1,10,-13,1,-8,7,-1,-14,14,4,3,-12,0,3,14,10,1,7,0,15,-11,3,-12,6,3,-3,-10,10,9,-4,11,7,13,1,11,-4,14,-1,-10,-12,9,-4,2,15,8,5,6,-14,13,11,-9,-5,5,-1,-2,3,6,-5,-13,11,9,-2,-6,4,1,10,-11,3,-8,-14,9,11,11,-1,9,-11,-4,-12,9,7,3,16,2,-10,-12,15,13,-3,-11,-13,-14,-2,3,-3,12,5,-11,0,-10,13,5,7,-15,-7,-5,14,5,8,-5,12,-2,12,-5,9,12,2,-13,-7,14,-8,12,-10,-16,7,17,6,0,6,7,-11,-11,-1,-14,-5,12,6,9,13,11,-13,4,-14,11,8,11,-4,3,-10,13,-4,5,1,16,-6,10,-7,13,0,-6,14,-13,-10,-10,14,-12,13,-3,0,0,-2,14,6,-4,-8,11,15,-11,-8,-2,5,-14,-12,-5,-15,-9,-13,6,-4,14,3,-9,13,-3,-13,-11,9,6,12,-9,-8,7,-5,-3,0,-11,-9,-13,0,9,-3,-13,12,-6,8,13,-5,4,-14,-14,-12,10,9,-8,-5,-5,-4,-12,-1,2,5,13,12,-1,9,15,8,-1,-11,-2,-13,15,14,8,-1,-7,-6,6,15,9,-8,-9,-10,13,-11,3,9,14,-5,0,7,2,8,15,-13,-13,-11,-1,-13,-3,12,-6,12,-5,-12,13,7,14,4,6,10,-3,-12,-12,11,13,-8,16,12,12,-4,-11,-13,-13,-8,7,-7,-12,-10,-8,6,4,3,-7,-13,-6,-2,13,12,10,1,15,5,6,-9,-2,-12,14,-4,1,-5,5,12,-12,16,0,11,-4,4,-12,2,11,-10,-8,-11,-1,0,13,5,6,7,9,-15,-6,3,1,-14,13,9,4,-9,25,-2,11,3,-11,-10,-13,-4,0,-10,-5,7,16,-1,3,0,-14,14,12,-9,7,13,14,6,-2,-6,0,11,0,-2,-10,4,26,4,7,5,13,-12,-11,14,3,-7,3,12,-5,1,-5,-5,-11,5,8,15,12,-7,1,9,25,-10,15,10,-10,1,6,3,12,0,10,-10,-3,-12,13,5,-3,3,5,7,11,-3,-14,12,6,1,13,15,13,14,-9,6,5,-12,15,3,-3,-11,4,9,11,7,10,15,4,5,11,12,-1,-3,6,7,1,-6,-14,0,-1,9,3,14,8,-10,8,11,5,-4,3,-1,-8,9,-4,-7,9,-14,-1,2,3,-10,-7,-8,-6,-13,4,-3,-2,-10,3,8,-2,-3,-12,6,-10,-6,10,-9,-2,2,-10,10,12,18,6,5,29,-14,2,-9,7,-1,5,-7,4,-14,8,-13,6,-1,13,-13,3,-10,12,4,-8,-11,-11,12,25,7,6,3,14,8,4,12,23,8,-8,-9,-1,4,-3,-7,0,14,15,13,-8,9,14,-15,13,-14,4,-10,-7,-6,6,15,10,12,-5,-13,-13,-1,11,7,-8,-11,0,3,3,8,11,8,18,5,-9,-3,15,2,0,5,-3,3,-4,-12,-6,6,11,13,3,-7,0,16,2,-1,10,11,11,6,-2,12,-9,-11,15,-11,4,-8,5,10,5,14,10,-12,7,9,-5,-10,11,8,11,-8,12,13,8,-2,-5,-2,11,-9,1,10,-9,-3,14,5,14,-8,6,14,-8,4,7,-10,-14,-13,0,-10,1,-13,5,-7,1,7,-5,17,-6,-8,14,1,-13,11,-1,0,0,-14,-4,5,0,-6,-4,-3,-9,1,8,14,15,-3,-11,6,-2,-2,2,0,10,9,17,-1,-5,15,1,-12,-12,10,2,-1,10,-2,-14,-12,-2,-8,16,-6,0,15,4,15,4,6,-6,12,13,-9,-6,-15,13,2,4,-5,10,10,7,11,-2,-3,-7,-2,-7,13,-9,2,13,-5,3,7,9,-16,14,13,13,9,4,9,2,-12,-13,-7,9,6,-2,1,2,-14,1,-19,5,12,-6,-2,11,10,-4,-2,-7,10,1,14,-1,-4,14,12,8,14,2,0,-6,7,-15,-1,-2,-3,6,15,-9,2,-9,-12,12,4,-5,-1,-12,-9,-2,-11,11,-11,3,11,2,-11,12,-12,2,-9,-14,12,-13,-6,-6,-17,0,-5,6,0,9,-4,-12,6,11,-6,12,14,-7,2,7,7,-5,-13,-2,13,12,-4,0,10,12,14,7,1,0,15,9,4,1,-11,1,11,-1,-6,-4,-3,16,3,-7,6,0,-9,5,3,-12,-3,14,-9,14,-9,-7,-12,7,10,-11,0,0,-11,-3,3,15,7,19,-9,-9,16,-2,5,3,8,4,-4,-2,11,11,16,-13,10,-12,-3,0,15,6,5,-19,-1,-8,-6,12,3,8,12,-2,-6,1,-4,-11,-12,-4,2,5,3,14,-7,8,9,-3,-16,-9,3,3,12,4,6,0,4,3,2,-14,14,-7,9,15,-16,9,4,-2,-11,2,-9,-11,-7,15,-1,2,-14,6,-5,7,-7,8,-15,12,17,14,12,13,-5,1,-7,3,-12,-1,-5,13,-4,7,-12,-4,-12,-5,14,-2,5,10,-4,4,12,13,-2,11,-4,-15,-12,0,11,-11,11,-14,-11,9,2,14,13,7,-5,1,-13,10,3,-2,2,5,-2,-7,7,-21,2,31,7,1,-16,5,-12,-7,2,-6,7,-8,13,-5,15,-5,9,16,-10,3,2,-1,-9,1,-7,26,3,-10,7,-13,-10,-5,-6,-1,14,-3,8,-9,8,12,12,-2,-6,14,5,-6,-13,-7,15,14,-14,9,-4,10,0,4,-7,-7,-2,6,-7,-7,-2,-7,-11,3,10,13,-8,-7,15,10,-12,7,13,5,-6,-1,14,1,8,-7,8,7,0,10,7,11,-8,2,1,10,9,-5,16,-2,0,-5,2,2,-7,4,-8,2,14,-11,-9,5,-16,3,-13,2,1,-9,-10,-8,-11,13,-8,-13,12,1,11,-7,9,-3,-8,-1,-1,30,-3,-6,-3,14,4,12,10,-1,2,-6,-8,-6,9,3,-6,-10,-9,5,-10,5,10,1,5,7,6,0,-10,-7,-2,6,-1,-4,-9,12,14,-3,-10,1,-2,-7,-12,6,3,12,-5,15,1,15,0,-2,-5,11,-2,-4,6,-9,15,-6,13,-12,-4,14,-9,-10,-10,1,15,1,-9,-3,-7,15,6,9,4,10,-13,-10,10,13,8,7,7,-9,14,-1,7,-10,0,0,11,16,-13,6,8,-8,-12,10,1,8,1,2,14,12,-1,3,13,-11,5,16,12,6,-8,9,-14,0,4,6,15,13,-13,11,-7,-11,10,-9,8,28,-15,-13,4,-8,-7,-2,1,-2,-2,-10,-5,-13,-2,-2,0,3,6,1,7,9,7,10,-8,11,-14,-10,12,-4,9,-4,-13,19,11,-10,0,13,16,9,11,2,7,-11,11,13,-5,-11,-13,-12,-14,12,9,-5,17,-15,10,11,-3,-5,8,1,-3,12,5,4,14,8,3,10,9,-11,1,0,-1,-14,8,8,-13,-10,-9,-14,-10,8,10,12,0,-3,9,-7,-10,-3,-1,-4,6,-10,-14,-1,7,-2,-4,-11,15,12,1,-1,3,-7,-9,-13,4,11,-1,-10,3,1,5,13,-6,10,3,-9,1,6,11,-3,-22,-14,2,9,-8,-10,0,-7,13,17,1,12,2,15,14,-4,-11,-10,-13,-13,4,14,-16,7,-4,13,-14,-11,16,0,-14,1,11,0,0,0,10,-5,-14,8,-1,5,-8,-2,8,12,-2,-8,-6,-14,8,1,10,4,-4,14,-4,6,-6,14,-4,0,-7,11,12,5,4,-7,-9,4,-12,-12,-15,9,0,-7,1,-8,6,13,-8,3,-7,-11,4,10,-8,1,11,7,-11,-1,-4,11,1,7,8,-5,2,-3,-7,11,15,-13,7,12,-13,15,-6,10,-16,11,10,-4,12,-3,8,-3,-10,10,3,14,-10,20,7,-1,18,14,-14,-8,5,6,1,8,7,-12,11,7,-5,0,13,12,-10,-6,12,12,-6,-9,12,-5,-16,-6,-10,-5,1,0,-11,-2,-1,2,-10,8,3,1,0,-13,-8,8,-4,-14,2,6,-13,-5,2,6,12,-8,-10,14,-3,7,12,4,-21,4,3,0,-8,-13,-14,-3,-9,-4,-12,-1,-11,5,6,-7,-14,-11,2,19,15,8,-11,6,1,0,3,12,0,7,1,12,7,-5,-3,-9,8,4,-12,-6,3,9,12,19,9,-13,-12,1,-9,7,-13,-3,5,8,-2,-1,-14,-4,-6,9,-1,-12,-5,0,8,7,-5,26,-6,-1,1,-7,-13,-24,-9,11,-4,-4,-16,1,10,-12,-8,-4,7,5,1,-11,15,-10,6,5,8,-1,-14,-11,-10,-10,-2,24,14,-6,-17,10,-5,12,-15,29,14,2,-12,-3,-2,-10,-10,-3,6,-5,-6,-8,-9,8,-5,-6,2,-4,7,-1,-10,-11,1,7,-9,-7,4,-10,0,-4,-9,-14,0,-9,5,5,-3,-9,-7,-3,-1,-12,-11,0,-10,1,11,2,3,-15,2,11,-6,-7,2,8,0,1,-9,8,-6,7,-1,10,3,-9,-1,-5,11,-18,15,2,15,14,7,14,11,22,-3,-13,5,-5,-14,3,-18,-1,-7,25,-5,-13,15,8,-10,-24,-18,18,4,3,-1,14,4,4,8,6,-5,-14,8,-6,16,1,0,1,6,2,3,2,9,8,-10,14,8,-2,-13,-1,-7,1,-13,16,-1,4,-4,2,-19,-8,5,21,3,15,-12,12,6,14,9,-2,14,-8,-5,13,-13,0,14,4,7,12,1,-7,-4,14,2,-7,-10,8,0,-11,-2,-16,-6,23,6,3,-14,-3,4,-7,9,11,10,-14,3,-2,13,-3,-14,-14,0,14,6,-5,10,12,3,3,5,12,6,14,14,2,5,5,14,3,-15,-10,15,3,9,-14,13,0,2,-10,1,2,3,7,12,14,-2,-10,1,8,-10,15,13,-11,1,-10,15,-14,-9,14,14,-6,1,6,-3,3,-12,8,7,-12,6,3,1,6,3,-3,4,-2,-7,12,8,1,0,19,3,9,7,6,-8,-8,-5,2,-9,13,-7,8,-3,4,-8,-2,1,-14,11,10,-9,-11,-3,-7,-14,-14,9,12,2,-6,-6,5,5,4,16,17,3,-2,13,13,-15,-6,-7,-1,11,-1,10,-10,-2,0,1,-3,-8,-12,10,-3,9,-10,-6,4,-4,7,-13,1,-2,-4,-10,4,-11,-9,-3,3,-5,-9,-4,5,9,9,1,-4,-11,6,14,15,9,-4,7,6,-14,-12,-6,13,12,-2,7,20,-11,10,9,12,12,4,14,-13,6,4,8,8,3,-12,-3,-11,15,14,7,-1,-9,4,-1,17,15,11,5,-7,-16,12,7,7,-3,-3,-2,-5,-14,-8,7,6,-6,-4,-12,-13,5,7,-2,5,-8,-14,16,16,-1,-5,-8,-15,-4,-5,12,-1,-2,14,9,-14,12,-11,11,-11,6,-8,-8,21,15,-12,11,4,8,-2,13,-6,-10,-8,5,-10,4,-8,-13,-3,1,13,6,11,-4,-14,-4,-6,-10,6,-8,8,4,2,6,-9,-13,-7,8,15,3,5,-5,13,13,0,-3,-1,2,-5,-5,4,-14,13,9,13,-5,13,12,3,-2,-3,-17,9,-9,-10,-2,-14,-2,-4,13,-5,3,1,5,-9,5,8,0,-13,12,4,-2,15,10,6,11,12,6,2,9,8,-12,-12,-11,6,-8,11,-14,-7,-2,2,6,-5,14,-3,8,2,4,15,2,3,-5,13,-11,-1,-14,10,-7,1,-5,1,1,-10,-9,-4,2,-6,-4,12,-1,13,16,14,6,-10,5,3,-2,5,14,-7,-11,2,0,15,3,15,-5,6,-14,13,-8,-6,14,8,15,6,-2,8,-6,-11,4,-8,8,-10,-15,13,-2,2,-11,2,-4,-4,-1,-7,-8,4,4,9,4,4,-9,-11,-16,-17,14,-6,11,-4,-7,5,-7,-12,-12,-4,-8,11,0,9,8,-14,14,14,-6,10,-14,-8,-9,15,11,12,6,8,9,-13,-10,12,-12,-8,-3,13,-6,-1,-1,-6,-9,9,-10,-5,-2,4,10,10,-7,8,-7,1,12,14,-10,0,15,-12,1,-13,-5,0,-5,-11,-14,-1,4,13,11,3,14,10,-12,17,6,11,-13,15,-4,-11,0,-10,-3,4,3,0,-7,-11,13,-3,3,6,3,-4,3,-16,14,5,-2,-5,5,7,1,3,11,1,-14,12,1,0,1,16,-9,-9,3,8,-11,-7,-8,3,-4,11,9,-13,-6,6,13,-14,6,16,12,-8,-14,13,11,-7,-7,2,8,-11,5,12,-12,11,6,3,0,9,6,12,0,5,0,11,7,1,1,-4,12,-5,-6,9,-8,-2,14,6,-10,-5,0,19,-2,-8,-8,3,-12,11,7,-7,10,5,-2,4,7,-11,-8,-2,-7,14,9,14,-11,13,-14,2,13,-16,8,0,4,-9,1,8,-5,-6,5,1,9,-12,-10,-5,7,0,-2,-8,9,-7,3,-2,-8,-4,10,0,3,-7,7,-6,-3,5,-10,-11,-6,-11,10,-3,9,-7,-3,-14,2,-11,-8,-9,15,0,16,-8,-7,5,2,20,-2,-13,9,18,-7,-13,9,14,-1,-14,-1,6,7,-8,-6,-9,-5,0,14,-11,2,3,15,2,-13,3,12,-7,13,8,8,20,13,9,-13,0,-2,14,9,23,-4,-11,-6,-5,12,-10,-12,5,9,9,-1,-5,-9,-2,15,14,-3,-4,-1,-10,-10,-14,-2,-17,10,15,4,-2,-6,-8,13,13,-7,-2,-11,-1,4,6,-10,18,5,-11,3,-4,5,0,7,-7,-12,-8,-9,-4,-3,-15,4,-1,-12,-13,-1,16,-10,-7,12,19,-15,-11,13,-7,-9,12,-8,9,3,4,-11,12,11,8,-6,-1,-1,2,9,17,-2,-1,1,23,6,9,-3,17,4,-16,-4,20,6,-3,0,-11,4,-15,-3,-4,-13,3,-2,7,-9,7,13,-14,-4,-14,9,1,-6,3,-6,-3,5,11,2,3,-15,3,-14,6,-10,-5,-6,-1,12,8,1,8,6,0,16,-2,-2,6,-6,2,-10,2,5,6,-10,-18,-13,5,10,-12,6,12,-11,-13,7,11,5,-14,11,5,-15,11,3,5,-7,-14,17,3,13,-5,-1,-5,-5,0,7,-6,3,13,-13,-13,-12,4,4,1,7,13,11,12,5,-9,8,1,-6,-1,-13,-11,10,-10,14,4,9,-2,-2,7,6,-5,-2,15,2,-6,8,10,2,0,-6,12,4,-17,-11,11,7,-8,0,10,-14,-2,-4,-10,-14,-7,9,-1,-7,-10,-13,1,15,12,-5,-4,-17,14,-4,-5,12,-5,15,-7,-5,10,-9,11,8,1,-4,-2,16,2,10,14,-4,-6,3,-6,2,-9,-2,-7,10,-1,-5,-8,6,-10,-10,-4,14,5,12,14,-4,-14,-14,12,16,0,8,16,-11,6,12,11,-15,1,-11,-2,-10,-13,1,-6,3,-6,8,6,5,6,2,2,9,-14,-1,11,-8,-7,2,11,-12,4,-13,0,12,-8,4,-5,-11,16,-3,4,-14,2,10,8,14,-6,12,-8,12,-15,4,11,13,-10,-11,-11,17,9,3,10,9,1,-9,-7,-9,17,12,-8,11,-15,15,1,-1,-10,-13,5,10,-11,4,-5,2,-8,8,-5,-7,16,1,10,-13,-9,-5,10,13,8,-11,-12,5,-6,-8,0,-13,7,-11,11,1,12,-8,11,12,7,10,4,-14,-6,-8,18,-13,1,5,2,0,3,-7,5,-9,1,6,11,8,5,5,-11,-12,7,-14,13,3,-12,-4,-6,-12,-12,12,15,0,14,-11,-9,11,-2,2,-11,0,14,15,-4,-9,-5,-12,0,15,-8,8,15,-3,5,-7,0,-11,8,-12,9,-9,8,11,4,2,1,-15,-2,-9,7,5,-8,18,-12,4,17,3,9,13,-2,9,-3,-14,-10,6,-12,7,12,8,-14,3,6,-12,-3,-10,6,14,11,-13,13,9,-3,14,10,18,-13,11,-2,-11,-7,-11,-3,5,-6,-6,16,-11,-4,13,-7,-12,13,9,-3,-13,10,5,16,0,6,-5,-11,7,-5,6,11,16,-6,5,14,1,-10,-5,10,9,-6,-13,12,-6,15,-6,15,3,12,-9,-8,-10,-1,-6,-5,-10,4,15,5,2,13,-14,-2,4,-11,14,-1,-10,-15,11,-6,10,13,6,6,1,-7,2,-11,6,7,-10,-11,13,-3,10,9,6,0,-13,2,14,4,-4,17,16,-9,5,10,7,1,-12,-3,13,1,7,15,8,-4,1,16,1,1,-10,-8,-4,16,-7,4,19,-3,7,-4,14,-8,12,12,-3,2,-6,8,3,8,15,-10,17,-6,-12,4,-4,-12,5,9,4,-5,-13,-2,-8,2,-7,7,11,11,-8,3,0,5,-12,-14,8,8,6,-15,-8,-15,-3,14,5,0,13,7,-9,-4,4,9,-12,-12,3,9,-5,-1,8,3,4,7,-6,-2,1,4,-5,-6,4,14,-4,7,7,-14,-3,-1,-13,-2,-10,11,14,-10,-7,7,6,18,15,-13,7,3,2,13,-13,8,3,10,11,-7,-11,10,16,-15,-5,15,-13,12,1,2,-1,-10,-13,-13,-12,9,0,-1,4,-1,-2,-7,-2,-4,9,14,14,2,-6,13,10,-10,5,10,-10,-3,10,1,-12,8,-8,5,-11,-8,13,8,-6,0,-6,1,1,-8,15,-8,-10,4,-7,18,-7,-1,-5,-8,-13,-7,14,-13,2,-1,6,12,-1,4,15,9,-9,-5,0,4,-7,-13,-2,-2,10,0,2,11,-6,5,-12,4,-6,10,-13,9,-11,2,14,8,14,10,0,6,-10,7,-11,1,7,-13,10,3,-14,11,-11,-14,-4,9,0,-4,5,3,12,3,4,-12,0,18,-7,-3,10,-6,7,-15,-5,19,-5,-3,0,11,11,6,-14,-7,9,4,-6,7,16,-3,4,-6,-2,-1,-6,10,8,10,-8,1,14,4,-9,-1,6,-1,14,12,-12,10,10,-9,1,14,14,3,5,9,0,4,-3,-2,1,2,11,15,1,-4,3,13,-10,10,-5,-6,6,-14,12,8,6,6,-6,9,5,2,-7,5,1,2,-1,10,14,-12,-5,11,-2,0,1,-11,-3,-3,1,0,-11,8,-5,6,15,3,4,1,-7,7,-3,0,-13,-11,13,-15,-7,13,-8,-12,-13,8,-6,9,-5,0,0,12,14,16,15,8,-9,-8,-15,-11,6,-9,14,4,14,13,-3,3,-2,-11,8,13,-11,-1,0,4,-10,5,-2,3,-3,-12,10,-4,8,9,7,-4,-14,13,13,10,6,15,-8,-4,6,1,-7,-12,5,-8,7,1,3,-2,-7,-6,-6,3,2,0,4,13,11,11,-12,-1,19,5,1,-4,-14,2,-10,-8,0,4,3,-10,14,0,16,6,10,1,-13,-2,9,3,-13,-11,-2,-10,10,11,15,-2,12,-10,-11,-7,1,5,-7,-9,-9,5,5,-13,8,8,0,-12,-3,-11,15,-3,-1,-14,12,17,11,6,-6,0,5,3,-8,5,-13,8,6,-3,8,12,-14,3,-3,-12,15,-6,8,23,8,8,4,14,13,14,-8,10,10,-14,3,-9,11,8,8,13,-5,-7,10,-19,16,9,-12,-8,-10,2,13,6,-2,22,-7,11,-8,11,-10,16,-1,-9,-7,20,-12,-3,-10,10,-10,1,-3,-14,-2,7,14,-3,16,13,8,19,0,-11,2,15,-3,4,5,6,-2,-5,16,-10,-2,-1,-9,-12,-14,6,17,4,6,2,14,-3,2,-16,-4,-13,-12,7,-11,5,-14,-9,13,-21,22,3,-8,-24,8,16,-13,12,7,0,-11,-12,1,16,-3,-14,-10,21,-7,2,-15,-13,6,6,15,8,12,-5,8,13,12,-25,15,9,-8,9,12,-12,10,8,-1,10,12,0,-11,5,4,2,-8,8,10,-1,14,-11,-9,-13,-1,10,-14,-14,-12,16,3,-6,8,4,9,-10,7,12,-9,-6,-2,0,11,0,-8,10,-6,-10,10,-5,-8,-9,2,9,-11,-18,4,5,-4,16,4,7,-4,-7,-8,6,8,-3,-8,-14,-3,-14,17,0,-6,-5,9,-16,-2,8,10,-3,-3,-12,-13,-13,6,7,13,2,5,-10,-10,-8,8,-1,22,8,-12,-4,-11,-12,-9,-9,-3,-12,10,-5,-1,15,3,-3,8,-7,-2,3,0,-9,4,-13,22,7,2,-4,0,2,8,-12,31,-1,-3,-7,-1,2,7,8,1,9,-14,6,9,16,-3,-11,10,6,11,11,13,-2,8,-8,20,20,-13,-3,-1,4,2,-7,31,21,-12,-31,-7,-2,14,9,3,2,-5,11,-3,15,10,8,-2,-6,15,4,-9,17,0,-6,2,0,-2,14,-15,14,2,5,-9,11,5,2,-2,-4,14,-12,20,-15,-4,-6,5,2,-4,2,-9,-14,2,-12,9,15,-5,14,-1,4,11,-14,9,-7,11,-9,4,6,-10,-16,-12,12,-7,-5,-6,14,9,7,-13,6,-3,9,-3,4,-5,2,-6,4,1,-25,17,10,9,-16,5,7,-3,-32,35,16,5,-36,-10,2,0,10,7,8,3,-10,13,12,14,4,13,1,-2,1,14,-14,15,3,24,1,5,-25,10,-8,-11,-22,6,12,1,-15,-8,11,11,0,0,-5,1,-8,7,8,8,-12,12,11,-11,-2,-1,1,7,2,-2,2,10,-7,5,-6,-2,7,-11,-2,-13,7,9,-3,12,-1,19,12,1,4,-9,8,-11,-9,11,1,4,6,9,14,12,2,1,12,0,6,6,-3,3,-17,5,0,-13,-9,14,2,-11,4,-10,2,-8,-8,14,-7,-4,12,-4,-2,8,8,4,1,-11,-10,28,-3,4,-11,4,30,13,-48,35,17,-6,-29,13,-2,6,8,-10,11,-1,13,-8,25,-3,-10,0,22,-1,-1,-2,-28,-11,6,6,7,11,-13,11,-2,-13,7,9,-13,-8,-2,-1,14,-10,-27,4,1,-6,-6,1,-1,2,-13,-1,-9,-7,4,12,0,-5,-28,0,-7,8,-15,1,13,10,1,-15,2,-1,-6,8,28,11,-17,23,14,-7,-25,-10,-9,8,-4,-6,-6,8,-7,13,11,7,12,5,16,3,10,-5,17,-9,-8,-6,20,10,0,6,0,-4,-4,6,21,3,1,8,-11,1,2,6,1,13,6,-12,13,12,-10,8,9,-10,-1,13,29,-3,-6,3,15,0,-20,-6,-7,12,7,-9,5,-6,-13,6,39,3,-14,-11,20,3,-15,-5,-8,3,10,10,-4,-5,8,1,6,-14,-8,-3,18,-5,-12,-11,-14,16,-18,-4,-5,-2,-18,-2,-4,6,11,-12,4,-12,6,12,9,8,-21,-16,17,14,-16,-13,-14,-1,-19,11,1,-10,-1,4,20,3,-1,1,1,6,-18,-6,6,-1,12,6,10,4,0,-1,4,-10,11,0,9,2,1,-2,4,-12,-19,-2,-18,-1,3,-5,7,14,-18,5,-5,2,-5,-7,7,3,-14,-5,30,-15,-6,0,-5,5,-2,14,-4,6,22,10,7,-1,21,7,-10,7,25,-8,-3,14,-8,-7,-7,-12,14,-11,-8,-5,14,16,7,-6,1,-10,0,3,-3,15,-3,10,-8,12,-6,2,-8,-1,9,-4,-2,-2,15,6,24,17,-4,-1,13,10,-3,-9,4,7,3,-14,0,-9,8,-7,8,-9,20,-2,15,5,-8,-5,20,7,8,4,9,-12,-2,11,-3,5,-10,-1,-18,-9,1,2,2,4,-11,15,-1,13,-5,-6,10,-6,-13,6,-3,-3,18,-13,-10,-5,2,6,-4,14,12,6,-16,12,19,-5,-8,-9,-5,2,-13,-5,-11,14,-4,-14,2,-4,-23,-6,-13,4,7,10,20,9,-27,16,-8,11,-15,5,-11,9,-6,2,-7,-3,14,10,5,4,-1,-7,-7,-14,0,0,0,16,0,7,16,-9,-1,12,5,-2,-1,24,15,-11,-26,-14,14,8,-13,2,-8,-10,0,-7,7,-9,-1,14,-4,5,-13,11,-15,5,-8,-13,3,1,-7,-3,0,9,-5,-3,-11,5,4,-9,11,14,-2,20,7,-9,-23,9,-16,10,8,6,5,-12,-7,7,-6,6,7,15,-12,-13,-12,-4,-5,-12,-22,22,9,-8,-27,-13,-4,15,3,14,-3,-11,-13,-13,-8,-11,13,4,-11,-14,-10,1,-5,5,-2,17,3,-13,-12,4,0,7,-14,15,-3,-9,-12,-1,-5,-8,2,-14,9,3,-6,8,19,16,-10,-6,22,-3,-16,3,-6,-7,-10,4,2,-12,-3,-7,-4,9,-12,14,11,8,-5,12,-2,-13,6,2,15,-6,2,-15,8,3,-7,2,16,-2,9,0,-11,-2,6,2,9,-6,1,-12,2,-10,5,-2,9,0,-12,-3,11,5,-13,26,15,-7,-20,2,-17,0,8,-12,3,-6,15,3,-10,-7,3,-6,-8,-1,10,-14,-3,6,-13,17,14,4,-26,-4,-7,5,10,-11,-5,14,-1,3,-10,16,16,6,6,14,-2,-13,17,16,6,16,6,13,-8,0,21,2,-26,28,-1,4,-42,-6,-4,9,10,3,11,13,-4,13,22,4,4,-13,19,8,3,11,-2,9,-12,26,-3,-1,0,13,-13,-1,-4,24,-1,5,-6,-14,-2,-11,-13,14,13,-14,-24,-4,8,-4,-14,5,12,6,4,-14,12,6,1,8,17,-2,-10,-8,12,1,4,3,9,-11,5,9,23,10,-13,1,2,6,-13,4,-13,-12,-13,-10,-1,-8,5,14,16,10,11,-2,14,13,6,-9,12,-14,-15,21,27,1,-16,11,26,-20,1,9,14,13,12,13,4,-6,13,-3,-7,-14,-3,-6,3,15,-19,20,-13,11,-2,1,50,8,-13,38,43,-12,-42,-5,8,11,-3,3,16,-2,-8,7,13,5,7,-7,24,-9,11,10,4,15,4,10,0,-4,-25,-11,-7,-4,-8,16,-4,-13,-26,-5,14,6,-24,10,-10,5,-17,-14,-4,0,15,-7,7,5,-7,1,-5,-3,-2,1,9,14,-7,5,14,4,0,-11,8,-10,-6,6,34,-6,2,10,28,-15,-22,-11,-2,-4,14,9,4,-11,12,-12,24,-7,-9,11,14,7,9,10,14,-11,-17,17,23,5,2,-5,15,-19,14,-7,28,-5,-2,5,-7,-6,-13,5,1,9,-3,12,-1,-11,7,16,2,1,-20,-11,71,-13,-6,13,33,2,2,8,-8,-12,8,10,7,9,3,-13,56,-7,-9,12,36,-2,6,-4,-10,-11,10,-1,-2,3,-10,7,9,13,11,23,3,0,1,11,10,-2,-23,4,-14,-10,-15,3,7,5,-10,12,-11,15,-7,14,17,-13,-23,-10,8,-1,15,1,6,-9,0,1,-11,-2,-10,11,41,-10,14,24,23,7,7,5,-13,3,-7,-12,-14,-12,7,-5,22,-9,10,3,5,4,6,1,-16,12,0,14,7,-5,-13,9,13,-6,-14,-3,4,6,8,-11,22,14,-14,8,30,13,7,-5,5,4,12,-8,-15,-9,15,8,-13,-1,29,20,0,-8,16,-6,18,-12,-9,-9,8,11,9,0,1,14,11,-7,-9,11,-5,11,12,12,15,0,22,-11,-14,13,9,7,-14,16,-2,-5,-9,6,-7,8,11,18,7,7,5,8,-2,-4,5,3,15,0,0,0,16,-12,20,7,8,-2,4,-1,9,-9,24,12,4,15,11,15,-11,-7,-8,10,-9,13,-8,15,-7,-1,-12,5,4,-6,-3,12,-12,-9,6,-5,-7,1,-12,-3,14,-1,-3,10,10,-10,-14,1,6,-7,-6,2,-5,6,-9,3,5,-6,-1,-4,-11,10,13,-10,13,9,-6,6,-4,-13,-24,9,3,3,-26,8,17,-12,-21,0,4,-15,-11,-10,4,-14,-12,5,6,-7,2,-12,21,4,-1,12,-12,0,-11,3,5,-6,-10,15,8,13,-6,19,12,15,-16,-12,-3,1,9,-4,-11,11,-19,10,2,7,-1,-2,5,10,-11,6,16,-9,-2,-9,11,2,4,-11,-10,9,1,14,-6,-15,10,-4,14,-6,-20,17,12,-4,-2,3,-9,-1,3,0,-6,14,-12,-2,-13,3,1,-10,-8,-3,2,-11,-3,-13,-7,2,-7,12,-12,-4,11,14,7,-11,-7,3,-3,10,12,17,-12,16,16,9,-8,11,-16,-2,-25,19,6,10,-6,14,25,15,-21,8,8,4,-29,-13,7,6,10,7,1,15,-13,-2,17,-13,3,-5,6,11,-13,14,-14,-7,2,24,-3,10,-30,8,4,8,1,8,3,12,-29,-3,10,8,1,20,9,-5,2,-12,15,-3,-9,7,-2,-11,-3,-3,-10,4,-3,4,19,14,6,8,-10,2,-3,-16,14,12,17,8,6,2,-16,2,-6,6,-25,-12,-1,14,13,9,11,10,0,6,-12,-1,2,18,2,-12,-1,7,8,-13,-18,1,1,10,-5,-2,12,-16,-1,-2,5,-10,-3,4,0,-2,10,12,-9,-1,14,-2,-3,-2,-23,7,-7,-13,-16,9,41,-15,-21,37,3,10,-24,0,0,-5,15,11,12,-14,11,10,15,-18,-16,13,26,-6,-15,-12,-12,3,-2,28,-1,12,-3,-7,-6,2,9,23,1,10,-23,-5,-1,-6,-31,7,3,-10,-29,-15,-4,-8,-2,-10,-13,-4,-1,-3,-13,-2,-21,-8,18,14,4,-6,-5,2,12,-11,-8,-7,-9,11,7,-16,5,19,11,12,-19,5,-14,-11,-2,-14,-15,1,-11,1,-4,-4,-12,13,-5,10,-9,1,32,6,-9,11,10,9,2,9,11,0,5,-8,30,-3,-15,-8,1,-13,-6,5,-12,0,-3,1,3,6,-19,7,-6,-1,-19,-9,57,-6,-27,22,48,3,-33,-2,-2,-13,1,-5,2,-4,0,13,33,-10,1,-7,49,-9,9,6,0,14,-9,13,7,0,-5,14,-2,-15,-6,-1,2,12,-2,1,-12,15,-17,3,-5,-13,-17,1,6,-4,3,-10,3,11,-12,7,-8,5,-17,2,18,-2,3,1,-1,10,-15,-5,-7,-1,7,2,44,-15,11,3,31,-7,-8,-7,-14,1,-9,-4,-12,-14,6,5,10,-15,-12,10,4,10,-11,-8,34,8,-10,-9,22,-4,-10,1,34,-18,12,-9,28,-4,-1,-13,6,-10,4,9,-11,5,0,-5,2,-9,-13,-4,14,-7,-16,3,59,-1,-2,20,46,-1,-16,0,-15,12,6,0,13,8,-5,-5,33,-14,-4,2,32,8,13,-12,-12,12,10,12,11,-10,-20,-7,-10,-10,1,13,10,2,-11,0,15,-4,-19,5,9,-6,-7,-3,1,-13,-9,15,-2,-14,5,-13,2,2,-1,-3,3,7,-4,-10,3,4,1,-6,-1,13,1,1,50,3,0,22,37,6,-12,10,-6,-15,-13,9,-3,-1,-3,-5,18,2,15,8,11,0,-1,-4,-7,-6,5,-5,-8,15,-9,-7,-1,-8,-5,-3,16,-8,-12,13,4,-6,11,12,-11,-12,1,-2,-2,0,18,6,-18,-12,-6,7,20,8,23,16,17,7,-11,-12,8,14,2,-11,18,-1,-1,-12,18,2,-12,-12,-1,14,0,-7,7,16,14,12,-5,7,-7,-6,-1,9,3,19,3,-1,10,-13,2,-7,11,13,5,3,10,11,-3,11,2,5,11,-12,-14,5,-3,-3,-2,13,-11,-9,-1,-9,-5,-11,12,10,-10,5,-4,0,15,8,17,2,14,-14,-10,-7,-6,-9,-9,10,8,12,3,-9,5,9,3,10,-12,4,-9,-14,7,-11,-27,14,22,14,-15,-5,16,2,14,0,5,-11,13,15,-7,13,11,6,-8,-1,8,9,-5,0,-27,21,-9,6,-5,-2,6,-5,-43,27,-2,-4,-42,-14,-4,6,0,13,9,1,-8,15,0,1,4,6,10,6,-3,2,-26,9,-20,4,-17,11,-25,-13,3,7,-5,1,-1,5,-17,6,-13,-5,-25,4,-4,7,-11,6,-12,5,-13,-5,-1,-3,-10,-3,2,-2,-28,20,-5,2,-2,2,-2,-1,-11,-9,-2,1,8,1,1,15,-22,0,-12,-3,1,15,-16,19,0,1,-13,-8,-11,-4,0,-7,-13,0,10,1,3,2,-5,16,-6,4,-8,-7,-6,6,-1,-10,1,15,0,-12,-8,6,-4,-1,-13,4,-2,-2,10,10,15,-10,-15,0,11,-10,-14,7,19,0,-30,21,-10,2,-43,12,5,-15,-11,6,2,-9,-1,8,24,-2,3,7,3,14,0,-1,-9,5,-22,22,-12,-1,-15,-13,-12,-10,-9,11,-10,5,-20,-2,4,10,-20,21,-4,-4,-24,-3,-8,8,-2,-11,-12,15,-2,-4,-5,-4,-22,3,-2,4,-12,-6,0,-3,7,-11,-6,15,12,6,23,-2,-22,9,-11,5,-10,-3,-20,-6,15,-4,-6,-1,6,-7,-5,-5,13,3,-6,14,-5,-13,11,-8,-9,-6,3,2,3,9,24,-4,1,-4,13,8,1,5,-7,2,9,1,-10,9,-7,11,20,-7,-20,21,7,-11,-6,2,56,-3,-31,29,35,-7,-14,7,1,-11,10,-7,-10,7,0,5,36,-15,1,8,16,8,1,11,-10,-14,1,28,9,6,-11,2,-21,-12,12,34,-8,2,-5,-14,4,16,-23,-2,-12,-1,-21,14,-6,-8,7,-5,6,14,-9,11,-12,-6,-31,-8,20,10,-17,12,-7,-9,-16,-4,15,5,-7,-9,35,0,-14,7,7,-13,-22,13,-3,-13,-1,6,-5,14,-12,2,-7,-14,11,-8,-5,-6,-12,2,35,5,8,2,27,-6,-2,-7,22,-20,11,-7,32,-2,-8,-6,-17,-2,2,-13,-11,12,-9,15,13,9,-22,-1,5,3,-1,-10,57,-6,-19,18,28,-4,-26,-6,1,-15,9,8,6,8,3,-6,49,-22,5,0,52,8,-8,-13,-5,-4,-2,14,-10,13,5,-8,-17,1,4,2,-8,-14,7,15,15,-4,-14,-14,-11,2,-3,-11,-2,3,-10,3,5,-2,-13,2,16,-17,-6,-9,18,-1,-7,-9,5,-4,-4,-18,-2,-11,-1,2,38,7,3,18,19,8,6,-10,9,-6,14,1,7,6,8,0,1,-11,-2,7,20,-7,-10,9,15,-5,9,17,22,-9,9,-5,12,-1,-5,6,29,10,-11,-7,12,-3,-6,6,6,13,-12,2,2,11,2,22,-5,-6,-8,-12,52,2,-6,-2,38,10,-19,-4,-10,-7,3,-9,-12,-5,-9,-7,40,-17,-10,13,17,6,3,10,-2,9,3,18,14,-11,4,-7,7,-3,-6,27,-2,9,-5,9,-4,-7,-19,11,12,2,8,2,10,-1,-2,-13,-10,-14,-1,13,23,-18,-7,4,17,12,3,-8,6,9,13,-7,-3,1,-9,-14,29,-14,5,4,36,7,-2,-13,8,-6,-2,1,2,-1,1,4,10,-2,16,12,6,-9,-6,-11,0,15,-7,-3,9,9,-5,4,5,-7,13,-1,3,-10,4,-9,-8,1,13,7,12,-3,-10,-13,13,-10,0,5,3,1,10,-1,-5,-7,-18,16,11,11,-9,-9,-3,2,-4,13,1,-2,-11,14,17,6,-10,5,0,10,13,8,4,3,13,15,14,14,12,-11,0,11,12,10,8,6,-6,6,13,-9,-8,9,12,-13,-7,2,5,-6,-7,-4,-5,10,-6,5,8,9,-13,1,-14,-1,-1,3,8,0,1,-7,-6,2,-6,-7,14,-14,-7,0,15,-7,12,-11,-6,3,-7,-3,-11,-11,14,-6,-5,-3,3,15,-1,-8,-1,10,14,-5,-5,16,20,-13,-2,3,19,-4,9,-8,-3,12,-1,1,-11,13,5,-9,-12,7,-1,6,-4,-11,-4,-7,10,-14,-16,14,-15,8,-37,4,-7,-6,-38,-6,1,-11,6,-6,3,-11,-3,0,21,-10,6,-4,7,10,0,-6,-1,13,-21,16,-19,5,-3,-6,-25,1,-18,1,-5,-3,-11,2,-4,10,0,-7,7,-13,-25,2,7,14,-7,-13,7,14,6,11,-16,7,-26,18,3,-14,-11,10,-7,5,9,-5,-11,6,-8,5,-18,0,-11,2,-10,-5,-7,15,-10,-9,14,-2,0,1,14,-4,-3,9,-17,-1,1,12,0,5,-1,8,-19,11,5,-12,-10,-11,10,-2,10,-10,19,5,-14,15,-6,0,-7,-11,-12,0,8,-7,10,-4,-9,-2,1,6,-15,-13,25,-8,-22,5,15,6,-15,-15,-8,-10,-11,-9,3,12,-13,6,22,-11,1,5,13,-14,-3,0,-20,15,-6,5,-4,-5,-19,6,3,-13,3,19,-8,-1,-25,-15,4,12,-5,10,12,-11,-13,-4,-2,-11,12,-1,7,-3,5,-11,0,20,-30,12,14,8,-15,0,-3,-9,-18,1,-9,-4,5,-11,9,4,8,15,3,2,-2,5,-6,8,13,9,2,13,12,-5,10,-5,-19,0,2,-15,-1,3,33,5,-10,-1,27,-10,4,1,31,-6,13,12,28,1,-10,-13,-5,-5,-7,-8,10,7,10,-8,14,9,-17,14,-1,0,-14,12,52,-4,-18,16,22,-14,-15,-6,-19,-5,-6,-7,8,-4,-6,1,27,5,-12,-3,36,7,0,-6,-13,7,-8,-7,1,-10,-27,-5,-29,-3,16,18,10,13,-13,14,2,13,-4,-2,-13,6,-15,1,7,-8,-13,-3,11,-3,1,-8,-9,-5,-38,-15,13,10,-11,-6,4,7,10,9,-7,-5,-8,-10,25,0,4,-2,8,-7,-6,14,-5,-1,14,-8,-8,-6,14,-11,10,-5,-11,-4,19,9,-4,10,23,7,0,22,27,-8,-16,14,27,-15,-14,15,15,-14,-2,-9,14,7,15,-1,4,3,15,2,-13,-6,-14,5,3,1,-15,9,65,4,-10,-3,33,-9,-21,7,4,5,-12,-12,-9,-3,2,15,33,-3,12,14,20,-3,-14,-4,-12,-11,-5,-2,1,12,-7,-1,-25,12,15,6,13,13,4,8,-3,-3,3,5,1,-4,6,-1,-12,1,15,0,13,-13,-10,-9,14,-7,-31,-6,4,5,-2,-4,1,-6,2,-10,8,-13,3,10,42,-6,-7,10,33,7,-7,5,-8,8,-13,-4,-9,4,-10,-2,22,-4,2,-12,11,-1,-1,1,18,0,-19,8,14,-14,-8,3,10,1,10,-8,9,0,1,-10,3,17,-7,-6,6,-1,12,7,8,-3,-8,21,11,13,12,-6,36,-13,3,12,33,8,-16,-14,7,0,-9,-4,11,1,-14,3,32,6,-9,-5,32,-12,14,2,8,10,-3,-2,9,-7,-5,6,-2,-14,11,20,15,3,-11,0,-5,-14,-2,6,3,1,10,3,-5,2,-7,10,-2,15,-9,6,13,6,2,-8,23,-1,5,9,15,-2,11,8,11,-12,4,-15,18,0,6,6,2,8,6,-11,-9,4,-13,-9,4,-8,14,-7,-5,-8,-11,-8,-11,-11,17,2,6,3,10,-5,1,-1,-10,15,5,15,-3,6,11,1,15,1,-8,15,-9,-7,-7,5,-10,-13,12,2,-23,16,-5,8,-16,-7,10,-6,-2,-3,-4,-8,-1,-5,-4,-3,-9,10,-6,3,9,-14,7,-12,-13,11,12,-2,2,4,-5,-8,11,1,7,1,-8,1,-11,13,7,0,7,13,-16,-3,15,-12,-15,5,9,7,-1,3,10,-5,5,-7,-10,8,-14,-9,-18,-5,-1,-1,-11,6,-9,-10,-9,7,-10,-4,-7,14,-12,-7,-11,-13,10,8,-8,-14,2,3,-20,1,-9,4,-4,-1,11,0,-14,-12,9,10,3,-8,11,1,3,14,0,13,1,-5,-4,10,2,0,7,13,2,-13,15,-6,-8,-1,-13,-6,-18,9,-2,0,9,11,-16,-8,-13,-7,-31,10,9,-6,-50,15,7,-5,-31,-14,7,6,12,-4,-2,-8,11,-12,23,18,-20,15,0,-1,1,-13,-35,17,-22,-8,-3,-6,-24,6,-19,10,-11,4,-11,0,-26,14,3,-1,-14,-9,0,-4,-2,3,13,-4,-5,2,-3,-13,12,-14,-3,-9,-49,8,-8,0,-19,9,-1,11,-11,9,2,-10,-3,8,-5,9,-19,12,14,12,-8,3,-23,-8,0,0,12,-6,-9,-8,-13,15,8,11,13,-7,-17,10,14,4,-22,-6,10,-3,-16,-11,0,6,12,4,-10,-8,-13,-3,2,11,-9,2,-17,-6,-1,5,1,-1,-25,3,-10,3,-20,-13,21,2,-56,14,14,-3,-41,-14,6,-11,5,-1,3,-10,7,-3,19,11,-12,-1,12,-13,-8,12,-7,-3,-10,7,-7,9,-37,-4,-5,-9,-26,-3,-16,-14,-11,3,-2,12,-12,16,-3,7,-5,6,3,1,5,-14,10,-6,7,4,-12,-1,-38,6,5,-14,-19,3,-4,-5,-18,5,15,7,1,14,17,-3,2,15,6,-13,-27,2,-21,-4,1,12,-6,14,-8,-8,-4,7,-13,-13,3,8,-16,-9,4,-11,-13,1,-3,7,-5,0,18,-14,10,10,22,8,-15,9,-5,12,-13,-13,-12,-14,4,1,-1,11,-22,1,14,2,1,1,24,-7,-38,27,-13,8,-25,13,-19,-2,-1,-3,10,10,4,-13,29,1,-4,-11,0,-6,7,-7,-14,-5,-20,19,8,-12,-34,10,-16,-11,-21,23,-6,1,-39,8,3,6,-33,10,1,-11,-21,11,-5,5,14,-3,-1,-9,-3,1,-1,-7,-38,3,20,-12,-7,10,6,2,-14,-13,-10,12,-1,-13,13,17,7,9,5,-10,-16,-13,-6,-12,-6,2,13,1,-7,-3,-7,-5,12,17,-8,-13,-14,-3,23,-1,4,6,3,8,-18,-4,33,1,-7,17,4,-4,-12,5,-11,-2,3,-5,13,14,12,0,9,13,-1,17,8,13,-2,-9,41,6,-25,14,12,10,3,14,-5,-12,-6,6,10,-10,9,-12,23,-16,-3,-3,2,0,-8,2,-7,-8,-19,20,13,-15,-24,10,-19,7,6,11,-7,-8,-18,11,-10,12,4,4,9,1,13,14,13,-14,15,-3,-5,4,3,-4,1,2,-24,-11,9,-4,0,6,-13,-14,-15,-9,-8,14,-4,-14,42,-15,-12,13,0,9,-17,5,10,-1,-1,-14,14,1,-4,9,16,7,12,6,6,8,-17,0,12,-4,10,5,6,-13,7,-9,11,4,-10,-11,0,5,-7,4,5,6,-6,0,8,6,-2,14,-7,-11,-15,-10,8,3,-8,-12,25,0,-7,-3,13,-10,0,6,-1,6,-6,-9,-6,-13,15,4,28,-15,2,8,1,-12,-7,-5,-1,-10,-11,-2,16,13,3,-10,-19,-15,-3,13,5,0,7,-8,-5,13,-16,-13,11,6,0,7,-13,-6,12,10,7,-2,13,-6,19,-4,-23,-1,2,9,5,5,-10,5,8,1,0,-15,-10,-11,13,10,0,17,16,-15,-2,-4,1,5,-8,-10,11,12,-13,2,1,1,10,-2,6,10,-3,9,1,13,15,16,-11,-2,6,-5,-3,-2,11,-12,12,-7,1,-5,-3,8,-6,0,4,-4,1,-12,2,-10,-4,4,-14,12,-20,-14,-4,-1,4,30,14,2,-15,-7,-4,7,9,9,7,-2,13,3,13,-7,7,13,-14,15,12,15,12,21,-14,-4,-24,-8,-8,-7,-3,15,8,-1,-6,0,-12,-12,4,9,-8,17,14,4,-10,2,-2,7,13,6,-5,7,6,8,13,-5,11,4,-16,-8,0,-5,4,-1,-7,14,1,-10,7,9,-6,1,1,20,13,-2,6,-8,7,5,15,-7,-6,-5,-12,14,-5,-10,9,-6,-4,-8,-7,-5,0,-13,0,20,-5,0,-4,2,-5,15,2,12,11,7,-8,-8,9,-6,9,1,-5,10,11,3,15,8,4,18,10,5,-7,-13,8,5,-13,39,-2,12,-29,1,-3,15,-8,-6,-6,-7,-1,-8,-12,-8,0,6,7,7,-2,-6,-14,-3,2,1,-15,-12,-5,-11,-8,-4,-14,12,-9,8,-11,12,14,-2,-10,21,-6,14,-7,1,-9,-13,-14,14,5,-3,-8,-14,13,11,3,15,-3,13,-7,11,1,13,3,-6,11,12,7,3,15,-5,6,21,3,-8,3,-8,-9,-2,10,10,1,-7,16,-12,9,13,-6,-8,5,-3,13,14,10,4,-11,14,-6,4,10,-7,-5,10,-5,17,3,-11,7,13,10,4,7,16,-11,-2,14,-1,-8,12,-4,5,-12,4,-3,4,-12,-1,2,16,-5,15,-15,-14,9,11,14,-2,-5,-13,-7,-2,3,3,15,18,11,9,-14,15,-11,10,14,13,-15,12,8,4,0,-10,7,22,11,3,-3,12,-7,-6,-14,22,9,-4,-12,0,2,1,0,-3,13,2,1,-7,-7,10,-2,17,3,7,-8,-11,10,9,7,3,1,-8,5,-5,5,20,15,15,2,-7,0,8,-11,-11,-14,1,-1,0,1,3,4,-10,0,7,0,3,-12,-8,-2,12,-9,10,-10,7,6,-11,-10,4,2,7,-7,5,-4,8,-4,10,8,7,6,-4,16,-14,7,16,15,-3,11,-3,13,-3,7,15,-3,26,13,11,-1,-5,-2,9,13,6,-13,0,-13,-2,6,-5,-14,5,8,-10,4,6,-12,-5,-10,21,-19,-12,0,-13,-8,11,5,6,9,-4,5,-4,8,-14,5,7,-2,5,9,11,10,9,4,9,4,8,-4,9,-10,7,-6,15,2,15,-6,-13,-8,14,-5,3,-12,-5,-14,4,1,11,8,15,2,4,6,11,12,16,-9,12,-8,1,-13,-9,-3,18,0,-2,7,-1,17,14,-7,-3,7,20,-1,-6,-5,8,-5,1,-2,-8,-1,-5,15,-1,-2,12,-11,-10,12,-13,-5,-12,-10,-14,-1,12,9,-10,-16,14,3,-9,5,35,7,-9,11,0,-14,-1,0,-3,7,-14,-14,-4,11,-17,-8,13,-4,-6,-8,6,-7,0,7,22,-3,-12,-12,12,7,20,-4,1,-3,-5,-9,2,12,13,5,8,-11,0,9,-6,13,10,2,-6,6,5,-12,5,3,9,-8,4,-14,2,14,-11,-12,14,-7,0,-11,4,4,15,6,1,-12,1,-8,9,11,15,-1,1,-7,0,10,-12,-3,3,12,12,-10,13,8,-13,13,3,-13,-3,10,15,15,-11,14,13,-10,1,3,13,1,13,13,7,-2,15,13,-11,0,-9,1,3,4,0,6,5,-2,-1,-13,-9,1,-15,4,29,10,-3,-10,-7,4,16,7,11,1,-11,-14,-13,13,-19,6,-10,-6,-12,9,3,-9,4,11,6,-11,1,-3,3,-2,21,-7,10,3,5,5,-4,11,-11,-2,15,1,1,15,6,13,8,-14,-2,4,-7,1,-10,-5,11,-3,20,15,12,-1,0,-10,0,-14,6,10,-12,-4,1,5,5,12,-2,15,-14,-6,8,3,11,7,-4,-4,-6,6,-12,-4,-4,-2,-12,4,8,-6,-12,2,13,-12,17,8,12,-7,-13,-9,8,-13,-5,-1,-1,-12,-6,-13,15,-10,11,-9,-12,15,-4,-12,5,4,-3,-2,2,-12,-3,14,-4,-1,9,12,14,-2,-10,10,8,-5,-6,10,-10,12,-13,6,-3,4,-2,-4,11,0,1,-2,-9,-6,9,-3,12,-11,-14,-4,11,-10,4,11,-7,3,-6,3,-4,-6,10,9,7,5,1,-6,-5,9,-4,7,5,14,0,8,15,-1,14,-8,8,5,-11,-4,-9,-13,-6,-7,-10,-2,7,10,17,4,19,2,14,-13,10,-4,6,-8,9,14,9,-13,14,-3,-11,3,1,17,10,4,14,-5,-4,1,4,9,11,15,-7,-9,5,-13,-1,3,8,-1,10,-4,9,12,15,-4,-4,3,-7,7,-12,-11,4,-1,14,9,7,-2,5,-14,39,-4,-9,-18,-15,-5,2,-1,-2,9,0,15,3,-12,10,14,-5,-2,11,-10,9,-9,-2,-12,24,-14,10,15,-14,-3,15,9,14,3,4,3,-1,-4,10,11,5,-13,-2,-8,9,10,1,14,-10,1,-14,7,-6,-13,14,-2,31,-3,7,2,14,-3,1,5,7,-8,-1,9,-9,-1,-13,-10,6,5,4,3,15,8,-2,-4,15,-3,-3,-8,4,3,-9,-14,8,-4,7,2,-13,1,-13,3,12,0,1,20,-13,-4,-6,0,7,5,15,19,-9,-11,-2,-14,5,10,6,-7,-8,-6,-7,-12,3,0,2,17,12,-7,-2,9,20,-8,6,15,3,-13,6,-9,-6,1,-10,-12,-1,-7,5,3,7,6,9,6,2,2,16,8,22,-2,-7,-1,-4,-7,-11,14,9,13,14,-7,-4,9,-4,14,4,0,2,7,-2,-13,16,-3,1,-5,-4,-14,-10,12,4,-3,14,-13,7,-4,6,-9,-12,10,6,-4,-14,6,-11,-10,1,12,22,3,14,9,14,8,1,6,8,-9,13,14,-1,-10,-4,11,13,-2,-6,12,-13,-7,-12,-14,14,0,2,6,0,-12,-8,14,14,8,-2,-9,-4,2,0,-11,-6,5,-3,11,11,9,8,14,19,-9,-13,-8,12,-3,15,1,9,8,-4,8,-6,-8,10,3,-5,4,5,16,-10,-6,-11,1,-2,0,-1,-2,-8,-3,9,-8,10,-2,-7,16,6,0,4,11,10,-8,11,10,7,-15,-2,10,-2,7,-5,16,1,6,-6,-9,-7,-12,9,-10,-6,9,11,-4,7,-19,-9,-8,14,-11,-8,-12,-9,4,6,2,-4,14,10,-5,18,3,-13,-7,10,9,16,0,3,3,-5,5,9,-8,-5,5,-9,14,15,10,-8,1,-10,-14,6,-17,15,11,15,1,-11,-6,11,8,-8,-11,-3,1,16,-1,0,-9,10,-9,7,11,1,-5,9,-7,-3,-8,-5,0,-14,-12,15,15,-4,2,-4,-6,-7,-8,-7,14,9,12,-10,12,-8,1,-13,2,11,8,6,10,8,12,3,-17,8,-8,1,4,6,14,22,-8,6,-2,5,1,-9,-10,15,-3,8,-3,14,3,7,-3,-2,6,4,-9,8,-5,2,12,3,5,-14,-5,-12,14,-2,8,-3,3,-4,-6,-13,4,-12,-6,15,14,6,-12,-11,2,18,13,14,-9,-2,14,12,4,-9,-10,2,-9,6,-3,0,9,-20,-8,3,-13,4,15,15,12,-22,13,-2,-12,-1,3,7,4,-1,4,-5,-15,4,-14,-12,11,-2,4,15,2,16,6,-4,-3,-20,14,16,-2,1,-9,-11,4,-10,15,-5,-6,8,-8,0,6,-22,-14,2,-6,-1,15,-14,9,17,14,17,-11,4,18,-5,-4,5,4,7,-11,1,10,-8,-7,7,11,20,2,-13,-2,3,-6,5,11,-1,-5,-9,-1,6,15,-10,-9,18,11,-14,-13,8,14,-2,7,7,-3,0,-12,-3,-6,-13,-5,3,-6,4,-12,-1,-6,-14,7,11,5,-11,3,3,-4,-1,9,0,1,2,-7,5,14,5,11,18,-10,-6,2,-1,-1,-7,-7,3,4,9,6,5,-6,6,-8,-6,-15,-13,-10,-1,0,-2,10,10,-14,-1,-11,14,-4,-4,1,-4,5,-3,-16,-4,9,5,-3,2,6,-14,-6,-9,0,11,11,2,-3,-1,-3,13,-1,-9,-8,-1,-11,-6,-4,-15,9,2,13,14,-8,10,4,2,-1,14,-9,-3,12,11,0,-13,-2,-8,2,-14,12,-9,13,-2,14,9,-8,16,-15,1,5,-4,-6,13,10,-7,11,12,8,15,11,16,16,3,-6,-5,1,-11,-6,-10,14,-4,-6,-10,3,1,6,1,15,-7,7,0,5,1,-9,4,-12,4,7,-6,18,-10,-14,4,-14,-3,13,-9,-6,-10,-4,5,5,10,-1,-13,-13,0,-9,4,-11,20,15,11,-4,6,5,15,-7,18,18,-11,-8,-8,-10,-6,15,3,-8,-11,10,-8,-7,10,12,16,4,-14,-13,4,-12,-7,-7,26,1,-12,5,-2,-6,2,-6,22,5,10,-3,14,7,-1,10,24,-2,-14,8,-3,-14,6,12,-1,14,11,3,-2,3,7,-12,12,1,6,7,-6,-4,5,15,-6,14,15,14,2,-9,-3,13,0,-7,1,-4,7,-2,8,-12,8,-7,-10,10,12,14,14,-8,7,2,-8,5,6,-5,-1,-8,14,-5,-11,4,-9,9,8,11,-8,8,8,-7,-12,9,-3,-1,-6,0,-2,7,-3,1,14,-11,28,-11,-5,5,4,-7,-8,-6,26,3,-8,-5,14,7,-8,4,-6,-10,2,7,3,12,-2,-6,12,5,-7,7,-13,8,2,5,19,3,-3,18,5,12,-12,-2,8,-10,14,2,-9,4,0,5,10,7,4,-11,-8,-8,-10,-11,-2,9,5,-12,12,9,-6,5,11,-16,-1,13,-3,12,-9,8,1,13,-13,-1,-10,12,-9,-6,9,10,5,19,-1,-5,17,-3,-10,-15,4,13,-8,4,15,-10,-6,2,7,-3,-5,10,-8,1,19,-12,13,-6,-2,-11,5,9,-13,-5,0,18,-11,7,14,5,1,-5,3,2,-2,15,4,-12,21,-1,-1,-7,11,-11,-1,4,22,18,-5,-10,13,-13,5,-5,7,-3,12,12,10,6,14,-10,10,10,5,-6,-5,11,16,2,1,-8,5,1,-14,3,-4,12,15,12,-6,-1,13,-8,0,6,22,-1,-1,-15,-8,11,9,-5,-3,-4,12,2,-10,2,-8,-4,23,6,14,-22,-10,2,4,7,14,3,-7,-10,6,-2,-13,-14,10,-1,-8,9,-3,13,-9,10,-14,-10,4,14,-3,4,-3,14,14,-11,-13,12,-5,9,-10,-6,-2,-12,5,6,5,-5,4,0,-3,-14,-11,18,8,-7,-2,14,6,-18,6,-14,11,-11,-10,7,11,10,6,-11,-7,10,-12,11,13,-2,-2,2,-12,-12,-7,-4,13,-17,0,10,14,13,-4,-9,11,8,0,-11,-4,13,12,15,-11,5,-1,20,-2,-3,10,-10,13,-16,3,-2,6,4,13,-2,-3,4,-11,7,-1,5,-5,-10,2,7,-11,-3,-14,1,-1,-6,1,-11,6,-4,3,4,12,2,13,14,11,-1,6,-10,-22,0,11,-8,-13,-6,-10,5,13,-8,15,7,0,-12,4,-14,12,14,13,-4,3,15,-4,-3,-23,6,-8,2,7,-9,-14,5,-11,-12,-1,-14,9,21,12,1,-1,3,7,2,3,7,11,13,-5,-1,2,4,-8,13,9,-7,-27,-11,12,4,-4,1,-1,5,-8,-11,-5,-6,-13,-13,2,-5,-3,-10,-1,-3,-11,10,-11,11,-2,11,-6,4,-9,3,-2,13,1,14,-10,-11,-12,8,-3,13,-12,0,0,-4,-10,8,-12,-10,-12,3,-2,-13,-10,8,11,0,0,-7,0,15,14,-16,13,0,6,10,-3,-9,7,-10,-7,-5,-20,11,11,-3,-8,4,5,-3,-5,-8,-7,-1,15,8,-1,-10,-6,-6,-15,9,2,-3,-3,2,-2,11,8,15,7,8,-4,-14,-12,1,-9,8,14,5,11,-11,-6,-1,5,11,-14,-9,-8,-10,-6,1,3,13,-6,9,7,-2,12,-12,0,14,-7,-4,-3,7,12,10,-10,-14,1,15,-3,-14,9,-8,15,-3,-11,-15,4,-11,7,-4,13,-10,0,3,9,3,7,-11,4,-10,-1,-4,4,8,4,9,4,5,-13,11,-11,7,0,-8,-2,-8,14,12,-4,5,11,14,18,4,-4,3,4,-8,-8,-2,4,-3,2,-1,6,15,13,-9,-9,-7,-10,-1,-1,-4,7,5,10,8,-9,12,-11,-1,-3,-9,-7,7,-14,-1,-2,-13,7,8,4,14,-11,-2,13,-13,-9,-9,-12,-10,-14,-1,12,-5,-1,-6,-8,-4,-7,-8,-12,7,4,10,7,-9,-8,-5,-9,6,12,-9,27,10,-2,12,14,8,-2,8,0,4,11,0,15,-2,-15,-3,-9,9,-3,12,-12,-13,19,13,18,-8,2,17,-11,9,17,-1,7,-5,-5,2,-8,-9,-15,-6,7,-6,-5,-8,4,-7,-5,4,1,-7,13,13,4,-12,9,-6,30,-24,-1,5,-3,4,-2,6,-1,-3,-10,-16,-6,-10,18,4,12,11,-4,-14,-6,3,14,-10,12,-4,-2,-6,-3,2,4,14,14,-13,-11,-13,5,5,4,10,27,4,-12,0,7,7,-11,2,-7,3,4,-6,-8,9,9,2,5,-27,-2,10,5,6,13,-14,30,4,4,-12,5,-7,6,7,23,35,-6,-13,-6,10,-6,-5,-12,5,13,6,15,15,-9,-9,9,14,11,-1,2,-6,3,5,17,-9,14,2,-13,5,1,14,11,-2,6,12,-12,2,10,-12,8,13,7,-6,7,11,15,12,8,13,3,12,8,-15,-2,6,0,-8,-14,-4,-10,8,13,-5,4,-13,-13,7,12,8,-10,-9,20,21,-11,-7,7,10,21,15,-11,-13,8,13,-11,-13,17,2,0,-1,14,3,-12,-12,-17,-7,-6,7,-12,-9,-12,8,-10,15,-3,0,12,14,4,14,-2,-5,-8,5,0,-10,14,13,8,-2,27,-4,14,-9,-5,-9,0,-5,26,27,-14,-13,-6,11,-10,15,-11,-5,-6,3,-14,-10,10,12,15,6,15,13,2,-4,0,11,-1,-23,2,-8,-15,0,10,11,21,6,5,1,13,7,10,-5,21,-8,10,-5,-15,-12,-1,5,-7,-2,6,-8,9,-3,6,-9,15,7,1,-18,9,0,3,10,-5,7,-8,13,-4,2,-13,-1,10,17,-5,0,-7,15,12,10,-12,11,6,-8,-7,-6,-2,-12,-13,-2,-7,-10,6,11,-19,-12,-7,-9,6,11,-4,0,-14,-13,8,-5,2,16,10,-10,4,13,-7,-16,-12,-9,14,13,8,-14,9,-1,0,-18,-7,11,-12,-10,7,22,-5,-6,8,3,7,-13,2,11,-10,-11,10,-1,-12,0,3,2,4,-13,7,9,15,-1,10,-9,-8,-7,0,-14,18,1,10,-14,-12,13,-13,-10,10,9,-8,8,8,5,10,9,4,10,-1,6,-13,-7,12,-1,11,-10,23,6,-8,-3,-4,-6,3,-13,-12,8,-2,4,3,-2,-11,11,4,19,15,-5,-2,-3,17,-9,-8,5,-5,6,-2,-5,-5,-12,15,-6,-4,17,8,0,-10,6,1,-7,-5,0,6,6,-6,-13,13,8,-6,-5,-3,15,-6,1,13,-8,-7,-6,5,9,-13,-6,-3,-8,-2,4,4,-3,-16,-1,21,25,7,-18,12,0,-8,-6,9,5,0,-1,0,-10,-16,-13,-9,-9,9,12,14,11,16,4,15,8,-11,-2,-9,-1,18,8,-10,6,9,16,7,5,8,4,-8,7,-13,12,14,-6,-2,15,2,9,-1,3,3,-8,-10,-4,20,13,-3,-14,5,4,-2,-4,-11,0,-12,9,1,7,-12,11,-9,16,2,-9,-13,1,-6,5,13,2,-4,-2,-2,6,-11,9,3,14,12,7,-13,-13,-6,13,-10,10,-5,2,11,-12,-1,3,-3,2,-11,-7,13,-10,1,-7,-1,-4,-3,16,-12,15,-9,-15,11,-11,12,3,15,-4,-6,1,-1,4,4,-19,6,7,3,-7,15,4,-14,2,-13,-9,4,2,0,7,4,-8,6,4,-8,-6,12,-6,0,10,-8,14,17,-2,-8,-8,-11,9,-7,16,4,13,-3,11,-10,-8,7,0,3,13,-12,-14,12,13,6,-6,17,-8,12,-15,10,0,-6,14,4,11,12,-7,12,8,2,1,-6,-12,-10,4,6,-17,-11,-2,-1,-4,10,3,-14,-10,-1,-3,10,8,8,3,-8,5,-3,-4,6,2,5,-14,12,13,1,2,10,14,-2,-6,2,3,-3,-7,14,3,-9,-12,2,6,-12,7,-11,10,11,2,4,14,-14,13,24,9,20,15,-11,12,12,11,-2,7,-13,10,-3,-13,-13,-2,-5,-5,2,-1,9,-16,-5,-6,1,-13,19,-21,15,-2,-13,0,9,1,19,4,-10,1,-10,-8,-11,6,15,14,-9,7,11,-2,2,-14,11,12,-1,13,-12,-1,21,-1,13,3,-5,-4,10,8,9,3,9,10,11,2,-7,13,21,-13,-5,-7,-12,15,-7,-7,1,-12,-6,-20,-10,11,-9,8,13,13,-5,0,8,6,-10,-5,3,9,1,7,1,2,10,15,-4,-12,-11,-8,0,14,-13,-3,2,15,-11,6,15,-8,-14,11,-7,-9,10,18,0,-7,7,4,-4,-6,35,23,8,18,-14,2,13,-7,-11,4,-13,9,-3,-5,-6,-9,15,-7,-5,-2,2,1,-1,-6,2,-6,4,-3,-14,14,16,13,24,-8,5,26,-13,-3,11,3,23,7,-1,17,-13,-1,0,3,-5,5,-5,-10,-9,-4,8,-4,12,-19,6,-3,0,6,-14,12,3,14,2,4,-5,-15,11,-12,11,11,-5,11,-2,7,-7,5,9,-7,-14,5,11,11,1,9,12,4,-1,0,15,12,-6,-11,-1,18,12,25,5,-7,8,13,4,-10,14,23,-9,-7,6,6,-7,-5,-10,-11,2,12,5,-14,22,6,5,-1,11,-10,-3,-1,28,34,3,16,-8,10,5,2,-12,-11,-13,6,3,13,-2,14,-2,-5,-13,2,15,-9,12,3,-4,-17,14,23,-7,14,14,-4,8,0,-9,28,-7,9,-6,9,19,11,-2,-10,-10,-11,-10,2,0,-10,-3,12,-9,9,4,8,9,7,1,-7,10,-2,15,0,-11,-1,-13,10,2,1,-5,-5,-7,6,10,1,-14,5,1,13,13,7,-11,4,10,5,16,13,-12,17,-11,12,12,5,-9,-7,16,11,-4,-1,2,5,-6,6,1,-15,13,9,1,0,-8,-6,-5,-6,-9,-6,6,12,15,7,8,14,2,-7,-12,-9,-17,-4,16,2,15,-9,8,-3,-5,7,14,-7,12,9,9,-13,-11,7,5,5,1,10,-14,0,17,-1,13,-11,-4,8,-6,-2,0,11,4,-9,-7,1,-10,-9,-7,-4,13,6,14,4,3,7,-10,2,5,5,-3,-1,-7,-8,8,10,22,-4,2,-11,-13,1,-4,-14,-10,-2,12,-4,6,8,5,0,-3,-2,-9,10,-14,2,17,-9,10,-6,-1,-2,0,-7,14,14,11,8,2,12,-8,-11,0,-13,-7,4,-7,-12,5,15,-14,0,8,-2,9,-10,14,12,-6,-5,-9,4,12,11,-10,11,9,-13,8,11,6,-13,15,-11,-7,0,3,33,15,-3,12,12,7,0,-1,-3,3,14,7,-11,-11,-5,3,17,-11,-2,-8,14,-7,-13,-6,-1,5,-12,0,-7,16,-5,4,1,7,2,-1,-13,10,12,-4,-13,0,1,15,12,-10,11,-5,-9,-13,-8,9,10,4,4,2,10,-13,-1,7,6,10,-12,-2,0,0,6,14,9,13,-5,16,-3,0,-9,-13,-12,3,-5,0,10,8,10,-12,9,-4,-11,-3,10,6,0,-3,-13,-12,-5,1,7,-8,-15,-10,2,4,-10,-1,4,11,4,-14,-6,11,-8,13,-2,14,1,1,-13,11,-12,-4,1,-9,-8,2,3,-7,-13,8,15,13,-27,-6,0,7,-8,7,3,0,-13,14,6,-3,15,1,16,-12,1,12,-11,15,-14,-11,-20,2,8,-2,8,17,8,16,-13,11,-4,-14,-14,3,-1,-8,-11,3,-5,-8,1,6,-11,6,12,4,-11,-11,14,5,5,18,8,-1,-22,3,-11,-13,-5,9,-8,10,-18,7,-12,4,3,6,0,-9,-6,4,6,2,-6,-11,-10,-2,14,6,2,-7,9,-4,-11,-3,7,-1,8,-11,4,-4,6,12,11,-8,2,-11,14,11,-12,-14,-12,-5,11,21,3,-2,-17,5,-7,0,15,3,14,12,11,-3,-3,5,11,-6,13,32,0,-13,-19,-4,8,-9,-12,4,-19,-8,-10,-9,8,16,1,7,18,-8,12,-6,10,22,-5,17,-11,3,-10,-6,11,-3,4,-2,2,-14,0,8,7,0,-8,13,-7,-12,5,10,-7,-6,13,-7,3,8,-4,3,-10,7,10,23,-12,14,-15,-2,5,9,-1,2,3,8,-16,-1,11,9,-3,9,-8,9,-15,4,-4,21,8,15,-8,1,-5,-5,2,18,7,2,-1,-11,-10,10,-8,9,8,25,17,6,15,12,13,7,8,16,-2,-1,13,-4,14,8,3,13,-19,-4,-10,12,11,11,15,24,5,10,3,-1,-12,-4,13,43,16,11,15,-11,5,-7,12,10,7,2,-11,-1,4,19,-9,5,-6,11,7,15,-10,21,0,20,-2,-3,5,-4,-12,1,-13,25,-3,7,20,-11,5,10,-7,28,5,-12,2,15,-8,-12,-5,4,-8,9,2,4,-10,5,-15,12,-15,9,-19,2,14,10,12,6,-1,11,-6,13,5,-2,4,-2,21,-14,2,-13,-6,1,-4,-2,5,-3,8,9,-9,2,11,12,-2,2,16,7,-7,-1,-9,4,-9,-12,13,-6,-10,-6,2,11,6,-13,-3,-7,-6,6,-11,1,1,6,1,-12,-11,7,-3,-1,17,13,3,14,-11,9,-5,24,23,10,15,-11,-5,19,-5,-1,-2,4,0,-8,-14,12,2,14,1,10,-1,-6,7,16,13,4,8,-6,0,-3,3,17,13,3,1,-8,4,6,12,-9,12,-1,-12,2,-3,-1,-13,12,-10,-7,12,11,-5,-5,2,-9,-2,26,3,-6,2,6,2,8,-11,-11,-12,4,10,7,9,14,15,-2,20,3,1,-11,-4,0,-2,6,-16,-11,-1,0,4,-4,2,16,-12,-11,15,-6,11,7,-1,11,-8,5,-9,14,14,-4,-14,-7,6,7,10,-6,13,1,-7,-1,13,-5,-4,7,3,-9,2,10,-3,-10,-7,9,-4,2,14,7,24,1,12,0,3,11,9,-5,11,-14,-3,1,-13,1,6,-10,3,12,12,-13,13,-9,-10,14,3,15,-4,12,-14,22,-4,11,-2,0,12,6,8,13,7,8,-4,-3,2,-2,12,-2,1,-15,-7,13,-1,-9,-5,8,10,19,5,6,-19,-11,10,-2,11,-10,2,6,-12,5,5,5,10,-11,3,10,13,4,-5,-6,-2,15,9,-10,-11,-3,-3,-3,-9,-2,-1,-10,-4,11,-13,0,-13,16,19,12,13,-14,6,-4,-14,-12,15,-15,-4,-14,8,4,-14,-12,-10,-7,13,3,8,10,-6,-10,8,4,14,-2,-13,-21,-8,6,17,-11,0,13,8,-9,-7,-2,-11,3,-14,-1,6,-11,14,-8,27,1,14,-12,4,-10,3,9,-8,1,-8,8,-13,5,-11,-3,-5,-5,19,14,-14,8,12,18,-7,4,-3,-12,-6,-4,13,6,9,5,-6,-6,-6,10,-2,11,-2,-12,-6,-13,1,-11,7,11,8,-5,-12,8,-7,-3,-5,-13,-3,-4,5,11,7,3,1,-4,15,5,-7,12,-11,11,10,10,1,11,-12,8,10,13,6,-1,-14,-7,2,8,2,-3,-1,3,-7,-1,-10,13,-3,6,-10,3,-10,24,15,-4,0,-12,15,13,-12,1,0,13,10,3,39,64,7,3,0,36,9,3,14,2,1,5,-5,10,10,11,-11,10,-7,14,14,0,22,24,8,-6,9,23,-1,6,16,13,3,-7,-4,8,6,-16,1,-11,29,32,-7,8,-1,6,6,13,10,-6,1,14,12,7,18,8,17,49,3,8,-11,22,3,8,-6,21,14,0,3,9,-2,16,13,-5,-6,5,9,-12,14,1,-3,10,3,-13,13,12,15,1,-7,10,-3,14,10,-4,-10,7,-12,-5,7,9,0,-4,-13,-13,-6,11,-6,3,0,-7,5,5,-9,-1,5,0,1,9,-7,11,-2,13,15,-5,8,3,7,4,16,12,-5,11,-14,16,-2,20,1,-8,-7,-2,5,3,3,1,-4,2,-9,1,2,-13,-3,7,8,-12,-2,20,6,0,4,31,-7,-1,-2,0,-13,8,14,-11,1,7,15,14,-10,15,10,2,6,0,1,14,10,12,10,22,9,21,2,-7,-2,21,0,-2,-2,12,-12,-13,10,20,-12,-5,-7,-4,-13,0,3,-7,-1,-3,0,3,10,-6,14,-9,-8,17,12,12,15,16,11,-14,4,1,0,0,-7,0,10,-10,7,11,7,-1,1,17,-1,-3,3,-6,-10,5,-6,14,-8,-1,-5,-15,-1,-6,3,4,0,1,-9,-11,-21,-6,-10,-10,14,-6,11,12,-8,6,-13,1,11,-12,4,-1,14,8,9,1,-12,1,-2,-7,-4,-17,1,12,1,-4,-4,2,-9,-3,8,-1,-5,-19,13,2,-19,-11,-6,-8,-14,16,12,12,-1,3,0,10,-12,-4,19,2,4,6,10,16,-13,6,12,-7,4,16,-3,-5,-4,8,-7,2,-24,-17,12,10,-3,-10,-9,13,5,-3,4,16,3,5,1,14,-2,4,-9,3,-6,5,16,11,-15,0,7,-8,-14,-18,-12,-5,10,-6,-5,2,0,-8,-4,7,10,0,-14,2,13,14,0,-14,-16,-19,-5,-4,-10,-1,-8,16,-27,-26,-2,14,6,7,10,3,-7,-5,11,3,-13,9,14,-2,13,-12,15,10,7,-14,12,-10,-3,-26,-6,9,10,3,-10,5,-3,-17,14,-13,-1,3,11,-11,-21,-11,-1,14,3,-11,-14,12,3,-5,13,-5,12,4,11,4,9,-1,11,18,-15,10,-14,8,-11,20,-10,-10,-13,4,10,-2,-20,-18,-1,17,-14,-13,7,-2,-3,0,-3,7,-12,1,5,2,-14,-2,-11,-1,15,8,1,-17,-3,-11,4,-5,-1,-18,9,-5,-4,-6,-7,-4,-11,6,1,11,-13,-11,8,12,5,-13,-16,6,2,3,10,2,5,-2,-13,-21,6,18,0,15,5,-2,8,22,-11,9,8,4,4,14,-10,-16,-5,-9,-4,1,3,-1,17,19,-13,-30,2,-8,-3,-17,27,37,-4,-11,-1,-1,11,9,12,-11,19,29,4,8,4,3,-5,4,-1,0,12,15,13,12,14,28,7,36,-11,-1,9,4,12,10,-1,23,3,-12,-1,-12,-10,-1,-2,-10,11,6,-5,0,-5,-1,-1,-3,-7,-11,11,1,8,8,-7,3,-9,9,14,12,-5,-18,8,3,-1,10,-14,-13,6,6,12,5,-10,2,8,-10,27,26,-4,2,-6,10,-12,-11,11,4,4,12,15,-8,10,-12,-8,-12,21,41,9,-8,4,-4,10,3,12,-1,-8,4,8,-10,-1,-18,12,-15,-11,18,-10,4,14,24,6,2,5,4,11,-7,14,27,-20,-15,12,19,-11,-17,3,4,25,28,-1,-13,-9,15,10,15,14,-7,0,-7,-12,11,26,36,25,42,-8,15,-1,2,-9,-11,14,6,11,13,6,0,-5,2,-12,-19,7,7,15,-16,11,23,-2,5,1,8,3,10,-13,-11,11,-8,6,10,11,-12,-14,14,-12,-1,-10,-9,-3,5,1,3,6,11,-3,-13,-3,3,0,3,12,7,11,10,-4,14,13,-2,1,11,2,13,-6,15,16,11,6,33,13,4,-2,5,8,8,1,13,-13,-6,14,-1,-10,3,-9,5,3,2,-12,-6,16,21,-12,1,3,-2,-4,1,0,6,7,-12,-7,9,6,-9,-7,-11,23,3,-8,14,-7,7,9,-8,0,-11,2,0,4,-12,16,32,17,41,6,22,-7,6,2,-13,0,3,-10,-11,-8,-1,-1,14,-2,-12,-4,-7,11,9,-6,9,-14,-10,12,1,-12,-12,6,-1,15,11,0,12,13,6,1,-2,-3,8,10,4,-5,-3,3,-18,4,-5,-2,16,8,-10,-6,14,-14,11,8,18,2,-12,10,-2,-5,-4,-12,-14,2,3,-1,-21,9,46,14,-11,-4,-2,12,6,3,5,12,8,1,-10,-1,-17,-4,11,7,-6,14,-10,0,32,-11,-7,-12,0,-11,-6,13,29,-2,-13,-6,19,6,0,-4,0,2,23,13,-11,-2,14,-6,-8,4,8,8,-6,5,15,23,15,25,47,-8,16,-8,17,-13,7,-5,25,4,10,-2,16,-17,-6,-6,-14,-6,-14,5,5,-11,5,-4,-9,0,-7,-13,-10,4,22,-5,-6,3,3,10,2,-15,-4,0,-7,-12,-4,5,-3,13,-2,-11,0,8,-10,-3,11,-3,11,10,4,-13,3,6,-4,1,-2,-7,-7,-9,-3,-5,4,-12,-2,-12,-11,-2,16,-14,-27,16,23,10,11,0,4,-2,10,-7,10,0,13,6,14,7,12,8,28,-11,-14,14,10,-10,-2,16,-1,4,-2,-11,9,-3,-2,9,-10,17,1,5,14,1,-10,-3,1,14,12,0,12,-8,-9,17,3,25,29,-13,2,-4,-17,13,15,-9,15,-12,-10,6,15,-8,-11,5,-20,-1,-2,11,5,6,13,5,7,16,12,5,1,4,17,-7,10,-14,10,4,-7,3,-8,-1,-14,-1,0,-6,-10,6,6,-12,5,-9,10,-5,4,11,-12,3,-15,14,-6,12,11,9,10,4,-25,15,-6,2,0,-7,-5,-9,-14,2,4,2,-8,5,-7,7,14,-3,12,1,3,10,18,-1,-8,-5,16,-3,-6,3,7,-4,-30,-1,-11,4,-21,-4,9,-19,-17,9,-14,-14,5,-1,-14,-2,-8,7,7,15,-12,15,-2,-12,-12,3,4,-11,-3,7,5,1,-3,-14,10,-10,-2,-15,-5,7,10,1,3,12,1,6,6,-3,-18,-7,2,8,4,-3,-4,-12,-5,-3,-11,12,-12,0,2,-6,-7,-5,0,4,-11,-16,-10,-31,-9,-8,8,2,-3,2,-1,4,-8,4,-5,-8,-2,17,7,15,-15,13,0,-4,15,-15,-17,-10,-8,8,-10,10,-9,-12,-22,4,-8,-13,13,13,-17,-5,-3,-1,8,15,15,15,4,-12,10,12,3,0,28,15,14,-2,29,-25,-16,14,16,-10,-3,12,3,-23,-22,14,-10,3,-13,-6,11,2,5,-10,-6,-9,7,0,0,13,-5,-14,-5,-9,-1,-2,21,12,15,-6,10,-12,-4,7,12,17,2,-6,6,-5,-11,-16,-30,-21,-15,0,-6,5,-9,-2,-3,-8,-4,-2,7,-4,-10,-10,4,3,11,-10,2,-11,10,-11,-12,-9,-3,1,-5,-6,-12,8,3,-18,-4,1,6,-14,9,4,17,-2,2,0,-1,-13,8,10,-4,-5,0,2,-5,-3,-10,-9,-48,-9,8,-7,-2,14,-3,-1,9,6,-3,8,5,15,-2,-14,-30,-2,-5,-13,-1,8,-9,18,26,-30,-25,-5,12,-13,6,1,19,-25,-33,15,12,-2,-18,15,12,0,-4,-1,15,-14,-8,-14,-14,-10,-13,-14,5,-12,0,30,10,21,41,-3,14,13,2,-14,-13,13,6,7,9,12,13,-27,-10,-22,-25,-1,-4,0,-12,6,-5,14,0,3,-2,8,-9,6,2,-5,-18,15,-10,-5,-5,5,-11,8,-1,7,5,1,-13,6,-5,12,14,-9,0,-9,-5,-7,10,-12,-1,-11,0,-6,-14,14,-15,2,-9,3,3,13,19,-11,11,-2,-19,-8,10,-9,23,5,0,10,12,-14,11,1,15,-10,17,6,2,12,3,-1,17,-13,9,3,6,4,15,-14,-18,15,21,-7,-8,0,20,-5,-10,-6,-1,-1,-14,-13,-5,-1,14,-3,-9,6,12,-1,-2,-7,-4,2,20,1,1,14,1,-3,5,-10,-4,-13,14,-8,1,-1,12,14,14,-4,-9,10,-1,6,13,0,-6,-2,0,-2,10,-4,11,9,11,13,15,11,8,1,14,13,-14,-15,-7,-4,-7,2,-17,-10,-8,9,-10,9,7,13,-14,-3,-11,13,-5,6,15,10,-6,12,5,-12,5,-4,9,8,-19,-6,18,10,-3,2,0,-12,-16,2,24,8,-12,-8,14,2,13,0,-12,-5,-1,1,19,13,-1,20,16,-5,-17,-12,9,-4,-10,16,26,-2,-19,8,-11,2,-24,0,12,-7,4,4,7,12,-5,-9,-2,-5,-11,-8,-3,13,1,13,11,-3,16,7,22,-5,2,6,5,18,10,-7,0,2,-10,-10,16,-14,-21,-14,1,-1,-2,1,-2,-10,7,-12,14,15,-14,-1,22,0,-11,-10,18,-10,-9,16,1,0,-15,-7,21,-3,-7,-3,-7,10,-5,-1,-3,4,-2,0,14,8,14,-4,6,13,-4,12,-15,-19,-3,15,6,4,-25,26,0,-6,-4,6,-10,-7,-34,5,16,-13,-14,3,-2,2,-4,26,17,-6,8,11,15,8,3,7,17,-3,-15,-2,7,0,-10,7,14,-11,-2,-1,-15,1,-8,-13,-4,13,-2,-3,2,5,-9,-11,13,10,-6,13,-10,3,1,3,24,1,25,-11,18,-6,-10,10,13,14,-2,-7,-9,0,2,4,-3,6,-13,-7,11,3,-13,-2,10,-14,8,-4,-1,9,-2,8,18,-4,-12,-1,-5,-1,6,-6,9,-11,5,-14,0,2,-6,7,23,5,2,-10,11,7,-14,-3,-5,-9,-1,-6,0,-13,0,-5,2,-4,-24,-12,-11,5,-15,7,15,-4,-19,6,11,3,2,3,-12,-3,0,3,9,8,-12,24,25,3,16,3,15,-5,2,2,-3,-19,-26,14,17,3,-6,10,1,-13,-25,8,-9,-13,-17,11,4,8,-4,7,6,6,1,-3,1,-8,-7,4,-9,4,-8,4,11,23,13,2,5,9,11,-7,10,-9,5,-2,-8,3,-6,5,20,-2,-11,1,-1,9,-17,19,25,16,5,7,-4,3,-9,15,23,14,5,-9,13,-1,2,6,10,-8,-4,7,13,-14,-18,10,4,-10,3,7,5,11,-3,15,-6,0,5,11,10,-13,-6,9,-19,3,-13,-10,-14,8,0,-5,-1,-11,8,-2,1,5,-12,-3,18,8,-5,-6,5,-8,3,10,0,-9,6,11,9,-3,7,-1,30,-14,-10,6,17,13,-19,24,5,-11,-33,6,13,-13,-8,3,-4,16,17,9,14,10,-5,3,-6,11,9,11,16,11,-11,8,8,33,41,15,-6,10,22,-8,15,18,14,-1,12,-14,10,-2,2,4,-28,1,19,3,-17,14,8,0,-14,4,-10,2,1,4,-2,14,-2,-5,-10,-6,13,-13,-10,4,1,8,-5,-3,-1,2,-3,-8,-5,-3,-10,-9,4,15,2,15,0,5,11,12,5,7,3,-16,-12,-6,-16,-4,-2,-16,-35,0,6,-11,-13,8,-10,13,17,-5,12,-1,-12,-14,13,-3,-14,3,-1,-7,1,-8,3,13,31,-5,-27,-4,2,5,7,9,8,-9,-33,-7,2,-5,-10,12,0,1,7,-1,-10,-3,2,9,-15,-9,5,12,-9,-9,-4,27,3,39,43,14,-1,-3,-3,-9,4,13,13,5,-6,10,-13,-1,-25,-14,-24,11,-10,11,-12,7,-5,11,3,0,-4,14,-13,13,-9,-2,0,6,-9,-3,6,11,13,-9,8,-14,-10,-14,-17,-1,7,0,13,-13,-1,10,10,9,-11,4,12,-9,-7,-8,-8,4,-12,7,-8,14,-12,3,-3,19,5,-30,-33,-10,6,0,11,12,-16,1,7,5,-3,-14,13,-3,15,-12,10,-12,-7,13,12,6,-21,-3,-13,1,20,-9,-11,10,-12,6,-4,15,-9,-13,-19,-10,5,-8,1,-10,5,-13,-5,-7,-6,-6,15,-3,-9,1,-8,8,-5,3,-9,5,2,9,0,11,-5,-12,-3,-5,-9,1,8,6,9,4,-6,-7,-7,3,-15,14,7,14,0,-13,-1,4,-14,-7,15,-3,15,-5,7,-13,12,-8,5,-5,-10,-8,13,6,-3,-4,4,-9,6,-6,11,-12,8,-8,-14,1,-11,-2,2,-13,-2,-6,9,-11,4,-7,-8,0,4,26,25,5,9,-1,6,1,-32,1,20,6,7,15,15,15,-7,-2,5,7,10,4,-3,7,-10,10,15,1,-17,13,7,9,-19,-7,0,2,1,14,-13,13,-20,-13,13,-11,-2,-11,-7,-10,-6,-13,9,7,-6,0,-7,11,-12,-8,0,-3,16,-11,19,-3,-7,-6,-14,-4,-2,-4,14,0,10,4,9,-18,-4,-10,3,10,-14,-14,18,-8,-7,-11,-6,-8,15,13,9,13,-7,-5,8,9,-12,3,0,1,-12,-3,-3,10,2,4,20,-3,-11,7,16,3,-4,-6,21,-5,-3,11,9,-1,-5,-11,-12,-2,-5,-8,3,-13,10,11,2,7,-9,10,4,10,-20,6,-6,-6,-10,-9,16,-3,-9,7,17,11,6,-9,-5,-6,-9,-13,12,-24,-17,12,-11,-4,0,10,24,-7,-27,5,-2,1,-10,-2,6,-14,10,-6,4,6,-10,6,-13,-12,13,14,-8,8,0,4,11,15,28,14,0,-3,-7,3,-5,10,13,12,-12,-3,-12,7,-1,-2,-10,-3,14,9,-4,12,10,-7,0,3,3,-6,15,7,3,6,-16,-10,2,2,3,15,18,-17,-8,-10,11,0,13,16,4,-13,3,-4,-6,8,8,14,11,-15,1,-11,15,-3,6,-2,6,6,-4,1,15,-12,-4,12,9,-14,2,4,19,-12,7,10,15,5,-2,-13,6,-15,10,12,21,6,0,-10,17,15,-12,18,31,-18,-19,4,-5,-13,-15,10,15,-20,-21,-2,-8,-7,3,-3,-8,-12,17,5,-8,-1,8,11,-10,4,2,4,-9,-3,8,12,39,23,53,-8,-1,-7,22,0,10,4,5,-1,4,-8,4,-7,-8,-21,-24,5,3,7,-10,15,8,11,11,-2,0,9,-14,9,5,-1,-12,-11,-11,4,15,16,16,-13,-6,13,2,-4,-14,13,-9,-11,3,6,10,-7,-14,18,7,5,-5,-5,-5,0,12,-13,-15,-6,9,-10,7,0,8,8,-2,-18,11,-4,8,5,3,3,27,8,0,12,-8,2,3,7,-4,8,-10,6,-7,-2,0,9,35,-23,-23,-2,3,1,-15,29,27,-20,-9,15,19,-10,-5,-4,-3,2,15,-15,8,-5,14,-8,5,5,6,-9,-3,6,-3,1,25,29,21,4,-8,-11,7,6,-6,9,16,-13,-11,4,-12,-11,-18,-2,-18,-11,-8,-11,8,18,5,4,9,15,-8,8,10,12,11,-16,-10,14,-7,6,1,-12,-12,-18,12,6,-5,-8,13,7,14,-11,7,15,-8,-12,3,-14,-12,-10,7,-9,7,-9,-1,9,-10,5,-13,-13,15,10,3,-5,-19,2,-3,10,-8,14,3,16,5,-4,1,1,17,-7,-5,-8,-12,-4,-1,-7,-1,0,3,24,4,-10,-23,-3,0,2,0,5,32,-23,-29,-12,7,-4,2,-9,12,1,19,7,-9,9,6,3,-3,14,8,7,-1,-14,11,12,8,16,38,5,2,13,19,7,-14,5,-9,14,11,-13,-9,11,-9,-2,-2,-5,-10,-7,-4,-11,-6,12,3,9,12,-14,-1,-10,-7,-14,-12,-1,2,-5,-4,-2,7,0,-15,5,-12,4,-1,15,-8,-4,13,12,-4,-1,-10,9,12,-4,4,5,1,-7,7,14,9,-12,13,12,1,0,-6,-12,-3,-9,15,13,9,8,4,-10,4,11,-5,-6,-1,5,5,13,-11,2,-3,13,16,-1,16,1,-3,-3,3,-12,6,13,-3,9,17,7,-6,-14,-1,-9,-7,-11,0,17,21,15,-9,-4,1,-7,-3,13,2,7,0,14,2,15,21,0,21,-3,10,-9,13,11,-11,1,9,-14,-4,9,9,0,2,5,-20,15,-4,-14,-15,-6,2,11,5,7,-10,11,9,-3,-4,-3,4,3,-4,-7,-8,4,18,-9,-9,5,17,0,-24,-4,-10,-13,-5,1,2,-2,-8,6,-6,5,5,5,0,-6,-1,-6,2,-2,-3,4,1,0,-2,29,32,12,30,-13,-6,14,-23,19,20,-1,8,-9,-11,-2,15,-9,20,11,-5,-14,13,1,-9,5,22,7,0,-1,10,-5,-6,11,10,12,-13,-10,-12,3,4,12,0,11,3,2,-13,8,-15,-4,1,7,1,-3,-1,-4,-14,16,9,2,26,6,14,-6,10,10,-4,-5,-3,-11,-14,12,-8,9,26,6,0,8,8,0,-11,1,2,-10,0,14,10,4,-11,16,1,15,-6,6,-10,-1,10,8,3,10,-10,-5,10,15,-20,-2,-7,9,-10,-1,-8,-11,5,6,-6,-9,-4,-7,-5,0,-3,13,10,16,4,-7,12,15,-5,21,32,8,24,11,14,-13,-17,-11,6,9,8,-2,-5,-2,-1,5,11,3,1,-12,12,-5,-6,8,-1,0,-3,-11,15,6,-22,12,24,-26,-26,8,-7,5,-11,0,6,8,24,12,1,-9,-18,-5,-11,7,-4,-13,-5,-11,15,1,20,-4,25,15,11,0,-10,6,-4,13,2,3,-7,8,13,23,0,3,-17,1,11,-10,10,7,-6,-2,-13,13,7,5,13,11,15,-4,8,-9,8,-11,14,-7,6,14,-5,15,10,7,-6,12,21,9,12,3,17,11,5,10,7,-13,1,-12,12,-7,11,-8,-3,-11,16,14,-16,13,-15,11,10,-1,20,-11,15,-10,-3,-4,-1,2,0,5,4,7,-12,20,20,13,6,-1,17,-10,-1,1,26,-15,-13,6,-1,-10,-16,-1,35,-29,-31,-14,10,10,10,-7,-3,16,19,-3,-3,-13,2,-3,-5,-12,-3,14,-4,-1,-11,24,21,33,58,-6,22,-8,14,-14,1,11,26,11,3,8,13,4,0,-6,-24,0,21,-11,5,-7,7,13,-13,-4,2,13,-5,-1,0,9,7,-11,1,4,1,14,7,-3,1,-11,-1,-8,12,8,-13,7,-8,15,9,15,6,10,-5,-10,-12,8,6,-12,1,5,-3,4,-16,12,10,-7,-7,-8,-13,-8,15,-10,0,4,-6,-4,16,13,0,-11,9,12,-8,6,5,-8,1,3,8,7,-13,0,14,-21,-30,7,-10,3,-7,4,32,-9,-12,-13,0,-3,-12,10,-14,5,22,-2,9,-9,14,16,-13,15,-7,7,11,9,3,13,19,41,59,10,-10,6,-3,-7,13,5,19,7,3,-5,1,8,11,1,-23,1,7,-10,10,-5,6,-12,-11,11,3,5,-3,3,17,5,3,11,5,13,14,15,-9,9,0,9,-13,-5,1,9,2,1,-3,11,10,-9,11,-3,-13,6,9,-1,7,2,-7,2,13,1,2,12,-14,7,-2,-8,-16,-10,3,0,0,-7,7,-4,15,-9,5,-7,-7,-5,7,3,14,12,7,-5,-8,14,-14,13,2,-19,-26,14,-5,7,1,17,28,-29,-39,12,14,12,0,-12,-4,-4,15,-12,-9,-8,4,-4,3,12,-7,-8,-13,-9,8,24,7,34,47,-11,-5,-13,16,14,2,-4,-11,5,15,-1,-4,-4,5,-3,1,9,14,-9,-7,-3,9,-13,-1,-6,-3,5,-11,5,12,17,-2,7,1,-4,8,-6,-6,-11,-3,16,-5,-13,-16,7,-4,-4,-9,-13,10,7,15,16,8,-15,-2,10,0,12,3,-2,-5,12,-4,6,-9,-15,-3,-1,2,3,17,-13,2,2,19,-10,-10,-10,-12,-7,-7,-9,-11,1,8,17,-8,-10,4,-9,-11,-3,10,3,2,7,14,-3,-5,19,13,-1,-10,-6,-2,5,-4,-7,2,-5,22,2,-10,-10,2,-14,5,-1,-6,-9,-8,5,8,23,17,22,43,14,-7,-5,23,12,3,-6,-1,-13,13,-3,1,-1,14,-3,12,1,-2,1,9,4,0,-14,5,-13,-8,-3,12,-8,8,6,16,-4,11,9,-14,2,12,13,-5,-4,16,-1,9,11,4,0,4,-13,3,13,11,-5,6,3,12,14,-12,-12,12,2,5,17,20,-11,-9,14,-11,20,-4,15,28,3,4,10,-10,12,-4,-10,7,11,-3,1,-1,3,1,3,2,-14,-4,14,-10,25,14,-17,-19,-3,11,-5,5,16,12,-17,-18,-7,0,5,-3,11,8,28,16,-11,-11,-12,-12,-13,-8,-1,-2,2,15,1,15,21,25,36,58,-2,-7,0,-4,11,7,17,27,7,14,14,-9,5,24,-13,-2,-8,-4,-7,-7,-4,30,-13,5,-6,10,9,-7,-4,22,15,-7,-7,1,-1,10,8,17,0,-11,0,19,-10,-3,-7,-3,1,-7,6,7,8,-4,3,2,-9,-15,-12,-5,10,3,-6,-11,7,12,-3,14,-11,-12,20,16,16,18,-14,6,6,-24,12,28,11,1,15,5,1,10,8,8,13,-6,3,21,-11,13,15,20,-7,-2,13,-4,9,-13,6,32,-4,-8,-10,-2,-2,4,3,13,16,5,3,-2,14,-1,15,-6,10,-1,6,-13,-12,9,26,17,26,31,-8,-4,-6,-6,8,10,10,-3,15,-7,-1,9,18,10,-2,-22,-8,-5,13,5,-7,-3,5,1,-9,-10,-14,-13,1,12,-10,-4,-7,-11,15,-14,-10,-13,3,-2,-1,9,-4,2,7,7,-14,-3,14,8,-4,-17,4,7,7,-3,0,-2,14,-3,0,-4,-8,-11,9,-9,-11,-14,5,-13,5,26,6,14,-12,-16,-4,6,-1,9,-6,-8,1,1,6,-1,-15,7,-12,-2,12,-6,10,21,-11,-7,-13,-6,-11,-19,22,25,-22,-27,-5,-5,-13,-13,-6,-9,27,21,5,-14,10,-5,11,-5,9,13,10,-4,3,-13,14,19,26,44,1,11,-10,1,-9,10,19,-1,-8,3,-11,1,4,-14,-19,-29,-11,-4,10,2,-8,9,14,5,14,-7,9,1,0,22,-6,-16,2,10,-8,4,-14,-4,5,0,10,-12,-13,7,-5,-8,-9,7,15,13,9,5,-2,-1,-7,6,2,-5,9,11,-4,-6,-17,-2,-13,12,9,-12,-4,-24,-7,17,11,8,-11,11,3,23,8,-9,-11,8,14,-6,-8,-8,10,13,-4,6,10,1,25,17,-2,-13,3,18,11,0,27,19,-18,-33,1,14,0,-19,-8,4,7,16,-14,14,-5,12,7,-12,3,4,-9,3,13,-14,12,30,25,58,14,10,11,-3,0,-3,12,17,-7,-11,6,11,12,-6,-22,-7,-4,10,-2,-3,19,-7,-13,-7,10,-12,0,10,3,19,-13,-9,-1,6,-8,-9,-5,5,4,0,-9,-3,-8,-2,6,7,-15,6,4,3,-14,-3,14,14,1,4,-6,-13,-11,2,12,-5,-14,18,-6,15,13,4,5,10,4,7,-12,-5,-14,10,10,21,11,-2,2,-12,-1,11,-10,-18,-12,-10,5,-17,-5,15,13,16,-10,-5,13,14,12,-13,16,35,0,-21,-8,10,15,12,2,-10,-2,24,8,5,0,6,-1,9,12,0,15,-9,-13,12,16,26,34,25,-13,11,11,1,7,1,3,4,12,1,-6,-3,0,8,-9,-15,-4,10,15,-9,10,14,-9,10,-8,-5,3,1,-3,5,4,15,8,13,2,10} +#define INTERFACE_WT_SHAPE 36864 +#define INTERFACE_BIAS {21,-41,9,4,-13,-11,29,51,-13,20,-17,-13,-23,18,74,62,12,15,5,-28,76,8,25,19,-19,9,53,20,6,36,-8,23} +#define INTERFACE_BIAS_SHAPE 32 +#define LINEAR_WT {-39,-9,-54,56,31,24,-32,45,-19,-12,-23,22,-18,35,-30,-14,-14,-14,-40,-23,-7,29,3,29,-3,-17,-48,-7,6,-11,27,43,-13,-3,16,21,17,-3,-31,15,36,10,21,9,20,-26,15,-20,-6,-21,9,-54,12,2,53,-11,16,12,2,-28,23,-6,38,-26,15,17,-1,-21,17,27,0,23,-4,19,-5,-20,12,-10,3,18,64,-16,53,12,-2,-61,-30,-34,-22,-17,7,-9,9,32,9,-21,18,-7,-8,-16,-18,-9,17,-24,-38,24,-22,10,10,40,-13,-4,-16,36,-14,-20,-18,-29,-15,4,-31,68,19,24,-35,-17,-23,9} +#define LINEAR_WT_SHAPE 128 +#define LINEAR_BIAS {-43,-66,63,-51} +#define LINEAR_BIAS_SHAPE 4 diff --git a/APP_Framework/Applications/knowing_app/image_processing/TJpgDec_APP/Kconfig b/APP_Framework/Applications/knowing_app/image_processing/TJpgDec_APP/Kconfig index d39c44c42..49c1a850a 100644 --- a/APP_Framework/Applications/knowing_app/image_processing/TJpgDec_APP/Kconfig +++ b/APP_Framework/Applications/knowing_app/image_processing/TJpgDec_APP/Kconfig @@ -1,4 +1,5 @@ config IMAGE_PROCESSING_TJPGDEC_APP bool "image processing apps/TJpgDec(example)" + select USING_IMAGE_PROCESSING select IMAGE_PROCESSING_USING_TJPGD default n diff --git a/APP_Framework/Framework/knowing/tensorflow-lite/tensorflow-lite-for-mcu/Makefile b/APP_Framework/Framework/knowing/tensorflow-lite/tensorflow-lite-for-mcu/Makefile index d94a82b4f..e257b9a29 100644 --- a/APP_Framework/Framework/knowing/tensorflow-lite/tensorflow-lite-for-mcu/Makefile +++ b/APP_Framework/Framework/knowing/tensorflow-lite/tensorflow-lite-for-mcu/Makefile @@ -83,8 +83,7 @@ ifeq ($(CONFIG_USING_TENSORFLOWLITEMICRO),y) -Isource/third_party/ruy DEFINES += -DTF_LITE_USE_GLOBAL_CMATH_FUNCTIONS \ -DTF_LITE_USE_GLOBAL_MAX \ - -DTF_LITE_USE_GLOBAL_MIN \ - -DTF_LITE_STATIC_MEMORY + -DTF_LITE_USE_GLOBAL_MIN endif include $(KERNEL_ROOT)/compiler.mk diff --git a/APP_Framework/Framework/knowing/tensorflow-lite/tensorflow-lite-for-mcu/source/Makefile b/APP_Framework/Framework/knowing/tensorflow-lite/tensorflow-lite-for-mcu/source/Makefile index 81b754f30..9636a1e1f 100644 --- a/APP_Framework/Framework/knowing/tensorflow-lite/tensorflow-lite-for-mcu/source/Makefile +++ b/APP_Framework/Framework/knowing/tensorflow-lite/tensorflow-lite-for-mcu/source/Makefile @@ -23,8 +23,8 @@ $(patsubst %.cc,%.o,$(patsubst %.c,%.o,$(SRCS))) LIBRARY_OBJS := $(filter-out tensorflow/lite/micro/examples/%, $(OBJS)) -CXXFLAGS += -std=c++11 -fno-rtti -fno-exceptions -fno-threadsafe-statics -fno-unwind-tables -ffunction-sections -fdata-sections -fmessage-length=0 -DTF_LITE_STATIC_MEMORY -DTF_LITE_DISABLE_X86_NEON -O3 -Werror -Wsign-compare -Wdouble-promotion -Wshadow -Wunused-variable -Wmissing-field-initializers -Wunused-function -Wswitch -Wvla -Wall -Wextra -Wstrict-aliasing -Wno-unused-parameter -DCMSIS_NN -DTF_LITE_USE_CTIME -I. -I./third_party/gemmlowp -I./third_party/flatbuffers/include -I./third_party/ruy -CCFLAGS += -std=c11 -fno-unwind-tables -ffunction-sections -fdata-sections -fmessage-length=0 -DTF_LITE_STATIC_MEMORY -DTF_LITE_DISABLE_X86_NEON -O3 -Werror -Wsign-compare -Wdouble-promotion -Wshadow -Wunused-variable -Wmissing-field-initializers -Wunused-function -Wswitch -Wvla -Wall -Wextra -Wstrict-aliasing -Wno-unused-parameter -DCMSIS_NN -DTF_LITE_USE_CTIME -I. -I./third_party/gemmlowp -I./third_party/flatbuffers/include -I./third_party/ruy +CXXFLAGS += -std=c++11 -fno-rtti -fno-exceptions -fno-threadsafe-statics -fno-unwind-tables -ffunction-sections -fdata-sections -fmessage-length=0 -DTF_LITE_DISABLE_X86_NEON -O3 -Werror -Wsign-compare -Wdouble-promotion -Wshadow -Wunused-variable -Wmissing-field-initializers -Wunused-function -Wswitch -Wvla -Wall -Wextra -Wstrict-aliasing -Wno-unused-parameter -DCMSIS_NN -DTF_LITE_USE_CTIME -I. -I./third_party/gemmlowp -I./third_party/flatbuffers/include -I./third_party/ruy +CCFLAGS += -std=c11 -fno-unwind-tables -ffunction-sections -fdata-sections -fmessage-length=0 -DTF_LITE_DISABLE_X86_NEON -O3 -Werror -Wsign-compare -Wdouble-promotion -Wshadow -Wunused-variable -Wmissing-field-initializers -Wunused-function -Wswitch -Wvla -Wall -Wextra -Wstrict-aliasing -Wno-unused-parameter -DCMSIS_NN -DTF_LITE_USE_CTIME -I. -I./third_party/gemmlowp -I./third_party/flatbuffers/include -I./third_party/ruy LDFLAGS += -lm diff --git a/APP_Framework/lib/Kconfig b/APP_Framework/lib/Kconfig index cc9317522..b3497fca5 100755 --- a/APP_Framework/lib/Kconfig +++ b/APP_Framework/lib/Kconfig @@ -12,4 +12,5 @@ menu "lib" source "$APP_DIR/lib/cJSON/Kconfig" source "$APP_DIR/lib/queue/Kconfig" source "$APP_DIR/lib/lvgl/Kconfig" + source "$APP_DIR/lib/embedded_database/Kconfig" endmenu diff --git a/APP_Framework/lib/embedded_database/Kconfig b/APP_Framework/lib/embedded_database/Kconfig new file mode 100644 index 000000000..83f2c675e --- /dev/null +++ b/APP_Framework/lib/embedded_database/Kconfig @@ -0,0 +1,6 @@ +menuconfig USING_EMBEDDED_DATABASE + bool "embedded database" + default n +if USING_EMBEDDED_DATABASE + source "$APP_DIR/lib/embedded_database/flashdb/Kconfig" +endif diff --git a/APP_Framework/lib/embedded_database/SConscript b/APP_Framework/lib/embedded_database/SConscript new file mode 100644 index 000000000..1d6ca7a10 --- /dev/null +++ b/APP_Framework/lib/embedded_database/SConscript @@ -0,0 +1,14 @@ +import os +Import('RTT_ROOT') +from building import * + +cwd = GetCurrentDir() +objs = [] +list = os.listdir(cwd) + +for d in list: + path = os.path.join(cwd, d) + if os.path.isfile(os.path.join(path, 'SConscript')): + objs = objs + SConscript(os.path.join(path, 'SConscript')) + +Return('objs') \ No newline at end of file diff --git a/APP_Framework/lib/embedded_database/flashdb/Kconfig b/APP_Framework/lib/embedded_database/flashdb/Kconfig new file mode 100644 index 000000000..f8f3ca90c --- /dev/null +++ b/APP_Framework/lib/embedded_database/flashdb/Kconfig @@ -0,0 +1,25 @@ +menuconfig USING_EMBEDDED_DATABASE_FLASHDB + bool "flashdb an ultra-lightweight embedded database " + default n + +if USING_EMBEDDED_DATABASE_FLASHDB + comment " fal io is not supported so far" + choice + prompt "select io mode" + default FDB_USING_FILE_POSIX_MODE + help + Select io mode + config FDB_USING_FILE_POSIX_MODE + bool "file posix io " + config FDB_USING_FILE_LIBC_MODE + bool "file libc io " + config FDB_USING_FAL_MODE + bool "flash fal io " + endchoice + config FDB_USING_KVDB + bool "Use key value database" + default n + config FDB_USING_TSDB + bool "Use time series database" + default n +endif diff --git a/APP_Framework/lib/embedded_database/flashdb/SConscript b/APP_Framework/lib/embedded_database/flashdb/SConscript new file mode 100644 index 000000000..ef3414fd8 --- /dev/null +++ b/APP_Framework/lib/embedded_database/flashdb/SConscript @@ -0,0 +1,10 @@ +from building import * +import os + +cwd = GetCurrentDir() + +src = Glob('*.c') + +group = DefineGroup('embedded database', src, depend = ['USING_EMBEDDED_DATABASE_FLASHDB'], CPPPATH = [cwd]) + +Return('group') diff --git a/APP_Framework/lib/embedded_database/flashdb/fdb.c b/APP_Framework/lib/embedded_database/flashdb/fdb.c new file mode 100644 index 000000000..f2208277f --- /dev/null +++ b/APP_Framework/lib/embedded_database/flashdb/fdb.c @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2020, Armink, + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief Initialize interface. + * + * Some initialize interface for this library. + */ + +#include +#include +#include + +#define FDB_LOG_TAG "" + +#if !defined(FDB_USING_FAL_MODE) && !defined(FDB_USING_FILE_MODE) +#error "Please defined the FDB_USING_FAL_MODE or FDB_USING_FILE_MODE macro" +#endif + +fdb_err_t _fdb_init_ex(fdb_db_t db, const char *name, const char *path, fdb_db_type type, void *user_data) +{ + FDB_ASSERT(db); + FDB_ASSERT(name); + FDB_ASSERT(path); + + if (db->init_ok) { + return FDB_NO_ERR; + } + + db->name = name; + db->type = type; + db->user_data = user_data; + + if (db->file_mode) { +#ifdef FDB_USING_FILE_MODE + /* must set when using file mode */ + FDB_ASSERT(db->sec_size != 0); + FDB_ASSERT(db->max_size != 0); +#ifdef FDB_USING_FILE_POSIX_MODE + db->cur_file = -1; +#else + db->cur_file = 0; +#endif + db->storage.dir = path; + FDB_ASSERT(strlen(path) != 0) +#endif + } else { +#ifdef FDB_USING_FAL_MODE + size_t block_size; + + /* FAL (Flash Abstraction Layer) initialization */ + fal_init(); + /* check the flash partition */ + if ((db->storage.part = fal_partition_find(path)) == NULL) { + FDB_INFO("Error: Partition (%s) not found.\n", path); + return FDB_PART_NOT_FOUND; + } + + block_size = fal_flash_device_find(db->storage.part->flash_name)->blk_size; + if (db->sec_size == 0) { + db->sec_size = block_size; + } else { + /* must be aligned with block size */ + FDB_ASSERT(db->sec_size % block_size == 0); + } + + db->max_size = db->storage.part->len; +#endif /* FDB_USING_FAL_MODE */ + } + + /* must align with sector size */ + FDB_ASSERT(db->max_size % db->sec_size == 0); + /* must have more than or equal 2 sector */ + FDB_ASSERT(db->max_size / db->sec_size >= 2); + + return FDB_NO_ERR; +} + +void _fdb_init_finish(fdb_db_t db, fdb_err_t result) +{ + static bool log_is_show = false; + if (result == FDB_NO_ERR) { + db->init_ok = true; + if (!log_is_show) { + FDB_INFO("FlashDB V%s is initialize success.\n", FDB_SW_VERSION); + FDB_INFO("You can get the latest version on https://github.com/armink/FlashDB .\n"); + log_is_show = true; + } + } else if (!db->not_formatable) { + FDB_INFO("Error: %s (%s) is initialize fail (%d).\n", db->type == FDB_DB_TYPE_KV ? "KVDB" : "TSDB", + db->name, (int)result); + } +} + +void _fdb_deinit(fdb_db_t db) +{ + FDB_ASSERT(db); + + if (db->init_ok) { +#ifdef FDB_USING_FILE_MODE +#ifdef FDB_USING_FILE_POSIX_MODE + if (db->cur_file > 0) { +#if !defined(_MSC_VER) +#include +#endif + close(db->cur_file); + } +#else + if (db->cur_file != 0) { + fclose(db->cur_file); + } +#endif /* FDB_USING_FILE_POSIX_MODE */ +#endif /* FDB_USING_FILE_MODE */ + } + + db->init_ok = false; +} diff --git a/APP_Framework/lib/embedded_database/flashdb/fdb_cfg.h b/APP_Framework/lib/embedded_database/flashdb/fdb_cfg.h new file mode 100644 index 000000000..74421a4bf --- /dev/null +++ b/APP_Framework/lib/embedded_database/flashdb/fdb_cfg.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2020, Armink, + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief configuration file + */ + +#ifndef _FDB_CFG_H_ +#define _FDB_CFG_H_ + +#ifdef FDB_USING_FAL_MODE +/* the flash write granularity, unit: bit + * only support 1(nor flash)/ 8(stm32f2/f4)/ 32(stm32f1) */ +#define FDB_WRITE_GRAN /* @note you must define it for a value */ +#endif + +/* Using file storage mode by LIBC file API, like fopen/fread/fwrte/fclose */ +/* #define FDB_USING_FILE_LIBC_MODE */ + +/* Using file storage mode by POSIX file API, like open/read/write/close */ +/* #define FDB_USING_FILE_POSIX_MODE */ + +/* MCU Endian Configuration, default is Little Endian Order. */ +/* #define FDB_BIG_ENDIAN */ + +/* log print macro. default EF_PRINT macro is printf() */ +/* #define FDB_PRINT(...) my_printf(__VA_ARGS__) */ + +/* print debug information */ +#define FDB_DEBUG_ENABLE + +#endif /* _FDB_CFG_H_ */ diff --git a/APP_Framework/lib/embedded_database/flashdb/fdb_def.h b/APP_Framework/lib/embedded_database/flashdb/fdb_def.h new file mode 100644 index 000000000..8c2ab8bc7 --- /dev/null +++ b/APP_Framework/lib/embedded_database/flashdb/fdb_def.h @@ -0,0 +1,340 @@ +/* + * Copyright (c) 2020, Armink, + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief Public definition. + */ + +#ifndef _FDB_DEF_H_ +#define _FDB_DEF_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* software version number */ +#define FDB_SW_VERSION "1.1.0" +#define FDB_SW_VERSION_NUM 0x10100 + +/* the KV max name length must less then it */ +#ifndef FDB_KV_NAME_MAX +#define FDB_KV_NAME_MAX 64 +#endif + +/* the KV cache table size, it will improve KV search speed when using cache */ +#ifndef FDB_KV_CACHE_TABLE_SIZE +#define FDB_KV_CACHE_TABLE_SIZE 64 +#endif + +/* the sector cache table size, it will improve KV save speed when using cache */ +#ifndef FDB_SECTOR_CACHE_TABLE_SIZE +#define FDB_SECTOR_CACHE_TABLE_SIZE 4 +#endif + +#if (FDB_KV_CACHE_TABLE_SIZE > 0) && (FDB_SECTOR_CACHE_TABLE_SIZE > 0) +#define FDB_KV_USING_CACHE +#endif + +#if defined(FDB_USING_FILE_LIBC_MODE) || defined(FDB_USING_FILE_POSIX_MODE) +#define FDB_USING_FILE_MODE +#endif + +#ifndef FDB_WRITE_GRAN +#define FDB_WRITE_GRAN 1 +#endif + +/* log function. default FDB_PRINT macro is printf() */ +#ifndef FDB_PRINT +#define FDB_PRINT(...) printf(__VA_ARGS__) +#endif +#define FDB_LOG_PREFIX1() FDB_PRINT("[FlashDB]" FDB_LOG_TAG) +#define FDB_LOG_PREFIX2() FDB_PRINT(" ") +#define FDB_LOG_PREFIX() FDB_LOG_PREFIX1();FDB_LOG_PREFIX2() +#ifdef FDB_DEBUG_ENABLE +#define FDB_DEBUG(...) FDB_LOG_PREFIX();FDB_PRINT("(%s:%d) ", __FILE__, __LINE__);FDB_PRINT(__VA_ARGS__) +#else +#define FDB_DEBUG(...) +#endif +/* routine print function. Must be implement by user. */ +#define FDB_INFO(...) FDB_LOG_PREFIX();FDB_PRINT(__VA_ARGS__) +/* assert for developer. */ +#define FDB_ASSERT(EXPR) \ +if (!(EXPR)) \ +{ \ + FDB_DEBUG("(%s) has assert failed at %s.\n", #EXPR, __FUNCTION__); \ + while (1); \ +} + +#define FDB_KVDB_CTRL_SET_SEC_SIZE 0x00 /**< set sector size control command, this change MUST before database initialization */ +#define FDB_KVDB_CTRL_GET_SEC_SIZE 0x01 /**< get sector size control command */ +#define FDB_KVDB_CTRL_SET_LOCK 0x02 /**< set lock function control command */ +#define FDB_KVDB_CTRL_SET_UNLOCK 0x03 /**< set unlock function control command */ +#define FDB_KVDB_CTRL_SET_FILE_MODE 0x09 /**< set file mode control command, this change MUST before database initialization */ +#define FDB_KVDB_CTRL_SET_MAX_SIZE 0x0A /**< set database max size in file mode control command, this change MUST before database initialization */ +#define FDB_KVDB_CTRL_SET_NOT_FORMAT 0x0B /**< set database NOT format mode control command, this change MUST before database initialization */ + +#define FDB_TSDB_CTRL_SET_SEC_SIZE 0x00 /**< set sector size control command, this change MUST before database initialization */ +#define FDB_TSDB_CTRL_GET_SEC_SIZE 0x01 /**< get sector size control command */ +#define FDB_TSDB_CTRL_SET_LOCK 0x02 /**< set lock function control command */ +#define FDB_TSDB_CTRL_SET_UNLOCK 0x03 /**< set unlock function control command */ +#define FDB_TSDB_CTRL_SET_ROLLOVER 0x04 /**< set rollover control command, this change MUST after database initialization */ +#define FDB_TSDB_CTRL_GET_ROLLOVER 0x05 /**< get rollover control command */ +#define FDB_TSDB_CTRL_GET_LAST_TIME 0x06 /**< get last save time control command */ +#define FDB_TSDB_CTRL_SET_FILE_MODE 0x09 /**< set file mode control command, this change MUST before database initialization */ +#define FDB_TSDB_CTRL_SET_MAX_SIZE 0x0A /**< set database max size in file mode control command, this change MUST before database initialization */ +#define FDB_TSDB_CTRL_SET_NOT_FORMAT 0x0B /**< set database NOT formatable mode control command, this change MUST before database initialization */ + +#ifdef FDB_USING_TIMESTAMP_64BIT + typedef int64_t fdb_time_t; +#else + typedef int32_t fdb_time_t; +#endif /* FDB_USING_TIMESTAMP_64BIT */ + +typedef fdb_time_t (*fdb_get_time)(void); + +struct fdb_default_kv_node { + char *key; + void *value; + size_t value_len; +}; + +struct fdb_default_kv { + struct fdb_default_kv_node *kvs; + size_t num; +}; + +/* error code */ +typedef enum { + FDB_NO_ERR, + FDB_ERASE_ERR, + FDB_READ_ERR, + FDB_WRITE_ERR, + FDB_PART_NOT_FOUND, + FDB_KV_NAME_ERR, + FDB_KV_NAME_EXIST, + FDB_SAVED_FULL, + FDB_INIT_FAILED, +} fdb_err_t; + +enum fdb_kv_status { + FDB_KV_UNUSED, + FDB_KV_PRE_WRITE, + FDB_KV_WRITE, + FDB_KV_PRE_DELETE, + FDB_KV_DELETED, + FDB_KV_ERR_HDR, +#define FDB_KV_STATUS_NUM 6 +}; +typedef enum fdb_kv_status fdb_kv_status_t; + +enum fdb_tsl_status { + FDB_TSL_UNUSED, + FDB_TSL_PRE_WRITE, + FDB_TSL_WRITE, + FDB_TSL_USER_STATUS1, + FDB_TSL_DELETED, + FDB_TSL_USER_STATUS2, +#define FDB_TSL_STATUS_NUM 6 +}; +typedef enum fdb_tsl_status fdb_tsl_status_t; + +/* key-value node object */ +struct fdb_kv { + fdb_kv_status_t status; /**< node status, @see fdb_kv_status_t */ + bool crc_is_ok; /**< node CRC32 check is OK */ + uint8_t name_len; /**< name length */ + uint32_t magic; /**< magic word(`K`, `V`, `4`, `0`) */ + uint32_t len; /**< node total length (header + name + value), must align by FDB_WRITE_GRAN */ + uint32_t value_len; /**< value length */ + char name[FDB_KV_NAME_MAX]; /**< name */ + struct { + uint32_t start; /**< node start address */ + uint32_t value; /**< value start address */ + } addr; +}; +typedef struct fdb_kv *fdb_kv_t; + +struct fdb_kv_iterator { + struct fdb_kv curr_kv; /**< Current KV we get from the iterator */ + uint32_t iterated_cnt; /**< How many KVs have we iterated already */ + size_t iterated_obj_bytes; /**< Total storage size of KVs we have iterated. */ + size_t iterated_value_bytes; /**< Total value size of KVs we have iterated. */ + uint32_t sector_addr; /**< Current sector address we're iterating. DO NOT touch it. */ +}; +typedef struct fdb_kv_iterator *fdb_kv_iterator_t; + +/* time series log node object */ +struct fdb_tsl { + fdb_tsl_status_t status; /**< node status, @see fdb_log_status_t */ + fdb_time_t time; /**< node timestamp */ + uint32_t log_len; /**< log length, must align by FDB_WRITE_GRAN */ + struct { + uint32_t index; /**< node index address */ + uint32_t log; /**< log data address */ + } addr; +}; +typedef struct fdb_tsl *fdb_tsl_t; +typedef bool (*fdb_tsl_cb)(fdb_tsl_t tsl, void *arg); + +typedef enum { + FDB_DB_TYPE_KV, + FDB_DB_TYPE_TS, +} fdb_db_type; + +/* the flash sector store status */ +enum fdb_sector_store_status { + FDB_SECTOR_STORE_UNUSED, + FDB_SECTOR_STORE_EMPTY, + FDB_SECTOR_STORE_USING, + FDB_SECTOR_STORE_FULL, +#define FDB_SECTOR_STORE_STATUS_NUM 4 +}; +typedef enum fdb_sector_store_status fdb_sector_store_status_t; + +/* the flash sector dirty status */ +enum fdb_sector_dirty_status { + FDB_SECTOR_DIRTY_UNUSED, + FDB_SECTOR_DIRTY_FALSE, + FDB_SECTOR_DIRTY_TRUE, + FDB_SECTOR_DIRTY_GC, +#define FDB_SECTOR_DIRTY_STATUS_NUM 4 +}; +typedef enum fdb_sector_dirty_status fdb_sector_dirty_status_t; + +/* KVDB section information */ +struct kvdb_sec_info { + bool check_ok; /**< sector header check is OK */ + struct { + fdb_sector_store_status_t store; /**< sector store status @see fdb_sector_store_status_t */ + fdb_sector_dirty_status_t dirty; /**< sector dirty status @see sector_dirty_status_t */ + } status; + uint32_t addr; /**< sector start address */ + uint32_t magic; /**< magic word(`E`, `F`, `4`, `0`) */ + uint32_t combined; /**< the combined next sector number, 0xFFFFFFFF: not combined */ + size_t remain; /**< remain size */ + uint32_t empty_kv; /**< the next empty KV node start address */ +}; +typedef struct kvdb_sec_info *kv_sec_info_t; + +/* TSDB section information */ +struct tsdb_sec_info { + bool check_ok; /**< sector header check is OK */ + fdb_sector_store_status_t status; /**< sector store status @see fdb_sector_store_status_t */ + uint32_t addr; /**< sector start address */ + uint32_t magic; /**< magic word(`T`, `S`, `L`, `0`) */ + fdb_time_t start_time; /**< the first start node's timestamp, 0xFFFFFFFF: unused */ + fdb_time_t end_time; /**< the last end node's timestamp, 0xFFFFFFFF: unused */ + uint32_t end_idx; /**< the last end node's index, 0xFFFFFFFF: unused */ + fdb_tsl_status_t end_info_stat[2]; /**< the last end node's info status */ + size_t remain; /**< remain size */ + uint32_t empty_idx; /**< the next empty node index address */ + uint32_t empty_data; /**< the next empty node's data end address */ +}; +typedef struct tsdb_sec_info *tsdb_sec_info_t; + +struct kv_cache_node { + uint16_t name_crc; /**< KV name's CRC32 low 16bit value */ + uint16_t active; /**< KV node access active degree */ + uint32_t addr; /**< KV node address */ +}; +typedef struct kv_cache_node *kv_cache_node_t; + +struct sector_cache_node { + uint32_t addr; /**< sector start address */ + uint32_t empty_addr; /**< sector empty address */ +}; +typedef struct sector_cache_node *sector_cache_node_t; + +/* database structure */ +typedef struct fdb_db *fdb_db_t; +struct fdb_db { + const char *name; /**< database name */ + fdb_db_type type; /**< database type */ + union { +#ifdef FDB_USING_FAL_MODE + const struct fal_partition *part; /**< flash partition for saving database */ +#endif +#ifdef FDB_USING_FILE_MODE + const char *dir; /**< directory path for saving database */ +#endif + } storage; + uint32_t sec_size; /**< flash section size. It's a multiple of block size */ + uint32_t max_size; /**< database max size. It's a multiple of section size */ + bool init_ok; /**< initialized successfully */ + bool file_mode; /**< is file mode, default is false */ + bool not_formatable; /**< is can NOT be formated mode, default is false */ +#ifdef FDB_USING_FILE_MODE +#if defined(FDB_USING_FILE_POSIX_MODE) + int cur_file; /**< current file object */ +#elif defined(FDB_USING_FILE_LIBC_MODE) + FILE *cur_file; /**< current file object */ +#endif + uint32_t cur_sec; /**< current operate sector address */ +#endif + void (*lock)(fdb_db_t db); /**< lock the database operate */ + void (*unlock)(fdb_db_t db); /**< unlock the database operate */ + + void *user_data; +}; + +/* KVDB structure */ +struct fdb_kvdb { + struct fdb_db parent; /**< inherit from fdb_db */ + struct fdb_default_kv default_kvs; /**< default KV */ + bool gc_request; /**< request a GC check */ + bool in_recovery_check; /**< is in recovery check status when first reboot */ + struct fdb_kv cur_kv; + struct kvdb_sec_info cur_sector; + bool last_is_complete_del; + +#ifdef FDB_KV_USING_CACHE + /* KV cache table */ + struct kv_cache_node kv_cache_table[FDB_KV_CACHE_TABLE_SIZE]; + /* sector cache table, it caching the sector info which status is current using */ + struct sector_cache_node sector_cache_table[FDB_SECTOR_CACHE_TABLE_SIZE]; +#endif /* FDB_KV_USING_CACHE */ + +#ifdef FDB_KV_AUTO_UPDATE + uint32_t ver_num; /**< setting version number for update */ +#endif + + void *user_data; +}; +typedef struct fdb_kvdb *fdb_kvdb_t; + +/* TSDB structure */ +struct fdb_tsdb { + struct fdb_db parent; /**< inherit from fdb_db */ + struct tsdb_sec_info cur_sec; /**< current using sector */ + fdb_time_t last_time; /**< last TSL timestamp */ + fdb_get_time get_time; /**< the current timestamp get function */ + size_t max_len; /**< the maximum length of each log */ + uint32_t oldest_addr; /**< the oldest sector start address */ + bool rollover; /**< the oldest data will rollover by newest data, default is true */ + + void *user_data; +}; +typedef struct fdb_tsdb *fdb_tsdb_t; + +/* blob structure */ +struct fdb_blob { + void *buf; /**< blob data buffer */ + size_t size; /**< blob data buffer size */ + struct { + uint32_t meta_addr; /**< saved KV or TSL index address */ + uint32_t addr; /**< blob data saved address */ + size_t len; /**< blob data saved length */ + } saved; +}; +typedef struct fdb_blob *fdb_blob_t; + +#ifdef __cplusplus +} +#endif + +#endif /* _FDB_DEF_H_ */ + diff --git a/APP_Framework/lib/embedded_database/flashdb/fdb_file.c b/APP_Framework/lib/embedded_database/flashdb/fdb_file.c new file mode 100644 index 000000000..7744ca9db --- /dev/null +++ b/APP_Framework/lib/embedded_database/flashdb/fdb_file.c @@ -0,0 +1,221 @@ +/* + * Copyright (c) 2020, Armink, + * Copyright (c) 2020, enkiller, <462747508@qq.com> + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +#define FDB_LOG_TAG "[file]" + +#ifdef FDB_USING_FILE_MODE + +#define DB_PATH_MAX 256 + +static void get_db_file_path(fdb_db_t db, uint32_t addr, char *path, size_t size) +{ +#define DB_NAME_MAX 8 + + /* from db_name.fdb.0 to db_name.fdb.n */ + char file_name[DB_NAME_MAX + 4 + 10]; + uint32_t sec_addr = FDB_ALIGN_DOWN(addr, db->sec_size); + int index = sec_addr / db->sec_size; + + snprintf(file_name, sizeof(file_name), "%.*s.fdb.%d", DB_NAME_MAX, db->name, index); + if (strlen(db->storage.dir) + 1 + strlen(file_name) >= size) { + /* path is too long */ + FDB_ASSERT(0) + } + snprintf(path, size, "%s/%s", db->storage.dir, file_name); +} + +#if defined(FDB_USING_FILE_POSIX_MODE) +#include +#include +#include +#if !defined(_MSC_VER) +#include +#endif + +static int open_db_file(fdb_db_t db, uint32_t addr, bool clean) +{ + uint32_t sec_addr = FDB_ALIGN_DOWN(addr, db->sec_size); + int fd = db->cur_file; + char path[DB_PATH_MAX]; + + if (sec_addr != db->cur_sec || fd <= 0 || clean) { + get_db_file_path(db, addr, path, DB_PATH_MAX); + + if (fd > 0) { + close(fd); + fd = -1; + } + if (clean) { + /* clean the old file */ + fd = open(path, O_RDWR | O_CREAT | O_TRUNC, 0777); + if (fd <= 0) { + FDB_INFO("Error: open (%s) file failed.\n", path); + } + else { + close(fd); + fd = -1; + } + } + /* open the database file */ + fd = open(path, O_RDWR, 0777); + db->cur_sec = sec_addr; + } + db->cur_file = fd; + + return db->cur_file; +} + +fdb_err_t _fdb_file_read(fdb_db_t db, uint32_t addr, void *buf, size_t size) +{ + fdb_err_t result = FDB_NO_ERR; + int fd = open_db_file(db, addr, false); + if (fd > 0) { + addr = addr % db->sec_size; + lseek(fd, addr, SEEK_SET); + read(fd, buf, size); + } else { + result = FDB_READ_ERR; + } + return result; +} + +fdb_err_t _fdb_file_write(fdb_db_t db, uint32_t addr, const void *buf, size_t size, bool sync) +{ + fdb_err_t result = FDB_NO_ERR; + int fd = open_db_file(db, addr, false); + if (fd > 0) { + addr = addr % db->sec_size; + lseek(fd, addr, SEEK_SET); + write(fd, buf, size); + if(sync) { + fsync(fd); + } + } else { + result = FDB_READ_ERR; + } + + return result; +} + +fdb_err_t _fdb_file_erase(fdb_db_t db, uint32_t addr, size_t size) +{ + fdb_err_t result = FDB_NO_ERR; + int fd = open_db_file(db, addr, true); + if (fd > 0) { +#define BUF_SIZE 32 + uint8_t buf[BUF_SIZE]; + size_t i; + lseek(fd, 0, SEEK_SET); + for (i = 0; i * BUF_SIZE < size; i++) + { + memset(buf, 0xFF, BUF_SIZE); + write(fd, buf, BUF_SIZE); + } + memset(buf, 0xFF, BUF_SIZE); + write(fd, buf, size - i * BUF_SIZE); + fsync(fd); + } else { + result = FDB_ERASE_ERR; + } + return result; +} +#elif defined(FDB_USING_FILE_LIBC_MODE) +static FILE *open_db_file(fdb_db_t db, uint32_t addr, bool clean) +{ + uint32_t sec_addr = FDB_ALIGN_DOWN(addr, db->sec_size); + + if (sec_addr != db->cur_sec || db->cur_file == NULL || clean) { + char path[DB_PATH_MAX]; + + get_db_file_path(db, addr, path, DB_PATH_MAX); + + if (db->cur_file) { + fclose(db->cur_file); + } + + if (clean) { + /* clean the old file */ + db->cur_file = fopen(path, "wb+"); + if (db->cur_file == NULL) { + FDB_INFO("Error: open (%s) file failed.\n", path); + } else { + fclose(db->cur_file); + } + } + + /* open the database file */ + db->cur_file = fopen(path, "rb+"); + db->cur_sec = sec_addr; + } + + return db->cur_file; +} + +fdb_err_t _fdb_file_read(fdb_db_t db, uint32_t addr, void *buf, size_t size) +{ + fdb_err_t result = FDB_NO_ERR; + FILE *fp = open_db_file(db, addr, false); + if (fp) { + addr = addr % db->sec_size; + fseek(fp, addr, SEEK_SET); + fread(buf, size, 1, fp); + } else { + result = FDB_READ_ERR; + } + return result; +} + +fdb_err_t _fdb_file_write(fdb_db_t db, uint32_t addr, const void *buf, size_t size, bool sync) +{ + fdb_err_t result = FDB_NO_ERR; + FILE *fp = open_db_file(db, addr, false); + if (fp) { + addr = addr % db->sec_size; + fseek(fp, addr, SEEK_SET); + fwrite(buf, size, 1, fp); + if(sync) { + fflush(fp); + } + } else { + result = FDB_READ_ERR; + } + + return result; +} + +fdb_err_t _fdb_file_erase(fdb_db_t db, uint32_t addr, size_t size) +{ + fdb_err_t result = FDB_NO_ERR; + + FILE *fp = open_db_file(db, addr, true); + if (fp != NULL) { +#define BUF_SIZE 32 + uint8_t buf[BUF_SIZE]; + size_t i; + fseek(fp, 0, SEEK_SET); + for (i = 0; i * BUF_SIZE < size; i++) + { + memset(buf, 0xFF, BUF_SIZE); + fwrite(buf, BUF_SIZE, 1, fp); + } + memset(buf, 0xFF, BUF_SIZE); + fwrite(buf, size - i * BUF_SIZE, 1, fp); + fflush(fp); + } else { + result = FDB_ERASE_ERR; + } + return result; +} +#endif /* defined(FDB_USING_FILE_LIBC_MODE) */ + +#endif /* FDB_USING_FILE_MODE */ + diff --git a/APP_Framework/lib/embedded_database/flashdb/fdb_kvdb.c b/APP_Framework/lib/embedded_database/flashdb/fdb_kvdb.c new file mode 100644 index 000000000..4faa63c65 --- /dev/null +++ b/APP_Framework/lib/embedded_database/flashdb/fdb_kvdb.c @@ -0,0 +1,1734 @@ +/* + * Copyright (c) 2020, Armink, + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief KVDB feature. + * + * Key-Value Database feature implement source file. + */ + +#include +#include +#include +#include + +#define FDB_LOG_TAG "[kv]" +/* rewrite log prefix */ +#undef FDB_LOG_PREFIX2 +#define FDB_LOG_PREFIX2() FDB_PRINT("[%s] ", db_name(db)) + +#if defined(FDB_USING_KVDB) + +#ifndef FDB_WRITE_GRAN +#error "Please configure flash write granularity (in fdb_cfg.h)" +#endif + +#if FDB_WRITE_GRAN != 1 && FDB_WRITE_GRAN != 8 && FDB_WRITE_GRAN != 32 +#error "the write gran can be only setting as 1, 8 and 32" +#endif + +/* magic word(`F`, `D`, `B`, `1`) */ +#define SECTOR_MAGIC_WORD 0x30424446 +/* magic word(`K`, `V`, `0`, `0`) */ +#define KV_MAGIC_WORD 0x3030564B + +/* the sector remain threshold before full status */ +#ifndef FDB_SEC_REMAIN_THRESHOLD +#define FDB_SEC_REMAIN_THRESHOLD (KV_HDR_DATA_SIZE + FDB_KV_NAME_MAX) +#endif + +/* the total remain empty sector threshold before GC */ +#ifndef FDB_GC_EMPTY_SEC_THRESHOLD +#define FDB_GC_EMPTY_SEC_THRESHOLD 1 +#endif + +/* the string KV value buffer size for legacy fdb_get_kv(db, ) function */ +#ifndef FDB_STR_KV_VALUE_MAX_SIZE +#define FDB_STR_KV_VALUE_MAX_SIZE 128 +#endif + +#if FDB_KV_CACHE_TABLE_SIZE > 0xFFFF +#error "The KV cache table size must less than 0xFFFF" +#endif + +/* the sector is not combined value */ +#define SECTOR_NOT_COMBINED 0xFFFFFFFF +/* the next address is get failed */ +#define FAILED_ADDR 0xFFFFFFFF + +#define KV_STATUS_TABLE_SIZE FDB_STATUS_TABLE_SIZE(FDB_KV_STATUS_NUM) + +#define SECTOR_NUM (db_max_size(db) / db_sec_size(db)) + +#define SECTOR_HDR_DATA_SIZE (FDB_WG_ALIGN(sizeof(struct sector_hdr_data))) +#define SECTOR_DIRTY_OFFSET ((unsigned long)(&((struct sector_hdr_data *)0)->status_table.dirty)) +#define KV_HDR_DATA_SIZE (FDB_WG_ALIGN(sizeof(struct kv_hdr_data))) +#define KV_MAGIC_OFFSET ((unsigned long)(&((struct kv_hdr_data *)0)->magic)) +#define KV_LEN_OFFSET ((unsigned long)(&((struct kv_hdr_data *)0)->len)) +#define KV_NAME_LEN_OFFSET ((unsigned long)(&((struct kv_hdr_data *)0)->name_len)) + +#define db_name(db) (((fdb_db_t)db)->name) +#define db_init_ok(db) (((fdb_db_t)db)->init_ok) +#define db_sec_size(db) (((fdb_db_t)db)->sec_size) +#define db_max_size(db) (((fdb_db_t)db)->max_size) + +#define db_lock(db) \ + do { \ + if (((fdb_db_t)db)->lock) ((fdb_db_t)db)->lock((fdb_db_t)db); \ + } while(0); + +#define db_unlock(db) \ + do { \ + if (((fdb_db_t)db)->unlock) ((fdb_db_t)db)->unlock((fdb_db_t)db); \ + } while(0); + +#define VER_NUM_KV_NAME "__ver_num__" + +struct sector_hdr_data { + struct { + uint8_t store[FDB_STORE_STATUS_TABLE_SIZE]; /**< sector store status @see fdb_sector_store_status_t */ + uint8_t dirty[FDB_DIRTY_STATUS_TABLE_SIZE]; /**< sector dirty status @see fdb_sector_dirty_status_t */ + } status_table; + uint32_t magic; /**< magic word(`E`, `F`, `4`, `0`) */ + uint32_t combined; /**< the combined next sector number, 0xFFFFFFFF: not combined */ + uint32_t reserved; +}; +typedef struct sector_hdr_data *sector_hdr_data_t; + +struct kv_hdr_data { + uint8_t status_table[KV_STATUS_TABLE_SIZE]; /**< KV node status, @see fdb_kv_status_t */ + uint32_t magic; /**< magic word(`K`, `V`, `4`, `0`) */ + uint32_t len; /**< KV node total length (header + name + value), must align by FDB_WRITE_GRAN */ + uint32_t crc32; /**< KV node crc32(name_len + data_len + name + value) */ + uint8_t name_len; /**< name length */ + uint32_t value_len; /**< value length */ +}; +typedef struct kv_hdr_data *kv_hdr_data_t; + +struct alloc_kv_cb_args { + fdb_kvdb_t db; + size_t kv_size; + uint32_t *empty_kv; +}; + +static void gc_collect(fdb_kvdb_t db); + +#ifdef FDB_KV_USING_CACHE +/* + * It's only caching the current using status sector's empty_addr + */ +static void update_sector_cache(fdb_kvdb_t db, uint32_t sec_addr, uint32_t empty_addr) +{ + size_t i, empty_index = FDB_SECTOR_CACHE_TABLE_SIZE; + + for (i = 0; i < FDB_SECTOR_CACHE_TABLE_SIZE; i++) { + if ((empty_addr > sec_addr) && (empty_addr < sec_addr + db_sec_size(db))) { + /* update the sector empty_addr in cache */ + if (db->sector_cache_table[i].addr == sec_addr) { + db->sector_cache_table[i].addr = sec_addr; + db->sector_cache_table[i].empty_addr = empty_addr; + return; + } else if ((db->sector_cache_table[i].addr == FDB_DATA_UNUSED) && (empty_index == FDB_SECTOR_CACHE_TABLE_SIZE)) { + empty_index = i; + } + } else if (db->sector_cache_table[i].addr == sec_addr) { + /* delete the sector which status is not current using */ + db->sector_cache_table[i].addr = FDB_DATA_UNUSED; + return; + } + } + /* add the sector empty_addr to cache */ + if (empty_index < FDB_SECTOR_CACHE_TABLE_SIZE) { + db->sector_cache_table[empty_index].addr = sec_addr; + db->sector_cache_table[empty_index].empty_addr = empty_addr; + } +} + +/* + * Get sector info from cache. It's return true when cache is hit. + */ +static bool get_sector_from_cache(fdb_kvdb_t db, uint32_t sec_addr, uint32_t *empty_addr) +{ + size_t i; + + for (i = 0; i < FDB_SECTOR_CACHE_TABLE_SIZE; i++) { + if (db->sector_cache_table[i].addr == sec_addr) { + if (empty_addr) { + *empty_addr = db->sector_cache_table[i].empty_addr; + } + return true; + } + } + + return false; +} + +static void update_kv_cache(fdb_kvdb_t db, const char *name, size_t name_len, uint32_t addr) +{ + size_t i, empty_index = FDB_KV_CACHE_TABLE_SIZE, min_activity_index = FDB_KV_CACHE_TABLE_SIZE; + uint16_t name_crc = (uint16_t) (fdb_calc_crc32(0, name, name_len) >> 16), min_activity = 0xFFFF; + + for (i = 0; i < FDB_KV_CACHE_TABLE_SIZE; i++) { + if (addr != FDB_DATA_UNUSED) { + /* update the KV address in cache */ + if (db->kv_cache_table[i].name_crc == name_crc) { + db->kv_cache_table[i].addr = addr; + return; + } else if ((db->kv_cache_table[i].addr == FDB_DATA_UNUSED) && (empty_index == FDB_KV_CACHE_TABLE_SIZE)) { + empty_index = i; + } else if (db->kv_cache_table[i].addr != FDB_DATA_UNUSED) { + if (db->kv_cache_table[i].active > 0) { + db->kv_cache_table[i].active--; + } + if (db->kv_cache_table[i].active < min_activity) { + min_activity_index = i; + min_activity = db->kv_cache_table[i].active; + } + } + } else if (db->kv_cache_table[i].name_crc == name_crc) { + /* delete the KV */ + db->kv_cache_table[i].addr = FDB_DATA_UNUSED; + db->kv_cache_table[i].active = 0; + return; + } + } + /* add the KV to cache, using LRU (Least Recently Used) like algorithm */ + if (empty_index < FDB_KV_CACHE_TABLE_SIZE) { + db->kv_cache_table[empty_index].addr = addr; + db->kv_cache_table[empty_index].name_crc = name_crc; + db->kv_cache_table[empty_index].active = 0; + } else if (min_activity_index < FDB_KV_CACHE_TABLE_SIZE) { + db->kv_cache_table[min_activity_index].addr = addr; + db->kv_cache_table[min_activity_index].name_crc = name_crc; + db->kv_cache_table[min_activity_index].active = 0; + } +} + +/* + * Get KV info from cache. It's return true when cache is hit. + */ +static bool get_kv_from_cache(fdb_kvdb_t db, const char *name, size_t name_len, uint32_t *addr) +{ + size_t i; + uint16_t name_crc = (uint16_t) (fdb_calc_crc32(0, name, name_len) >> 16); + + for (i = 0; i < FDB_KV_CACHE_TABLE_SIZE; i++) { + if ((db->kv_cache_table[i].addr != FDB_DATA_UNUSED) && (db->kv_cache_table[i].name_crc == name_crc)) { + char saved_name[FDB_KV_NAME_MAX]; + /* read the KV name in flash */ + _fdb_flash_read((fdb_db_t)db, db->kv_cache_table[i].addr + KV_HDR_DATA_SIZE, (uint32_t *) saved_name, FDB_KV_NAME_MAX); + if (!strncmp(name, saved_name, name_len)) { + *addr = db->kv_cache_table[i].addr; + if (db->kv_cache_table[i].active >= 0xFFFF - FDB_KV_CACHE_TABLE_SIZE) { + db->kv_cache_table[i].active = 0xFFFF; + } else { + db->kv_cache_table[i].active += FDB_KV_CACHE_TABLE_SIZE; + } + return true; + } + } + } + + return false; +} +#endif /* FDB_KV_USING_CACHE */ + +/* + * find the next KV address by magic word on the flash + */ +static uint32_t find_next_kv_addr(fdb_kvdb_t db, uint32_t start, uint32_t end) +{ + uint8_t buf[32]; + uint32_t start_bak = start, i; + uint32_t magic; + +#ifdef FDB_KV_USING_CACHE + uint32_t empty_kv; + + if (get_sector_from_cache(db, FDB_ALIGN_DOWN(start, db_sec_size(db)), &empty_kv) && start == empty_kv) { + return FAILED_ADDR; + } +#endif /* FDB_KV_USING_CACHE */ + + for (; start < end && start + sizeof(buf) < end; start += (sizeof(buf) - sizeof(uint32_t))) { + _fdb_flash_read((fdb_db_t)db, start, (uint32_t *) buf, sizeof(buf)); + for (i = 0; i < sizeof(buf) - sizeof(uint32_t) && start + i < end; i++) { +#ifndef FDB_BIG_ENDIAN /* Little Endian Order */ + magic = buf[i] + (buf[i + 1] << 8) + (buf[i + 2] << 16) + (buf[i + 3] << 24); +#else /* Big Endian Order */ + magic = buf[i + 3] + (buf[i + 2] << 8) + (buf[i + 1] << 16) + (buf[i] << 24); +#endif + if (magic == KV_MAGIC_WORD && (start + i - KV_MAGIC_OFFSET) >= start_bak) { + return start + i - KV_MAGIC_OFFSET; + } + } + } + + return FAILED_ADDR; +} + +static uint32_t get_next_kv_addr(fdb_kvdb_t db, kv_sec_info_t sector, fdb_kv_t pre_kv) +{ + uint32_t addr = FAILED_ADDR; + + if (sector->status.store == FDB_SECTOR_STORE_EMPTY) { + return FAILED_ADDR; + } + + if (pre_kv->addr.start == FAILED_ADDR) { + /* the first KV address */ + addr = sector->addr + SECTOR_HDR_DATA_SIZE; + } else { + if (pre_kv->addr.start <= sector->addr + db_sec_size(db)) { + if (pre_kv->crc_is_ok) { + addr = pre_kv->addr.start + pre_kv->len; + } else { + /* when pre_kv CRC check failed, maybe the flash has error data + * find_next_kv_addr after pre_kv address */ + addr = pre_kv->addr.start + FDB_WG_ALIGN(1); + } + /* check and find next KV address */ + addr = find_next_kv_addr(db, addr, sector->addr + db_sec_size(db) - SECTOR_HDR_DATA_SIZE); + + if (addr > sector->addr + db_sec_size(db) || pre_kv->len == 0) { + //TODO 扇区连续模式 + return FAILED_ADDR; + } + } else { + /* no KV */ + return FAILED_ADDR; + } + } + + return addr; +} + +static fdb_err_t read_kv(fdb_kvdb_t db, fdb_kv_t kv) +{ + struct kv_hdr_data kv_hdr; + uint8_t buf[32]; + uint32_t calc_crc32 = 0, crc_data_len, kv_name_addr; + fdb_err_t result = FDB_NO_ERR; + size_t len, size; + /* read KV header raw data */ + _fdb_flash_read((fdb_db_t)db, kv->addr.start, (uint32_t *)&kv_hdr, sizeof(struct kv_hdr_data)); + kv->status = (fdb_kv_status_t) _fdb_get_status(kv_hdr.status_table, FDB_KV_STATUS_NUM); + kv->len = kv_hdr.len; + + if (kv->len == ~0UL || kv->len > db_max_size(db) || kv->len < KV_NAME_LEN_OFFSET) { + /* the KV length was not write, so reserved the info for current KV */ + kv->len = KV_HDR_DATA_SIZE; + if (kv->status != FDB_KV_ERR_HDR) { + kv->status = FDB_KV_ERR_HDR; + FDB_DEBUG("Error: The KV @0x%08" PRIX32 " length has an error.\n", kv->addr.start); + _fdb_write_status((fdb_db_t)db, kv->addr.start, kv_hdr.status_table, FDB_KV_STATUS_NUM, FDB_KV_ERR_HDR, true); + } + kv->crc_is_ok = false; + return FDB_READ_ERR; + } else if (kv->len > db_sec_size(db) - SECTOR_HDR_DATA_SIZE && kv->len < db_max_size(db)) { + //TODO 扇区连续模式,或者写入长度没有写入完整 + FDB_ASSERT(0); + } + + /* CRC32 data len(header.name_len + header.value_len + name + value) */ + crc_data_len = kv->len - KV_NAME_LEN_OFFSET; + /* calculate the CRC32 value */ + for (len = 0, size = 0; len < crc_data_len; len += size) { + if (len + sizeof(buf) < crc_data_len) { + size = sizeof(buf); + } else { + size = crc_data_len - len; + } + + _fdb_flash_read((fdb_db_t)db, kv->addr.start + KV_NAME_LEN_OFFSET + len, (uint32_t *) buf, FDB_WG_ALIGN(size)); + calc_crc32 = fdb_calc_crc32(calc_crc32, buf, size); + } + /* check CRC32 */ + if (calc_crc32 != kv_hdr.crc32) { + kv->crc_is_ok = false; + result = FDB_READ_ERR; + } else { + kv->crc_is_ok = true; + /* the name is behind aligned KV header */ + kv_name_addr = kv->addr.start + KV_HDR_DATA_SIZE; + _fdb_flash_read((fdb_db_t)db, kv_name_addr, (uint32_t *) kv->name, FDB_WG_ALIGN(kv_hdr.name_len)); + /* the value is behind aligned name */ + kv->addr.value = kv_name_addr + FDB_WG_ALIGN(kv_hdr.name_len); + kv->value_len = kv_hdr.value_len; + kv->name_len = kv_hdr.name_len; + if (kv_hdr.name_len >= sizeof(kv->name) / sizeof(kv->name[0])) { + kv_hdr.name_len = sizeof(kv->name) / sizeof(kv->name[0]) - 1; + } + kv->name[kv_hdr.name_len] = '\0'; + } + + return result; +} + +static fdb_err_t read_sector_info(fdb_kvdb_t db, uint32_t addr, kv_sec_info_t sector, bool traversal) +{ + fdb_err_t result = FDB_NO_ERR; + struct sector_hdr_data sec_hdr = { 0 }; + + FDB_ASSERT(addr % db_sec_size(db) == 0); + FDB_ASSERT(sector); + + /* read sector header raw data */ + _fdb_flash_read((fdb_db_t)db, addr, (uint32_t *)&sec_hdr, sizeof(struct sector_hdr_data)); + + sector->addr = addr; + sector->magic = sec_hdr.magic; + /* check magic word */ + if (sector->magic != SECTOR_MAGIC_WORD) { + sector->check_ok = false; + sector->combined = SECTOR_NOT_COMBINED; + return FDB_INIT_FAILED; + } + sector->check_ok = true; + /* get other sector info */ + sector->combined = sec_hdr.combined; + sector->status.store = (fdb_sector_store_status_t) _fdb_get_status(sec_hdr.status_table.store, FDB_SECTOR_STORE_STATUS_NUM); + sector->status.dirty = (fdb_sector_dirty_status_t) _fdb_get_status(sec_hdr.status_table.dirty, FDB_SECTOR_DIRTY_STATUS_NUM); + /* traversal all KV and calculate the remain space size */ + if (traversal) { + sector->remain = 0; + sector->empty_kv = sector->addr + SECTOR_HDR_DATA_SIZE; + if (sector->status.store == FDB_SECTOR_STORE_EMPTY) { + sector->remain = db_sec_size(db) - SECTOR_HDR_DATA_SIZE; + } else if (sector->status.store == FDB_SECTOR_STORE_USING) { + struct fdb_kv kv_obj; + +#ifdef FDB_KV_USING_CACHE + if (get_sector_from_cache(db, addr, §or->empty_kv)) { + sector->remain = db_sec_size(db) - (sector->empty_kv - sector->addr); + return result; + } +#endif /* FDB_KV_USING_CACHE */ + + sector->remain = db_sec_size(db) - SECTOR_HDR_DATA_SIZE; + kv_obj.addr.start = sector->addr + SECTOR_HDR_DATA_SIZE; + do { + + read_kv(db, &kv_obj); + if (!kv_obj.crc_is_ok) { + if (kv_obj.status != FDB_KV_PRE_WRITE && kv_obj.status != FDB_KV_ERR_HDR) { + FDB_INFO("Error: The KV (@0x%08" PRIX32 ") CRC32 check failed!\n", kv_obj.addr.start); + sector->remain = 0; + result = FDB_READ_ERR; + break; + } + } + sector->empty_kv += kv_obj.len; + sector->remain -= kv_obj.len; + } while ((kv_obj.addr.start = get_next_kv_addr(db, sector, &kv_obj)) != FAILED_ADDR); + /* check the empty KV address by read continue 0xFF on flash */ + { + uint32_t ff_addr; + + ff_addr = _fdb_continue_ff_addr((fdb_db_t)db, sector->empty_kv, sector->addr + db_sec_size(db)); + /* check the flash data is clean */ + if (sector->empty_kv != ff_addr) { + /* update the sector information */ + sector->empty_kv = ff_addr; + sector->remain = db_sec_size(db) - (ff_addr - sector->addr); + } + } + +#ifdef FDB_KV_USING_CACHE + update_sector_cache(db, sector->addr, sector->empty_kv); +#endif + } + } + + return result; +} + +static uint32_t get_next_sector_addr(fdb_kvdb_t db, kv_sec_info_t pre_sec) +{ + uint32_t next_addr; + + if (pre_sec->addr == FAILED_ADDR) { + /* the next sector is on the top of the database */ + return 0; + } else { + /* check KV sector combined */ + if (pre_sec->combined == SECTOR_NOT_COMBINED) { + next_addr = pre_sec->addr + db_sec_size(db); + } else { + next_addr = pre_sec->addr + pre_sec->combined * db_sec_size(db); + } + /* check range */ + if (next_addr < db_max_size(db)) { + return next_addr; + } else { + /* no sector */ + return FAILED_ADDR; + } + } +} + +static void kv_iterator(fdb_kvdb_t db, fdb_kv_t kv, void *arg1, void *arg2, + bool (*callback)(fdb_kv_t kv, void *arg1, void *arg2)) +{ + struct kvdb_sec_info sector; + uint32_t sec_addr; + + sec_addr = 0; + /* search all sectors */ + do { + if (read_sector_info(db, sec_addr, §or, false) != FDB_NO_ERR) { + continue; + } + if (callback == NULL) { + continue; + } + /* sector has KV */ + if (sector.status.store == FDB_SECTOR_STORE_USING || sector.status.store == FDB_SECTOR_STORE_FULL) { + kv->addr.start = sector.addr + SECTOR_HDR_DATA_SIZE; + /* search all KV */ + do { + read_kv(db, kv); + /* iterator is interrupted when callback return true */ + if (callback(kv, arg1, arg2)) { + return; + } + } while ((kv->addr.start = get_next_kv_addr(db, §or, kv)) != FAILED_ADDR); + } + } while ((sec_addr = get_next_sector_addr(db, §or)) != FAILED_ADDR); +} + +static bool find_kv_cb(fdb_kv_t kv, void *arg1, void *arg2) +{ + const char *key = arg1; + bool *find_ok = arg2; + size_t key_len = strlen(key); + + if (key_len != kv->name_len) { + return false; + } + /* check KV */ + if (kv->crc_is_ok && kv->status == FDB_KV_WRITE && !strncmp(kv->name, key, key_len)) { + *find_ok = true; + return true; + } + return false; +} + +static bool find_kv_no_cache(fdb_kvdb_t db, const char *key, fdb_kv_t kv) +{ + bool find_ok = false; + + kv_iterator(db, kv, (void *)key, &find_ok, find_kv_cb); + + return find_ok; +} + +static bool find_kv(fdb_kvdb_t db, const char *key, fdb_kv_t kv) +{ + bool find_ok = false; + +#ifdef FDB_KV_USING_CACHE + size_t key_len = strlen(key); + + if (get_kv_from_cache(db, key, key_len, &kv->addr.start)) { + read_kv(db, kv); + return true; + } +#endif /* FDB_KV_USING_CACHE */ + + find_ok = find_kv_no_cache(db, key, kv); + +#ifdef FDB_KV_USING_CACHE + if (find_ok) { + update_kv_cache(db, key, key_len, kv->addr.start); + } +#endif /* FDB_KV_USING_CACHE */ + + return find_ok; +} + +static bool fdb_is_str(uint8_t *value, size_t len) +{ +#define __is_print(ch) ((unsigned int)((ch) - ' ') < 127u - ' ') + size_t i; + + for (i = 0; i < len; i++) { + if (!__is_print(value[i])) { + return false; + } + } + return true; +} + +static size_t get_kv(fdb_kvdb_t db, const char *key, void *value_buf, size_t buf_len, size_t *value_len) +{ + struct fdb_kv kv; + size_t read_len = 0; + + if (find_kv(db, key, &kv)) { + if (value_len) { + *value_len = kv.value_len; + } + if (buf_len > kv.value_len) { + read_len = kv.value_len; + } else { + read_len = buf_len; + } + if (value_buf){ + _fdb_flash_read((fdb_db_t)db, kv.addr.value, (uint32_t *) value_buf, read_len); + } + } else if (value_len) { + *value_len = 0; + } + + return read_len; +} + +/** + * Get a KV object by key name + * + * @param db database object + * @param key KV name + * @param kv KV object + * + * @return KV object when is not NULL + */ +fdb_kv_t fdb_kv_get_obj(fdb_kvdb_t db, const char *key, fdb_kv_t kv) +{ + bool find_ok = false; + + if (!db_init_ok(db)) { + FDB_INFO("Error: KV (%s) isn't initialize OK.\n", db_name(db)); + return 0; + } + + /* lock the KV cache */ + db_lock(db); + + find_ok = find_kv(db, key, kv); + + /* unlock the KV cache */ + db_unlock(db); + + return find_ok ? kv : NULL; +} + +/** + * Convert the KV object to blob object + * + * @param kv KV object + * @param blob blob object + * + * @return new blob object + */ +fdb_blob_t fdb_kv_to_blob(fdb_kv_t kv, fdb_blob_t blob) +{ + blob->saved.meta_addr = kv->addr.start; + blob->saved.addr = kv->addr.value; + blob->saved.len = kv->value_len; + + return blob; +} + +/** + * Get a blob KV value by key name. + * + * @param db database object + * @param key KV name + * @param blob blob object + * + * @return the actually get size on successful + */ +size_t fdb_kv_get_blob(fdb_kvdb_t db, const char *key, fdb_blob_t blob) +{ + size_t read_len = 0; + + if (!db_init_ok(db)) { + FDB_INFO("Error: KV (%s) isn't initialize OK.\n", db_name(db)); + return 0; + } + + /* lock the KV cache */ + db_lock(db); + + read_len = get_kv(db, key, blob->buf, blob->size, &blob->saved.len); + + /* unlock the KV cache */ + db_unlock(db); + + return read_len; +} + +/** + * Get an KV value by key name. + * + * @note this function is NOT supported reentrant + * @note this function is DEPRECATED + * + * @param db database object + * @param key KV name + * + * @return value + */ +char *fdb_kv_get(fdb_kvdb_t db, const char *key) +{ + static char value[FDB_STR_KV_VALUE_MAX_SIZE + 1]; + size_t get_size; + struct fdb_blob blob; + + if ((get_size = fdb_kv_get_blob(db, key, fdb_blob_make(&blob, value, FDB_STR_KV_VALUE_MAX_SIZE))) > 0) { + /* the return value must be string */ + if (fdb_is_str((uint8_t *)value, get_size)) { + value[get_size] = '\0'; + return value; + } else if (blob.saved.len > FDB_STR_KV_VALUE_MAX_SIZE) { + FDB_INFO("Warning: The default string KV value buffer length (%d) is too less (%u).\n", FDB_STR_KV_VALUE_MAX_SIZE, + (uint32_t)blob.saved.len); + } else { + FDB_INFO("Warning: The KV value isn't string. Could not be returned\n"); + return NULL; + } + } + + return NULL; +} + +static fdb_err_t write_kv_hdr(fdb_kvdb_t db, uint32_t addr, kv_hdr_data_t kv_hdr) +{ + fdb_err_t result = FDB_NO_ERR; + /* write the status will by write granularity */ + result = _fdb_write_status((fdb_db_t)db, addr, kv_hdr->status_table, FDB_KV_STATUS_NUM, FDB_KV_PRE_WRITE, false); + if (result != FDB_NO_ERR) { + return result; + } + /* write other header data */ + result = _fdb_flash_write((fdb_db_t)db, addr + KV_MAGIC_OFFSET, &kv_hdr->magic, sizeof(struct kv_hdr_data) - KV_MAGIC_OFFSET, false); + + return result; +} + +static fdb_err_t format_sector(fdb_kvdb_t db, uint32_t addr, uint32_t combined_value) +{ + fdb_err_t result = FDB_NO_ERR; + struct sector_hdr_data sec_hdr = { 0 }; + + FDB_ASSERT(addr % db_sec_size(db) == 0); + + result = _fdb_flash_erase((fdb_db_t)db, addr, db_sec_size(db)); + if (result == FDB_NO_ERR) { + /* initialize the header data */ + memset(&sec_hdr, 0xFF, sizeof(struct sector_hdr_data)); + _fdb_set_status(sec_hdr.status_table.store, FDB_SECTOR_STORE_STATUS_NUM, FDB_SECTOR_STORE_EMPTY); + _fdb_set_status(sec_hdr.status_table.dirty, FDB_SECTOR_DIRTY_STATUS_NUM, FDB_SECTOR_DIRTY_FALSE); + sec_hdr.magic = SECTOR_MAGIC_WORD; + sec_hdr.combined = combined_value; + sec_hdr.reserved = 0xFFFFFFFF; + /* save the header */ + result = _fdb_flash_write((fdb_db_t)db, addr, (uint32_t *)&sec_hdr, sizeof(struct sector_hdr_data), true); + +#ifdef FDB_KV_USING_CACHE + /* delete the sector cache */ + update_sector_cache(db, addr, addr + db_sec_size(db)); +#endif /* FDB_KV_USING_CACHE */ + } + + return result; +} + +static fdb_err_t update_sec_status(fdb_kvdb_t db, kv_sec_info_t sector, size_t new_kv_len, bool *is_full) +{ + uint8_t status_table[FDB_STORE_STATUS_TABLE_SIZE]; + fdb_err_t result = FDB_NO_ERR; + /* change the current sector status */ + if (sector->status.store == FDB_SECTOR_STORE_EMPTY) { + /* change the sector status to using */ + result = _fdb_write_status((fdb_db_t)db, sector->addr, status_table, FDB_SECTOR_STORE_STATUS_NUM, FDB_SECTOR_STORE_USING, true); + } else if (sector->status.store == FDB_SECTOR_STORE_USING) { + /* check remain size */ + if (sector->remain < FDB_SEC_REMAIN_THRESHOLD || sector->remain - new_kv_len < FDB_SEC_REMAIN_THRESHOLD) { + /* change the sector status to full */ + result = _fdb_write_status((fdb_db_t)db, sector->addr, status_table, FDB_SECTOR_STORE_STATUS_NUM, FDB_SECTOR_STORE_FULL, true); + +#ifdef FDB_KV_USING_CACHE + /* delete the sector cache */ + update_sector_cache(db, sector->addr, sector->addr + db_sec_size(db)); +#endif /* FDB_KV_USING_CACHE */ + + if (is_full) { + *is_full = true; + } + } else if (is_full) { + *is_full = false; + } + } + + return result; +} + +static void sector_iterator(fdb_kvdb_t db, kv_sec_info_t sector, fdb_sector_store_status_t status, void *arg1, void *arg2, + bool (*callback)(kv_sec_info_t sector, void *arg1, void *arg2), bool traversal_kv) +{ + uint32_t sec_addr; + + /* search all sectors */ + sec_addr = 0; + do { + read_sector_info(db, sec_addr, sector, false); + if (status == FDB_SECTOR_STORE_UNUSED || status == sector->status.store) { + if (traversal_kv) { + read_sector_info(db, sec_addr, sector, true); + } + /* iterator is interrupted when callback return true */ + if (callback && callback(sector, arg1, arg2)) { + return; + } + } + } while ((sec_addr = get_next_sector_addr(db, sector)) != FAILED_ADDR); +} + +static bool sector_statistics_cb(kv_sec_info_t sector, void *arg1, void *arg2) +{ + size_t *empty_sector = arg1, *using_sector = arg2; + + if (sector->check_ok && sector->status.store == FDB_SECTOR_STORE_EMPTY) { + (*empty_sector)++; + } else if (sector->check_ok && sector->status.store == FDB_SECTOR_STORE_USING) { + (*using_sector)++; + } + + return false; +} + +static bool alloc_kv_cb(kv_sec_info_t sector, void *arg1, void *arg2) +{ + struct alloc_kv_cb_args *arg = arg1; + + /* 1. sector has space + * 2. the NO dirty sector + * 3. the dirty sector only when the gc_request is false */ + if (sector->check_ok && sector->remain > arg->kv_size + && ((sector->status.dirty == FDB_SECTOR_DIRTY_FALSE) + || (sector->status.dirty == FDB_SECTOR_DIRTY_TRUE && !arg->db->gc_request))) { + *(arg->empty_kv) = sector->empty_kv; + return true; + } + + return false; +} + +static uint32_t alloc_kv(fdb_kvdb_t db, kv_sec_info_t sector, size_t kv_size) +{ + uint32_t empty_kv = FAILED_ADDR; + size_t empty_sector = 0, using_sector = 0; + struct alloc_kv_cb_args arg = {db, kv_size, &empty_kv}; + + /* sector status statistics */ + sector_iterator(db, sector, FDB_SECTOR_STORE_UNUSED, &empty_sector, &using_sector, sector_statistics_cb, false); + if (using_sector > 0) { + /* alloc the KV from the using status sector first */ + sector_iterator(db, sector, FDB_SECTOR_STORE_USING, &arg, NULL, alloc_kv_cb, true); + } + if (empty_sector > 0 && empty_kv == FAILED_ADDR) { + if (empty_sector > FDB_GC_EMPTY_SEC_THRESHOLD || db->gc_request) { + sector_iterator(db, sector, FDB_SECTOR_STORE_EMPTY, &arg, NULL, alloc_kv_cb, true); + } else { + /* no space for new KV now will GC and retry */ + FDB_DEBUG("Trigger a GC check after alloc KV failed.\n"); + db->gc_request = true; + } + } + + return empty_kv; +} + +static fdb_err_t del_kv(fdb_kvdb_t db, const char *key, fdb_kv_t old_kv, bool complete_del) +{ + fdb_err_t result = FDB_NO_ERR; + uint32_t dirty_status_addr; + +#if (KV_STATUS_TABLE_SIZE >= FDB_DIRTY_STATUS_TABLE_SIZE) + uint8_t status_table[KV_STATUS_TABLE_SIZE]; +#else + uint8_t status_table[DIRTY_STATUS_TABLE_SIZE]; +#endif + + /* need find KV */ + if (!old_kv) { + struct fdb_kv kv; + /* find KV */ + if (find_kv(db, key, &kv)) { + old_kv = &kv; + } else { + FDB_DEBUG("Not found '%s' in KV.\n", key); + return FDB_KV_NAME_ERR; + } + } + /* change and save the new status */ + if (!complete_del) { + result = _fdb_write_status((fdb_db_t)db, old_kv->addr.start, status_table, FDB_KV_STATUS_NUM, FDB_KV_PRE_DELETE, false); + db->last_is_complete_del = true; + } else { + result = _fdb_write_status((fdb_db_t)db, old_kv->addr.start, status_table, FDB_KV_STATUS_NUM, FDB_KV_DELETED, true); + + if (!db->last_is_complete_del && result == FDB_NO_ERR) { +#ifdef FDB_KV_USING_CACHE + /* delete the KV in flash and cache */ + if (key != NULL) { + /* when using del_kv(db, key, NULL, true) or del_kv(db, key, kv, true) in fdb_del_kv(db, ) and set_kv(db, ) */ + update_kv_cache(db, key, strlen(key), FDB_DATA_UNUSED); + } else if (old_kv != NULL) { + /* when using del_kv(db, NULL, kv, true) in move_kv(db, ) */ + update_kv_cache(db, old_kv->name, old_kv->name_len, FDB_DATA_UNUSED); + } +#endif /* FDB_KV_USING_CACHE */ + } + + db->last_is_complete_del = false; + } + + dirty_status_addr = FDB_ALIGN_DOWN(old_kv->addr.start, db_sec_size(db)) + SECTOR_DIRTY_OFFSET; + /* read and change the sector dirty status */ + if (result == FDB_NO_ERR + && _fdb_read_status((fdb_db_t)db, dirty_status_addr, status_table, FDB_SECTOR_DIRTY_STATUS_NUM) == FDB_SECTOR_DIRTY_FALSE) { + result = _fdb_write_status((fdb_db_t)db, dirty_status_addr, status_table, FDB_SECTOR_DIRTY_STATUS_NUM, FDB_SECTOR_DIRTY_TRUE, true); + } + + return result; +} + +/* + * move the KV to new space + */ +static fdb_err_t move_kv(fdb_kvdb_t db, fdb_kv_t kv) +{ + fdb_err_t result = FDB_NO_ERR; + uint8_t status_table[KV_STATUS_TABLE_SIZE]; + uint32_t kv_addr; + struct kvdb_sec_info sector; + + /* prepare to delete the current KV */ + if (kv->status == FDB_KV_WRITE) { + del_kv(db, NULL, kv, false); + } + + if ((kv_addr = alloc_kv(db, §or, kv->len)) != FAILED_ADDR) { + if (db->in_recovery_check) { + struct fdb_kv kv_bak; + char name[FDB_KV_NAME_MAX + 1] = { 0 }; + strncpy(name, kv->name, kv->name_len); + /* check the KV in flash is already create success */ + if (find_kv_no_cache(db, name, &kv_bak)) { + /* already create success, don't need to duplicate */ + result = FDB_NO_ERR; + goto __exit; + } + } + } else { + return FDB_SAVED_FULL; + } + /* start move the KV */ + { + uint8_t buf[32]; + size_t len, size, kv_len = kv->len; + + /* update the new KV sector status first */ + update_sec_status(db, §or, kv->len, NULL); + + _fdb_write_status((fdb_db_t)db, kv_addr, status_table, FDB_KV_STATUS_NUM, FDB_KV_PRE_WRITE, false); + kv_len -= KV_MAGIC_OFFSET; + for (len = 0, size = 0; len < kv_len; len += size) { + if (len + sizeof(buf) < kv_len) { + size = sizeof(buf); + } else { + size = kv_len - len; + } + _fdb_flash_read((fdb_db_t)db, kv->addr.start + KV_MAGIC_OFFSET + len, (uint32_t *) buf, FDB_WG_ALIGN(size)); + result = _fdb_flash_write((fdb_db_t)db, kv_addr + KV_MAGIC_OFFSET + len, (uint32_t *) buf, size, true); + } + _fdb_write_status((fdb_db_t)db, kv_addr, status_table, FDB_KV_STATUS_NUM, FDB_KV_WRITE, true); + +#ifdef FDB_KV_USING_CACHE + update_sector_cache(db, FDB_ALIGN_DOWN(kv_addr, db_sec_size(db)), + kv_addr + KV_HDR_DATA_SIZE + FDB_WG_ALIGN(kv->name_len) + FDB_WG_ALIGN(kv->value_len)); + update_kv_cache(db, kv->name, kv->name_len, kv_addr); +#endif /* FDB_KV_USING_CACHE */ + } + + FDB_DEBUG("Moved the KV (%.*s) from 0x%08" PRIX32 " to 0x%08" PRIX32 ".\n", kv->name_len, kv->name, kv->addr.start, kv_addr); + +__exit: + del_kv(db, NULL, kv, true); + + return result; +} + +static uint32_t new_kv(fdb_kvdb_t db, kv_sec_info_t sector, size_t kv_size) +{ + bool already_gc = false; + uint32_t empty_kv = FAILED_ADDR; + +__retry: + + if ((empty_kv = alloc_kv(db, sector, kv_size)) == FAILED_ADDR) { + if (db->gc_request && !already_gc) { + FDB_DEBUG("Warning: Alloc an KV (size %u) failed when new KV. Now will GC then retry.\n", (uint32_t)kv_size); + gc_collect(db); + already_gc = true; + goto __retry; + } else if (already_gc) { + FDB_DEBUG("Error: Alloc an KV (size %u) failed after GC. KV full.\n", kv_size); + db->gc_request = false; + } + } + + return empty_kv; +} + +static uint32_t new_kv_ex(fdb_kvdb_t db, kv_sec_info_t sector, size_t key_len, size_t buf_len) +{ + size_t kv_len = KV_HDR_DATA_SIZE + FDB_WG_ALIGN(key_len) + FDB_WG_ALIGN(buf_len); + + return new_kv(db, sector, kv_len); +} + +static bool gc_check_cb(kv_sec_info_t sector, void *arg1, void *arg2) +{ + size_t *empty_sec = arg1; + + if (sector->check_ok) { + *empty_sec = *empty_sec + 1; + } + + return false; + +} + +static bool do_gc(kv_sec_info_t sector, void *arg1, void *arg2) +{ + struct fdb_kv kv; + fdb_kvdb_t db = arg1; + + if (sector->check_ok && (sector->status.dirty == FDB_SECTOR_DIRTY_TRUE || sector->status.dirty == FDB_SECTOR_DIRTY_GC)) { + uint8_t status_table[FDB_DIRTY_STATUS_TABLE_SIZE]; + /* change the sector status to GC */ + _fdb_write_status((fdb_db_t)db, sector->addr + SECTOR_DIRTY_OFFSET, status_table, FDB_SECTOR_DIRTY_STATUS_NUM, FDB_SECTOR_DIRTY_GC, true); + /* search all KV */ + kv.addr.start = sector->addr + SECTOR_HDR_DATA_SIZE; + do { + read_kv(db, &kv); + if (kv.crc_is_ok && (kv.status == FDB_KV_WRITE || kv.status == FDB_KV_PRE_DELETE)) { + /* move the KV to new space */ + if (move_kv(db, &kv) != FDB_NO_ERR) { + FDB_DEBUG("Error: Moved the KV (%.*s) for GC failed.\n", kv.name_len, kv.name); + } + } + } while ((kv.addr.start = get_next_kv_addr(db, sector, &kv)) != FAILED_ADDR); + format_sector(db, sector->addr, SECTOR_NOT_COMBINED); + FDB_DEBUG("Collect a sector @0x%08" PRIX32 "\n", sector->addr); + } + + return false; +} + +/* + * The GC will be triggered on the following scene: + * 1. alloc an KV when the flash not has enough space + * 2. write an KV then the flash not has enough space + */ +static void gc_collect(fdb_kvdb_t db) +{ + struct kvdb_sec_info sector; + size_t empty_sec = 0; + + /* GC check the empty sector number */ + sector_iterator(db, §or, FDB_SECTOR_STORE_EMPTY, &empty_sec, NULL, gc_check_cb, false); + + /* do GC collect */ + FDB_DEBUG("The remain empty sector is %u, GC threshold is %d.\n", (uint32_t)empty_sec, FDB_GC_EMPTY_SEC_THRESHOLD); + if (empty_sec <= FDB_GC_EMPTY_SEC_THRESHOLD) { + sector_iterator(db, §or, FDB_SECTOR_STORE_UNUSED, db, NULL, do_gc, false); + } + + db->gc_request = false; +} + +static fdb_err_t align_write(fdb_kvdb_t db, uint32_t addr, const uint32_t *buf, size_t size) +{ + fdb_err_t result = FDB_NO_ERR; + size_t align_remain; + +#if (FDB_WRITE_GRAN / 8 > 0) + uint8_t align_data[FDB_WRITE_GRAN / 8]; + size_t align_data_size = sizeof(align_data); +#else + /* For compatibility with C89 */ + uint8_t align_data_u8, *align_data = &align_data_u8; + size_t align_data_size = 1; +#endif + + memset(align_data, 0xFF, align_data_size); + result = _fdb_flash_write((fdb_db_t)db, addr, buf, FDB_WG_ALIGN_DOWN(size), false); + + align_remain = size - FDB_WG_ALIGN_DOWN(size); + if (result == FDB_NO_ERR && align_remain) { + memcpy(align_data, (uint8_t *)buf + FDB_WG_ALIGN_DOWN(size), align_remain); + result = _fdb_flash_write((fdb_db_t)db, addr + FDB_WG_ALIGN_DOWN(size), (uint32_t *) align_data, align_data_size, false); + } + + return result; +} + +static fdb_err_t create_kv_blob(fdb_kvdb_t db, kv_sec_info_t sector, const char *key, const void *value, size_t len) +{ + fdb_err_t result = FDB_NO_ERR; + struct kv_hdr_data kv_hdr; + bool is_full = false; + uint32_t kv_addr = sector->empty_kv; + + if (strlen(key) > FDB_KV_NAME_MAX) { + FDB_INFO("Error: The KV name length is more than %d\n", FDB_KV_NAME_MAX); + return FDB_KV_NAME_ERR; + } + + memset(&kv_hdr, 0xFF, sizeof(struct kv_hdr_data)); + kv_hdr.magic = KV_MAGIC_WORD; + kv_hdr.name_len = strlen(key); + kv_hdr.value_len = len; + kv_hdr.len = KV_HDR_DATA_SIZE + FDB_WG_ALIGN(kv_hdr.name_len) + FDB_WG_ALIGN(kv_hdr.value_len); + + if (kv_hdr.len > db_sec_size(db) - SECTOR_HDR_DATA_SIZE) { + FDB_INFO("Error: The KV size is too big\n"); + return FDB_SAVED_FULL; + } + + if (kv_addr != FAILED_ADDR || (kv_addr = new_kv(db, sector, kv_hdr.len)) != FAILED_ADDR) { + size_t align_remain; + /* update the sector status */ + if (result == FDB_NO_ERR) { + result = update_sec_status(db, sector, kv_hdr.len, &is_full); + } + if (result == FDB_NO_ERR) { + uint8_t ff = 0xFF; + /* start calculate CRC32 */ + kv_hdr.crc32 = fdb_calc_crc32(0, &kv_hdr.name_len, KV_HDR_DATA_SIZE - KV_NAME_LEN_OFFSET); + kv_hdr.crc32 = fdb_calc_crc32(kv_hdr.crc32, key, kv_hdr.name_len); + align_remain = FDB_WG_ALIGN(kv_hdr.name_len) - kv_hdr.name_len; + while (align_remain--) { + kv_hdr.crc32 = fdb_calc_crc32(kv_hdr.crc32, &ff, 1); + } + kv_hdr.crc32 = fdb_calc_crc32(kv_hdr.crc32, value, kv_hdr.value_len); + align_remain = FDB_WG_ALIGN(kv_hdr.value_len) - kv_hdr.value_len; + while (align_remain--) { + kv_hdr.crc32 = fdb_calc_crc32(kv_hdr.crc32, &ff, 1); + } + /* write KV header data */ + result = write_kv_hdr(db, kv_addr, &kv_hdr); + + } + /* write key name */ + if (result == FDB_NO_ERR) { + result = align_write(db, kv_addr + KV_HDR_DATA_SIZE, (uint32_t *) key, kv_hdr.name_len); + +#ifdef FDB_KV_USING_CACHE + if (!is_full) { + update_sector_cache(db, sector->addr, + kv_addr + KV_HDR_DATA_SIZE + FDB_WG_ALIGN(kv_hdr.name_len) + FDB_WG_ALIGN(kv_hdr.value_len)); + } + update_kv_cache(db, key, kv_hdr.name_len, kv_addr); +#endif /* FDB_KV_USING_CACHE */ + } + /* write value */ + if (result == FDB_NO_ERR) { + result = align_write(db, kv_addr + KV_HDR_DATA_SIZE + FDB_WG_ALIGN(kv_hdr.name_len), value, + kv_hdr.value_len); + } + /* change the KV status to KV_WRITE */ + if (result == FDB_NO_ERR) { + result = _fdb_write_status((fdb_db_t)db, kv_addr, kv_hdr.status_table, FDB_KV_STATUS_NUM, FDB_KV_WRITE, true); + } + /* trigger GC collect when current sector is full */ + if (result == FDB_NO_ERR && is_full) { + FDB_DEBUG("Trigger a GC check after created KV.\n"); + db->gc_request = true; + } + } else { + result = FDB_SAVED_FULL; + } + + return result; +} + +/** + * Delete an KV. + * + * @param db database object + * @param key KV name + * + * @return result + */ +fdb_err_t fdb_kv_del(fdb_kvdb_t db, const char *key) +{ + fdb_err_t result = FDB_NO_ERR; + + if (!db_init_ok(db)) { + FDB_INFO("Error: KV (%s) isn't initialize OK.\n", db_name(db)); + return FDB_INIT_FAILED; + } + + /* lock the KV cache */ + db_lock(db); + + result = del_kv(db, key, NULL, true); + + /* unlock the KV cache */ + db_unlock(db); + + return result; +} + +static fdb_err_t set_kv(fdb_kvdb_t db, const char *key, const void *value_buf, size_t buf_len) +{ + fdb_err_t result = FDB_NO_ERR; + bool kv_is_found = false; + + if (value_buf == NULL) { + result = del_kv(db, key, NULL, true); + } else { + /* make sure the flash has enough space */ + if (new_kv_ex(db, &db->cur_sector, strlen(key), buf_len) == FAILED_ADDR) { + return FDB_SAVED_FULL; + } + kv_is_found = find_kv(db, key, &db->cur_kv); + /* prepare to delete the old KV */ + if (kv_is_found) { + result = del_kv(db, key, &db->cur_kv, false); + } + /* create the new KV */ + if (result == FDB_NO_ERR) { + result = create_kv_blob(db, &db->cur_sector, key, value_buf, buf_len); + } + /* delete the old KV */ + if (kv_is_found && result == FDB_NO_ERR) { + result = del_kv(db, key, &db->cur_kv, true); + } + /* process the GC after set KV */ + if (db->gc_request) { + gc_collect(db); + } + } + + return result; +} + +/** + * Set a blob KV. If it blob value is NULL, delete it. + * If not find it in flash, then create it. + * + * @param db database object + * @param key KV name + * @param blob blob object + * + * @return result + */ +fdb_err_t fdb_kv_set_blob(fdb_kvdb_t db, const char *key, fdb_blob_t blob) +{ + fdb_err_t result = FDB_NO_ERR; + + if (!db_init_ok(db)) { + FDB_INFO("Error: KV (%s) isn't initialize OK.\n", db_name(db)); + return FDB_INIT_FAILED; + } + + /* lock the KV cache */ + db_lock(db); + + result = set_kv(db, key, blob->buf, blob->size); + + /* unlock the KV cache */ + db_unlock(db); + + return result; +} + +/** + * Set a string KV. If it value is NULL, delete it. + * If not find it in flash, then create it. + * + * @param db database object + * @param key KV name + * @param value KV value + * + * @return result + */ +fdb_err_t fdb_kv_set(fdb_kvdb_t db, const char *key, const char *value) +{ + struct fdb_blob blob; + + return fdb_kv_set_blob(db, key, fdb_blob_make(&blob, value, strlen(value))); +} + +/** + * recovery all KV to default. + * + * @param db database object + * @return result + */ +fdb_err_t fdb_kv_set_default(fdb_kvdb_t db) +{ + fdb_err_t result = FDB_NO_ERR; + uint32_t addr, i, value_len; + struct kvdb_sec_info sector; + + /* lock the KV cache */ + db_lock(db); + /* format all sectors */ + for (addr = 0; addr < db_max_size(db); addr += db_sec_size(db)) { + result = format_sector(db, addr, SECTOR_NOT_COMBINED); + if (result != FDB_NO_ERR) { + goto __exit; + } + } + /* create default KV */ + for (i = 0; i < db->default_kvs.num; i++) { + /* It seems to be a string when value length is 0. + * This mechanism is for compatibility with older versions (less then V4.0). */ + if (db->default_kvs.kvs[i].value_len == 0) { + value_len = strlen(db->default_kvs.kvs[i].value); + } else { + value_len = db->default_kvs.kvs[i].value_len; + } + sector.empty_kv = FAILED_ADDR; + create_kv_blob(db, §or, db->default_kvs.kvs[i].key, db->default_kvs.kvs[i].value, value_len); + if (result != FDB_NO_ERR) { + goto __exit; + } + } + +__exit: + /* unlock the KV cache */ + db_unlock(db); + + return result; +} + +static bool print_kv_cb(fdb_kv_t kv, void *arg1, void *arg2) +{ + bool value_is_str = true, print_value = false; + size_t *using_size = arg1; + fdb_kvdb_t db = arg2; + + if (kv->crc_is_ok) { + /* calculate the total using flash size */ + *using_size += kv->len; + /* check KV */ + if (kv->status == FDB_KV_WRITE) { + FDB_PRINT("%.*s=", kv->name_len, kv->name); + + if (kv->value_len < FDB_STR_KV_VALUE_MAX_SIZE ) { + uint8_t buf[32]; + size_t len, size; +__reload: + /* check the value is string */ + for (len = 0, size = 0; len < kv->value_len; len += size) { + if (len + sizeof(buf) < kv->value_len) { + size = sizeof(buf); + } else { + size = kv->value_len - len; + } + _fdb_flash_read((fdb_db_t)db, kv->addr.value + len, (uint32_t *) buf, FDB_WG_ALIGN(size)); + if (print_value) { + FDB_PRINT("%.*s", (int)size, buf); + } else if (!fdb_is_str(buf, size)) { + value_is_str = false; + break; + } + } + } else { + value_is_str = false; + } + if (value_is_str && !print_value) { + print_value = true; + goto __reload; + } else if (!value_is_str) { + FDB_PRINT("blob @0x%08" PRIX32 " %" PRIu32 "bytes", kv->addr.value, kv->value_len); + } + FDB_PRINT("\n"); + } + } + + return false; +} + + +/** + * Print all KV. + * + * @param db database object + */ +void fdb_kv_print(fdb_kvdb_t db) +{ + struct fdb_kv kv; + size_t using_size = 0; + + if (!db_init_ok(db)) { + FDB_INFO("Error: KV (%s) isn't initialize OK.\n", db_name(db)); + return; + } + + /* lock the KV cache */ + db_lock(db); + + kv_iterator(db, &kv, &using_size, db, print_kv_cb); + + FDB_PRINT("\nmode: next generation\n"); + FDB_PRINT("size: %u/%u bytes.\n", (uint32_t)using_size + ((SECTOR_NUM - FDB_GC_EMPTY_SEC_THRESHOLD) * SECTOR_HDR_DATA_SIZE), + db_max_size(db) - db_sec_size(db) * FDB_GC_EMPTY_SEC_THRESHOLD); + + /* unlock the KV cache */ + db_unlock(db); +} + +#ifdef FDB_KV_AUTO_UPDATE +/* + * Auto update KV to latest default when current setting version number is changed. + */ +static void kv_auto_update(fdb_kvdb_t db) +{ + size_t saved_ver_num, setting_ver_num = db->ver_num; + + if (get_kv(db, VER_NUM_KV_NAME, &saved_ver_num, sizeof(size_t), NULL) > 0) { + /* check version number */ + if (saved_ver_num != setting_ver_num) { + size_t i, value_len; + FDB_DEBUG("Update the KV from version %u to %u.\n", (uint32_t)saved_ver_num, (uint32_t)setting_ver_num); + for (i = 0; i < db->default_kvs.num; i++) { + /* add a new KV when it's not found */ + if (!find_kv(db, db->default_kvs.kvs[i].key, &db->cur_kv)) { + /* It seems to be a string when value length is 0. + * This mechanism is for compatibility with older versions (less then V4.0). */ + if (db->default_kvs.kvs[i].value_len == 0) { + value_len = strlen(db->default_kvs.kvs[i].value); + } else { + value_len = db->default_kvs.kvs[i].value_len; + } + db->cur_sector.empty_kv = FAILED_ADDR; + create_kv_blob(db, &db->cur_sector, db->default_kvs.kvs[i].key, db->default_kvs.kvs[i].value, value_len); + } + } + } else { + /* version number not changed now return */ + return; + } + } + + set_kv(db, VER_NUM_KV_NAME, &setting_ver_num, sizeof(size_t)); +} +#endif /* FDB_KV_AUTO_UPDATE */ + +static bool check_sec_hdr_cb(kv_sec_info_t sector, void *arg1, void *arg2) +{ + if (!sector->check_ok) { + size_t *failed_count = arg1; + fdb_kvdb_t db = arg2; + + (*failed_count) ++; + if (db->parent.not_formatable) { + return true; + } else { + FDB_DEBUG("Sector header info is incorrect. Auto format this sector (0x%08" PRIX32 ").\n", sector->addr); + format_sector(db, sector->addr, SECTOR_NOT_COMBINED); + } + } + + return false; +} + +static bool check_and_recovery_gc_cb(kv_sec_info_t sector, void *arg1, void *arg2) +{ + fdb_kvdb_t db = arg1; + + if (sector->check_ok && sector->status.dirty == FDB_SECTOR_DIRTY_GC) { + /* make sure the GC request flag to true */ + db->gc_request = true; + /* resume the GC operate */ + gc_collect(db); + } + + return false; +} + +static bool check_and_recovery_kv_cb(fdb_kv_t kv, void *arg1, void *arg2) +{ + fdb_kvdb_t db = arg1; + + /* recovery the prepare deleted KV */ + if (kv->crc_is_ok && kv->status == FDB_KV_PRE_DELETE) { + FDB_INFO("Found an KV (%.*s) which has changed value failed. Now will recovery it.\n", kv->name_len, kv->name); + /* recovery the old KV */ + if (move_kv(db, kv) == FDB_NO_ERR) { + FDB_DEBUG("Recovery the KV successful.\n"); + } else { + FDB_DEBUG("Warning: Moved an KV (size %" PRIu32 ") failed when recovery. Now will GC then retry.\n", kv->len); + return true; + } + } else if (kv->status == FDB_KV_PRE_WRITE) { + uint8_t status_table[KV_STATUS_TABLE_SIZE]; + /* the KV has not write finish, change the status to error */ + //TODO 绘制异常处理的状态装换图 + _fdb_write_status((fdb_db_t)db, kv->addr.start, status_table, FDB_KV_STATUS_NUM, FDB_KV_ERR_HDR, true); + return true; + } else if (kv->crc_is_ok && kv->status == FDB_KV_WRITE) { + /* update the cache when first load */ + update_kv_cache(db, kv->name, kv->name_len, kv->addr.start); + } + + return false; +} + +/** + * Check and load the flash KV. + * + * @return result + */ +fdb_err_t _fdb_kv_load(fdb_kvdb_t db) +{ + fdb_err_t result = FDB_NO_ERR; + struct fdb_kv kv; + struct kvdb_sec_info sector; + size_t check_failed_count = 0; + + db->in_recovery_check = true; + /* check all sector header */ + sector_iterator(db, §or, FDB_SECTOR_STORE_UNUSED, &check_failed_count, db, check_sec_hdr_cb, false); + if (db->parent.not_formatable && check_failed_count > 0) { + result = FDB_READ_ERR; + goto __exit; + } + /* all sector header check failed */ + if (check_failed_count == SECTOR_NUM) { + FDB_INFO("All sector header is incorrect. Set it to default.\n"); + fdb_kv_set_default(db); + } + + /* lock the KV cache */ + db_lock(db); + /* check all sector header for recovery GC */ + sector_iterator(db, §or, FDB_SECTOR_STORE_UNUSED, db, NULL, check_and_recovery_gc_cb, false); + +__retry: + /* check all KV for recovery */ + kv_iterator(db, &kv, db, NULL, check_and_recovery_kv_cb); + if (db->gc_request) { + gc_collect(db); + goto __retry; + } + + db->in_recovery_check = false; + +__exit: + /* unlock the KV cache */ + db_unlock(db); + + return result; +} + +/** + * This function will get or set some options of the database + * + * @param db database object + * @param cmd the control command + * @param arg the argument + */ +void fdb_kvdb_control(fdb_kvdb_t db, int cmd, void *arg) +{ + FDB_ASSERT(db); + + switch (cmd) { + case FDB_KVDB_CTRL_SET_SEC_SIZE: + /* this change MUST before database initialization */ + FDB_ASSERT(db->parent.init_ok == false); + db->parent.sec_size = *(uint32_t *)arg; + break; + case FDB_KVDB_CTRL_GET_SEC_SIZE: + *(uint32_t *)arg = db->parent.sec_size; + break; + case FDB_KVDB_CTRL_SET_LOCK: + db->parent.lock = (void (*)(fdb_db_t db))arg; + break; + case FDB_KVDB_CTRL_SET_UNLOCK: + db->parent.unlock = (void (*)(fdb_db_t db))arg; + break; + case FDB_KVDB_CTRL_SET_FILE_MODE: +#ifdef FDB_USING_FILE_MODE + /* this change MUST before database initialization */ + FDB_ASSERT(db->parent.init_ok == false); + db->parent.file_mode = *(bool *)arg; +#else + FDB_INFO("Error: set file mode Failed. Please defined the FDB_USING_FILE_MODE macro."); +#endif + break; + case FDB_KVDB_CTRL_SET_MAX_SIZE: +#ifdef FDB_USING_FILE_MODE + /* this change MUST before database initialization */ + FDB_ASSERT(db->parent.init_ok == false); + db->parent.max_size = *(uint32_t *)arg; +#endif + break; + case FDB_KVDB_CTRL_SET_NOT_FORMAT: + /* this change MUST before database initialization */ + FDB_ASSERT(db->parent.init_ok == false); + db->parent.not_formatable = *(bool *)arg; + break; + } +} + +/** + * The KV database initialization. + * + * @param db database object + * @param name database name + * @param path FAL mode: partition name, file mode: database saved directory path + * @param default_kv the default KV set @see fdb_default_kv + * @param user_data user data + * + * @return result + */ +fdb_err_t fdb_kvdb_init(fdb_kvdb_t db, const char *name, const char *path, struct fdb_default_kv *default_kv, + void *user_data) +{ + fdb_err_t result = FDB_NO_ERR; + +#ifdef FDB_KV_USING_CACHE + size_t i; +#endif + + /* must be aligned with write granularity */ + FDB_ASSERT((FDB_STR_KV_VALUE_MAX_SIZE * 8) % FDB_WRITE_GRAN == 0); + + result = _fdb_init_ex((fdb_db_t)db, name, path, FDB_DB_TYPE_KV, user_data); + if (result != FDB_NO_ERR) { + goto __exit; + } + + db->gc_request = false; + db->in_recovery_check = false; + if (default_kv) { + db->default_kvs = *default_kv; + } + else { + db->default_kvs.num = 0; + db->default_kvs.kvs = NULL; + } + /* there is at least one empty sector for GC. */ + FDB_ASSERT((FDB_GC_EMPTY_SEC_THRESHOLD > 0 && FDB_GC_EMPTY_SEC_THRESHOLD < SECTOR_NUM)) + +#ifdef FDB_KV_USING_CACHE + for (i = 0; i < FDB_SECTOR_CACHE_TABLE_SIZE; i++) { + db->sector_cache_table[i].addr = FDB_DATA_UNUSED; + } + for (i = 0; i < FDB_KV_CACHE_TABLE_SIZE; i++) { + db->kv_cache_table[i].addr = FDB_DATA_UNUSED; + } +#endif /* FDB_KV_USING_CACHE */ + + FDB_DEBUG("KVDB size is %u bytes.\n", db_max_size(db)); + + result = _fdb_kv_load(db); + +#ifdef FDB_KV_AUTO_UPDATE + if (result == FDB_NO_ERR) { + kv_auto_update(db); + } +#endif + +__exit: + + _fdb_init_finish((fdb_db_t)db, result); + + return result; +} + +/** + * The KV database initialization. + * + * @param db database object + * + * @return result + */ +fdb_err_t fdb_kvdb_deinit(fdb_kvdb_t db) +{ + _fdb_deinit((fdb_db_t) db); + + return FDB_NO_ERR; +} + +/** + * The KV database initialization. + * + * @param itr iterator structure to be initialized + * + * @return pointer to the iterator initialized. + */ +fdb_kv_iterator_t fdb_kv_iterator_init(fdb_kv_iterator_t itr) +{ + itr->curr_kv.addr.start = 0; + + /* If iterator statistics is needed */ + itr->iterated_cnt = 0; + itr->iterated_obj_bytes = 0; + itr->iterated_value_bytes = 0; + /* Start from sector head */ + itr->sector_addr = 0; + return itr; +} + +/** + * The KV database iterator. + * + * @param db database object + * @param itr the iterator structure + * + * @return false if iteration is ended, true if iteration is not ended. + */ +bool fdb_kv_iterate(fdb_kvdb_t db, fdb_kv_iterator_t itr) +{ + struct kvdb_sec_info sector; + fdb_kv_t kv = &(itr->curr_kv); + do { + if (read_sector_info(db, itr->sector_addr, §or, false) == FDB_NO_ERR) { + if (sector.status.store == FDB_SECTOR_STORE_USING || sector.status.store == FDB_SECTOR_STORE_FULL) { + if (kv->addr.start == 0) { + kv->addr.start = sector.addr + SECTOR_HDR_DATA_SIZE; + } + else if ((kv->addr.start = get_next_kv_addr(db, §or, kv)) == FAILED_ADDR) { + kv->addr.start = 0; + continue; + } + do { + read_kv(db, kv); + if (kv->status == FDB_KV_WRITE) { + /* We got a valid kv here. */ + /* If iterator statistics is needed */ + itr->iterated_cnt++; + itr->iterated_obj_bytes += kv->len; + itr->iterated_value_bytes += kv->value_len; + return true; + } + } while((kv->addr.start = get_next_kv_addr(db, §or, kv)) != FAILED_ADDR); + } + } + /** Set kv->addr.start to 0 when we get into a new sector so that if we successfully get the next sector info, + * the kv->addr.start is set to the new sector.addr + SECTOR_HDR_DATA_SIZE. + */ + kv->addr.start = 0; + } while ((itr->sector_addr = get_next_sector_addr(db, §or)) != FAILED_ADDR); + /* Finally we have iterated all the KVs. */ + return false; +} + +#endif /* defined(FDB_USING_KVDB) */ + diff --git a/APP_Framework/lib/embedded_database/flashdb/fdb_low_lvl.h b/APP_Framework/lib/embedded_database/flashdb/fdb_low_lvl.h new file mode 100644 index 000000000..d7f287e24 --- /dev/null +++ b/APP_Framework/lib/embedded_database/flashdb/fdb_low_lvl.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2020, Armink, + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief low level API and definition + */ + +#ifndef _FDB_LOW_LVL_H_ +#define _FDB_LOW_LVL_H_ + +#include +#include + +#if (FDB_WRITE_GRAN == 1) +#define FDB_STATUS_TABLE_SIZE(status_number) ((status_number * FDB_WRITE_GRAN + 7)/8) +#else +#define FDB_STATUS_TABLE_SIZE(status_number) (((status_number - 1) * FDB_WRITE_GRAN + 7)/8) +#endif + +/* Return the most contiguous size aligned at specified width. RT_ALIGN(13, 4) + * would return 16. + */ +#define FDB_ALIGN(size, align) (((size) + (align) - 1) & ~((align) - 1)) +/* align by write granularity */ +#define FDB_WG_ALIGN(size) (FDB_ALIGN(size, (FDB_WRITE_GRAN + 7)/8)) +/** + * Return the down number of aligned at specified width. RT_ALIGN_DOWN(13, 4) + * would return 12. + */ +#define FDB_ALIGN_DOWN(size, align) ((size) & ~((align) - 1)) +/* align down by write granularity */ +#define FDB_WG_ALIGN_DOWN(size) (FDB_ALIGN_DOWN(size, (FDB_WRITE_GRAN + 7)/8)) + +#define FDB_STORE_STATUS_TABLE_SIZE FDB_STATUS_TABLE_SIZE(FDB_SECTOR_STORE_STATUS_NUM) +#define FDB_DIRTY_STATUS_TABLE_SIZE FDB_STATUS_TABLE_SIZE(FDB_SECTOR_DIRTY_STATUS_NUM) + +/* the data is unused */ +#define FDB_DATA_UNUSED 0xFFFFFFFF + +fdb_err_t _fdb_kv_load(fdb_kvdb_t db); +size_t _fdb_set_status(uint8_t status_table[], size_t status_num, size_t status_index); +size_t _fdb_get_status(uint8_t status_table[], size_t status_num); +uint32_t _fdb_continue_ff_addr(fdb_db_t db, uint32_t start, uint32_t end); +fdb_err_t _fdb_init_ex(fdb_db_t db, const char *name, const char *part_name, fdb_db_type type, void *user_data); +void _fdb_init_finish(fdb_db_t db, fdb_err_t result); +void _fdb_deinit(fdb_db_t db); +fdb_err_t _fdb_write_status(fdb_db_t db, uint32_t addr, uint8_t status_table[], size_t status_num, size_t status_index, bool sync); +size_t _fdb_read_status(fdb_db_t db, uint32_t addr, uint8_t status_table[], size_t total_num); +fdb_err_t _fdb_flash_read(fdb_db_t db, uint32_t addr, void *buf, size_t size); +fdb_err_t _fdb_flash_erase(fdb_db_t db, uint32_t addr, size_t size); +fdb_err_t _fdb_flash_write(fdb_db_t db, uint32_t addr, const void *buf, size_t size, bool sync); + +#endif /* _FDB_LOW_LVL_H_ */ diff --git a/APP_Framework/lib/embedded_database/flashdb/fdb_tsdb.c b/APP_Framework/lib/embedded_database/flashdb/fdb_tsdb.c new file mode 100644 index 000000000..5ae085374 --- /dev/null +++ b/APP_Framework/lib/embedded_database/flashdb/fdb_tsdb.c @@ -0,0 +1,831 @@ +/* + * Copyright (c) 2020, Armink, + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief TSDB feature. + * + * Time series log (like TSDB) feature implement source file. + * + * TSL is time series log, the TSDB saved many TSLs. + */ + +#include +#include +#include +#include + +#define FDB_LOG_TAG "[tsl]" +/* rewrite log prefix */ +#undef FDB_LOG_PREFIX2 +#define FDB_LOG_PREFIX2() FDB_PRINT("[%s] ", db_name(db)) + +#if defined(FDB_USING_TSDB) + +/* magic word(`T`, `S`, `L`, `0`) */ +#define SECTOR_MAGIC_WORD 0x304C5354 + +#define TSL_STATUS_TABLE_SIZE FDB_STATUS_TABLE_SIZE(FDB_TSL_STATUS_NUM) + +#define SECTOR_HDR_DATA_SIZE (FDB_WG_ALIGN(sizeof(struct sector_hdr_data))) +#define LOG_IDX_DATA_SIZE (FDB_WG_ALIGN(sizeof(struct log_idx_data))) +#define LOG_IDX_TS_OFFSET ((unsigned long)(&((struct log_idx_data *)0)->time)) +#define SECTOR_MAGIC_OFFSET ((unsigned long)(&((struct sector_hdr_data *)0)->magic)) +#define SECTOR_START_TIME_OFFSET ((unsigned long)(&((struct sector_hdr_data *)0)->start_time)) +#define SECTOR_END0_TIME_OFFSET ((unsigned long)(&((struct sector_hdr_data *)0)->end_info[0].time)) +#define SECTOR_END0_IDX_OFFSET ((unsigned long)(&((struct sector_hdr_data *)0)->end_info[0].index)) +#define SECTOR_END0_STATUS_OFFSET ((unsigned long)(&((struct sector_hdr_data *)0)->end_info[0].status)) +#define SECTOR_END1_TIME_OFFSET ((unsigned long)(&((struct sector_hdr_data *)0)->end_info[1].time)) +#define SECTOR_END1_IDX_OFFSET ((unsigned long)(&((struct sector_hdr_data *)0)->end_info[1].index)) +#define SECTOR_END1_STATUS_OFFSET ((unsigned long)(&((struct sector_hdr_data *)0)->end_info[1].status)) + +/* the next address is get failed */ +#define FAILED_ADDR 0xFFFFFFFF + +#define db_name(db) (((fdb_db_t)db)->name) +#define db_init_ok(db) (((fdb_db_t)db)->init_ok) +#define db_sec_size(db) (((fdb_db_t)db)->sec_size) +#define db_max_size(db) (((fdb_db_t)db)->max_size) + +#define db_lock(db) \ + do { \ + if (((fdb_db_t)db)->lock) ((fdb_db_t)db)->lock((fdb_db_t)db); \ + } while(0); + +#define db_unlock(db) \ + do { \ + if (((fdb_db_t)db)->unlock) ((fdb_db_t)db)->unlock((fdb_db_t)db); \ + } while(0); + +#define _FDB_WRITE_STATUS(db, addr, status_table, status_num, status_index, sync) \ + do { \ + result = _fdb_write_status((fdb_db_t)db, addr, status_table, status_num, status_index, sync);\ + if (result != FDB_NO_ERR) return result; \ + } while(0); + +#define FLASH_WRITE(db, addr, buf, size, sync) \ + do { \ + result = _fdb_flash_write((fdb_db_t)db, addr, buf, size, sync); \ + if (result != FDB_NO_ERR) return result; \ + } while(0); + +struct sector_hdr_data { + uint8_t status[FDB_STORE_STATUS_TABLE_SIZE]; /**< sector store status @see fdb_sector_store_status_t */ + uint32_t magic; /**< magic word(`T`, `S`, `L`, `0`) */ + fdb_time_t start_time; /**< the first start node's timestamp */ + struct { + fdb_time_t time; /**< the last end node's timestamp */ + uint32_t index; /**< the last end node's index */ + uint8_t status[TSL_STATUS_TABLE_SIZE]; /**< end node status, @see fdb_tsl_status_t */ + } end_info[2]; + uint32_t reserved; +}; +typedef struct sector_hdr_data *sector_hdr_data_t; + +/* time series log node index data */ +struct log_idx_data { + uint8_t status_table[TSL_STATUS_TABLE_SIZE]; /**< node status, @see fdb_tsl_status_t */ + fdb_time_t time; /**< node timestamp */ + uint32_t log_len; /**< node total length (header + name + value), must align by FDB_WRITE_GRAN */ + uint32_t log_addr; /**< node address */ +}; +typedef struct log_idx_data *log_idx_data_t; + +struct query_count_args { + fdb_tsl_status_t status; + size_t count; +}; + +struct check_sec_hdr_cb_args { + fdb_tsdb_t db; + bool check_failed; + size_t empty_num; + uint32_t empty_addr; +}; + +static fdb_err_t read_tsl(fdb_tsdb_t db, fdb_tsl_t tsl) +{ + struct log_idx_data idx; + /* read TSL index raw data */ + _fdb_flash_read((fdb_db_t)db, tsl->addr.index, (uint32_t *) &idx, sizeof(struct log_idx_data)); + tsl->status = (fdb_tsl_status_t) _fdb_get_status(idx.status_table, FDB_TSL_STATUS_NUM); + if (tsl->status == FDB_TSL_PRE_WRITE) { + tsl->log_len = db->max_len; + tsl->addr.log = FDB_DATA_UNUSED; + tsl->time = 0; + } else { + tsl->log_len = idx.log_len; + tsl->addr.log = idx.log_addr; + tsl->time = idx.time; + } + + return FDB_NO_ERR; +} + +static uint32_t get_next_sector_addr(fdb_tsdb_t db, tsdb_sec_info_t pre_sec, uint32_t traversed_len) +{ + if (traversed_len + db_sec_size(db) <= db_max_size(db)) { + if (pre_sec->addr + db_sec_size(db) < db_max_size(db)) { + return pre_sec->addr + db_sec_size(db); + } else { + /* the next sector is on the top of the database */ + return 0; + } + } else { + /* finished */ + return FAILED_ADDR; + } +} + +static uint32_t get_next_tsl_addr(tsdb_sec_info_t sector, fdb_tsl_t pre_tsl) +{ + uint32_t addr = FAILED_ADDR; + + if (sector->status == FDB_SECTOR_STORE_EMPTY) { + return FAILED_ADDR; + } + + if (pre_tsl->addr.index + LOG_IDX_DATA_SIZE <= sector->end_idx) { + addr = pre_tsl->addr.index + LOG_IDX_DATA_SIZE; + } else { + /* no TSL */ + return FAILED_ADDR; + } + + return addr; +} + +static fdb_err_t read_sector_info(fdb_tsdb_t db, uint32_t addr, tsdb_sec_info_t sector, bool traversal) +{ + fdb_err_t result = FDB_NO_ERR; + struct sector_hdr_data sec_hdr; + + FDB_ASSERT(sector); + + /* read sector header raw data */ + _fdb_flash_read((fdb_db_t)db, addr, (uint32_t *)&sec_hdr, sizeof(struct sector_hdr_data)); + + sector->addr = addr; + sector->magic = sec_hdr.magic; + + /* check magic word */ + if (sector->magic != SECTOR_MAGIC_WORD) { + sector->check_ok = false; + return FDB_INIT_FAILED; + } + sector->check_ok = true; + sector->status = (fdb_sector_store_status_t) _fdb_get_status(sec_hdr.status, FDB_SECTOR_STORE_STATUS_NUM); + sector->start_time = sec_hdr.start_time; + sector->end_info_stat[0] = (fdb_tsl_status_t) _fdb_get_status(sec_hdr.end_info[0].status, FDB_TSL_STATUS_NUM); + sector->end_info_stat[1] = (fdb_tsl_status_t) _fdb_get_status(sec_hdr.end_info[1].status, FDB_TSL_STATUS_NUM); + if (sector->end_info_stat[0] == FDB_TSL_WRITE) { + sector->end_time = sec_hdr.end_info[0].time; + sector->end_idx = sec_hdr.end_info[0].index; + } else if (sector->end_info_stat[1] == FDB_TSL_WRITE) { + sector->end_time = sec_hdr.end_info[1].time; + sector->end_idx = sec_hdr.end_info[1].index; + } else if (sector->end_info_stat[0] == FDB_TSL_PRE_WRITE && sector->end_info_stat[1] == FDB_TSL_PRE_WRITE) { + //TODO There is no valid end node info on this sector, need impl fast query this sector by fdb_tsl_iter_by_time + FDB_ASSERT(0); + } + /* traversal all TSL and calculate the remain space size */ + sector->empty_idx = sector->addr + SECTOR_HDR_DATA_SIZE; + sector->empty_data = sector->addr + db_sec_size(db); + /* the TSL's data is saved from sector bottom, and the TSL's index saved from the sector top */ + sector->remain = sector->empty_data - sector->empty_idx; + if (sector->status == FDB_SECTOR_STORE_USING && traversal) { + struct fdb_tsl tsl; + + tsl.addr.index = sector->empty_idx; + while (read_tsl(db, &tsl) == FDB_NO_ERR) { + if (tsl.status == FDB_TSL_UNUSED) { + break; + } + sector->end_time = tsl.time; + sector->end_idx = tsl.addr.index; + sector->empty_idx += LOG_IDX_DATA_SIZE; + sector->empty_data -= FDB_WG_ALIGN(tsl.log_len); + tsl.addr.index += LOG_IDX_DATA_SIZE; + if (sector->remain > LOG_IDX_DATA_SIZE + FDB_WG_ALIGN(tsl.log_len)) { + sector->remain -= (LOG_IDX_DATA_SIZE + FDB_WG_ALIGN(tsl.log_len)); + } else { + FDB_INFO("Error: this TSL (0x%08" PRIX32 ") size (%" PRIu32 ") is out of bound.\n", tsl.addr.index, tsl.log_len); + sector->remain = 0; + result = FDB_READ_ERR; + break; + } + } + } + + return result; +} + +static fdb_err_t format_sector(fdb_tsdb_t db, uint32_t addr) +{ + fdb_err_t result = FDB_NO_ERR; + struct sector_hdr_data sec_hdr; + + FDB_ASSERT(addr % db_sec_size(db) == 0); + + result = _fdb_flash_erase((fdb_db_t)db, addr, db_sec_size(db)); + if (result == FDB_NO_ERR) { + _FDB_WRITE_STATUS(db, addr, sec_hdr.status, FDB_SECTOR_STORE_STATUS_NUM, FDB_SECTOR_STORE_EMPTY, true); + /* set the magic */ + sec_hdr.magic = SECTOR_MAGIC_WORD; + FLASH_WRITE(db, addr + SECTOR_MAGIC_OFFSET, &sec_hdr.magic, sizeof(sec_hdr.magic), true); + } + + return result; +} + +static void sector_iterator(fdb_tsdb_t db, tsdb_sec_info_t sector, fdb_sector_store_status_t status, void *arg1, + void *arg2, bool (*callback)(tsdb_sec_info_t sector, void *arg1, void *arg2), bool traversal) +{ + uint32_t sec_addr = sector->addr, traversed_len = 0; + + /* search all sectors */ + do { + read_sector_info(db, sec_addr, sector, false); + if (status == FDB_SECTOR_STORE_UNUSED || status == sector->status) { + if (traversal) { + read_sector_info(db, sec_addr, sector, true); + } + /* iterator is interrupted when callback return true */ + if (callback && callback(sector, arg1, arg2)) { + return; + } + } + traversed_len += db_sec_size(db); + } while ((sec_addr = get_next_sector_addr(db, sector, traversed_len)) != FAILED_ADDR); +} + +static fdb_err_t write_tsl(fdb_tsdb_t db, fdb_blob_t blob, fdb_time_t time) +{ + fdb_err_t result = FDB_NO_ERR; + struct log_idx_data idx; + uint32_t idx_addr = db->cur_sec.empty_idx; + + idx.log_len = blob->size; + idx.time = time; + idx.log_addr = db->cur_sec.empty_data - FDB_WG_ALIGN(idx.log_len); + /* write the status will by write granularity */ + _FDB_WRITE_STATUS(db, idx_addr, idx.status_table, FDB_TSL_STATUS_NUM, FDB_TSL_PRE_WRITE, false); + /* write other index info */ + FLASH_WRITE(db, idx_addr + LOG_IDX_TS_OFFSET, &idx.time, sizeof(struct log_idx_data) - LOG_IDX_TS_OFFSET, false); + /* write blob data */ + FLASH_WRITE(db, idx.log_addr, blob->buf, blob->size, false); + /* write the status will by write granularity */ + _FDB_WRITE_STATUS(db, idx_addr, idx.status_table, FDB_TSL_STATUS_NUM, FDB_TSL_WRITE, true); + + return result; +} + +static fdb_err_t update_sec_status(fdb_tsdb_t db, tsdb_sec_info_t sector, fdb_blob_t blob, fdb_time_t cur_time) +{ + fdb_err_t result = FDB_NO_ERR; + uint8_t status[FDB_STORE_STATUS_TABLE_SIZE]; + + if (sector->status == FDB_SECTOR_STORE_USING && sector->remain < LOG_IDX_DATA_SIZE + FDB_WG_ALIGN(blob->size)) { + uint8_t end_status[TSL_STATUS_TABLE_SIZE]; + uint32_t end_index = sector->empty_idx - LOG_IDX_DATA_SIZE, new_sec_addr, cur_sec_addr = sector->addr; + /* save the end node index and timestamp */ + if (sector->end_info_stat[0] == FDB_TSL_UNUSED) { + _FDB_WRITE_STATUS(db, cur_sec_addr + SECTOR_END0_STATUS_OFFSET, end_status, FDB_TSL_STATUS_NUM, FDB_TSL_PRE_WRITE, false); + FLASH_WRITE(db, cur_sec_addr + SECTOR_END0_TIME_OFFSET, (uint32_t * )&db->last_time, sizeof(fdb_time_t), false); + FLASH_WRITE(db, cur_sec_addr + SECTOR_END0_IDX_OFFSET, &end_index, sizeof(end_index), false); + _FDB_WRITE_STATUS(db, cur_sec_addr + SECTOR_END0_STATUS_OFFSET, end_status, FDB_TSL_STATUS_NUM, FDB_TSL_WRITE, true); + } else if (sector->end_info_stat[1] == FDB_TSL_UNUSED) { + _FDB_WRITE_STATUS(db, cur_sec_addr + SECTOR_END1_STATUS_OFFSET, end_status, FDB_TSL_STATUS_NUM, FDB_TSL_PRE_WRITE, false); + FLASH_WRITE(db, cur_sec_addr + SECTOR_END1_TIME_OFFSET, (uint32_t * )&db->last_time, sizeof(fdb_time_t), false); + FLASH_WRITE(db, cur_sec_addr + SECTOR_END1_IDX_OFFSET, &end_index, sizeof(end_index), false); + _FDB_WRITE_STATUS(db, cur_sec_addr + SECTOR_END1_STATUS_OFFSET, end_status, FDB_TSL_STATUS_NUM, FDB_TSL_WRITE, true); + } + /* change current sector to full */ + _FDB_WRITE_STATUS(db, cur_sec_addr, status, FDB_SECTOR_STORE_STATUS_NUM, FDB_SECTOR_STORE_FULL, true); + sector->status = FDB_SECTOR_STORE_FULL; + /* calculate next sector address */ + if (sector->addr + db_sec_size(db) < db_max_size(db)) { + new_sec_addr = sector->addr + db_sec_size(db); + } + else if (db->rollover) { + new_sec_addr = 0; + } else { + /* not rollover */ + return FDB_SAVED_FULL; + } + read_sector_info(db, new_sec_addr, &db->cur_sec, false); + if (sector->status != FDB_SECTOR_STORE_EMPTY) { + /* calculate the oldest sector address */ + if (new_sec_addr + db_sec_size(db) < db_max_size(db)) { + db->oldest_addr = new_sec_addr + db_sec_size(db); + } else { + db->oldest_addr = 0; + } + format_sector(db, new_sec_addr); + read_sector_info(db, new_sec_addr, &db->cur_sec, false); + } + } else if (sector->status == FDB_SECTOR_STORE_FULL) { + /* database full */ + return FDB_SAVED_FULL; + } + + if (sector->status == FDB_SECTOR_STORE_EMPTY) { + /* change the sector to using */ + sector->status = FDB_SECTOR_STORE_USING; + sector->start_time = cur_time; + _FDB_WRITE_STATUS(db, sector->addr, status, FDB_SECTOR_STORE_STATUS_NUM, FDB_SECTOR_STORE_USING, true); + /* save the start timestamp */ + FLASH_WRITE(db, sector->addr + SECTOR_START_TIME_OFFSET, (uint32_t *)&cur_time, sizeof(fdb_time_t), true); + } + + return result; +} + +static fdb_err_t tsl_append(fdb_tsdb_t db, fdb_blob_t blob) +{ + fdb_err_t result = FDB_NO_ERR; + fdb_time_t cur_time = db->get_time(); + + FDB_ASSERT(blob->size <= db->max_len); + + /* check the current timestamp, MUST more than the last save timestamp */ + if (cur_time < db->last_time) { + FDB_INFO("Warning: current timestamp (%" PRIdMAX ") is less than the last save timestamp (%" PRIdMAX "). This tsl will be dropped.\n", + (intmax_t )cur_time, (intmax_t )(db->last_time)); + return FDB_WRITE_ERR; + } + + result = update_sec_status(db, &db->cur_sec, blob, cur_time); + if (result != FDB_NO_ERR) { + return result; + } + + /* write the TSL node */ + result = write_tsl(db, blob, cur_time); + if (result != FDB_NO_ERR) { + return result; + } + + /* recalculate the current using sector info */ + db->cur_sec.end_idx = db->cur_sec.empty_idx; + db->cur_sec.end_time = cur_time; + db->cur_sec.empty_idx += LOG_IDX_DATA_SIZE; + db->cur_sec.empty_data -= FDB_WG_ALIGN(blob->size); + db->cur_sec.remain -= LOG_IDX_DATA_SIZE + FDB_WG_ALIGN(blob->size); + db->last_time = cur_time; + + return result; +} + +/** + * Append a new log to TSDB. + * + * @param db database object + * @param blob log blob data + * + * @return result + */ +fdb_err_t fdb_tsl_append(fdb_tsdb_t db, fdb_blob_t blob) +{ + fdb_err_t result = FDB_NO_ERR; + + if (!db_init_ok(db)) { + FDB_INFO("Error: TSL (%s) isn't initialize OK.\n", db_name(db)); + return FDB_INIT_FAILED; + } + + db_lock(db); + result = tsl_append(db, blob); + db_unlock(db); + + return result; +} + +/** + * The TSDB iterator for each TSL. + * + * @param db database object + * @param cb callback + * @param arg callback argument + */ +void fdb_tsl_iter(fdb_tsdb_t db, fdb_tsl_cb cb, void *arg) +{ + struct tsdb_sec_info sector; + uint32_t sec_addr, traversed_len = 0; + struct fdb_tsl tsl; + + if (!db_init_ok(db)) { + FDB_INFO("Error: TSL (%s) isn't initialize OK.\n", db_name(db)); + } + + if (cb == NULL) { + return; + } + + sec_addr = db->oldest_addr; + /* search all sectors */ + do { + traversed_len += db_sec_size(db); + if (read_sector_info(db, sec_addr, §or, false) != FDB_NO_ERR) { + continue; + } + /* sector has TSL */ + if (sector.status == FDB_SECTOR_STORE_USING || sector.status == FDB_SECTOR_STORE_FULL) { + if (sector.status == FDB_SECTOR_STORE_USING) { + /* copy the current using sector status */ + sector = db->cur_sec; + } + tsl.addr.index = sector.addr + SECTOR_HDR_DATA_SIZE; + /* search all TSL */ + do { + read_tsl(db, &tsl); + /* iterator is interrupted when callback return true */ + if (cb(&tsl, arg)) { + return; + } + } while ((tsl.addr.index = get_next_tsl_addr(§or, &tsl)) != FAILED_ADDR); + } + } while ((sec_addr = get_next_sector_addr(db, §or, traversed_len)) != FAILED_ADDR); +} + +/** + * The TSDB iterator for each TSL by timestamp. + * + * @param db database object + * @param from starting timestap + * @param to ending timestap + * @param cb callback + * @param arg callback argument + */ +void fdb_tsl_iter_by_time(fdb_tsdb_t db, fdb_time_t from, fdb_time_t to, fdb_tsl_cb cb, void *cb_arg) +{ + struct tsdb_sec_info sector; + uint32_t sec_addr, oldest_addr = db->oldest_addr, traversed_len = 0; + struct fdb_tsl tsl; + bool found_start_tsl = false; + + if (!db_init_ok(db)) { + FDB_INFO("Error: TSL (%s) isn't initialize OK.\n", db_name(db)); + } + +// FDB_INFO("from %s", ctime((const time_t * )&from)); +// FDB_INFO("to %s", ctime((const time_t * )&to)); + + if (cb == NULL) { + return; + } + + sec_addr = oldest_addr; + /* search all sectors */ + do { + traversed_len += db_sec_size(db); + if (read_sector_info(db, sec_addr, §or, false) != FDB_NO_ERR) { + continue; + } + /* sector has TSL */ + if ((sector.status == FDB_SECTOR_STORE_USING || sector.status == FDB_SECTOR_STORE_FULL)) { + if (sector.status == FDB_SECTOR_STORE_USING) { + /* copy the current using sector status */ + sector = db->cur_sec; + } + if ((found_start_tsl) || (!found_start_tsl && ((from >= sector.start_time && from <= sector.end_time) + || (sec_addr == oldest_addr && from <= sector.start_time)))) { + uint32_t start = sector.addr + SECTOR_HDR_DATA_SIZE, end = sector.end_idx; + + found_start_tsl = true; + /* search start TSL address, using binary search algorithm */ + while (start <= end) { + tsl.addr.index = start + ((end - start) / 2 + 1) / LOG_IDX_DATA_SIZE * LOG_IDX_DATA_SIZE; + read_tsl(db, &tsl); + if (tsl.time < from) { + start = tsl.addr.index + LOG_IDX_DATA_SIZE; + } else { + end = tsl.addr.index - LOG_IDX_DATA_SIZE; + } + } + tsl.addr.index = start; + /* search all TSL */ + do { + read_tsl(db, &tsl); + if (tsl.time >= from && tsl.time <= to) { + /* iterator is interrupted when callback return true */ + if (cb(&tsl, cb_arg)) { + return; + } + } else { + return; + } + } while ((tsl.addr.index = get_next_tsl_addr(§or, &tsl)) != FAILED_ADDR); + } + } else if (sector.status == FDB_SECTOR_STORE_EMPTY) { + return; + } + } while ((sec_addr = get_next_sector_addr(db, §or, traversed_len)) != FAILED_ADDR); +} + +static bool query_count_cb(fdb_tsl_t tsl, void *arg) +{ + struct query_count_args *args = arg; + + if (tsl->status == args->status) { + args->count++; + } + + return false; +} + +/** + * Query some TSL's count by timestamp and status. + * + * @param db database object + * @param from starting timestap + * @param to ending timestap + * @param status status + */ +size_t fdb_tsl_query_count(fdb_tsdb_t db, fdb_time_t from, fdb_time_t to, fdb_tsl_status_t status) +{ + struct query_count_args arg = { FDB_TSL_UNUSED, 0 }; + + arg.status = status; + + if (!db_init_ok(db)) { + FDB_INFO("Error: TSL (%s) isn't initialize OK.\n", db_name(db)); + return FDB_INIT_FAILED; + } + + fdb_tsl_iter_by_time(db, from, to, query_count_cb, &arg); + + return arg.count; + +} + +/** + * Set the TSL status. + * + * @param db database object + * @param tsl TSL object + * @param status status + * + * @return result + */ +fdb_err_t fdb_tsl_set_status(fdb_tsdb_t db, fdb_tsl_t tsl, fdb_tsl_status_t status) +{ + fdb_err_t result = FDB_NO_ERR; + uint8_t status_table[TSL_STATUS_TABLE_SIZE]; + + /* write the status will by write granularity */ + _FDB_WRITE_STATUS(db, tsl->addr.index, status_table, FDB_TSL_STATUS_NUM, status, true); + + return result; +} + +/** + * Convert the TSL object to blob object + * + * @param tsl TSL object + * @param blob blob object + * + * @return new blob object + */ +fdb_blob_t fdb_tsl_to_blob(fdb_tsl_t tsl, fdb_blob_t blob) +{ + blob->saved.addr = tsl->addr.log; + blob->saved.meta_addr = tsl->addr.index; + blob->saved.len = tsl->log_len; + + return blob; +} + +static bool check_sec_hdr_cb(tsdb_sec_info_t sector, void *arg1, void *arg2) +{ + struct check_sec_hdr_cb_args *arg = arg1; + fdb_tsdb_t db = arg->db; + + if (!sector->check_ok) { + FDB_INFO("Sector (0x%08" PRIX32 ") header info is incorrect.\n", sector->addr); + (arg->check_failed) = true; + return true; + } else if (sector->status == FDB_SECTOR_STORE_USING) { + if (db->cur_sec.addr == FDB_DATA_UNUSED) { + memcpy(&db->cur_sec, sector, sizeof(struct tsdb_sec_info)); + } else { + FDB_INFO("Warning: Sector status is wrong, there are multiple sectors in use.\n"); + (arg->check_failed) = true; + return true; + } + } else if (sector->status == FDB_SECTOR_STORE_EMPTY) { + (arg->empty_num) += 1; + arg->empty_addr = sector->addr; + if ((arg->empty_num) == 1 && db->cur_sec.addr == FDB_DATA_UNUSED) { + memcpy(&db->cur_sec, sector, sizeof(struct tsdb_sec_info)); + } + } + + return false; +} +static bool format_all_cb(tsdb_sec_info_t sector, void *arg1, void *arg2) +{ + fdb_tsdb_t db = arg1; + + format_sector(db, sector->addr); + + return false; +} + +static void tsl_format_all(fdb_tsdb_t db) +{ + struct tsdb_sec_info sector; + + sector.addr = 0; + sector_iterator(db, §or, FDB_SECTOR_STORE_UNUSED, db, NULL, format_all_cb, false); + db->oldest_addr = 0; + db->cur_sec.addr = 0; + db->last_time = 0; + /* read the current using sector info */ + read_sector_info(db, db->cur_sec.addr, &db->cur_sec, false); + + FDB_INFO("All sector format finished.\n"); +} + +/** + * Clean all the data in the TSDB. + * + * @note It's DANGEROUS. This operation is not reversible. + * + * @param db database object + */ +void fdb_tsl_clean(fdb_tsdb_t db) +{ + db_lock(db); + tsl_format_all(db); + db_unlock(db); +} + +/** + * This function will get or set some options of the database + * + * @param db database object + * @param cmd the control command + * @param arg the argument + */ +void fdb_tsdb_control(fdb_tsdb_t db, int cmd, void *arg) +{ + FDB_ASSERT(db); + + switch (cmd) { + case FDB_TSDB_CTRL_SET_SEC_SIZE: + /* this change MUST before database initialization */ + FDB_ASSERT(db->parent.init_ok == false); + db->parent.sec_size = *(uint32_t *)arg; + break; + case FDB_TSDB_CTRL_GET_SEC_SIZE: + *(uint32_t *)arg = db->parent.sec_size; + break; + case FDB_TSDB_CTRL_SET_LOCK: + db->parent.lock = (void (*)(fdb_db_t db))arg; + break; + case FDB_TSDB_CTRL_SET_UNLOCK: + db->parent.unlock = (void (*)(fdb_db_t db))arg; + break; + case FDB_TSDB_CTRL_SET_ROLLOVER: + /* this change MUST after database initialized */ + FDB_ASSERT(db->parent.init_ok == true); + db->rollover = *(bool *)arg; + break; + case FDB_TSDB_CTRL_GET_ROLLOVER: + *(bool *)arg = db->rollover; + break; + case FDB_TSDB_CTRL_GET_LAST_TIME: + *(fdb_time_t *)arg = db->last_time; + break; + case FDB_TSDB_CTRL_SET_FILE_MODE: +#ifdef FDB_USING_FILE_MODE + /* this change MUST before database initialization */ + FDB_ASSERT(db->parent.init_ok == false); + db->parent.file_mode = *(bool *)arg; +#else + FDB_INFO("Error: set file mode Failed. Please defined the FDB_USING_FILE_MODE macro."); +#endif + break; + case FDB_TSDB_CTRL_SET_MAX_SIZE: +#ifdef FDB_USING_FILE_MODE + /* this change MUST before database initialization */ + FDB_ASSERT(db->parent.init_ok == false); + db->parent.max_size = *(uint32_t *)arg; +#endif + break; + case FDB_TSDB_CTRL_SET_NOT_FORMAT: + /* this change MUST before database initialization */ + FDB_ASSERT(db->parent.init_ok == false); + db->parent.not_formatable = *(bool *)arg; + break; + } +} + +/** + * The time series database initialization. + * + * @param db database object + * @param name database name + * @param path FAL mode: partition name, file mode: database saved directory path + * @param get_time get current time function + * @param max_len maximum length of each log + * @param user_data user data + * + * @return result + */ +fdb_err_t fdb_tsdb_init(fdb_tsdb_t db, const char *name, const char *path, fdb_get_time get_time, size_t max_len, void *user_data) +{ + fdb_err_t result = FDB_NO_ERR; + struct tsdb_sec_info sector; + struct check_sec_hdr_cb_args check_sec_arg = { db, false, 0, 0}; + + FDB_ASSERT(get_time); + + result = _fdb_init_ex((fdb_db_t)db, name, path, FDB_DB_TYPE_TS, user_data); + if (result != FDB_NO_ERR) { + goto __exit; + } + + db->get_time = get_time; + db->max_len = max_len; + /* default rollover flag is true */ + db->rollover = true; + db->oldest_addr = FDB_DATA_UNUSED; + db->cur_sec.addr = FDB_DATA_UNUSED; + /* must less than sector size */ + FDB_ASSERT(max_len < db_sec_size(db)); + + /* check all sector header */ + sector.addr = 0; + sector_iterator(db, §or, FDB_SECTOR_STORE_UNUSED, &check_sec_arg, NULL, check_sec_hdr_cb, true); + /* format all sector when check failed */ + if (check_sec_arg.check_failed) { + if (db->parent.not_formatable) { + result = FDB_READ_ERR; + goto __exit; + } else { + tsl_format_all(db); + } + } else { + uint32_t latest_addr; + if (check_sec_arg.empty_num > 0) { + latest_addr = check_sec_arg.empty_addr; + } else { + latest_addr = db->cur_sec.addr; + /* There is no empty sector. */ + latest_addr = db->cur_sec.addr = db_max_size(db) - db_sec_size(db); + } + /* db->cur_sec is the latest sector, and the next is the oldest sector */ + if (latest_addr + db_sec_size(db) >= db_max_size(db)) { + /* db->cur_sec is the the bottom of the database */ + db->oldest_addr = 0; + } else { + db->oldest_addr = latest_addr + db_sec_size(db); + } + } + FDB_DEBUG("TSDB (%s) oldest sectors is 0x%08" PRIX32 ", current using sector is 0x%08" PRIX32 ".\n", db_name(db), db->oldest_addr, + db->cur_sec.addr); + /* read the current using sector info */ + read_sector_info(db, db->cur_sec.addr, &db->cur_sec, true); + /* get last save time */ + if (db->cur_sec.status == FDB_SECTOR_STORE_USING) { + db->last_time = db->cur_sec.end_time; + } else if (db->cur_sec.status == FDB_SECTOR_STORE_EMPTY && db->oldest_addr != db->cur_sec.addr) { + struct tsdb_sec_info sec; + uint32_t addr = db->cur_sec.addr; + + if (addr == 0) { + addr = db_max_size(db) - db_sec_size(db); + } else { + addr -= db_sec_size(db); + } + read_sector_info(db, addr, &sec, false); + db->last_time = sec.end_time; + } + +__exit: + + _fdb_init_finish((fdb_db_t)db, result); + + return result; +} + +/** + * The time series database deinitialization. + * + * @param db database object + * + * @return result + */ +fdb_err_t fdb_tsdb_deinit(fdb_tsdb_t db) +{ + _fdb_deinit((fdb_db_t) db); + + return FDB_NO_ERR; +} + +#endif /* defined(FDB_USING_TSDB) */ diff --git a/APP_Framework/lib/embedded_database/flashdb/fdb_utils.c b/APP_Framework/lib/embedded_database/flashdb/fdb_utils.c new file mode 100644 index 000000000..7e945bacc --- /dev/null +++ b/APP_Framework/lib/embedded_database/flashdb/fdb_utils.c @@ -0,0 +1,305 @@ +/* + * Copyright (c) 2020, Armink, + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief utils + * + * Some utils for this library. + */ + +#include +#include +#include +#include + +#define FDB_LOG_TAG "[utils]" + +static const uint32_t crc32_table[] = +{ + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, + 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, + 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, + 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, + 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, + 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, + 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, + 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, + 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, + 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, + 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, + 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, + 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, + 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, + 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, + 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, + 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d +}; + +/** + * Calculate the CRC32 value of a memory buffer. + * + * @param crc accumulated CRC32 value, must be 0 on first call + * @param buf buffer to calculate CRC32 value for + * @param size bytes in buffer + * + * @return calculated CRC32 value + */ +uint32_t fdb_calc_crc32(uint32_t crc, const void *buf, size_t size) +{ + const uint8_t *p; + + p = (const uint8_t *)buf; + crc = crc ^ ~0U; + + while (size--) { + crc = crc32_table[(crc ^ *p++) & 0xFF] ^ (crc >> 8); + } + + return crc ^ ~0U; +} + +size_t _fdb_set_status(uint8_t status_table[], size_t status_num, size_t status_index) +{ + size_t byte_index = ~0UL; + /* + * | write garn | status0 | status1 | status2 | + * --------------------------------------------------------------------------------- + * | 1bit | 0xFF | 0x7F | 0x3F | + * | 8bit | 0xFFFF | 0x00FF | 0x0000 | + * | 32bit | 0xFFFFFFFF FFFFFFFF | 0x00FFFFFF FFFFFFFF | 0x00FFFFFF 00FFFFFF | + */ + memset(status_table, 0xFF, FDB_STATUS_TABLE_SIZE(status_num)); + if (status_index > 0) { +#if (FDB_WRITE_GRAN == 1) + byte_index = (status_index - 1) / 8; + status_table[byte_index] &= (0x00ff >> (status_index % 8)); +#else + byte_index = (status_index - 1) * (FDB_WRITE_GRAN / 8); + status_table[byte_index] = 0x00; +#endif /* FDB_WRITE_GRAN == 1 */ + } + + return byte_index; +} + +size_t _fdb_get_status(uint8_t status_table[], size_t status_num) +{ + size_t i = 0, status_num_bak = --status_num; + + while (status_num --) { + /* get the first 0 position from end address to start address */ +#if (FDB_WRITE_GRAN == 1) + if ((status_table[status_num / 8] & (0x80 >> (status_num % 8))) == 0x00) { + break; + } +#else /* (FDB_WRITE_GRAN == 8) || (FDB_WRITE_GRAN == 32) || (FDB_WRITE_GRAN == 64) */ + if (status_table[status_num * FDB_WRITE_GRAN / 8] == 0x00) { + break; + } +#endif /* FDB_WRITE_GRAN == 1 */ + i++; + } + + return status_num_bak - i; +} + +fdb_err_t _fdb_write_status(fdb_db_t db, uint32_t addr, uint8_t status_table[], size_t status_num, size_t status_index, bool sync) +{ + fdb_err_t result = FDB_NO_ERR; + size_t byte_index; + + FDB_ASSERT(status_index < status_num); + FDB_ASSERT(status_table); + + /* set the status first */ + byte_index = _fdb_set_status(status_table, status_num, status_index); + + /* the first status table value is all 1, so no need to write flash */ + if (byte_index == ~0UL) { + return FDB_NO_ERR; + } +#if (FDB_WRITE_GRAN == 1) + result = _fdb_flash_write(db, addr + byte_index, (uint32_t *)&status_table[byte_index], 1, sync); +#else /* (FDB_WRITE_GRAN == 8) || (FDB_WRITE_GRAN == 32) || (FDB_WRITE_GRAN == 64) */ + /* write the status by write granularity + * some flash (like stm32 onchip) NOT supported repeated write before erase */ + result = _fdb_flash_write(db, addr + byte_index, (uint32_t *) &status_table[byte_index], FDB_WRITE_GRAN / 8, sync); +#endif /* FDB_WRITE_GRAN == 1 */ + + return result; +} + +size_t _fdb_read_status(fdb_db_t db, uint32_t addr, uint8_t status_table[], size_t total_num) +{ + FDB_ASSERT(status_table); + + _fdb_flash_read(db, addr, (uint32_t *) status_table, FDB_STATUS_TABLE_SIZE(total_num)); + + return _fdb_get_status(status_table, total_num); +} + +/* + * find the continue 0xFF flash address to end address + */ +uint32_t _fdb_continue_ff_addr(fdb_db_t db, uint32_t start, uint32_t end) +{ + uint8_t buf[32], last_data = 0x00; + size_t i, addr = start, read_size; + + for (; start < end; start += sizeof(buf)) { + if (start + sizeof(buf) < end) { + read_size = sizeof(buf); + } else { + read_size = end - start; + } + _fdb_flash_read(db, start, (uint32_t *) buf, read_size); + for (i = 0; i < read_size; i++) { + if (last_data != 0xFF && buf[i] == 0xFF) { + addr = start + i; + } + last_data = buf[i]; + } + } + + if (last_data == 0xFF) { + return FDB_WG_ALIGN(addr); + } else { + return end; + } +} + +/** + * Make a blob object. + * + * @param blob blob object + * @param value_buf value buffer + * @param buf_len buffer length + * + * @return new blob object + */ +fdb_blob_t fdb_blob_make(fdb_blob_t blob, const void *value_buf, size_t buf_len) +{ + blob->buf = (void *)value_buf; + blob->size = buf_len; + + return blob; +} + +/** + * Read the blob object in database. + * + * @param db database object + * @param blob blob object + * + * @return read length + */ +size_t fdb_blob_read(fdb_db_t db, fdb_blob_t blob) +{ + size_t read_len = blob->size; + + if (read_len > blob->saved.len) { + read_len = blob->saved.len; + } + _fdb_flash_read(db, blob->saved.addr, blob->buf, read_len); + + return read_len; +} + +#ifdef FDB_USING_FILE_MODE +extern fdb_err_t _fdb_file_read(fdb_db_t db, uint32_t addr, void *buf, size_t size); +extern fdb_err_t _fdb_file_write(fdb_db_t db, uint32_t addr, const void *buf, size_t size, bool sync); +extern fdb_err_t _fdb_file_erase(fdb_db_t db, uint32_t addr, size_t size); +#endif /* FDB_USING_FILE_LIBC */ + +fdb_err_t _fdb_flash_read(fdb_db_t db, uint32_t addr, void *buf, size_t size) +{ + fdb_err_t result = FDB_NO_ERR; + + if (db->file_mode) { +#ifdef FDB_USING_FILE_MODE + return _fdb_file_read(db, addr, buf, size); +#else + return FDB_READ_ERR; +#endif + } else { +#ifdef FDB_USING_FAL_MODE + fal_partition_read(db->storage.part, addr, (uint8_t *) buf, size); +#endif + } + + return result; +} + +fdb_err_t _fdb_flash_erase(fdb_db_t db, uint32_t addr, size_t size) +{ + fdb_err_t result = FDB_NO_ERR; + + if (db->file_mode) { +#ifdef FDB_USING_FILE_MODE + return _fdb_file_erase(db, addr, size); +#else + return FDB_ERASE_ERR; +#endif /* FDB_USING_FILE_MODE */ + } else { +#ifdef FDB_USING_FAL_MODE + if (fal_partition_erase(db->storage.part, addr, size) < 0) { + result = FDB_ERASE_ERR; + } +#endif + } + + return result; +} + +fdb_err_t _fdb_flash_write(fdb_db_t db, uint32_t addr, const void *buf, size_t size, bool sync) +{ + fdb_err_t result = FDB_NO_ERR; + + if (db->file_mode) { +#ifdef FDB_USING_FILE_MODE + return _fdb_file_write(db, addr, buf, size, sync); +#else + return FDB_READ_ERR; +#endif /* FDB_USING_FILE_MODE */ + } else { +#ifdef FDB_USING_FAL_MODE + if (fal_partition_write(db->storage.part, addr, (uint8_t *)buf, size) < 0) + { + result = FDB_WRITE_ERR; + } +#endif + } + + return result; + +} diff --git a/APP_Framework/lib/embedded_database/flashdb/flashdb.h b/APP_Framework/lib/embedded_database/flashdb/flashdb.h new file mode 100644 index 000000000..e64fd8f68 --- /dev/null +++ b/APP_Framework/lib/embedded_database/flashdb/flashdb.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2020, Armink, + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief Public APIs. + */ + +#ifndef _FLASHDB_H_ +#define _FLASHDB_H_ + +#include +#include +#include +#include +#include +#include + +#ifdef FDB_USING_FAL_MODE +#include +#endif + +#include + + +#ifdef __cplusplus +extern "C" { +#endif + +/* FlashDB database API */ +fdb_err_t fdb_kvdb_init (fdb_kvdb_t db, const char *name, const char *path, struct fdb_default_kv *default_kv, + void *user_data); +void fdb_kvdb_control(fdb_kvdb_t db, int cmd, void *arg); +fdb_err_t fdb_kvdb_deinit(fdb_kvdb_t db); +fdb_err_t fdb_tsdb_init (fdb_tsdb_t db, const char *name, const char *path, fdb_get_time get_time, size_t max_len, + void *user_data); +void fdb_tsdb_control(fdb_tsdb_t db, int cmd, void *arg); +fdb_err_t fdb_tsdb_deinit(fdb_tsdb_t db); + +/* blob API */ +fdb_blob_t fdb_blob_make (fdb_blob_t blob, const void *value_buf, size_t buf_len); +size_t fdb_blob_read (fdb_db_t db, fdb_blob_t blob); + +/* Key-Value API like a KV DB */ +fdb_err_t fdb_kv_set (fdb_kvdb_t db, const char *key, const char *value); +char *fdb_kv_get (fdb_kvdb_t db, const char *key); +fdb_err_t fdb_kv_set_blob (fdb_kvdb_t db, const char *key, fdb_blob_t blob); +size_t fdb_kv_get_blob (fdb_kvdb_t db, const char *key, fdb_blob_t blob); +fdb_err_t fdb_kv_del (fdb_kvdb_t db, const char *key); +fdb_kv_t fdb_kv_get_obj (fdb_kvdb_t db, const char *key, fdb_kv_t kv); +fdb_blob_t fdb_kv_to_blob (fdb_kv_t kv, fdb_blob_t blob); +fdb_err_t fdb_kv_set_default (fdb_kvdb_t db); +void fdb_kv_print (fdb_kvdb_t db); +fdb_kv_iterator_t fdb_kv_iterator_init(fdb_kv_iterator_t itr); +bool fdb_kv_iterate (fdb_kvdb_t db, fdb_kv_iterator_t itr); + +/* Time series log API like a TSDB */ +fdb_err_t fdb_tsl_append (fdb_tsdb_t db, fdb_blob_t blob); +void fdb_tsl_iter (fdb_tsdb_t db, fdb_tsl_cb cb, void *cb_arg); +void fdb_tsl_iter_by_time(fdb_tsdb_t db, fdb_time_t from, fdb_time_t to, fdb_tsl_cb cb, void *cb_arg); +size_t fdb_tsl_query_count (fdb_tsdb_t db, fdb_time_t from, fdb_time_t to, fdb_tsl_status_t status); +fdb_err_t fdb_tsl_set_status (fdb_tsdb_t db, fdb_tsl_t tsl, fdb_tsl_status_t status); +void fdb_tsl_clean (fdb_tsdb_t db); +fdb_blob_t fdb_tsl_to_blob (fdb_tsl_t tsl, fdb_blob_t blob); + +/* fdb_utils.c */ +uint32_t fdb_calc_crc32(uint32_t crc, const void *buf, size_t size); + +#ifdef __cplusplus +} +#endif + +#endif /* _FLASHDB_H_ */ diff --git a/Ubiquitous/RT_Thread/build.sh b/Ubiquitous/RT_Thread/download.sh similarity index 100% rename from Ubiquitous/RT_Thread/build.sh rename to Ubiquitous/RT_Thread/download.sh