-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathComplex.cs
More file actions
460 lines (383 loc) · 17 KB
/
Copy pathComplex.cs
File metadata and controls
460 lines (383 loc) · 17 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
using System.Globalization;
using System.Numerics;
namespace ComplexMathematics
{
/// <summary>
/// A Class that provides Complex numbers and operations with them.
/// Contains <paramref name="real"/> and <paramref name="imaginary"/> parts of number represented by double Type.
/// </summary>
[Serializable]
public readonly struct Complex(double real, double imaginary) : IEquatable<Complex>, IFormattable, IAdditionOperators<Complex, Complex, Complex>,
IAdditiveIdentity<Complex, Complex>, IDecrementOperators<Complex>, IDivisionOperators<Complex, Complex, Complex>, IEqualityOperators<Complex, Complex, bool>,
IIncrementOperators<Complex>, IMultiplicativeIdentity<Complex, Complex>, IMultiplyOperators<Complex, Complex, Complex>, ISubtractionOperators<Complex, Complex, Complex>, IUnaryNegationOperators<Complex, Complex>
{
public double Real => _real;
public double Imaginary => _imaginary;
private readonly double _real = real;
private readonly double _imaginary = imaginary;
public double Argument => Math.Atan2(_imaginary, _real);
public double Magnitude => Abs(this);
public Complex Conjugated => Conjugate(this);
#region Constants
public static Complex Zero => new(0, 0);
public static Complex AdditiveIdentity => Zero;
public static Complex RealOne => new(1, 0);
public static Complex MultiplicativeIdentity => RealOne;
public static Complex ImaginaryOne => new(0, 1);
public static Complex EpsilonForAnalysis => new(0.00001d, 0);
public static Complex EulersConstant => new(0.5772157, 0);
public static Complex PI => new(Math.PI, 0);
public static Complex Tau => new(Math.Tau, 0);
public static Complex E => new(Math.E, 0);
#endregion
#region Generators
public static Complex FromPolarCoordinates(double magnitude, double argument) => new(magnitude * Math.Cos(argument), magnitude * Math.Sin(argument));
public static implicit operator Complex(short value) => new(value, 0);
public static implicit operator Complex(int value) => new(value, 0);
public static implicit operator Complex(long value) => new(value, 0);
public static implicit operator Complex(ushort value) => new(value, 0);
public static implicit operator Complex(uint value) => new(value, 0);
public static implicit operator Complex(ulong value) => new(value, 0);
public static implicit operator Complex(sbyte value) => new(value, 0);
public static implicit operator Complex(byte value) => new(value, 0);
public static implicit operator Complex(float value) => new(value, 0);
public static implicit operator Complex(double value) => new(value, 0);
public static explicit operator Complex(BigInteger value) => new((double)value, 0);
public static explicit operator Complex(decimal value) => new((double)value, 0);
#endregion
#region ComplexFunctions
public static double Abs(Complex value)
{
if (double.IsInfinity(value._real) || double.IsInfinity(value._imaginary))
return double.PositiveInfinity;
var real = Math.Abs(value._real);
var imaginary = Math.Abs(value._imaginary);
if (real > imaginary)
{
var ratio1 = imaginary / real;
return real * Math.Sqrt(1 + ratio1 * ratio1);
}
if (imaginary == 0)
return real;
var ratio2 = real / imaginary;
return imaginary * Math.Sqrt(1 + ratio2 * ratio2);
}
public static Complex Conjugate(Complex value)
{
return new Complex(value._real, -value._imaginary);
}
public static Complex Log(Complex value)
{
return new(Math.Log(value.Magnitude), value.Argument);
}
public static Complex Log(Complex value, Complex baseValue)
{
return Log(value) / Log(baseValue);
}
public static Complex Exp(Complex value)
{
return FromPolarCoordinates(Math.Exp(value._real), value._imaginary);
}
public static Complex Pow(Complex value, Complex power)
{
if (power == Zero)
return RealOne;
if (value == Zero)
return Zero;
var valueArgument = value.Argument;
var valueMagnitude = value.Magnitude;
var resultArgument = power._real * valueArgument + power._imaginary * Math.Log(valueMagnitude);
var resultMagnitude = Math.Pow(valueMagnitude, power._real) * Math.Exp(-power._imaginary * valueArgument);
return FromPolarCoordinates(resultMagnitude, resultArgument);
}
public static Complex Sqrt(Complex value)
{
return FromPolarCoordinates(Math.Sqrt(value.Magnitude), value.Argument / 2);
}
public static Complex Sin(Complex value)
{
return new(Math.Sin(value._real) * Math.Cosh(value._imaginary), Math.Cos(value._real) * Math.Sinh(value._imaginary));
}
public static Complex Sinh(Complex value)
{
return new(Math.Sinh(value._real) * Math.Cos(value._imaginary), Math.Cosh(value._real) * Math.Sin(value._imaginary));
}
public static Complex Asin(Complex value)
{
return -ImaginaryOne * Log(ImaginaryOne * value + Sqrt(RealOne - value * value));
}
public static Complex Asinh(Complex value)
{
return Log(value + Sqrt(RealOne + value * value));
}
public static Complex Cos(Complex value)
{
return new(Math.Cos(value._real) * Math.Cosh(value._imaginary), -Math.Sin(value._real) * Math.Sinh(value._imaginary));
}
public static Complex Cosh(Complex value)
{
return new(Math.Cosh(value._real) * Math.Cos(value._imaginary), Math.Sinh(value._real) * Math.Sin(value._imaginary));
}
public static Complex Acos(Complex value)
{
return -ImaginaryOne * Log(value + ImaginaryOne * Sqrt(RealOne - value * value));
}
public static Complex Acosh(Complex value)
{
return Log(ImaginaryOne * (-value + Sqrt(RealOne + value * value)));
}
public static Complex Tan(Complex value)
{
return Sin(value) / Cos(value);
}
public static Complex Tanh(Complex value)
{
return Sinh(value) / Cosh(value);
}
public static Complex Atan(Complex value)
{
return 0.5 * ImaginaryOne * (Log(RealOne - ImaginaryOne * value) - Log(RealOne + ImaginaryOne * value));
}
public static Complex Atanh(Complex value)
{
return -0.5 * (Log(RealOne - value) - Log(RealOne + value));
}
public static Complex Lerp(Complex first, Complex second, double factor)
{
return first + (second - first) * factor;
}
public static double InverseLerp(Complex first, Complex second, Complex value)
{
return ((first - value) / (first - second)).Real;
}
public static Complex SLerp(Complex first, Complex second, double factor)
{
var ratio = second / first;
return first * FromPolarCoordinates(1 + (ratio.Magnitude - 1) * factor, ratio.Argument * factor);
}
public static Complex Gamma(Complex value)
{
var result = value * Exp(EulersConstant * value);
for (int k = 1; k < 1000; k++)
result *= (1 + value / k) * Exp(-value / k);
return 1 / result;
}
public static Complex Beta(Complex a, Complex b)
{
return Gamma(a) * Gamma(b) / Gamma(a + b);
}
public static Complex Zeta(Complex value)
{
if (value == Zero) return -0.5;
if (value == RealOne) return new(double.PositiveInfinity, 0);
if (value._real < 0.5)
return Pow(2, value) * Pow(PI, value - 1) * Sin(0.5 * Math.PI * value) * Gamma(1 - value) * Zeta(1 - value);
var sum = Zero;
for (int k = 1; k < 1000; k++)
sum += k * (k + 1) / 2 * ((2 * k + 3 + value) / Pow(k + 1, value + 2) - (2 * k - 1 - value) / Pow(k, value + 2));
return sum / (value - 1);
}
public static Complex LambertW(Complex value)
{
var w = Zero;
for (int k = 0; k < 1000; k++)
{
var wTimesExpW = w * Exp(w);
w -= (wTimesExpW - value) / ((w + 1) * Exp(w) - (w + 2) * (wTimesExpW - value) / (2 * w + 2));
}
return w;
}
#endregion
#region Transforms
/// <summary>
/// Fast Fourier Transform Algorithm with 0-padding for not 2-th power inputs.
/// </summary>
/// <returns> Complex[] with length of input array scaled to minimal power of 2 greater then k </returns>
public static Complex[] FastFourierTransform(Complex[] input)
{
var initialPointsCount = input.Length;
if (initialPointsCount <= 1) return input;
var pointsCount = (int)Math.Pow(2, (int)Math.Log2(initialPointsCount - 1) + 1);
var even = new Complex[pointsCount / 2];
var odd = new Complex[pointsCount / 2];
for (int i = 0; i < initialPointsCount / 2; i++)
{
even[i] = input[2 * i];
odd[i] = input[2 * i + 1];
}
even = FastFourierTransform(even);
odd = FastFourierTransform(odd);
var result = new Complex[pointsCount];
for (int frequency = 0; frequency < pointsCount / 2; frequency++)
{
var value = FromPolarCoordinates(1, -Math.Tau * frequency / pointsCount) * odd[frequency];
result[frequency] = even[frequency] + value;
result[frequency + pointsCount / 2] = even[frequency] - value;
}
return result;
}
/// <summary>
/// Discrete Fourier Transform Algorithm. Always prefer to use FastFourierTransform.
/// </summary>
/// <returns> Complex[] with length of input array</returns>
public static Complex[] DiscreteFourierTransform(Complex[] input)
{
int pointsCount = input.Length;
var result = new Complex[pointsCount];
for (int point = 0; point < pointsCount; point++)
{
Complex coefficient = 0;
for (int frequency = 0; frequency < pointsCount; frequency++)
coefficient += input[frequency] * FromPolarCoordinates(1, -Math.Tau * frequency * point / pointsCount);
result[point] = coefficient;
}
return result;
}
/// <summary>
/// Inverse Fast Fourier Transform Algorithm with 0-padding for not 2-th power inputs.
/// </summary>
/// <returns> Complex[] with length of input array scaled to minimal power of 2 greater then k </returns>
public static Complex[] InverseFastFourierTransform(Complex[] input)
{
var pointsCount = input.Length;
var result = new Complex[pointsCount];
for (int i = 0; i < pointsCount; i++)
result[i] = result[i].Conjugated;
result = FastFourierTransform(result);
for (int i = 0; i < pointsCount; i++)
result[i] = result[i].Conjugated;
return result;
}
/// <summary>
/// Inverse Discrete Fourier Transform Algorithm. Always prefer to use InverseFastFourierTransform.
/// </summary>
/// <returns> Complex[] with length of input array</returns>
public static Complex[] InverseDiscreteFourierTransform(Complex[] input)
{
var pointsCount = input.Length;
var result = new Complex[pointsCount];
for (int i = 0; i < pointsCount; i++)
result[i] = result[i].Conjugated;
result = DiscreteFourierTransform(result);
for (int i = 0; i < pointsCount; i++)
result[i] = result[i].Conjugated;
return result;
}
#endregion
#region ToString
public override string ToString()
{
return string.Format(CultureInfo.CurrentCulture, $"{_real} + {_imaginary}i");
}
public string ToString(string format)
{
var real = this._real.ToString(format, CultureInfo.CurrentCulture);
var imaginary = this._imaginary.ToString(format, CultureInfo.CurrentCulture);
return string.Format(CultureInfo.CurrentCulture, $"{real} + {imaginary}i");
}
public string ToString(IFormatProvider provider)
{
return string.Format(provider, $"{_real} + {_imaginary}i");
}
public string ToString(string? format, IFormatProvider? provider)
{
var real = this._real.ToString(format, CultureInfo.CurrentCulture);
var imaginary = this._imaginary.ToString(format, CultureInfo.CurrentCulture);
return string.Format(provider, $"{real} + {imaginary}i");
}
#endregion
#region Operators
public static Complex operator +(Complex left, Complex right)
{
return new Complex(left._real + right._real, left._imaginary + right._imaginary);
}
public static Complex operator ++(Complex value)
{
return new(value._real + 1, value._imaginary);
}
public static Complex operator -(Complex value)
{
return new(-value._real, -value._imaginary);
}
public static Complex operator -(Complex left, Complex right)
{
return new(left._real - right._real, left._imaginary - right._imaginary);
}
public static Complex operator --(Complex value)
{
return new(value._real - 1, value._imaginary);
}
public static Complex operator *(Complex left, Complex right)
{
var real = left._real * right._real - left._imaginary * right._imaginary;
var imaginary = left._imaginary * right._real + left._real * right._imaginary;
return new(real, imaginary);
}
public static Complex operator *(Complex left, double right)
{
return new(left._real * right, left._imaginary * right);
}
public static Complex operator *(double left, Complex right)
{
return new(left * right._real, left * right._real);
}
public static Complex operator /(Complex left, Complex right)
{
if (Math.Abs(right._imaginary) < Math.Abs(right._real))
{
var num = right._imaginary / right._real;
return new((left._real + left._imaginary * num) / (right._real + right._imaginary * num), (left._imaginary - left._real * num) / (right._real + right._imaginary * num));
}
var num2 = right._real / right._imaginary;
return new((left._imaginary + left._real * num2) / (right._imaginary + right._real * num2), (-left._real + left._imaginary * num2) / (right._imaginary + right._real * num2));
}
public static Complex operator /(Complex left, double right)
{
return new(left._real / right, left._imaginary / right);
}
public static bool operator ==(Complex left, Complex right)
{
return left._real == right._real && left._imaginary == right._imaginary;
}
public static bool operator ==(double left, Complex right)
{
return left == right._real && right._imaginary == 0;
}
public static bool operator ==(Complex left, double right)
{
return left._real == right && left._imaginary == 0;
}
public static bool operator !=(Complex left, Complex right)
{
return left._real != right._real || left._imaginary != right._imaginary;
}
public static bool operator !=(double left, Complex right)
{
return left != right._real || right._imaginary != 0;
}
public static bool operator !=(Complex left, double right)
{
return left._real != right || left._imaginary != 0;
}
#endregion
#region Equals
public override bool Equals(object? obj)
{
if (obj is not Complex)
return false;
return this == (Complex)obj;
}
public bool Equals(Complex value)
{
return _real.Equals(value._real) && _imaginary.Equals(value._imaginary);
}
public override int GetHashCode()
{
int num = 99999997;
int num2 = _real.GetHashCode() % num;
int hashCode = _imaginary.GetHashCode();
return num2 ^ hashCode;
}
#endregion
}
}