numbl 0.1.2 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -62
- package/binding.gyp +3 -1
- package/dist-cli/cli.js +661 -480
- package/dist-lib/lib.js +322 -76
- package/dist-lib/numbl-core/fileIOAdapter.d.ts +12 -2
- package/dist-lib/numbl-core/helpers/prng.d.ts +3 -0
- package/dist-lib/numbl-core/interpreter/builtins/time-system.d.ts +1 -1
- package/dist-lib/numbl-core/interpreter/builtins/types.d.ts +0 -1
- package/dist-lib/numbl-core/native/lapack-bridge.d.ts +18 -0
- package/dist-lib/numbl-core/version.d.ts +1 -1
- package/native/elemwise.cpp +58 -0
- package/native/numbl_addon.cpp +6 -0
- package/native/numbl_addon_common.h +3 -0
- package/native/randn.cpp +101 -0
- package/native/unary_elemwise.cpp +67 -0
- package/package.json +3 -1
|
@@ -5,6 +5,16 @@
|
|
|
5
5
|
* Concrete implementations (e.g. NodeFileIOAdapter) are injected
|
|
6
6
|
* from the CLI or other host environments via ExecOptions.fileIO.
|
|
7
7
|
*/
|
|
8
|
+
/** Options extracted from a weboptions struct for websave/webread. */
|
|
9
|
+
export interface WebOptions {
|
|
10
|
+
timeout?: number;
|
|
11
|
+
requestMethod?: string;
|
|
12
|
+
headerFields?: [string, string][];
|
|
13
|
+
username?: string;
|
|
14
|
+
password?: string;
|
|
15
|
+
keyName?: string;
|
|
16
|
+
keyValue?: string;
|
|
17
|
+
}
|
|
8
18
|
export interface FileIOAdapter {
|
|
9
19
|
/** Open a file, returns an integer file identifier (fid). */
|
|
10
20
|
fopen(filename: string, permission: string): number;
|
|
@@ -39,9 +49,9 @@ export interface FileIOAdapter {
|
|
|
39
49
|
/** Create a directory (and parents). Returns true on success. Optional. */
|
|
40
50
|
mkdir?(dirPath: string): boolean;
|
|
41
51
|
/** Download a URL to a file. Optional. */
|
|
42
|
-
websave?(url: string, filename: string): void;
|
|
52
|
+
websave?(url: string, filename: string, options?: WebOptions): void;
|
|
43
53
|
/** Fetch content from a URL and return as a string. Optional. */
|
|
44
|
-
webread?(url: string): string;
|
|
54
|
+
webread?(url: string, options?: WebOptions): string;
|
|
45
55
|
/** Delete files matching a pattern (supports globs). Optional. */
|
|
46
56
|
deleteFile?(pattern: string): void;
|
|
47
57
|
/** Remove a directory. If recursive is true, remove contents first. Returns true on success. Optional. */
|
|
@@ -2,12 +2,15 @@
|
|
|
2
2
|
* Seedable PRNG (xoshiro128**) for random number generation
|
|
3
3
|
*/
|
|
4
4
|
import { RuntimeValue } from "../runtime/index.js";
|
|
5
|
+
import { type FloatXArrayType } from "../runtime/types.js";
|
|
5
6
|
export declare function setRngShuffle(): void;
|
|
6
7
|
export declare function setRngSeed(seed: number): void;
|
|
7
8
|
export declare function seedRng(seed: number): void;
|
|
8
9
|
/** Return a random float in [0, 1) using seeded or unseeded PRNG */
|
|
9
10
|
export declare function rngRandom(): number;
|
|
10
11
|
export declare function boxMullerRandom(): number;
|
|
12
|
+
/** Fill a typed array with normal random values (bulk polar method) */
|
|
13
|
+
export declare function fillRandn(data: FloatXArrayType): void;
|
|
11
14
|
/** Return the current RNG state as a struct {Type, Seed, State} */
|
|
12
15
|
export declare function getRngStateStruct(): RuntimeValue;
|
|
13
16
|
/** Restore RNG state from a struct previously returned by rng() */
|
|
@@ -43,7 +43,6 @@ export declare function mkc(re: number, im: number): number | RuntimeComplexNumb
|
|
|
43
43
|
export declare function makeTensor(data: InstanceType<typeof FloatXArray>, imag: InstanceType<typeof FloatXArray> | undefined, shape: number[]): RuntimeTensor;
|
|
44
44
|
/** Type rule requiring two scalar numbers */
|
|
45
45
|
export declare function binaryNumberOnly(argTypes: JitType[]): JitType[] | null;
|
|
46
|
-
/** Apply a unary element-wise function with complex support */
|
|
47
46
|
export declare function applyUnaryElemwise(v: RuntimeValue, realFn: (x: number) => number, complexFn: (re: number, im: number) => {
|
|
48
47
|
re: number;
|
|
49
48
|
im: number;
|
|
@@ -375,8 +375,26 @@ export interface LapackBridge {
|
|
|
375
375
|
WRe?: Float64Array;
|
|
376
376
|
WIm?: Float64Array;
|
|
377
377
|
};
|
|
378
|
+
/**
|
|
379
|
+
* Bulk fill normal random values using Marsaglia polar + xoshiro128**.
|
|
380
|
+
* State is mutated in-place. Spare is passed in/out to preserve pair caching.
|
|
381
|
+
*/
|
|
382
|
+
fillRandn?(state: Uint32Array, n: number, spare: number, hasSpare: boolean): {
|
|
383
|
+
data: Float64Array;
|
|
384
|
+
spare: number;
|
|
385
|
+
hasSpare: boolean;
|
|
386
|
+
};
|
|
387
|
+
/**
|
|
388
|
+
* Unary element-wise math on a real Float64Array.
|
|
389
|
+
* op: 0=exp, 1=log, 2=log2, 3=log10, 4=sqrt, 5=abs, 6=floor, 7=ceil,
|
|
390
|
+
* 8=round, 9=trunc, 10=sin, 11=cos, 12=tan, 13=asin, 14=acos, 15=atan,
|
|
391
|
+
* 16=sinh, 17=cosh, 18=tanh, 19=sign
|
|
392
|
+
*/
|
|
393
|
+
unaryElemwise?(arr: Float64Array, op: number): Float64Array;
|
|
378
394
|
/** Element-wise binary op on real Float64Arrays. op: 0=add, 1=sub, 2=mul, 3=div */
|
|
379
395
|
elemwise?(a: Float64Array, b: Float64Array, op: number): Float64Array;
|
|
396
|
+
/** Scalar-tensor element-wise op. scalarOnLeft: true → scalar op arr[i], false → arr[i] op scalar */
|
|
397
|
+
elemwiseScalar?(scalar: number, arr: Float64Array, op: number, scalarOnLeft: boolean): Float64Array;
|
|
380
398
|
/** Element-wise binary op on complex Float64Arrays. op: 0=add, 1=sub, 2=mul, 3=div */
|
|
381
399
|
elemwiseComplex?(aRe: Float64Array, aIm: Float64Array | null, bRe: Float64Array, bIm: Float64Array | null, op: number): {
|
|
382
400
|
re: Float64Array;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/** Numbl version, used for JIT disk cache invalidation. */
|
|
2
|
-
export declare const NUMBL_VERSION = "0.1.
|
|
2
|
+
export declare const NUMBL_VERSION = "0.1.3";
|
package/native/elemwise.cpp
CHANGED
|
@@ -5,6 +5,11 @@
|
|
|
5
5
|
* elemwise(a: Float64Array, b: Float64Array, op: number): Float64Array
|
|
6
6
|
* op: 0=add, 1=sub, 2=mul, 3=div
|
|
7
7
|
*
|
|
8
|
+
* elemwiseScalar(scalar: number, arr: Float64Array, op: number, scalarOnLeft: boolean): Float64Array
|
|
9
|
+
* op: 0=add, 1=sub, 2=mul, 3=div
|
|
10
|
+
* scalarOnLeft=true: result[i] = scalar op arr[i]
|
|
11
|
+
* scalarOnLeft=false: result[i] = arr[i] op scalar
|
|
12
|
+
*
|
|
8
13
|
* Complex:
|
|
9
14
|
* elemwiseComplex(aRe: Float64Array, aIm: Float64Array,
|
|
10
15
|
* bRe: Float64Array, bIm: Float64Array,
|
|
@@ -166,3 +171,56 @@ Napi::Value ElemwiseComplex(const Napi::CallbackInfo& info) {
|
|
|
166
171
|
}
|
|
167
172
|
return result;
|
|
168
173
|
}
|
|
174
|
+
|
|
175
|
+
// ── elemwiseScalar() — scalar-tensor element-wise binary op ────────────────
|
|
176
|
+
|
|
177
|
+
Napi::Value ElemwiseScalar(const Napi::CallbackInfo& info) {
|
|
178
|
+
Napi::Env env = info.Env();
|
|
179
|
+
|
|
180
|
+
if (info.Length() < 4
|
|
181
|
+
|| !info[0].IsNumber()
|
|
182
|
+
|| !info[1].IsTypedArray()
|
|
183
|
+
|| !info[2].IsNumber()
|
|
184
|
+
|| !info[3].IsBoolean()) {
|
|
185
|
+
Napi::TypeError::New(env,
|
|
186
|
+
"elemwiseScalar: expected (number scalar, Float64Array arr, number op, boolean scalarOnLeft)")
|
|
187
|
+
.ThrowAsJavaScriptException();
|
|
188
|
+
return env.Null();
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
double scalar = info[0].As<Napi::Number>().DoubleValue();
|
|
192
|
+
auto arr = info[1].As<Napi::Float64Array>();
|
|
193
|
+
int op = info[2].As<Napi::Number>().Int32Value();
|
|
194
|
+
bool scalarOnLeft = info[3].As<Napi::Boolean>().Value();
|
|
195
|
+
|
|
196
|
+
size_t n = arr.ElementLength();
|
|
197
|
+
auto result = Napi::Float64Array::New(env, n);
|
|
198
|
+
const double* a = arr.Data();
|
|
199
|
+
double* out = result.Data();
|
|
200
|
+
|
|
201
|
+
if (scalarOnLeft) {
|
|
202
|
+
switch (op) {
|
|
203
|
+
case 0: for (size_t i = 0; i < n; i++) out[i] = scalar + a[i]; break;
|
|
204
|
+
case 1: for (size_t i = 0; i < n; i++) out[i] = scalar - a[i]; break;
|
|
205
|
+
case 2: for (size_t i = 0; i < n; i++) out[i] = scalar * a[i]; break;
|
|
206
|
+
case 3: for (size_t i = 0; i < n; i++) out[i] = scalar / a[i]; break;
|
|
207
|
+
default:
|
|
208
|
+
Napi::RangeError::New(env, "elemwiseScalar: op must be 0-3")
|
|
209
|
+
.ThrowAsJavaScriptException();
|
|
210
|
+
return env.Null();
|
|
211
|
+
}
|
|
212
|
+
} else {
|
|
213
|
+
switch (op) {
|
|
214
|
+
case 0: for (size_t i = 0; i < n; i++) out[i] = a[i] + scalar; break;
|
|
215
|
+
case 1: for (size_t i = 0; i < n; i++) out[i] = a[i] - scalar; break;
|
|
216
|
+
case 2: for (size_t i = 0; i < n; i++) out[i] = a[i] * scalar; break;
|
|
217
|
+
case 3: for (size_t i = 0; i < n; i++) out[i] = a[i] / scalar; break;
|
|
218
|
+
default:
|
|
219
|
+
Napi::RangeError::New(env, "elemwiseScalar: op must be 0-3")
|
|
220
|
+
.ThrowAsJavaScriptException();
|
|
221
|
+
return env.Null();
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return result;
|
|
226
|
+
}
|
package/native/numbl_addon.cpp
CHANGED
|
@@ -93,8 +93,14 @@ Napi::Object Init(Napi::Env env, Napi::Object exports) {
|
|
|
93
93
|
Napi::Function::New(env, FftAlongDim));
|
|
94
94
|
exports.Set(Napi::String::New(env, "elemwise"),
|
|
95
95
|
Napi::Function::New(env, Elemwise));
|
|
96
|
+
exports.Set(Napi::String::New(env, "elemwiseScalar"),
|
|
97
|
+
Napi::Function::New(env, ElemwiseScalar));
|
|
96
98
|
exports.Set(Napi::String::New(env, "elemwiseComplex"),
|
|
97
99
|
Napi::Function::New(env, ElemwiseComplex));
|
|
100
|
+
exports.Set(Napi::String::New(env, "fillRandn"),
|
|
101
|
+
Napi::Function::New(env, FillRandn));
|
|
102
|
+
exports.Set(Napi::String::New(env, "unaryElemwise"),
|
|
103
|
+
Napi::Function::New(env, UnaryElemwise));
|
|
98
104
|
return exports;
|
|
99
105
|
}
|
|
100
106
|
|
|
@@ -282,4 +282,7 @@ Napi::Value Fft1d(const Napi::CallbackInfo& info);
|
|
|
282
282
|
Napi::Value Fft1dComplex(const Napi::CallbackInfo& info);
|
|
283
283
|
Napi::Value FftAlongDim(const Napi::CallbackInfo& info);
|
|
284
284
|
Napi::Value Elemwise(const Napi::CallbackInfo& info);
|
|
285
|
+
Napi::Value ElemwiseScalar(const Napi::CallbackInfo& info);
|
|
285
286
|
Napi::Value ElemwiseComplex(const Napi::CallbackInfo& info);
|
|
287
|
+
Napi::Value FillRandn(const Napi::CallbackInfo& info);
|
|
288
|
+
Napi::Value UnaryElemwise(const Napi::CallbackInfo& info);
|
package/native/randn.cpp
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Native bulk fill for randn (Marsaglia polar method + xoshiro128**).
|
|
3
|
+
*
|
|
4
|
+
* fillRandn(state: Uint32Array(4), n: number, spare: number, hasSpare: boolean)
|
|
5
|
+
* → { data: Float64Array, spare: number, hasSpare: boolean }
|
|
6
|
+
*
|
|
7
|
+
* The xoshiro128** state is mutated in-place through the Uint32Array.
|
|
8
|
+
* The caller passes in any cached Box-Muller spare and gets the updated spare back.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
#include "numbl_addon_common.h"
|
|
12
|
+
#include <cmath>
|
|
13
|
+
|
|
14
|
+
// Inline xoshiro128** — returns a uint32 and advances state in place.
|
|
15
|
+
static inline uint32_t xoshiro128ss(uint32_t* s) {
|
|
16
|
+
uint32_t tmp = s[1] * 5;
|
|
17
|
+
uint32_t result = ((tmp << 7) | (tmp >> 25)) * 9;
|
|
18
|
+
uint32_t t = s[1] << 9;
|
|
19
|
+
s[2] ^= s[0];
|
|
20
|
+
s[3] ^= s[1];
|
|
21
|
+
s[1] ^= s[2];
|
|
22
|
+
s[0] ^= s[3];
|
|
23
|
+
s[2] ^= t;
|
|
24
|
+
s[3] = (s[3] << 11) | (s[3] >> 21);
|
|
25
|
+
return result;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
static inline double rngUniform(uint32_t* s) {
|
|
29
|
+
return static_cast<double>(xoshiro128ss(s)) / 4294967296.0;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
Napi::Value FillRandn(const Napi::CallbackInfo& info) {
|
|
33
|
+
Napi::Env env = info.Env();
|
|
34
|
+
|
|
35
|
+
if (info.Length() < 4
|
|
36
|
+
|| !info[0].IsTypedArray()
|
|
37
|
+
|| !info[1].IsNumber()
|
|
38
|
+
|| !info[2].IsNumber()
|
|
39
|
+
|| !info[3].IsBoolean()) {
|
|
40
|
+
Napi::TypeError::New(env,
|
|
41
|
+
"fillRandn: expected (Uint32Array state, number n, number spare, boolean hasSpare)")
|
|
42
|
+
.ThrowAsJavaScriptException();
|
|
43
|
+
return env.Null();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
auto stateArr = info[0].As<Napi::Uint32Array>();
|
|
47
|
+
int n = info[1].As<Napi::Number>().Int32Value();
|
|
48
|
+
double spare = info[2].As<Napi::Number>().DoubleValue();
|
|
49
|
+
bool hasSpare = info[3].As<Napi::Boolean>().Value();
|
|
50
|
+
|
|
51
|
+
if (stateArr.ElementLength() < 4) {
|
|
52
|
+
Napi::RangeError::New(env, "fillRandn: state must have 4 elements")
|
|
53
|
+
.ThrowAsJavaScriptException();
|
|
54
|
+
return env.Null();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
uint32_t* state = stateArr.Data();
|
|
58
|
+
auto result = Napi::Float64Array::New(env, static_cast<size_t>(n));
|
|
59
|
+
double* out = result.Data();
|
|
60
|
+
|
|
61
|
+
int i = 0;
|
|
62
|
+
|
|
63
|
+
// Drain cached spare
|
|
64
|
+
if (hasSpare && i < n) {
|
|
65
|
+
out[i++] = spare;
|
|
66
|
+
hasSpare = false;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Generate pairs via Marsaglia polar method
|
|
70
|
+
for (; i + 1 < n; i += 2) {
|
|
71
|
+
double u, v, s;
|
|
72
|
+
do {
|
|
73
|
+
u = 2.0 * rngUniform(state) - 1.0;
|
|
74
|
+
v = 2.0 * rngUniform(state) - 1.0;
|
|
75
|
+
s = u * u + v * v;
|
|
76
|
+
} while (s >= 1.0 || s == 0.0);
|
|
77
|
+
double mul = std::sqrt((-2.0 * std::log(s)) / s);
|
|
78
|
+
out[i] = u * mul;
|
|
79
|
+
out[i + 1] = v * mul;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Handle odd trailing element
|
|
83
|
+
if (i < n) {
|
|
84
|
+
double u, v, s;
|
|
85
|
+
do {
|
|
86
|
+
u = 2.0 * rngUniform(state) - 1.0;
|
|
87
|
+
v = 2.0 * rngUniform(state) - 1.0;
|
|
88
|
+
s = u * u + v * v;
|
|
89
|
+
} while (s >= 1.0 || s == 0.0);
|
|
90
|
+
double mul = std::sqrt((-2.0 * std::log(s)) / s);
|
|
91
|
+
out[i] = u * mul;
|
|
92
|
+
spare = v * mul;
|
|
93
|
+
hasSpare = true;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
auto obj = Napi::Object::New(env);
|
|
97
|
+
obj.Set("data", result);
|
|
98
|
+
obj.Set("spare", Napi::Number::New(env, spare));
|
|
99
|
+
obj.Set("hasSpare", Napi::Boolean::New(env, hasSpare));
|
|
100
|
+
return obj;
|
|
101
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Native unary element-wise math operations on Float64Arrays.
|
|
3
|
+
*
|
|
4
|
+
* unaryElemwise(arr: Float64Array, op: number): Float64Array
|
|
5
|
+
* op codes:
|
|
6
|
+
* 0=exp, 1=log, 2=log2, 3=log10,
|
|
7
|
+
* 4=sqrt, 5=abs, 6=floor, 7=ceil, 8=round, 9=trunc (fix),
|
|
8
|
+
* 10=sin, 11=cos, 12=tan, 13=asin, 14=acos, 15=atan,
|
|
9
|
+
* 16=sinh, 17=cosh, 18=tanh, 19=sign
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
#include "numbl_addon_common.h"
|
|
13
|
+
#include <cmath>
|
|
14
|
+
|
|
15
|
+
Napi::Value UnaryElemwise(const Napi::CallbackInfo& info) {
|
|
16
|
+
Napi::Env env = info.Env();
|
|
17
|
+
|
|
18
|
+
if (info.Length() < 2
|
|
19
|
+
|| !info[0].IsTypedArray()
|
|
20
|
+
|| !info[1].IsNumber()) {
|
|
21
|
+
Napi::TypeError::New(env,
|
|
22
|
+
"unaryElemwise: expected (Float64Array arr, number op)")
|
|
23
|
+
.ThrowAsJavaScriptException();
|
|
24
|
+
return env.Null();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
auto arr = info[0].As<Napi::Float64Array>();
|
|
28
|
+
int op = info[1].As<Napi::Number>().Int32Value();
|
|
29
|
+
|
|
30
|
+
size_t n = arr.ElementLength();
|
|
31
|
+
auto result = Napi::Float64Array::New(env, n);
|
|
32
|
+
const double* a = arr.Data();
|
|
33
|
+
double* out = result.Data();
|
|
34
|
+
|
|
35
|
+
switch (op) {
|
|
36
|
+
case 0: for (size_t i = 0; i < n; i++) out[i] = std::exp(a[i]); break;
|
|
37
|
+
case 1: for (size_t i = 0; i < n; i++) out[i] = std::log(a[i]); break;
|
|
38
|
+
case 2: for (size_t i = 0; i < n; i++) out[i] = std::log2(a[i]); break;
|
|
39
|
+
case 3: for (size_t i = 0; i < n; i++) out[i] = std::log10(a[i]); break;
|
|
40
|
+
case 4: for (size_t i = 0; i < n; i++) out[i] = std::sqrt(a[i]); break;
|
|
41
|
+
case 5: for (size_t i = 0; i < n; i++) out[i] = std::abs(a[i]); break;
|
|
42
|
+
case 6: for (size_t i = 0; i < n; i++) out[i] = std::floor(a[i]); break;
|
|
43
|
+
case 7: for (size_t i = 0; i < n; i++) out[i] = std::ceil(a[i]); break;
|
|
44
|
+
case 8: for (size_t i = 0; i < n; i++) out[i] = std::round(a[i]); break;
|
|
45
|
+
case 9: for (size_t i = 0; i < n; i++) out[i] = std::trunc(a[i]); break;
|
|
46
|
+
case 10: for (size_t i = 0; i < n; i++) out[i] = std::sin(a[i]); break;
|
|
47
|
+
case 11: for (size_t i = 0; i < n; i++) out[i] = std::cos(a[i]); break;
|
|
48
|
+
case 12: for (size_t i = 0; i < n; i++) out[i] = std::tan(a[i]); break;
|
|
49
|
+
case 13: for (size_t i = 0; i < n; i++) out[i] = std::asin(a[i]); break;
|
|
50
|
+
case 14: for (size_t i = 0; i < n; i++) out[i] = std::acos(a[i]); break;
|
|
51
|
+
case 15: for (size_t i = 0; i < n; i++) out[i] = std::atan(a[i]); break;
|
|
52
|
+
case 16: for (size_t i = 0; i < n; i++) out[i] = std::sinh(a[i]); break;
|
|
53
|
+
case 17: for (size_t i = 0; i < n; i++) out[i] = std::cosh(a[i]); break;
|
|
54
|
+
case 18: for (size_t i = 0; i < n; i++) out[i] = std::tanh(a[i]); break;
|
|
55
|
+
case 19:
|
|
56
|
+
for (size_t i = 0; i < n; i++) {
|
|
57
|
+
out[i] = (a[i] > 0.0) ? 1.0 : (a[i] < 0.0) ? -1.0 : 0.0;
|
|
58
|
+
}
|
|
59
|
+
break;
|
|
60
|
+
default:
|
|
61
|
+
Napi::RangeError::New(env, "unaryElemwise: unknown op code")
|
|
62
|
+
.ThrowAsJavaScriptException();
|
|
63
|
+
return env.Null();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return result;
|
|
67
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "numbl",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Run .m source files in the browser and on the command line by compiling to JavaScript",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -71,7 +71,9 @@
|
|
|
71
71
|
"fflate": "^0.8.2",
|
|
72
72
|
"node-addon-api": "^8.3.0",
|
|
73
73
|
"pako": "^2.1.0",
|
|
74
|
+
"react-markdown": "^10.1.0",
|
|
74
75
|
"react-syntax-highlighter": "^16.1.1",
|
|
76
|
+
"remark-gfm": "^4.0.1",
|
|
75
77
|
"three": "^0.183.2"
|
|
76
78
|
},
|
|
77
79
|
"devDependencies": {
|