verbena-complex 0.1.0 → 0.2.0

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/lib/index.d.ts CHANGED
@@ -1,10 +1,4 @@
1
1
  import { Complex } from 'complex.js';
2
- import { Library } from 'verbena/lib';
3
2
  import { vbFunction } from 'verbena/function';
4
- /**
5
- * Library for complex functions.
6
- *
7
- * Includes complex operations and functions, as well as constants for i, pi and e.
8
- */
9
- export declare const complexLib: Library<Complex>;
3
+ export * from './lib';
10
4
  export declare function ComplexFunction(source: string): vbFunction<Complex>;
package/lib/index.js CHANGED
@@ -1,130 +1,52 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
2
16
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ComplexFunction = exports.complexLib = void 0;
4
- const complex_js_1 = require("complex.js");
5
- const lib_1 = require("verbena/lib");
17
+ exports.ComplexFunction = ComplexFunction;
18
+ const function_1 = require("verbena/function");
6
19
  const lexer_1 = require("verbena/lexer");
7
- const parser_1 = require("verbena/parser");
8
20
  const compileFn_1 = require("verbena/compileFn");
9
21
  const token_1 = require("verbena/token");
10
- /**
11
- * Library for complex functions.
12
- *
13
- * Includes complex operations and functions, as well as constants for i, pi and e.
14
- */
15
- exports.complexLib = {
16
- operations: {
17
- add: forceComplex((l, r) => l.add(r)),
18
- sub: forceComplex((l, r) => l.sub(r)),
19
- mul: forceComplex((l, r) => l.mul(r)),
20
- div: forceComplex((l, r) => l.div(r)),
21
- pow: forceComplex((l, r) => l.pow(r)),
22
- neg: forceComplex((z) => z.mul(-1)),
23
- mod: forceReal((x, y) => x % y),
24
- fac: forceReal(lib_1.standard.functions.fac)
25
- },
26
- functions: {
27
- re: forceComplex((z) => (0, complex_js_1.Complex)(z.re)),
28
- im: forceComplex((z) => (0, complex_js_1.Complex)(0, z.im)),
29
- abs: forceComplex((z) => (0, complex_js_1.Complex)(z.abs())),
30
- acos: forceComplex((z) => z.acos()),
31
- acosh: forceComplex((z) => z.acosh()),
32
- asin: forceComplex((z) => z.asin()),
33
- asinh: forceComplex((z) => z.asinh()),
34
- atan: forceComplex((z) => z.atan()),
35
- atanh: forceComplex((z) => z.atanh()),
36
- cbrt: forceComplex((z) => z.pow(1 / 3)),
37
- ceil: forceComplex((z) => z.ceil(0)),
38
- cos: forceComplex((z) => z.cos()),
39
- cosh: forceComplex((z) => z.cosh()),
40
- exp: forceComplex((z) => z.exp()),
41
- floor: forceComplex((z) => z.floor(0)),
42
- hypot: forceComplex((...zs) => zs.reduce((acc, z) => acc.add(z.pow(2)), (0, complex_js_1.Complex)(0)).sqrt()),
43
- log_: forceComplex((z, b = (0, complex_js_1.Complex)(10)) => z.log().div(b.log())),
44
- ln: forceComplex((z) => z.log()),
45
- max: forceReal(Math.max),
46
- min: forceReal(Math.min),
47
- pow: forceComplex((z, p) => z.pow(p)),
48
- random: () => (0, complex_js_1.Complex)(Math.random()),
49
- round: forceComplex((z) => z.round(0)),
50
- sign: forceComplex((z) => z.sign()),
51
- sin: forceComplex((z) => z.sin()),
52
- sinh: forceComplex((z) => z.sinh()),
53
- sqrt: forceComplex((z) => z.sqrt()),
54
- tan: forceComplex((z) => z.tan()),
55
- tanh: forceComplex((z) => z.tanh())
56
- },
57
- constants: {
58
- i: complex_js_1.Complex.I,
59
- pi: complex_js_1.Complex.PI,
60
- e: complex_js_1.Complex.E,
61
- }
62
- };
22
+ const lib_1 = require("./lib");
23
+ __exportStar(require("./lib"), exports);
63
24
  function ComplexFunction(source) {
64
- let tokens = (0, lexer_1.scan)(source, exports.complexLib).map(token => {
65
- // Little hack to allow implicit of identifiers with i, like ix
66
- if (token.type == token_1.TokenType.IDENTIFIER && token.lexeme == 'i') {
67
- token.type = token_1.TokenType.CONSTANT;
68
- }
69
- return token;
70
- });
71
- let decl = (0, parser_1.parse)(tokens);
72
- // Remove function clauses
73
- decl.clauses = [];
74
- // Compile the function as a real function using the complex library
75
- let compiledFn = (0, compileFn_1.compileFn)(decl, exports.complexLib);
76
- // Force conversion of function arguments and result to a complex number
77
- let complexFn = forceComplex((...args) => (0, complex_js_1.Complex)(compiledFn(...args)));
78
- // Copy over the real function's properties to the complex one
79
- Object.defineProperties(complexFn, {
80
- name: {
81
- value: compiledFn.name,
82
- writable: false,
83
- enumerable: false
84
- },
85
- ast: {
86
- value: compiledFn.ast,
87
- writable: false,
88
- enumerable: false
89
- },
90
- paramList: {
91
- value: compiledFn.paramList,
92
- writable: false,
93
- enumerable: false
94
- },
95
- body: {
96
- value: compiledFn.body,
97
- writable: false,
98
- enumerable: false
25
+ return (0, function_1.Function)(source, {
26
+ lib: lib_1.complexLib,
27
+ scanner: (source, lib) => (0, lexer_1.scan)(source, lib).map(token => {
28
+ // Little hack to allow implicit of identifiers with i, like ix
29
+ if (token.type == token_1.TokenType.IDENTIFIER && token.lexeme == 'i') {
30
+ token.type = token_1.TokenType.CONSTANT;
31
+ }
32
+ return token;
33
+ }),
34
+ compiler: (decl, lib) => {
35
+ // Error if clauses
36
+ if (decl.clauses.length > 0) {
37
+ throw Error("Function clauses are not allowed in complex functions");
38
+ }
39
+ // Compile the function as a real function using the complex library
40
+ let compiledFn = (0, compileFn_1.compileFn)(decl, lib);
41
+ // Force conversion of function arguments and result to a complex number
42
+ let complexFn = (0, lib_1.forceComplex)((...args) => compiledFn(...args));
43
+ // Copy over the real function's properties to the complex one
44
+ Object.defineProperties(complexFn, Object.fromEntries(Reflect.ownKeys(compiledFn).map(prop => [prop, {
45
+ value: compiledFn[prop],
46
+ writable: false,
47
+ enumerable: false
48
+ }])));
49
+ return complexFn;
99
50
  }
100
51
  });
101
- return complexFn;
102
- }
103
- exports.ComplexFunction = ComplexFunction;
104
- /**
105
- * Utility function to wrap a complex function, converting all its arguments to a Complex value.
106
- *
107
- * @param f Complex function
108
- * @returns `f` with all its parameters converted to Complex
109
- */
110
- function forceComplex(f) {
111
- return (...args) => f(...args.map(z => (0, complex_js_1.Complex)(z)));
112
- }
113
- /**
114
- * Utility function to wrap a real function, forcing all its arguments to be real numbers.
115
- *
116
- * @param f Real function
117
- * @returns `f`, but will throw an error on any complex arguments
118
- */
119
- function forceReal(f) {
120
- return (...args) => {
121
- let realArgs = args.map(z => {
122
- z = (0, complex_js_1.Complex)(z);
123
- if (z.im != 0) {
124
- throw Error("complex " + f.name + "is unsupported");
125
- }
126
- return z.re;
127
- });
128
- return (0, complex_js_1.Complex)(f(...realArgs));
129
- };
130
52
  }
package/lib/lib.d.ts ADDED
@@ -0,0 +1,22 @@
1
+ import { Complex } from 'complex.js';
2
+ import { Library } from 'verbena/lib';
3
+ /**
4
+ * Library for complex functions.
5
+ *
6
+ * Includes complex operations and functions, as well as constants for i, pi and e.
7
+ */
8
+ export declare const complexLib: Library<Complex>;
9
+ /**
10
+ * Utility function to wrap a complex function, converting all its arguments to a Complex value.
11
+ *
12
+ * @param f Complex function
13
+ * @returns `f` with all its parameters converted to Complex
14
+ */
15
+ export declare function forceComplex(f: (...args: Complex[]) => Complex | number): (...args: Complex[]) => Complex;
16
+ /**
17
+ * Utility function to wrap a real function, forcing all its arguments to be real numbers.
18
+ *
19
+ * @param f Real function
20
+ * @returns `f`, but will throw an error on any complex arguments
21
+ */
22
+ export declare function forceReal(f: (...args: number[]) => number): (...args: Complex[]) => Complex;
package/lib/lib.js ADDED
@@ -0,0 +1,87 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.complexLib = void 0;
4
+ exports.forceComplex = forceComplex;
5
+ exports.forceReal = forceReal;
6
+ const complex_js_1 = require("complex.js");
7
+ const lib_1 = require("verbena/lib");
8
+ /**
9
+ * Library for complex functions.
10
+ *
11
+ * Includes complex operations and functions, as well as constants for i, pi and e.
12
+ */
13
+ exports.complexLib = {
14
+ operations: {
15
+ add: forceComplex((l, r) => l.add(r)),
16
+ sub: forceComplex((l, r) => l.sub(r)),
17
+ mul: forceComplex((l, r) => l.mul(r)),
18
+ div: forceComplex((l, r) => l.div(r)),
19
+ pow: forceComplex((l, r) => l.pow(r)),
20
+ neg: forceComplex((z) => z.mul(-1)),
21
+ mod: forceReal((x, y) => x % y),
22
+ fac: forceReal(lib_1.standard.functions.fac)
23
+ },
24
+ functions: {
25
+ re: forceComplex((z) => z.re),
26
+ im: forceComplex((z) => z.im),
27
+ abs: forceComplex((z) => z.abs()),
28
+ acos: forceComplex((z) => z.acos()),
29
+ acosh: forceComplex((z) => z.acosh()),
30
+ asin: forceComplex((z) => z.asin()),
31
+ asinh: forceComplex((z) => z.asinh()),
32
+ atan: forceComplex((z) => z.atan()),
33
+ atanh: forceComplex((z) => z.atanh()),
34
+ cbrt: forceComplex((z) => z.pow(1 / 3)),
35
+ ceil: forceComplex((z) => z.ceil(0)),
36
+ cos: forceComplex((z) => z.cos()),
37
+ cosh: forceComplex((z) => z.cosh()),
38
+ exp: forceComplex((z) => z.exp()),
39
+ floor: forceComplex((z) => z.floor(0)),
40
+ hypot: forceComplex((...zs) => zs.reduce((acc, z) => acc.add(z.pow(2)), (0, complex_js_1.Complex)(0)).sqrt()),
41
+ log_: forceComplex((z, b = (0, complex_js_1.Complex)(10)) => z.log().div(b.log())),
42
+ ln: forceComplex((z) => z.log()),
43
+ max: forceReal(Math.max),
44
+ min: forceReal(Math.min),
45
+ pow: forceComplex((z, p) => z.pow(p)),
46
+ random: () => (0, complex_js_1.Complex)(Math.random()),
47
+ round: forceComplex((z) => z.round(0)),
48
+ sign: forceComplex((z) => z.sign()),
49
+ sin: forceComplex((z) => z.sin()),
50
+ sinh: forceComplex((z) => z.sinh()),
51
+ sqrt: forceComplex((z) => z.sqrt()),
52
+ tan: forceComplex((z) => z.tan()),
53
+ tanh: forceComplex((z) => z.tanh())
54
+ },
55
+ constants: {
56
+ i: complex_js_1.Complex.I,
57
+ pi: complex_js_1.Complex.PI,
58
+ e: complex_js_1.Complex.E,
59
+ }
60
+ };
61
+ /**
62
+ * Utility function to wrap a complex function, converting all its arguments to a Complex value.
63
+ *
64
+ * @param f Complex function
65
+ * @returns `f` with all its parameters converted to Complex
66
+ */
67
+ function forceComplex(f) {
68
+ return (...args) => (0, complex_js_1.Complex)(f(...args.map(z => (0, complex_js_1.Complex)(z))));
69
+ }
70
+ /**
71
+ * Utility function to wrap a real function, forcing all its arguments to be real numbers.
72
+ *
73
+ * @param f Real function
74
+ * @returns `f`, but will throw an error on any complex arguments
75
+ */
76
+ function forceReal(f) {
77
+ return (...args) => {
78
+ let realArgs = args.map(z => {
79
+ z = (0, complex_js_1.Complex)(z);
80
+ if (z.im != 0) {
81
+ throw Error("complex " + f.name + "is unsupported");
82
+ }
83
+ return z.re;
84
+ });
85
+ return (0, complex_js_1.Complex)(f(...realArgs));
86
+ };
87
+ }
package/package.json CHANGED
@@ -1,13 +1,9 @@
1
1
  {
2
2
  "name": "verbena-complex",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "verbena + complex numbers",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",
7
- "scripts": {
8
- "test": "echo \"Error: no test specified\" && exit 1",
9
- "build": "tsc"
10
- },
11
7
  "files": [
12
8
  "lib",
13
9
  "README.md"
@@ -28,9 +24,12 @@
28
24
  "mathjs",
29
25
  "math.js"
30
26
  ],
31
- "packageManager": "pnpm@9.14.2+sha512.6e2baf77d06b9362294152c851c4f278ede37ab1eba3a55fda317a4a17b209f4dbb973fb250a77abc463a341fcb1f17f17cfa24091c4eb319cda0d9b84278387",
32
27
  "dependencies": {
33
28
  "complex.js": "^2.4.2",
34
29
  "verbena": "^0.4.0"
30
+ },
31
+ "scripts": {
32
+ "test": "echo \"Error: no test specified\" && exit 1",
33
+ "build": "tsc"
35
34
  }
36
35
  }
package/readme.md CHANGED
@@ -1,23 +1,23 @@
1
- # verbena-complex
2
-
3
- This package acts as a complex number extension to the [verbena](https://github.com/p2js/verbena) math function transcompiler to JS.
4
-
5
- It uses verbena's standard lexer, parser and compiler, all completely unmodified. The magic happens with the library: All operations are redefined to convert all numbers to complex.js `Complex` numbers, and `i` is treated as a simple constant.
6
-
7
- Some additional logic is implemented after lexing and parsing to allow for additional robustness and ergonomics:
8
- - any identifier `i` will be converted to the constant `i` to allow implicit multiplications of variables by i, such as `ix`.
9
- - due to the lack of logical operator overloading in verbena, as well as the lack of a natural order on the complex numbers, function clauses are unimplemented and will be removed before compilation.
10
-
11
- ## Usage
12
-
13
- ```
14
- npm install verbena-complex
15
- ```
16
-
17
- ```js
18
- import { ComplexFunction } from 'verbena-complex';
19
-
20
- const f = ComplexFunction("f(x) = re(x * (3+2i))");
21
-
22
- console.log(f(2)); // { re: 6, im: 0 }
1
+ # verbena-complex
2
+
3
+ This package acts as a complex number extension to the [verbena](https://github.com/p2js/verbena) math function transcompiler to JS.
4
+
5
+ It uses verbena's standard lexer, parser and compiler, all completely unmodified. The magic happens with the library: All operations are redefined to convert all numbers to complex.js `Complex` numbers, and `i` is treated as a simple constant.
6
+
7
+ Some additional logic is implemented after lexing and parsing to allow for additional robustness and ergonomics:
8
+ - any identifier `i` will be converted to the constant `i` to allow implicit multiplications of variables by i, such as `ix`.
9
+ - due to the lack of logical operator overloading in verbena, as well as the lack of a natural order on the complex numbers, function clauses are unimplemented and adding them to a function definition will cause an error.
10
+
11
+ ## Usage
12
+
13
+ ```
14
+ npm install verbena-complex
15
+ ```
16
+
17
+ ```js
18
+ import { ComplexFunction } from 'verbena-complex';
19
+
20
+ const f = ComplexFunction("f(x) = re(x * (3+2i))");
21
+
22
+ console.log(f(2)); // { re: 6, im: 0 }
23
23
  ```