diff --git a/applications/system/js_app/application.fam b/applications/system/js_app/application.fam index 40fe06cff..779ecd8ad 100644 --- a/applications/system/js_app/application.fam +++ b/applications/system/js_app/application.fam @@ -78,3 +78,13 @@ App( requires=["js_app"], sources=["modules/js_blebeacon.c"], ) + +App( + appid="js_math", + apptype=FlipperAppType.PLUGIN, + entry_point="js_math_ep", + requires=["js_app"], + sources=["modules/js_math.c"], +) + + diff --git a/applications/system/js_app/examples/apps/Scripts/math.js b/applications/system/js_app/examples/apps/Scripts/math.js new file mode 100644 index 000000000..49212f904 --- /dev/null +++ b/applications/system/js_app/examples/apps/Scripts/math.js @@ -0,0 +1,47 @@ +let math = require("math"); + +let absResult = math.abs(-5); +let acosResult = math.acos(0.5); +let acoshResult = math.acosh(2); +let asinResult = math.asin(0.5); +let asinhResult = math.asinh(2); +let atanResult = math.atan(1); +let atan2Result = math.atan2(1, 1); +let atanhResult = math.atanh(0.5); +let cbrtResult = math.cbrt(27); +let ceilResult = math.ceil(5.3); +let clz32Result = math.clz32(1); +let cosResult = math.cos(math.PI); +let expResult = math.exp(1); +let floorResult = math.floor(5.7); +let maxResult = math.max(3, 5); +let minResult = math.min(3, 5); +let powResult = math.pow(2, 3); +let randomResult = math.random(); +let signResult = math.sign(-5); +let sinResult = math.sin(math.PI / 2); +let sqrtResult = math.sqrt(25); +let truncResult = math.trunc(5.7); + +print("math.abs(-5):", absResult); +print("math.acos(0.5):", acosResult); +print("math.acosh(2):", acoshResult); +print("math.asin(0.5):", asinResult); +print("math.asinh(2):", asinhResult); +print("math.atan(1):", atanResult); +print("math.atan2(1, 1):", atan2Result); +print("math.atanh(0.5):", atanhResult); +print("math.cbrt(27):", cbrtResult); +print("math.ceil(5.3):", ceilResult); +print("math.clz32(1):", clz32Result); +print("math.cos(math.PI):", cosResult); +print("math.exp(1):", expResult); +print("math.floor(5.7):", floorResult); +print("math.max(3, 5):", maxResult); +print("math.min(3, 5):", minResult); +print("math.pow(2, 3):", powResult); +print("math.random():", randomResult); +print("math.sign(-5):", signResult); +print("math.sin(math.PI/2):", sinResult); +print("math.sqrt(25):", sqrtResult); +print("math.trunc(5.7):", truncResult); \ No newline at end of file diff --git a/applications/system/js_app/modules/js_blebeacon.c b/applications/system/js_app/modules/js_blebeacon.c index 3fbf7f6da..ff7176c52 100644 --- a/applications/system/js_app/modules/js_blebeacon.c +++ b/applications/system/js_app/modules/js_blebeacon.c @@ -9,7 +9,7 @@ typedef struct { GapExtraBeaconConfig beacon_config; } JSblebeaconInst; -// Define the OUI Map as a constant array of structs + struct OUI_MAP_ENTRY { const char *brand; const char *oui; diff --git a/applications/system/js_app/modules/js_math.c b/applications/system/js_app/modules/js_math.c new file mode 100644 index 000000000..d17b72b88 --- /dev/null +++ b/applications/system/js_app/modules/js_math.c @@ -0,0 +1,309 @@ +#include "../js_modules.h" +#include "furi_hal_random.h" + +#define MJS_PI (double)3.14159265358979323846 +#define MJS_E (double)2.7182818284590452354 + +static void ret_bad_args(struct mjs* mjs, const char* error) { + mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, "%s", error); + mjs_return(mjs, mjs_mk_undefined()); +} + +static bool check_arg_count(struct mjs* mjs, size_t count) { + size_t num_args = mjs_nargs(mjs); + if(num_args != count) { + ret_bad_args(mjs, "Wrong argument count"); + return false; + } + return true; +} + +void mjs_abs(struct mjs* mjs) { + if (!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { + mjs_return(mjs, MJS_UNDEFINED); + } + double x = mjs_get_double(mjs, mjs_arg(mjs, 0)); + mjs_return(mjs, x < 0 ? mjs_mk_number(mjs, -x) : mjs_arg(mjs, 0)); +} + +void mjs_acos(struct mjs* mjs) { + if (!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { + mjs_return(mjs, MJS_UNDEFINED); + } + double x = mjs_get_double(mjs, mjs_arg(mjs, 0)); + if (x < -1 || x > 1) { + ret_bad_args(mjs, "Invalid input value for Math.acos"); + mjs_return(mjs, MJS_UNDEFINED); + } + mjs_return(mjs, mjs_mk_number(mjs, MJS_PI / (double)2 - atan(x / sqrt(1 - x * x)))); +} + +void mjs_acosh(struct mjs* mjs) { + if (!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { + mjs_return(mjs, MJS_UNDEFINED); + } + double x = mjs_get_double(mjs, mjs_arg(mjs, 0)); + if (x < 1) { + ret_bad_args(mjs, "Invalid input value for Math.acosh"); + mjs_return(mjs, MJS_UNDEFINED); + } + mjs_return(mjs, mjs_mk_number(mjs, log(x + sqrt(x * x - 1)))); +} + +void mjs_asin(struct mjs* mjs) { + if (!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { + mjs_return(mjs, MJS_UNDEFINED); + } + double x = mjs_get_double(mjs, mjs_arg(mjs, 0)); + mjs_return(mjs, mjs_mk_number(mjs, atan(x / sqrt(1 - x * x)))); +} + +void mjs_asinh(struct mjs* mjs) { + if (!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { + mjs_return(mjs, MJS_UNDEFINED); + } + double x = mjs_get_double(mjs, mjs_arg(mjs, 0)); + mjs_return(mjs, mjs_mk_number(mjs, log(x + sqrt(x * x + 1)))); +} + +void mjs_atan(struct mjs* mjs) { + if (!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { + mjs_return(mjs, MJS_UNDEFINED); + } + double x = mjs_get_double(mjs, mjs_arg(mjs, 0)); + mjs_return(mjs, mjs_mk_number(mjs, atan(x))); +} + +void mjs_atan2(struct mjs* mjs) { + if (!check_arg_count(mjs, 2) || !mjs_is_number(mjs_arg(mjs, 0)) || !mjs_is_number(mjs_arg(mjs, 1))) { + mjs_return(mjs, MJS_UNDEFINED); + } + double y = mjs_get_double(mjs, mjs_arg(mjs, 0)); + double x = mjs_get_double(mjs, mjs_arg(mjs, 1)); + mjs_return(mjs, mjs_mk_number(mjs, atan2(y, x))); +} + +void mjs_atanh(struct mjs* mjs) { + if (!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { + mjs_return(mjs, MJS_UNDEFINED); + } + double x = mjs_get_double(mjs, mjs_arg(mjs, 0)); + if (x <= -1 || x >= 1) { + ret_bad_args(mjs, "Invalid input value for Math.atanh"); + mjs_return(mjs, MJS_UNDEFINED); + } + mjs_return(mjs, mjs_mk_number(mjs, (double)0.5 * log((1 + x) / (1 - x)))); +} + +void mjs_cbrt(struct mjs* mjs) { + if (!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { + mjs_return(mjs, MJS_UNDEFINED); + } + double x = mjs_get_double(mjs, mjs_arg(mjs, 0)); + mjs_return(mjs, mjs_mk_number(mjs, pow(x, 1.0 / 3.0))); +} + +void mjs_ceil(struct mjs* mjs) { + if (!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { + mjs_return(mjs, MJS_UNDEFINED); + } + double x = mjs_get_double(mjs, mjs_arg(mjs, 0)); + mjs_return(mjs, mjs_mk_number(mjs, (int)(x + (double)0.5))); +} + +void mjs_clz32(struct mjs* mjs) { + if (!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { + mjs_return(mjs, MJS_UNDEFINED); + } + unsigned int x = (unsigned int)mjs_get_int(mjs, mjs_arg(mjs, 0)); + int count = 0; + while (x) { + x >>= 1; + count++; + } + mjs_return(mjs, mjs_mk_number(mjs, 32 - count)); +} + +void mjs_cos(struct mjs* mjs) { + if (!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { + mjs_return(mjs, MJS_UNDEFINED); + } + double x = mjs_get_double(mjs, mjs_arg(mjs, 0)); + mjs_return(mjs, mjs_mk_number(mjs, cos(x))); +} + +void mjs_exp(struct mjs* mjs) { + if (!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { + mjs_return(mjs, MJS_UNDEFINED); + } + double x = mjs_get_double(mjs, mjs_arg(mjs, 0)); + double result = 1; + double term = 1; + for (int i = 1; i < 100; i++) { + term *= x / i; + result += term; + } + mjs_return(mjs, mjs_mk_number(mjs, result)); +} + +void mjs_floor(struct mjs* mjs) { + if (!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { + mjs_return(mjs, MJS_UNDEFINED); + } + double x = mjs_get_double(mjs, mjs_arg(mjs, 0)); + mjs_return(mjs, mjs_mk_number(mjs, (int)x)); +} + +void mjs_log(struct mjs* mjs) { + if (!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { + mjs_return(mjs, MJS_UNDEFINED); + } + double x = mjs_get_double(mjs, mjs_arg(mjs, 0)); + if (x <= 0) { + ret_bad_args(mjs, "Invalid input value for Math.log"); + mjs_return(mjs, MJS_UNDEFINED); + } + double result = 0; + while (x >= MJS_E) { + x /= MJS_E; + result++; + } + mjs_return(mjs, mjs_mk_number(mjs, result + log(x))); +} + +void mjs_max(struct mjs* mjs) { + if (!check_arg_count(mjs, 2) || !mjs_is_number(mjs_arg(mjs, 0)) || !mjs_is_number(mjs_arg(mjs, 1))) { + mjs_return(mjs, MJS_UNDEFINED); + } + double x = mjs_get_double(mjs, mjs_arg(mjs, 0)); + double y = mjs_get_double(mjs, mjs_arg(mjs, 1)); + mjs_return(mjs, mjs_mk_number(mjs, x > y ? x : y)); +} + +void mjs_min(struct mjs* mjs) { + if (!check_arg_count(mjs, 2) || !mjs_is_number(mjs_arg(mjs, 0)) || !mjs_is_number(mjs_arg(mjs, 1))) { + mjs_return(mjs, MJS_UNDEFINED); + } + double x = mjs_get_double(mjs, mjs_arg(mjs, 0)); + double y = mjs_get_double(mjs, mjs_arg(mjs, 1)); + mjs_return(mjs, mjs_mk_number(mjs, x < y ? x : y)); +} + +void mjs_pow(struct mjs* mjs) { + if (!check_arg_count(mjs, 2) || !mjs_is_number(mjs_arg(mjs, 0)) || !mjs_is_number(mjs_arg(mjs, 1))) { + mjs_return(mjs, MJS_UNDEFINED); + } + double base = mjs_get_double(mjs, mjs_arg(mjs, 0)); + double exponent = mjs_get_double(mjs, mjs_arg(mjs, 1)); + double result = 1; + for (int i = 0; i < exponent; i++) { + result *= base; + } + mjs_return(mjs, mjs_mk_number(mjs, result)); +} + +void mjs_random(struct mjs* mjs) { + if (!check_arg_count(mjs, 0)) { + mjs_return(mjs, MJS_UNDEFINED); + } + const uint32_t random_val = furi_hal_random_get(); + double rnd = (double)random_val / RAND_MAX; + mjs_return(mjs, mjs_mk_number(mjs, rnd)); +} + +void mjs_sign(struct mjs* mjs) { + if (!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { + mjs_return(mjs, MJS_UNDEFINED); + } + double x = mjs_get_double(mjs, mjs_arg(mjs, 0)); + mjs_return(mjs, mjs_mk_number(mjs, x == 0 ? 0 : (x < 0 ? -1 : 1))); +} + +void mjs_sin(struct mjs* mjs) { + if (!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { + mjs_return(mjs, MJS_UNDEFINED); + } + double x = mjs_get_double(mjs, mjs_arg(mjs, 0)); + double result = x; + double term = x; + for (int i = 1; i < 10; i++) { + term *= -x * x / ((2 * i) * (2 * i + 1)); + result += term; + } + mjs_return(mjs, mjs_mk_number(mjs, result)); +} + +void mjs_sqrt(struct mjs* mjs) { + if (!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { + mjs_return(mjs, MJS_UNDEFINED); + } + double x = mjs_get_double(mjs, mjs_arg(mjs, 0)); + if (x < 0) { + ret_bad_args(mjs, "Invalid input value for Math.sqrt"); + mjs_return(mjs, MJS_UNDEFINED); + } + double result = 1; + while (result * result < x) { + result += (double)0.001; + } + mjs_return(mjs, mjs_mk_number(mjs, result)); +} + +void mjs_trunc(struct mjs* mjs) { + if (!check_arg_count(mjs, 1) || !mjs_is_number(mjs_arg(mjs, 0))) { + mjs_return(mjs, MJS_UNDEFINED); + } + double x = mjs_get_double(mjs, mjs_arg(mjs, 0)); + mjs_return(mjs, mjs_mk_number(mjs, x < 0 ? ceil(x) : floor(x))); +} + + +static void* js_math_create(struct mjs *mjs, mjs_val_t* object) { + mjs_val_t math_obj = mjs_mk_object(mjs); + mjs_set(mjs, math_obj, "abs", ~0, MJS_MK_FN(mjs_abs)); + mjs_set(mjs, math_obj, "acos", ~0, MJS_MK_FN(mjs_acos)); + mjs_set(mjs, math_obj, "acosh", ~0, MJS_MK_FN(mjs_acosh)); + mjs_set(mjs, math_obj, "asin", ~0, MJS_MK_FN(mjs_asin)); + mjs_set(mjs, math_obj, "asinh", ~0, MJS_MK_FN(mjs_asinh)); + mjs_set(mjs, math_obj, "atan", ~0, MJS_MK_FN(mjs_atan)); + mjs_set(mjs, math_obj, "atan2", ~0, MJS_MK_FN(mjs_atan2)); + mjs_set(mjs, math_obj, "atanh", ~0, MJS_MK_FN(mjs_atanh)); + mjs_set(mjs, math_obj, "cbrt", ~0, MJS_MK_FN(mjs_cbrt)); + mjs_set(mjs, math_obj, "ceil", ~0, MJS_MK_FN(mjs_ceil)); + mjs_set(mjs, math_obj, "clz32", ~0, MJS_MK_FN(mjs_clz32)); + mjs_set(mjs, math_obj, "cos", ~0, MJS_MK_FN(mjs_cos)); + mjs_set(mjs, math_obj, "exp", ~0, MJS_MK_FN(mjs_exp)); + mjs_set(mjs, math_obj, "floor", ~0, MJS_MK_FN(mjs_floor)); + mjs_set(mjs, math_obj, "log", ~0, MJS_MK_FN(mjs_log)); + mjs_set(mjs, math_obj, "max", ~0, MJS_MK_FN(mjs_max)); + mjs_set(mjs, math_obj, "min", ~0, MJS_MK_FN(mjs_min)); + mjs_set(mjs, math_obj, "pow", ~0, MJS_MK_FN(mjs_pow)); + mjs_set(mjs, math_obj, "random", ~0, MJS_MK_FN(mjs_random)); + mjs_set(mjs, math_obj, "sign", ~0, MJS_MK_FN(mjs_sign)); + mjs_set(mjs, math_obj, "sin", ~0, MJS_MK_FN(mjs_sin)); + mjs_set(mjs, math_obj, "sqrt", ~0, MJS_MK_FN(mjs_sqrt)); + mjs_set(mjs, math_obj, "trunc", ~0, MJS_MK_FN(mjs_trunc)); + mjs_set(mjs, math_obj, "PI", ~0, mjs_mk_number(mjs, MJS_PI)); + *object = math_obj; + return object; +} + +static void js_math_destroy(void *ptr) { + UNUSED(ptr); +} + +static const JsModuleDescriptor js_math_desc = { + "math", + js_math_create, + js_math_destroy, +}; + +static const FlipperAppPluginDescriptor btkicker_plugin_descriptor = { + .appid = PLUGIN_APP_ID, + .ep_api_version = PLUGIN_API_VERSION, + .entry_point = &js_math_desc, +}; + +const FlipperAppPluginDescriptor* js_math_ep(void) { + return &btkicker_plugin_descriptor; +} \ No newline at end of file