smithkit 0.0.0-stage → 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/LICENSE +21 -0
- package/README.md +481 -2
- package/dist/lib/Smith.d.ts +103 -0
- package/dist/lib/Smith.js +551 -0
- package/dist/lib/SmithArcsDefs.d.ts +10 -0
- package/dist/lib/SmithArcsDefs.js +10 -0
- package/dist/lib/SmithConstantCircle.d.ts +62 -0
- package/dist/lib/SmithConstantCircle.js +222 -0
- package/dist/lib/arcs/Tick.d.ts +33 -0
- package/dist/lib/arcs/Tick.js +40 -0
- package/dist/lib/complex/Complex.d.ts +130 -0
- package/dist/lib/complex/Complex.js +636 -0
- package/dist/lib/components.d.ts +14 -0
- package/dist/lib/components.js +26 -0
- package/dist/lib/draw/ConstCircles.d.ts +61 -0
- package/dist/lib/draw/ConstCircles.js +151 -0
- package/dist/lib/draw/ConstConductance.d.ts +4 -0
- package/dist/lib/draw/ConstConductance.js +6 -0
- package/dist/lib/draw/ConstQCircles.d.ts +29 -0
- package/dist/lib/draw/ConstQCircles.js +86 -0
- package/dist/lib/draw/ConstReactance.d.ts +4 -0
- package/dist/lib/draw/ConstReactance.js +6 -0
- package/dist/lib/draw/ConstResistance.d.ts +4 -0
- package/dist/lib/draw/ConstResistance.js +6 -0
- package/dist/lib/draw/ConstSusceptance.d.ts +4 -0
- package/dist/lib/draw/ConstSusceptance.js +6 -0
- package/dist/lib/draw/ConstSwrCircles.d.ts +29 -0
- package/dist/lib/draw/ConstSwrCircles.js +85 -0
- package/dist/lib/draw/MouseGesture.d.ts +6 -0
- package/dist/lib/draw/MouseGesture.js +26 -0
- package/dist/lib/draw/SmithArc.d.ts +10 -0
- package/dist/lib/draw/SmithArc.js +26 -0
- package/dist/lib/draw/SmithCircle.d.ts +10 -0
- package/dist/lib/draw/SmithCircle.js +24 -0
- package/dist/lib/draw/SmithCursor.d.ts +49 -0
- package/dist/lib/draw/SmithCursor.js +186 -0
- package/dist/lib/draw/SmithData.d.ts +66 -0
- package/dist/lib/draw/SmithData.js +366 -0
- package/dist/lib/draw/SmithDrawOptions.d.ts +5 -0
- package/dist/lib/draw/SmithDrawOptions.js +1 -0
- package/dist/lib/draw/SmithGridLayer.d.ts +19 -0
- package/dist/lib/draw/SmithGridLayer.js +48 -0
- package/dist/lib/draw/SmithGroup.d.ts +11 -0
- package/dist/lib/draw/SmithGroup.js +31 -0
- package/dist/lib/draw/SmithLine.d.ts +11 -0
- package/dist/lib/draw/SmithLine.js +27 -0
- package/dist/lib/draw/SmithMarker.d.ts +25 -0
- package/dist/lib/draw/SmithMarker.js +122 -0
- package/dist/lib/draw/SmithPath.d.ts +8 -0
- package/dist/lib/draw/SmithPath.js +20 -0
- package/dist/lib/draw/SmithScaler.d.ts +19 -0
- package/dist/lib/draw/SmithScaler.js +48 -0
- package/dist/lib/draw/SmithShape.d.ts +15 -0
- package/dist/lib/draw/SmithShape.js +42 -0
- package/dist/lib/draw/SmithSvg.d.ts +9 -0
- package/dist/lib/draw/SmithSvg.js +27 -0
- package/dist/lib/draw/SmithText.d.ts +22 -0
- package/dist/lib/draw/SmithText.js +56 -0
- package/dist/lib/formatting.d.ts +7 -0
- package/dist/lib/formatting.js +16 -0
- package/dist/lib/grid/definitions.d.ts +3 -0
- package/dist/lib/grid/definitions.js +123 -0
- package/dist/lib/grid/geometry.d.ts +20 -0
- package/dist/lib/grid/geometry.js +55 -0
- package/dist/lib/grid/labels.d.ts +3 -0
- package/dist/lib/grid/labels.js +41 -0
- package/dist/lib/grid/types.d.ts +30 -0
- package/dist/lib/grid/types.js +6 -0
- package/dist/lib/index.d.ts +16 -0
- package/dist/lib/index.js +9 -0
- package/dist/lib/io/touchstone.d.ts +7 -0
- package/dist/lib/io/touchstone.js +72 -0
- package/dist/lib/layers.d.ts +48 -0
- package/dist/lib/layers.js +73 -0
- package/dist/lib/math/complexRatio.d.ts +3 -0
- package/dist/lib/math/complexRatio.js +30 -0
- package/dist/lib/measurements.d.ts +53 -0
- package/dist/lib/measurements.js +19 -0
- package/dist/lib/renormalization.d.ts +10 -0
- package/dist/lib/renormalization.js +62 -0
- package/dist/lib/rf.d.ts +35 -0
- package/dist/lib/rf.js +74 -0
- package/dist/lib/samples.d.ts +6 -0
- package/dist/lib/samples.js +1 -0
- package/dist/lib/scales/SmithPeripheralScales.d.ts +10 -0
- package/dist/lib/scales/SmithPeripheralScales.js +124 -0
- package/dist/lib/scales/SmithScales.d.ts +16 -0
- package/dist/lib/scales/SmithScales.js +134 -0
- package/dist/lib/scales/peripheralScales.d.ts +12 -0
- package/dist/lib/scales/peripheralScales.js +49 -0
- package/dist/lib/scales/radialScales.d.ts +12 -0
- package/dist/lib/scales/radialScales.js +109 -0
- package/dist/lib/shapes/Arc.d.ts +6 -0
- package/dist/lib/shapes/Arc.js +1 -0
- package/dist/lib/shapes/Circle.d.ts +5 -0
- package/dist/lib/shapes/Circle.js +1 -0
- package/dist/lib/shapes/Line.d.ts +5 -0
- package/dist/lib/shapes/Line.js +1 -0
- package/dist/lib/shapes/Point.d.ts +1 -0
- package/dist/lib/shapes/Point.js +1 -0
- package/package.json +76 -4
|
@@ -0,0 +1,636 @@
|
|
|
1
|
+
export class Complex {
|
|
2
|
+
realPart;
|
|
3
|
+
imaginaryPart;
|
|
4
|
+
tolerance = Number.EPSILON;
|
|
5
|
+
constructor(re, im) {
|
|
6
|
+
this.realPart = re;
|
|
7
|
+
this.imaginaryPart = im;
|
|
8
|
+
}
|
|
9
|
+
static from(...args) {
|
|
10
|
+
if (args.length === 1) {
|
|
11
|
+
const arg = args[0];
|
|
12
|
+
if (typeof arg === 'number') {
|
|
13
|
+
return new Complex(arg, 0);
|
|
14
|
+
}
|
|
15
|
+
if (typeof arg === 'object' &&
|
|
16
|
+
arg !== null &&
|
|
17
|
+
're' in arg &&
|
|
18
|
+
'im' in arg &&
|
|
19
|
+
typeof arg.re === 'number' &&
|
|
20
|
+
typeof arg.im === 'number') {
|
|
21
|
+
return new Complex(arg.re, arg.im);
|
|
22
|
+
}
|
|
23
|
+
if (typeof arg === 'object' &&
|
|
24
|
+
arg !== null &&
|
|
25
|
+
'r' in arg &&
|
|
26
|
+
'phi' in arg &&
|
|
27
|
+
typeof arg.r === 'number' &&
|
|
28
|
+
typeof arg.phi === 'number') {
|
|
29
|
+
return Complex.fromPolar(arg.r, arg.phi);
|
|
30
|
+
}
|
|
31
|
+
if (Array.isArray(arg) && arg.length === 2) {
|
|
32
|
+
return new Complex(arg[0], arg[1]);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
if (args.length === 2) {
|
|
36
|
+
const arg0 = args[0];
|
|
37
|
+
const arg1 = args[1];
|
|
38
|
+
if (typeof arg0 === 'number' && typeof arg1 === 'number') {
|
|
39
|
+
return new Complex(arg0, arg1);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
throw new Error('Cannot create complex number, invalid arguments.');
|
|
43
|
+
}
|
|
44
|
+
static fromPolar(r, phi) {
|
|
45
|
+
return new Complex(r * Math.cos(phi), r * Math.sin(phi));
|
|
46
|
+
}
|
|
47
|
+
static one() {
|
|
48
|
+
return new Complex(1, 0);
|
|
49
|
+
}
|
|
50
|
+
static zero() {
|
|
51
|
+
return new Complex(0, 0);
|
|
52
|
+
}
|
|
53
|
+
static get i() {
|
|
54
|
+
return new Complex(0, 1);
|
|
55
|
+
}
|
|
56
|
+
get re() {
|
|
57
|
+
return this.realPart;
|
|
58
|
+
}
|
|
59
|
+
get im() {
|
|
60
|
+
return this.imaginaryPart;
|
|
61
|
+
}
|
|
62
|
+
get epsilon() {
|
|
63
|
+
return this.tolerance;
|
|
64
|
+
}
|
|
65
|
+
set epsilon(e) {
|
|
66
|
+
this.tolerance = e;
|
|
67
|
+
}
|
|
68
|
+
abs() {
|
|
69
|
+
return Complex.abs(this);
|
|
70
|
+
}
|
|
71
|
+
arg() {
|
|
72
|
+
return Complex.arg(this);
|
|
73
|
+
}
|
|
74
|
+
sign() {
|
|
75
|
+
return Complex.sign(this);
|
|
76
|
+
}
|
|
77
|
+
conj() {
|
|
78
|
+
return Complex.conj(this);
|
|
79
|
+
}
|
|
80
|
+
neg() {
|
|
81
|
+
return Complex.neg(this);
|
|
82
|
+
}
|
|
83
|
+
inv() {
|
|
84
|
+
return Complex.inv(this);
|
|
85
|
+
}
|
|
86
|
+
add(v) {
|
|
87
|
+
return Complex.add(this, v);
|
|
88
|
+
}
|
|
89
|
+
sub(v) {
|
|
90
|
+
return Complex.sub(this, v);
|
|
91
|
+
}
|
|
92
|
+
mul(v) {
|
|
93
|
+
return Complex.mul(this, v);
|
|
94
|
+
}
|
|
95
|
+
div(v) {
|
|
96
|
+
return Complex.div(this, v);
|
|
97
|
+
}
|
|
98
|
+
exp() {
|
|
99
|
+
return Complex.exp(this);
|
|
100
|
+
}
|
|
101
|
+
log() {
|
|
102
|
+
return Complex.log(this);
|
|
103
|
+
}
|
|
104
|
+
log2() {
|
|
105
|
+
return Complex.log2(this);
|
|
106
|
+
}
|
|
107
|
+
log10() {
|
|
108
|
+
return Complex.log10(this);
|
|
109
|
+
}
|
|
110
|
+
pow(exponent) {
|
|
111
|
+
return Complex.pow(this, exponent);
|
|
112
|
+
}
|
|
113
|
+
sqrt() {
|
|
114
|
+
return Complex.sqrt(this);
|
|
115
|
+
}
|
|
116
|
+
sin() {
|
|
117
|
+
return Complex.sin(this);
|
|
118
|
+
}
|
|
119
|
+
asin() {
|
|
120
|
+
return Complex.asin(this);
|
|
121
|
+
}
|
|
122
|
+
sinh() {
|
|
123
|
+
return Complex.sinh(this);
|
|
124
|
+
}
|
|
125
|
+
asinh() {
|
|
126
|
+
return Complex.asinh(this);
|
|
127
|
+
}
|
|
128
|
+
cos() {
|
|
129
|
+
return Complex.cos(this);
|
|
130
|
+
}
|
|
131
|
+
acos() {
|
|
132
|
+
return Complex.acos(this);
|
|
133
|
+
}
|
|
134
|
+
cosh() {
|
|
135
|
+
return Complex.cosh(this);
|
|
136
|
+
}
|
|
137
|
+
acosh() {
|
|
138
|
+
return Complex.acosh(this);
|
|
139
|
+
}
|
|
140
|
+
tan() {
|
|
141
|
+
return Complex.tan(this);
|
|
142
|
+
}
|
|
143
|
+
atan() {
|
|
144
|
+
return Complex.atan(this);
|
|
145
|
+
}
|
|
146
|
+
tanh() {
|
|
147
|
+
return Complex.tanh(this);
|
|
148
|
+
}
|
|
149
|
+
atanh() {
|
|
150
|
+
return Complex.atanh(this);
|
|
151
|
+
}
|
|
152
|
+
cot() {
|
|
153
|
+
return Complex.cot(this);
|
|
154
|
+
}
|
|
155
|
+
acot() {
|
|
156
|
+
return Complex.acot(this);
|
|
157
|
+
}
|
|
158
|
+
coth() {
|
|
159
|
+
return Complex.coth(this);
|
|
160
|
+
}
|
|
161
|
+
acoth() {
|
|
162
|
+
return Complex.acoth(this);
|
|
163
|
+
}
|
|
164
|
+
sec() {
|
|
165
|
+
return Complex.sec(this);
|
|
166
|
+
}
|
|
167
|
+
asec() {
|
|
168
|
+
return Complex.asec(this);
|
|
169
|
+
}
|
|
170
|
+
sech() {
|
|
171
|
+
return Complex.sech(this);
|
|
172
|
+
}
|
|
173
|
+
asech() {
|
|
174
|
+
return Complex.asech(this);
|
|
175
|
+
}
|
|
176
|
+
csc() {
|
|
177
|
+
return Complex.csc(this);
|
|
178
|
+
}
|
|
179
|
+
acsc() {
|
|
180
|
+
return Complex.acsc(this);
|
|
181
|
+
}
|
|
182
|
+
csch() {
|
|
183
|
+
return Complex.csch(this);
|
|
184
|
+
}
|
|
185
|
+
acsch() {
|
|
186
|
+
return Complex.acsch(this);
|
|
187
|
+
}
|
|
188
|
+
equals(z) {
|
|
189
|
+
return Complex.equals(this, z, this.epsilon);
|
|
190
|
+
}
|
|
191
|
+
toPolar() {
|
|
192
|
+
return Complex.toPolar(this);
|
|
193
|
+
}
|
|
194
|
+
toVector() {
|
|
195
|
+
return Complex.toVector(this);
|
|
196
|
+
}
|
|
197
|
+
toString(dp = 3) {
|
|
198
|
+
return Complex.toString(this, dp);
|
|
199
|
+
}
|
|
200
|
+
static abs(z) {
|
|
201
|
+
return Math.sqrt(z.re ** 2 + z.im ** 2);
|
|
202
|
+
}
|
|
203
|
+
static arg(z) {
|
|
204
|
+
return Math.atan2(z.im, z.re);
|
|
205
|
+
}
|
|
206
|
+
static sign(z) {
|
|
207
|
+
const abs = z.abs();
|
|
208
|
+
return Complex.from({
|
|
209
|
+
re: z.re / abs,
|
|
210
|
+
im: z.im / abs,
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
static conj(z) {
|
|
214
|
+
return Complex.from(z.re, -z.im);
|
|
215
|
+
}
|
|
216
|
+
static neg(z) {
|
|
217
|
+
return Complex.from(-z.re, -z.im);
|
|
218
|
+
}
|
|
219
|
+
static inv(z) {
|
|
220
|
+
return Complex.reciprocal(z);
|
|
221
|
+
}
|
|
222
|
+
static add(z, v) {
|
|
223
|
+
if (typeof v === 'number') {
|
|
224
|
+
return Complex.from(z.re + v, z.im);
|
|
225
|
+
}
|
|
226
|
+
else {
|
|
227
|
+
return Complex.from(z.re + v.re, z.im + v.im);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
static sub(z, v) {
|
|
231
|
+
if (typeof v === 'number') {
|
|
232
|
+
return Complex.from(z.re - v, z.im);
|
|
233
|
+
}
|
|
234
|
+
else {
|
|
235
|
+
return Complex.from(z.re - v.re, z.im - v.im);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
static mul(z, v) {
|
|
239
|
+
if (!Complex.isFinite(z) ||
|
|
240
|
+
(typeof v === 'number' ? !Number.isFinite(v) : !Complex.isFinite(v))) {
|
|
241
|
+
return Complex.nan();
|
|
242
|
+
}
|
|
243
|
+
if (typeof v === 'number') {
|
|
244
|
+
return Complex.from(z.re * v, z.im * v);
|
|
245
|
+
}
|
|
246
|
+
const real = Complex.productSum(z.re, v.re, -z.im, v.im);
|
|
247
|
+
const imaginary = Complex.productSum(z.re, v.im, z.im, v.re);
|
|
248
|
+
return Complex.from(Complex.scalePowerOfTwo(...real), Complex.scalePowerOfTwo(...imaginary));
|
|
249
|
+
}
|
|
250
|
+
static div(z, v) {
|
|
251
|
+
if (!Complex.isFinite(z) ||
|
|
252
|
+
(typeof v === 'number' ? !Number.isFinite(v) : !Complex.isFinite(v))) {
|
|
253
|
+
return Complex.nan();
|
|
254
|
+
}
|
|
255
|
+
if (typeof v === 'number') {
|
|
256
|
+
return v === 0 ? Complex.nan() : Complex.from(z.re / v, z.im / v);
|
|
257
|
+
}
|
|
258
|
+
if (v.re === 0 && v.im === 0) {
|
|
259
|
+
return Complex.nan();
|
|
260
|
+
}
|
|
261
|
+
// Keep products as mantissa/exponent pairs, including subnormal inputs.
|
|
262
|
+
// Neither squaring the denominator nor cancellation requires an infinite intermediate.
|
|
263
|
+
const [denominator, exponent] = Complex.productSum(v.re, v.re, v.im, v.im);
|
|
264
|
+
const quotient = (a, b, c, d) => {
|
|
265
|
+
const [numerator, power] = Complex.productSum(a, b, c, d);
|
|
266
|
+
return Complex.scalePowerOfTwo(numerator / denominator, power - exponent);
|
|
267
|
+
};
|
|
268
|
+
return Complex.from(quotient(z.re, v.re, z.im, v.im), quotient(z.im, v.re, -z.re, v.im));
|
|
269
|
+
}
|
|
270
|
+
static exp(z) {
|
|
271
|
+
// e^(a + bi) = e^a * e^bi = e^a * (cos(b) + i sin(b))
|
|
272
|
+
const eRe = Math.exp(z.re);
|
|
273
|
+
return Complex.from({
|
|
274
|
+
re: eRe * Math.cos(z.im),
|
|
275
|
+
im: eRe * Math.sin(z.im),
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
static log(z) {
|
|
279
|
+
if (!Complex.isFinite(z)) {
|
|
280
|
+
return Complex.nan();
|
|
281
|
+
}
|
|
282
|
+
return Complex.from(Complex.logHypot(z.re, z.im), z.arg());
|
|
283
|
+
}
|
|
284
|
+
static log2(z) {
|
|
285
|
+
const value = Complex.log(z);
|
|
286
|
+
return Complex.from(value.re * Math.LOG2E, value.im * Math.LOG2E);
|
|
287
|
+
}
|
|
288
|
+
static log10(z) {
|
|
289
|
+
const value = Complex.log(z);
|
|
290
|
+
return Complex.from(value.re * Math.LOG10E, value.im * Math.LOG10E);
|
|
291
|
+
}
|
|
292
|
+
static pow(z, exponent) {
|
|
293
|
+
if (!Complex.isFinite(z) || !Number.isFinite(exponent)) {
|
|
294
|
+
return Complex.nan();
|
|
295
|
+
}
|
|
296
|
+
if (exponent === 0) {
|
|
297
|
+
return Complex.one();
|
|
298
|
+
}
|
|
299
|
+
if (exponent === 1) {
|
|
300
|
+
return Complex.from(z.re, z.im);
|
|
301
|
+
}
|
|
302
|
+
if (exponent === -1) {
|
|
303
|
+
return Complex.inv(z);
|
|
304
|
+
}
|
|
305
|
+
if (exponent === 0.5) {
|
|
306
|
+
return Complex.sqrt(z);
|
|
307
|
+
}
|
|
308
|
+
if (exponent === 2) {
|
|
309
|
+
return Complex.mul(z, z);
|
|
310
|
+
}
|
|
311
|
+
if (z.re === 0 && z.im === 0) {
|
|
312
|
+
return exponent > 0 ? Complex.zero() : Complex.nan();
|
|
313
|
+
}
|
|
314
|
+
const logRadius = Complex.logHypot(z.re, z.im) * exponent;
|
|
315
|
+
const angle = z.arg() * exponent;
|
|
316
|
+
const component = (factor) => factor === 0
|
|
317
|
+
? factor
|
|
318
|
+
: Complex.copySign(Math.exp(logRadius + Math.log(Math.abs(factor))), factor);
|
|
319
|
+
return Complex.from(component(Math.cos(angle)), component(Math.sin(angle)));
|
|
320
|
+
}
|
|
321
|
+
static sqrt(z) {
|
|
322
|
+
if (!Complex.isFinite(z)) {
|
|
323
|
+
return Complex.nan();
|
|
324
|
+
}
|
|
325
|
+
const scale = Math.max(Math.abs(z.re), Math.abs(z.im));
|
|
326
|
+
if (scale === 0) {
|
|
327
|
+
return Complex.from(0, z.im);
|
|
328
|
+
}
|
|
329
|
+
const x = z.re / scale;
|
|
330
|
+
const y = z.im / scale;
|
|
331
|
+
const root = Math.sqrt(scale) * Math.sqrt((Math.hypot(x, y) + Math.abs(x)) / 2);
|
|
332
|
+
return z.re >= 0
|
|
333
|
+
? Complex.from(root, z.im / (2 * root))
|
|
334
|
+
: Complex.from(Math.abs(z.im) / (2 * root), Complex.copySign(root, z.im));
|
|
335
|
+
}
|
|
336
|
+
static sin(z) {
|
|
337
|
+
// sin(a+bi) = sin(a)cosh(b) + icos(a)sinh(b)
|
|
338
|
+
// https://proofwiki.org/wiki/Sine_of_Complex_Number
|
|
339
|
+
return Complex.from(Math.sin(z.re) * Math.cosh(z.im), Math.cos(z.re) * Math.sinh(z.im));
|
|
340
|
+
}
|
|
341
|
+
static asin(z) {
|
|
342
|
+
// asin(z) = -i * ln(zi + sqrt(1 - z^2))
|
|
343
|
+
// ref. https://en.wikipedia.org/wiki/Inverse_trigonometric_functions#Logarithmic_forms
|
|
344
|
+
const C = Complex;
|
|
345
|
+
const i = C.i;
|
|
346
|
+
return i.neg().mul(C.log(z.mul(i).add(C.sqrt(C.one().sub(z.pow(2))))));
|
|
347
|
+
}
|
|
348
|
+
static sinh(z) {
|
|
349
|
+
if (!Complex.isFinite(z)) {
|
|
350
|
+
return Complex.nan();
|
|
351
|
+
}
|
|
352
|
+
return Complex.from(Complex.hyperbolicProduct(z.re, Math.cos(z.im), true), Complex.hyperbolicProduct(z.re, Math.sin(z.im), false));
|
|
353
|
+
}
|
|
354
|
+
static asinh(z) {
|
|
355
|
+
if (!Complex.isFinite(z)) {
|
|
356
|
+
return Complex.nan();
|
|
357
|
+
}
|
|
358
|
+
const x = Math.abs(z.re);
|
|
359
|
+
const y = Math.abs(z.im);
|
|
360
|
+
if (Math.max(x, y) < 1e-8) {
|
|
361
|
+
return Complex.from(z.re, z.im);
|
|
362
|
+
}
|
|
363
|
+
if (y < 1 && x < 1e-150) {
|
|
364
|
+
return Complex.from(z.re / (Math.sqrt(1 - y) * Math.sqrt(1 + y)), Math.asin(z.im));
|
|
365
|
+
}
|
|
366
|
+
if (Math.max(x, y) > 1e150) {
|
|
367
|
+
return Complex.from(Complex.copySign(Complex.logHypot(x, y) + Math.LN2, z.re), Math.atan2(z.im, x));
|
|
368
|
+
}
|
|
369
|
+
const { real, transverse } = Complex.inverseHyperbolicParts(y, x);
|
|
370
|
+
return Complex.from(Complex.copySign(real, z.re), Complex.copySign(Math.atan2(y, transverse), z.im));
|
|
371
|
+
}
|
|
372
|
+
static cos(z) {
|
|
373
|
+
// cos(a+bi) = cos(a)cosh(b) + isin(a)sinh(b)
|
|
374
|
+
// https://proofwiki.org/wiki/Cosine_of_Complex_Number
|
|
375
|
+
return Complex.from(Math.cos(z.re) * Math.cosh(z.im), -Math.sin(z.re) * Math.sinh(z.im));
|
|
376
|
+
}
|
|
377
|
+
static acos(z) {
|
|
378
|
+
// acos(z) = -i * ln(z + sqrt(z^2 - 1))
|
|
379
|
+
// = pi/2 + i * ln(zi + sqrt(1 - z^2))
|
|
380
|
+
// = pi/2 - asin(z)
|
|
381
|
+
// ref. https://en.wikipedia.org/wiki/Inverse_trigonometric_functions#Logarithmic_forms
|
|
382
|
+
return Complex.from(Math.PI / 2).sub(z.asin());
|
|
383
|
+
}
|
|
384
|
+
static cosh(z) {
|
|
385
|
+
if (!Complex.isFinite(z)) {
|
|
386
|
+
return Complex.nan();
|
|
387
|
+
}
|
|
388
|
+
return Complex.from(Complex.hyperbolicProduct(z.re, Math.cos(z.im), false), Complex.hyperbolicProduct(z.re, Math.sin(z.im), true));
|
|
389
|
+
}
|
|
390
|
+
static acosh(z) {
|
|
391
|
+
if (!Complex.isFinite(z)) {
|
|
392
|
+
return Complex.nan();
|
|
393
|
+
}
|
|
394
|
+
if (Math.abs(z.re) < 1 && Math.abs(z.im) < 1e-150) {
|
|
395
|
+
return Complex.from(Math.abs(z.im) / (Math.sqrt(1 - z.re) * Math.sqrt(1 + z.re)), Complex.copySign(Math.acos(z.re), z.im));
|
|
396
|
+
}
|
|
397
|
+
if (Math.max(Math.abs(z.re), Math.abs(z.im)) > 1e150) {
|
|
398
|
+
return Complex.from(Complex.logHypot(z.re, z.im) + Math.LN2, Math.atan2(z.im, z.re));
|
|
399
|
+
}
|
|
400
|
+
const { real, transverse } = Complex.inverseHyperbolicParts(Math.abs(z.re), Math.abs(z.im));
|
|
401
|
+
return Complex.from(real, Complex.copySign(Math.atan2(transverse, z.re), z.im));
|
|
402
|
+
}
|
|
403
|
+
static tan(z) {
|
|
404
|
+
return z.sin().div(z.cos());
|
|
405
|
+
}
|
|
406
|
+
static atan(z) {
|
|
407
|
+
// atan(z) = i / 2 * ln((i + z) / (i - z))
|
|
408
|
+
// = i / 2 * [ln(1 - iz) - ln(1 + iz)]
|
|
409
|
+
// ref. https://en.wikipedia.org/wiki/Inverse_trigonometric_functions#Logarithmic_forms
|
|
410
|
+
const C = Complex;
|
|
411
|
+
const i = C.i;
|
|
412
|
+
const z1 = C.log(C.one().sub(i.mul(z)));
|
|
413
|
+
const z2 = C.log(C.one().add(i.mul(z)));
|
|
414
|
+
return i.div(2).mul(z1.sub(z2));
|
|
415
|
+
}
|
|
416
|
+
static tanh(z) {
|
|
417
|
+
if (!Complex.isFinite(z)) {
|
|
418
|
+
return Complex.nan();
|
|
419
|
+
}
|
|
420
|
+
// Divide through by exp(2|x|); expm1 retains the real part near zero.
|
|
421
|
+
const q = Math.exp(-2 * Math.abs(z.re));
|
|
422
|
+
const difference = -Math.expm1(-2 * Math.abs(z.re));
|
|
423
|
+
const cos = Math.cos(z.im);
|
|
424
|
+
const sin = Math.sin(z.im);
|
|
425
|
+
const denominator = difference * difference + 4 * q * cos * cos;
|
|
426
|
+
return Complex.from(Complex.copySign((difference * (1 + q)) / denominator, z.re), (4 * q * sin * cos) / denominator);
|
|
427
|
+
}
|
|
428
|
+
static atanh(z) {
|
|
429
|
+
if (!Complex.isFinite(z)) {
|
|
430
|
+
return Complex.nan();
|
|
431
|
+
}
|
|
432
|
+
const x = Math.abs(z.re);
|
|
433
|
+
const y = Math.abs(z.im);
|
|
434
|
+
if (Math.max(x, y) > 1e150) {
|
|
435
|
+
return Complex.from(Complex.reciprocal(z).re, Complex.copySign(Math.PI / 2, z.im));
|
|
436
|
+
}
|
|
437
|
+
const distance = Math.hypot(1 - x, y);
|
|
438
|
+
const real = distance < 1e-150
|
|
439
|
+
? (Complex.logHypot(1 + x, y) - Complex.logHypot(1 - x, y)) / 2
|
|
440
|
+
: Math.log1p((4 * (x / distance)) / distance) / 4;
|
|
441
|
+
const imag = Math.atan2(2 * z.im, (1 - x) * (1 + x) - y * y) / 2;
|
|
442
|
+
return Complex.from(Complex.copySign(real, z.re), imag);
|
|
443
|
+
}
|
|
444
|
+
static cot(z) {
|
|
445
|
+
return z.cos().div(z.sin());
|
|
446
|
+
}
|
|
447
|
+
static acot(z) {
|
|
448
|
+
// atan(z) = i / 2 * ln((i - z) / (i + z))
|
|
449
|
+
// = i / 2 * [ln(1 - i/z) - ln(1 + i/z)]
|
|
450
|
+
// ref. https://en.wikipedia.org/wiki/Inverse_trigonometric_functions#Logarithmic_forms
|
|
451
|
+
const C = Complex;
|
|
452
|
+
const i = C.i;
|
|
453
|
+
const z1 = C.log(C.one().sub(i.div(z)));
|
|
454
|
+
const z2 = C.log(C.one().add(i.div(z)));
|
|
455
|
+
return i.div(2).mul(z1.sub(z2));
|
|
456
|
+
}
|
|
457
|
+
static coth(z) {
|
|
458
|
+
if (!Complex.isFinite(z)) {
|
|
459
|
+
return Complex.nan();
|
|
460
|
+
}
|
|
461
|
+
return Complex.reciprocal(Complex.tanh(z));
|
|
462
|
+
}
|
|
463
|
+
static acoth(z) {
|
|
464
|
+
if (!Complex.isFinite(z)) {
|
|
465
|
+
return Complex.nan();
|
|
466
|
+
}
|
|
467
|
+
if (Math.max(Math.abs(z.re), Math.abs(z.im)) < 1e-150) {
|
|
468
|
+
return Complex.from(z.re, Complex.copySign(Math.PI / 2, -z.im));
|
|
469
|
+
}
|
|
470
|
+
return Complex.atanh(Complex.reciprocal(z));
|
|
471
|
+
}
|
|
472
|
+
static sec(z) {
|
|
473
|
+
return Complex.sech(Complex.from(-z.im, z.re));
|
|
474
|
+
}
|
|
475
|
+
static asec(z) {
|
|
476
|
+
// asec(z) = -i * ln(sqrt(1 / z^2 - 1) + 1 / z))
|
|
477
|
+
// ref. https://en.wikipedia.org/wiki/Inverse_trigonometric_functions#Logarithmic_forms
|
|
478
|
+
const C = Complex;
|
|
479
|
+
const i = C.i;
|
|
480
|
+
return i.neg().mul(C.log(C.sqrt(C.one().div(z.pow(2)).sub(1)).add(z.inv())));
|
|
481
|
+
}
|
|
482
|
+
static sech(z) {
|
|
483
|
+
return Complex.hyperbolicReciprocal(z, true);
|
|
484
|
+
}
|
|
485
|
+
static asech(z) {
|
|
486
|
+
if (!Complex.isFinite(z) || (z.re === 0 && z.im === 0)) {
|
|
487
|
+
return Complex.nan();
|
|
488
|
+
}
|
|
489
|
+
if (Math.max(Math.abs(z.re), Math.abs(z.im)) < 1e-150) {
|
|
490
|
+
return Complex.from(Math.LN2 - Complex.logHypot(z.re, z.im), -Math.atan2(z.im, z.re));
|
|
491
|
+
}
|
|
492
|
+
return Complex.acosh(Complex.reciprocal(z));
|
|
493
|
+
}
|
|
494
|
+
static csc(z) {
|
|
495
|
+
const value = Complex.csch(Complex.from(-z.im, z.re));
|
|
496
|
+
return Complex.from(-value.im, value.re);
|
|
497
|
+
}
|
|
498
|
+
static acsc(z) {
|
|
499
|
+
// acsc(z) = -i * ln(sqrt(1 - 1 / z^2) + i / z))
|
|
500
|
+
// ref. https://en.wikipedia.org/wiki/Inverse_trigonometric_functions#Logarithmic_forms
|
|
501
|
+
const C = Complex;
|
|
502
|
+
const i = C.i;
|
|
503
|
+
return i.neg().mul(C.log(C.sqrt(C.one().sub(C.one().div(z.pow(2)))).add(i.div(z))));
|
|
504
|
+
}
|
|
505
|
+
static csch(z) {
|
|
506
|
+
return Complex.hyperbolicReciprocal(z, false);
|
|
507
|
+
}
|
|
508
|
+
static acsch(z) {
|
|
509
|
+
if (!Complex.isFinite(z) || (z.re === 0 && z.im === 0)) {
|
|
510
|
+
return Complex.nan();
|
|
511
|
+
}
|
|
512
|
+
if (Math.max(Math.abs(z.re), Math.abs(z.im)) < 1e-150) {
|
|
513
|
+
return Complex.from(Complex.copySign(Math.LN2 - Complex.logHypot(z.re, z.im), z.re), Math.atan2(-z.im, Math.abs(z.re)));
|
|
514
|
+
}
|
|
515
|
+
return Complex.asinh(Complex.reciprocal(z));
|
|
516
|
+
}
|
|
517
|
+
static isFinite(z) {
|
|
518
|
+
return Number.isFinite(z.re) && Number.isFinite(z.im);
|
|
519
|
+
}
|
|
520
|
+
static nan() {
|
|
521
|
+
return Complex.from(NaN, NaN);
|
|
522
|
+
}
|
|
523
|
+
static copySign(magnitude, sign) {
|
|
524
|
+
return sign < 0 || Object.is(sign, -0) ? -Math.abs(magnitude) : Math.abs(magnitude);
|
|
525
|
+
}
|
|
526
|
+
static logHypot(x, y) {
|
|
527
|
+
const scale = Math.max(Math.abs(x), Math.abs(y));
|
|
528
|
+
return scale === 0 ? -Infinity : Math.log(scale) + Math.log(Math.hypot(x / scale, y / scale));
|
|
529
|
+
}
|
|
530
|
+
/** Sum two products without premature overflow/underflow. Result is mantissa × 2^exponent. */
|
|
531
|
+
static productSum(a, b, c, d) {
|
|
532
|
+
const product = (x, y) => {
|
|
533
|
+
if (x === 0 || y === 0) {
|
|
534
|
+
return [x * y, 0];
|
|
535
|
+
}
|
|
536
|
+
const ex = Math.min(1023, Math.floor(Math.log2(Math.abs(x))));
|
|
537
|
+
const ey = Math.min(1023, Math.floor(Math.log2(Math.abs(y))));
|
|
538
|
+
return [(x / 2 ** ex) * (y / 2 ** ey), ex + ey];
|
|
539
|
+
};
|
|
540
|
+
const [first, ef] = product(a, b);
|
|
541
|
+
const [second, es] = product(c, d);
|
|
542
|
+
if (first === 0) {
|
|
543
|
+
return second === 0 ? [first + second, 0] : [second, es];
|
|
544
|
+
}
|
|
545
|
+
if (second === 0) {
|
|
546
|
+
return [first, ef];
|
|
547
|
+
}
|
|
548
|
+
const exponent = Math.max(ef, es);
|
|
549
|
+
return [first * 2 ** (ef - exponent) + second * 2 ** (es - exponent), exponent];
|
|
550
|
+
}
|
|
551
|
+
static scalePowerOfTwo(value, exponent) {
|
|
552
|
+
if (value === 0) {
|
|
553
|
+
return value;
|
|
554
|
+
}
|
|
555
|
+
// Normalize after cancellation before splitting the final exponent.
|
|
556
|
+
const shift = Math.min(1023, Math.floor(Math.log2(Math.abs(value))));
|
|
557
|
+
value /= 2 ** shift;
|
|
558
|
+
exponent += shift;
|
|
559
|
+
if (exponent > 1023) {
|
|
560
|
+
return value * 2 ** (exponent - 1023) * 2 ** 1023;
|
|
561
|
+
}
|
|
562
|
+
if (exponent < -1022) {
|
|
563
|
+
return value * 2 ** (exponent + 1022) * 2 ** -1022;
|
|
564
|
+
}
|
|
565
|
+
return value * 2 ** exponent;
|
|
566
|
+
}
|
|
567
|
+
/** Cartesian reciprocal, preserving signed zeros on branch cuts. */
|
|
568
|
+
static reciprocal(z) {
|
|
569
|
+
const value = Complex.div(Complex.one(), z);
|
|
570
|
+
if (Number.isNaN(value.re)) {
|
|
571
|
+
return value;
|
|
572
|
+
}
|
|
573
|
+
return Complex.from(z.re === 0 ? Complex.copySign(0, z.re) : value.re, z.im === 0 ? Complex.copySign(0, -z.im) : value.im);
|
|
574
|
+
}
|
|
575
|
+
static hyperbolicProduct(x, factor, odd) {
|
|
576
|
+
if (Math.abs(x) < 20) {
|
|
577
|
+
return factor * (odd ? Math.sinh(x) : Math.cosh(x));
|
|
578
|
+
}
|
|
579
|
+
const sign = odd ? factor * Math.sign(x) : factor;
|
|
580
|
+
if (factor === 0) {
|
|
581
|
+
return sign;
|
|
582
|
+
}
|
|
583
|
+
// Combine the factor before exponentiation to avoid a spurious Infinity * 0.
|
|
584
|
+
return Complex.copySign(Math.exp(Math.abs(x) - Math.LN2 + Math.log(Math.abs(factor))), sign);
|
|
585
|
+
}
|
|
586
|
+
static hyperbolicReciprocal(z, even) {
|
|
587
|
+
if (!Complex.isFinite(z)) {
|
|
588
|
+
return Complex.nan();
|
|
589
|
+
}
|
|
590
|
+
const x = Math.abs(z.re);
|
|
591
|
+
const q = Math.exp(-2 * x);
|
|
592
|
+
const difference = -Math.expm1(-2 * x);
|
|
593
|
+
const cos = Math.cos(z.im);
|
|
594
|
+
const sin = Math.sin(z.im);
|
|
595
|
+
const denominator = even
|
|
596
|
+
? Complex.from(cos * (1 + q), sin * Complex.copySign(difference, z.re))
|
|
597
|
+
: Complex.from(cos * Complex.copySign(difference, z.re), sin * (1 + q));
|
|
598
|
+
return Complex.reciprocal(denominator).mul(Math.exp(Math.LN2 - x));
|
|
599
|
+
}
|
|
600
|
+
/**
|
|
601
|
+
* Elliptic coordinates for principal inverse functions (x,y >= 0).
|
|
602
|
+
* With a = (|z+1| + |z-1|)/2, return acosh(a) and sqrt(a²-x²).
|
|
603
|
+
* Rationalized hypot differences retain tiny components near the branch cuts.
|
|
604
|
+
* Principal branches: https://dlmf.nist.gov/4.37
|
|
605
|
+
*/
|
|
606
|
+
static inverseHyperbolicParts(x, y) {
|
|
607
|
+
const plus = Math.hypot(x + 1, y);
|
|
608
|
+
const minus = Math.hypot(x - 1, y);
|
|
609
|
+
const a = plus / 2 + minus / 2;
|
|
610
|
+
const first = y === 0 ? 0 : y / Math.sqrt(plus + x + 1);
|
|
611
|
+
const second = y === 0 ? 0 : y / Math.sqrt(minus + Math.abs(x - 1));
|
|
612
|
+
const correction = Math.hypot(first, second) / Math.SQRT2;
|
|
613
|
+
const rootAMinus1 = x <= 1 ? correction : Math.hypot(Math.sqrt(x - 1), correction);
|
|
614
|
+
const rootAMinusX = x <= 1 ? Math.hypot(Math.sqrt(1 - x), correction) : correction;
|
|
615
|
+
return {
|
|
616
|
+
real: 2 * Math.asinh(rootAMinus1 / Math.SQRT2),
|
|
617
|
+
transverse: rootAMinusX * Math.sqrt(a + x),
|
|
618
|
+
};
|
|
619
|
+
}
|
|
620
|
+
static equals(z1, z2, epsilon = Number.EPSILON) {
|
|
621
|
+
return Math.abs(z1.re - z2.re) < epsilon && Math.abs(z1.im - z2.im) < epsilon;
|
|
622
|
+
}
|
|
623
|
+
static toPolar(z) {
|
|
624
|
+
return [z.abs(), z.arg()];
|
|
625
|
+
}
|
|
626
|
+
static toVector(z) {
|
|
627
|
+
return [z.re, z.im];
|
|
628
|
+
}
|
|
629
|
+
// TODO: format
|
|
630
|
+
static toString(z, dp = 3) {
|
|
631
|
+
const re = z.re.toFixed(dp);
|
|
632
|
+
const im = Math.abs(z.im).toFixed(dp);
|
|
633
|
+
const imsign = z.im < 0 ? '-' : '+';
|
|
634
|
+
return `${re} ${imsign} ${im}i`;
|
|
635
|
+
}
|
|
636
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** An ideal series component equivalent to a reactance at one frequency. */
|
|
2
|
+
export type ReactiveComponent = {
|
|
3
|
+
readonly kind: 'inductor';
|
|
4
|
+
readonly inductanceHenries: number;
|
|
5
|
+
} | {
|
|
6
|
+
readonly kind: 'capacitor';
|
|
7
|
+
readonly capacitanceFarads: number;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Convert reactance in ohms at a frequency in Hz to an ideal series L or C.
|
|
11
|
+
* Zero frequency, zero reactance, or an unrepresentable component returns undefined.
|
|
12
|
+
* Non-finite reactance and negative/non-finite frequency throw RangeError.
|
|
13
|
+
*/
|
|
14
|
+
export declare function reactanceToComponent(reactanceOhms: number, frequencyHz: number): ReactiveComponent | undefined;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Convert reactance in ohms at a frequency in Hz to an ideal series L or C.
|
|
3
|
+
* Zero frequency, zero reactance, or an unrepresentable component returns undefined.
|
|
4
|
+
* Non-finite reactance and negative/non-finite frequency throw RangeError.
|
|
5
|
+
*/
|
|
6
|
+
export function reactanceToComponent(reactanceOhms, frequencyHz) {
|
|
7
|
+
if (!Number.isFinite(reactanceOhms)) {
|
|
8
|
+
throw new RangeError('Reactance must be finite.');
|
|
9
|
+
}
|
|
10
|
+
if (!Number.isFinite(frequencyHz) || frequencyHz < 0) {
|
|
11
|
+
throw new RangeError('Frequency must be finite and non-negative.');
|
|
12
|
+
}
|
|
13
|
+
if (frequencyHz === 0 || reactanceOhms === 0) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
// Compute in log space so intermediate products/quotients cannot overflow.
|
|
17
|
+
const logReactance = Math.log(Math.abs(reactanceOhms));
|
|
18
|
+
const logAngularFrequency = Math.log(2 * Math.PI) + Math.log(frequencyHz);
|
|
19
|
+
const value = Math.exp((reactanceOhms > 0 ? logReactance : -logReactance) - logAngularFrequency);
|
|
20
|
+
if (!Number.isFinite(value) || value === 0) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
return reactanceOhms > 0
|
|
24
|
+
? { kind: 'inductor', inductanceHenries: value }
|
|
25
|
+
: { kind: 'capacitor', capacitanceFarads: value };
|
|
26
|
+
}
|