verbena-complex 0.1.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 +10 -0
- package/lib/index.js +130 -0
- package/package.json +36 -0
- package/readme.md +23 -0
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Complex } from 'complex.js';
|
|
2
|
+
import { Library } from 'verbena/lib';
|
|
3
|
+
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>;
|
|
10
|
+
export declare function ComplexFunction(source: string): vbFunction<Complex>;
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
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");
|
|
6
|
+
const lexer_1 = require("verbena/lexer");
|
|
7
|
+
const parser_1 = require("verbena/parser");
|
|
8
|
+
const compileFn_1 = require("verbena/compileFn");
|
|
9
|
+
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
|
+
};
|
|
63
|
+
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
|
|
99
|
+
}
|
|
100
|
+
});
|
|
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
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "verbena-complex",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "verbena + complex numbers",
|
|
5
|
+
"main": "lib/index.js",
|
|
6
|
+
"types": "lib/index.d.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"test": "echo \"Error: no test specified\" && exit 1",
|
|
9
|
+
"build": "tsc"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"lib",
|
|
13
|
+
"README.md"
|
|
14
|
+
],
|
|
15
|
+
"author": "Alfio (https://github.com/p2js)",
|
|
16
|
+
"license": "ISC",
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "https://github.com/p2js/verbena-complex.git"
|
|
20
|
+
},
|
|
21
|
+
"keywords": [
|
|
22
|
+
"math",
|
|
23
|
+
"complex",
|
|
24
|
+
"complex.js",
|
|
25
|
+
"complex-numbers",
|
|
26
|
+
"functions",
|
|
27
|
+
"expressions",
|
|
28
|
+
"mathjs",
|
|
29
|
+
"math.js"
|
|
30
|
+
],
|
|
31
|
+
"packageManager": "pnpm@9.14.2+sha512.6e2baf77d06b9362294152c851c4f278ede37ab1eba3a55fda317a4a17b209f4dbb973fb250a77abc463a341fcb1f17f17cfa24091c4eb319cda0d9b84278387",
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"complex.js": "^2.4.2",
|
|
34
|
+
"verbena": "^0.4.0"
|
|
35
|
+
}
|
|
36
|
+
}
|
package/readme.md
ADDED
|
@@ -0,0 +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 }
|
|
23
|
+
```
|