public LargeArray <WWComplex> ForwardFft(LargeArray <WWComplex> aFrom) { if (aFrom == null || aFrom.LongLength != mNumPoints) { throw new ArgumentOutOfRangeException("aFrom"); } var aTo = new LargeArray <WWComplex>(aFrom.LongLength); var aTmp0 = new LargeArray <WWComplex>(mNumPoints); for (int i = 0; i < aTmp0.LongLength; ++i) { aTmp0.Set(i, aFrom.At((long)mBitReversalTable.At(i))); } var aTmp1 = new LargeArray <WWComplex>(mNumPoints); for (int i = 0; i < aTmp1.LongLength; ++i) { aTmp1.Set(i, WWComplex.Zero()); } var aTmps = new LargeArray <WWComplex> [2]; aTmps[0] = aTmp0; aTmps[1] = aTmp1; for (int i = 0; i < mNumStage - 1; ++i) { FftStageN(i, aTmps[((i & 1) == 1) ? 1 : 0], aTmps[((i & 1) == 0) ? 1 : 0]); } FftStageN(mNumStage - 1, aTmps[(((mNumStage - 1) & 1) == 1) ? 1 : 0], aTo); return(aTo); }
public WWComplex Evaluate(WWComplex x) { WWComplex n = WWComplex.Zero(); { WWComplex xN = WWComplex.Unity(); for (int i = 0; i < numer.Length; ++i) { n = WWComplex.Add(n, WWComplex.Mul(numer[i], xN)); xN = WWComplex.Mul(xN, x); } } WWComplex d = WWComplex.Zero(); { WWComplex xN = WWComplex.Unity(); for (int i = 0; i < denom.Length; ++i) { d = WWComplex.Add(d, WWComplex.Mul(denom[i], xN)); xN = WWComplex.Mul(xN, x); } } return(WWComplex.Div(n, d)); }
/// <summary> /// Linear Convolution x ** h を計算。 /// </summary> /// <param name="h">コンボリューションカーネル。左右反転される。</param> /// <param name="x">入力数列。</param> public WWComplex[] ConvolutionFft(WWComplex[] h, WWComplex[] x) { var r = new WWComplex[h.Length + x.Length - 1]; int fftSize = Functions.NextPowerOf2(r.Length); var h2 = new WWComplex[fftSize]; Array.Copy(h, 0, h2, 0, h.Length); for (int i = h.Length; i < h2.Length; ++i) { h2[i] = WWComplex.Zero(); } var x2 = new WWComplex[fftSize]; Array.Copy(x, 0, x2, 0, x.Length); for (int i = x.Length; i < x2.Length; ++i) { x2[i] = WWComplex.Zero(); } var fft = new WWRadix2Fft(fftSize); var H = fft.ForwardFft(h2); var X = fft.ForwardFft(x2); var Y = WWComplex.Mul(H, X); var y = fft.InverseFft(Y); Array.Copy(y, 0, r, 0, r.Length); return r; }
Add(ComplexPolynomial lhs, ComplexPolynomial rhs) { int order = lhs.Degree; if (order < rhs.Degree) { order = rhs.Degree; } var rv = new WWComplex[order + 1]; for (int i = 0; i <= order; ++i) { WWComplex ck = WWComplex.Zero(); if (i <= lhs.Degree) { ck = WWComplex.Add(ck, lhs.C(i)); } if (i <= rhs.Degree) { ck = WWComplex.Add(ck, rhs.C(i)); } rv[i] = ck; } // 最高次の項が0のとき削除して次数を減らす。 return(new ComplexPolynomial(rv).ReduceDegree()); }
static WWComplex Get(WWComplex[] v, int pos) { if (pos < 0 || v.Length <= pos) { return(WWComplex.Zero()); } return(v[pos]); }
MultiplyX(ComplexPolynomial p) { var rv = new WWComplex [p.Degree + 2]; rv[0] = WWComplex.Zero(); for (int i = 0; i <= p.Degree; ++i) { rv[i + 1] = p.C(i); } return(new ComplexPolynomial(rv)); }
PartialFractionDecomposition( WWComplex [] nCoeffs, WWComplex [] dRoots) { var result = new List <FirstOrderComplexRationalPolynomial>(); if (dRoots.Length == 1 && nCoeffs.Length == 1) { result.Add(new FirstOrderComplexRationalPolynomial(WWComplex.Zero(), nCoeffs[0], WWComplex.Unity(), WWComplex.Minus(dRoots[0]))); return(result); } if (dRoots.Length < 2) { throw new ArgumentException("denomR"); } if (dRoots.Length < nCoeffs.Length) { throw new ArgumentException("nCoeffs"); } for (int k = 0; k < dRoots.Length; ++k) { // cn = ... + nCoeffs[2]s^2 + nCoeffs[1]s + nCoeffs[0] // 係数c[0]は、s==denomR[0]としたときの、cn/(s-denomR[1])(s-denomR[2])(s-denomR[3])…(s-denomR[p-1]) // 係数c[1]は、s==denomR[1]としたときの、cn/(s-denomR[0])(s-denomR[2])(s-denomR[3])…(s-denomR[p-1]) // 係数c[2]は、s==denomR[2]としたときの、cn/(s-denomR[0])(s-denomR[1])(s-denomR[3])…(s-denomR[p-1]) // 分子の値c。 var c = WWComplex.Zero(); var s = WWComplex.Unity(); for (int j = 0; j < nCoeffs.Length; ++j) { c = WWComplex.Add(c, WWComplex.Mul(nCoeffs[j], s)); s = WWComplex.Mul(s, dRoots[k]); } for (int i = 0; i < dRoots.Length; ++i) { if (i == k) { continue; } c = WWComplex.Div(c, WWComplex.Sub(dRoots[k], dRoots[i])); } result.Add(new FirstOrderComplexRationalPolynomial( WWComplex.Zero(), c, WWComplex.Unity(), WWComplex.Minus(dRoots[k]))); } return(result); }
/// <summary> /// 連続FFT オーバーラップアド法でLinear convolution x ** hする。 /// </summary> /// <param name="h">コンボリューションカーネル。左右反転される。</param> /// <param name="x">入力数列。</param> /// <param name="fragmentSz">個々のFFTに入力するxの断片サイズ。</param> public WWComplex[] ConvolutionContinuousFft(WWComplex[] h, WWComplex[] x, int fragmentSz) { System.Diagnostics.Debug.Assert(2 <= fragmentSz); if (x.Length < h.Length) { // swap x and h var tmp = h; h = x; x = tmp; } // h.Len <= x.Len int fullConvLen = h.Length + x.Length - 1; var r = new WWComplex[fullConvLen]; for (int i = 0; i < r.Length; ++i) { r[i] = WWComplex.Zero(); } // hをFFTしてHを得る。 int fragConvLen = h.Length + fragmentSz - 1; int fftSize = Functions.NextPowerOf2(fragConvLen); var h2 = new WWComplex[fftSize]; Array.Copy(h, 0, h2, 0, h.Length); for (int i = h.Length; i < h2.Length; ++i) { h2[i] = WWComplex.Zero(); } var fft = new WWRadix2Fft(fftSize); var H = fft.ForwardFft(h2); for (int offs = 0; offs < x.Length; offs += fragmentSz) { // xFをFFTしてXを得る。 var xF = WWComplex.ZeroArray(fftSize); for (int i=0; i<fragmentSz; ++i) { if (i + offs < x.Length) { xF[i] = x[offs + i]; } else { break; } } var X = fft.ForwardFft(xF); var Y = WWComplex.Mul(H, X); var y = fft.InverseFft(Y); // オーバーラップアド法。FFT結果を足し合わせる。 for (int i = 0; i <fragConvLen; ++i) { r[offs + i] = WWComplex.Add(r[offs + i], y[i]); } } return r; }
/// <summary> /// 注:compensationが指定しないとき、結果をNで割りません。 /// </summary> /// <param name="aFrom">時間ドメイン値x[n] 0≦n<N</param> /// <param name="compensation">倍率。指定しないとき1.0倍となる。</param> /// <returns>周波数ドメイン値X^m(q)</returns> public WWComplex[] ForwardFft(WWComplex[] aFrom, double?compensation = null) { if (aFrom == null || aFrom.Length != mNumPoints) { throw new ArgumentOutOfRangeException("aFrom"); } var aTo = new WWComplex[aFrom.Length]; var aTmp0 = new WWComplex[mNumPoints]; for (int i = 0; i < aTmp0.Length; ++i) { aTmp0[i] = aFrom[mBitReversalTable[i]]; } var aTmp1 = new WWComplex[mNumPoints]; for (int i = 0; i < aTmp1.Length; ++i) { aTmp1[i] = WWComplex.Zero(); } var aTmps = new WWComplex[2][]; aTmps[0] = aTmp0; aTmps[1] = aTmp1; for (int i = 0; i < mNumStage - 1; ++i) { FftStageN(i, aTmps[((i & 1) == 1) ? 1 : 0], aTmps[((i & 1) == 0) ? 1 : 0]); } FftStageN(mNumStage - 1, aTmps[(((mNumStage - 1) & 1) == 1) ? 1 : 0], aTo); double c = 1.0; if (compensation != null) { c = (double)compensation; } if (c != 1.0) { var aToC = new WWComplex[aTo.Length]; for (int i = 0; i < aTo.Length; ++i) { aToC[i] = new WWComplex(aTo[i].real * c, aTo[i].imaginary * c); } return(aToC); } else { return(aTo); } }
Reduction(WWComplex [] aNumerCoeffList, WWComplex [] aDenomRootList) { var rv = new PolynomialAndRationalPolynomial(); rv.coeffList = new WWComplex[0]; rv.numerCoeffList = new WWComplex[aNumerCoeffList.Length]; Array.Copy(aNumerCoeffList, rv.numerCoeffList, aNumerCoeffList.Length); rv.denomRootList = new WWComplex[aDenomRootList.Length]; Array.Copy(aDenomRootList, rv.denomRootList, aDenomRootList.Length); if (rv.numerCoeffList.Length - 1 < rv.denomRootList.Length) { // 既に分子の多項式の次数が分母の多項式の次数よりも低い。 return(rv); } // 分母の根のリスト⇒分母の多項式の係数のリストに変換。 var denomCoeffList = RootListToCoeffList(aDenomRootList, new WWComplex(1, 0)); var quotient = new List <WWComplex>(); while (denomCoeffList.Length <= rv.numerCoeffList.Length) { // denomの最も次数が高い項の係数がcdn、numerの最も次数が高い項の係数がcnnとすると // denomiCoeffListを-cnn/cdn * s^(numerの次数-denomの次数)倍してnumerCoeffListと足し合わせてnumerの次数を1下げる。 // このときrv.coeffListにc == cnn/cdnを足す。 var c = WWComplex.Div(rv.numerCoeffList[rv.numerCoeffList.Length - 1], denomCoeffList[denomCoeffList.Length - 1]); quotient.Insert(0, c); var denomMulX = new ComplexPolynomial(denomCoeffList); while (denomMulX.Degree + 1 < rv.numerCoeffList.Length) { denomMulX = ComplexPolynomial.MultiplyX(denomMulX); } denomMulX = ComplexPolynomial.MultiplyC(denomMulX, WWComplex.Mul(c, -1)); // ここで引き算することで分子の多項式の次数が1減る。 int expectedOrder = rv.numerCoeffList.Length - 2; rv.numerCoeffList = ComplexPolynomial.TrimPolynomialOrder( ComplexPolynomial.Add(denomMulX, new ComplexPolynomial(rv.numerCoeffList)), expectedOrder).ToArray(); // 引き算によって一挙に2以上次数が減った場合coeffListに0を足す。 int actualOrder = rv.numerCoeffList.Length - 1; for (int i = 0; i < expectedOrder - actualOrder; ++i) { quotient.Insert(0, WWComplex.Zero()); } } rv.coeffList = quotient.ToArray(); return(rv); }
/// <summary> /// Calculate poly value at position z /// </summary> /// <param name="p">z position</param> /// <returns>y</returns> public WWComplex Evaluate(WWComplex z) { WWComplex y = WWComplex.Zero(); WWComplex zPower = WWComplex.Unity(); for (int i = 0; i < mCoeff.Length; ++i) { y = WWComplex.Add(y, WWComplex.Mul(mCoeff[i], zPower)); zPower = WWComplex.Mul(zPower, z); } return(y); }
/// <summary> /// この多項式が位置zで取る値を戻す。 /// </summary> /// <param name="z">ガウス平面上の座標(p,y)</param> /// <returns>多項式が位置zで取る値。</returns> public WWComplex Evaluate(WWComplex z) { WWComplex w = WWComplex.Zero(); WWComplex zPower = WWComplex.Unity(); for (int i = 0; i < mCoeff.Length; ++i) { w = WWComplex.Add(w, zPower.Scale(mCoeff[i])); zPower = WWComplex.Mul(zPower, z); } return(w); }
/// <summary> /// 時間ドメイン値xを入力すると、N点DFTの周波数ドメイン値Xのm番目の周波数binの値を戻す。 /// </summary> /// <param name="m">周波数binの番号。0≦m<N</param> /// <param name="N">DFTサイズN。</param> public WWGoertzel(int m, int N) { if (N <= 0) { throw new ArgumentOutOfRangeException("N"); } mN = N; mDelay = new DelayT <WWComplex>(1); mDelay.Fill(WWComplex.Zero()); mOsc = new WWQuadratureOscillatorInt(-m, N); }
/// <summary> /// Linear Convolution x ** h を計算。 /// </summary> /// <param name="h">コンボリューションカーネル。左右反転される。</param> /// <param name="x">入力数列。</param> public WWComplex[] ConvolutionBruteForce(WWComplex[] h, WWComplex[] x) { var r = new WWComplex[h.Length + x.Length - 1]; Parallel.For(0, r.Length, i => { WWComplex v = WWComplex.Zero(); for (int j = 0; j < h.Length; ++j) { int hPos = h.Length - j - 1; int xPos = i + j - (h.Length - 1); v = WWComplex.Add(v, WWComplex.Mul(Get(h, hPos), Get(x, xPos))); } r[i] = v; }); return r; }
/// <summary> /// 1次多項式を作る。 /// mCoeffs[0] + p * mCoeffs[1] /// </summary> public static List <FirstOrderComplexRationalPolynomial> CreateFromCoeffList(WWComplex [] coeffList) { var p = new List <FirstOrderComplexRationalPolynomial>(); if (coeffList.Length == 0) { return(p); } if (coeffList.Length == 1) { // 定数を追加。 p.Add(new FirstOrderComplexRationalPolynomial(WWComplex.Zero(), coeffList[0], WWComplex.Zero(), WWComplex.Unity())); } if (coeffList.Length == 2) { // 1次式を追加。 p.Add(new FirstOrderComplexRationalPolynomial(coeffList[1], coeffList[0], WWComplex.Zero(), WWComplex.Unity())); } return(p); }
public SlidingDFTbin(int m, int N, double compensation) { if (N <= 0) { throw new ArgumentOutOfRangeException("N"); } if (m < 0 || N <= m) { throw new ArgumentOutOfRangeException("m"); } mN = N; mCompensation = compensation; mM = m; double θ = 2.0 * Math.PI * m / N; mE = new WWComplex(Math.Cos(θ), Math.Sin(θ)); mDelay = new DelayT <WWComplex>(1); mDelay.Fill(WWComplex.Zero()); }
public WWComplex Evaluate(WWComplex x) { WWComplex n = WWComplex.Zero(); WWComplex xN = WWComplex.Unity(); for (int i = 0; i < numer.Length; ++i) { n = WWComplex.Add(n, WWComplex.Mul(xN, numer[i])); xN = WWComplex.Mul(xN, x); } WWComplex d = WWComplex.Zero(); xN = WWComplex.Unity(); for (int i = 0; i < denom.Length; ++i) { d = WWComplex.Add(d, WWComplex.Mul(xN, denom[i])); xN = WWComplex.Mul(xN, x); } WWComplex y = WWComplex.Div(n, d); return(y); }
private void FftStageN(int stageNr, LargeArray <WWComplex> x, LargeArray <WWComplex> y) { /* * stage0: 2つの入力データにバタフライ演算 (length=8の時) 4回 (nRepeat=4, nSubRepeat=2) * y[0] = x[0] + w_n^(0*4) * x[1] * y[1] = x[0] + w_n^(1*4) * x[1] * * y[2] = x[2] + w_n^(0*4) * x[3] * y[3] = x[2] + w_n^(1*4) * x[3] * * y[4] = x[4] + w_n^(0*4) * x[5] * y[5] = x[4] + w_n^(1*4) * x[5] * * y[6] = x[6] + w_n^(0*4) * x[7] * y[7] = x[6] + w_n^(1*4) * x[7] */ /* * stage1: 4つの入力データにバタフライ演算 (length=8の時) 2回 (nRepeat=2, nSubRepeat=4) * y[0] = x[0] + w_n^(0*2) * x[2] * y[1] = x[1] + w_n^(1*2) * x[3] * y[2] = x[0] + w_n^(2*2) * x[2] * y[3] = x[1] + w_n^(3*2) * x[3] * * y[4] = x[4] + w_n^(0*2) * x[6] * y[5] = x[5] + w_n^(1*2) * x[7] * y[6] = x[4] + w_n^(2*2) * x[6] * y[7] = x[5] + w_n^(3*2) * x[7] */ /* * stage2: 8つの入力データにバタフライ演算 (length=8の時) 1回 (nRepeat=1, nSubRepeat=8) * y[0] = x[0] + w_n^(0*1) * x[4] * y[1] = x[1] + w_n^(1*1) * x[5] * y[2] = x[2] + w_n^(2*1) * x[6] * y[3] = x[3] + w_n^(3*1) * x[7] * y[4] = x[0] + w_n^(4*1) * x[4] * y[5] = x[1] + w_n^(5*1) * x[5] * y[6] = x[2] + w_n^(6*1) * x[6] * y[7] = x[3] + w_n^(7*1) * x[7] */ /* * stageN: */ long nRepeat = Pow2(mNumStage - stageNr - 1); long nSubRepeat = mNumPoints / nRepeat; for (long i = 0; i < nRepeat; ++i) { long offsBase = i * nSubRepeat; bool allZero = true; for (long j = 0; j < nSubRepeat / 2; ++j) { long offs = offsBase + (j % (nSubRepeat / 2)); if (Double.Epsilon < x.At(offs).Magnitude()) { allZero = false; break; } if (Double.Epsilon < x.At(offs + nSubRepeat / 2).Magnitude()) { allZero = false; break; } } if (allZero) { for (long j = 0; j < nSubRepeat; ++j) { y.Set(j + offsBase, WWComplex.Zero()); } } else { for (long j = 0; j < nSubRepeat; ++j) { long offs = offsBase + (j % (nSubRepeat / 2)); var t = x.At(offs); var t2 = WWComplex.Mul(mWn.At(j * nRepeat), x.At(offs + nSubRepeat / 2)); var t3 = WWComplex.Add(t, t2); y.Set(j + offsBase, t3); } } } }
public static void Test() { { /* * 部分分数分解のテスト。 * s + 3 * X(s) = ------------- * (s+1)s(s-2) * * 部分分数分解すると * * 2/3 -3/2 5/6 * X(s) = ------- + ------ + ------- * s + 1 s s - 2 */ var numerCoeffs = new WWComplex [] { new WWComplex(3, 0), new WWComplex(1, 0) }; var dRoots = new WWComplex [] { new WWComplex(-1, 0), WWComplex.Zero(), new WWComplex(2, 0) }; var p = WWPolynomial.PartialFractionDecomposition(numerCoeffs, dRoots); for (int i = 0; i < p.Count; ++i) { Console.WriteLine(p[i].ToString("s")); if (i != p.Count - 1) { Console.WriteLine(" + "); } } Console.WriteLine(""); } { // 約分のテスト // p-1 (p+1) -(p+1) + (p-1) -2 // ───── ⇒ ────────────────────── ⇒ 1 + ───── // p+1 p+1 p+1 var numerC = new List <WWComplex>(); numerC.Add(new WWComplex(-1, 0)); // 定数項。 numerC.Add(WWComplex.Unity()); // 1乗の項。 var denomR = new List <WWComplex>(); denomR.Add(new WWComplex(-1, 0)); var r = Reduction(numerC.ToArray(), denomR.ToArray()); r.Print("p"); } { // 約分のテスト // p^2+3x+2 (p^2+3x+2) -(p^2+7x+12) -4x-10 // ──────────── ⇒ 1+ ───────────────────────── ⇒ 1 + ──────────── // (p+3)(p+4) p^2+7x+12 (p+3)(p+4) var numerC = new WWComplex [] { new WWComplex(2, 0), // 定数項。 new WWComplex(3, 0), // 1乗の項。 WWComplex.Unity() }; // 2乗の項。 var denomR = new WWComplex [] { new WWComplex(-3, 0), new WWComplex(-4, 0) }; var r = Reduction(numerC, denomR); r.Print("p"); } { // 部分分数分解。 // -4x-10 2 -6 // ──────────── ⇒ ───── + ───── // (p+3)(p+4) p+3 p+4 var numerC = new WWComplex [] { new WWComplex(-10, 0), new WWComplex(-4, 0) }; var denomR = new WWComplex [] { new WWComplex(-3, 0), new WWComplex(-4, 0) }; var p = WWPolynomial.PartialFractionDecomposition(numerC, denomR); for (int i = 0; i < p.Count; ++i) { Console.WriteLine(p[i].ToString("s")); if (i != p.Count - 1) { Console.WriteLine(" + "); } } Console.WriteLine(""); } { var deriv = new RealPolynomial(new double[] { 1, 1, 1, 1 }).Derivative(); Console.WriteLine("derivative of p^3+p^2+p+1={0}", deriv.ToString("p")); } { var r2 = AlgebraicLongDivision(new RealPolynomial(new double[] { 6, 3, 1 }), new RealPolynomial(new double[] { 1, 1 })); Console.WriteLine("(p^2+3x+6)/(p+1) = {0} r {1}", r2.quotient.ToString(), r2.remainder.ToString()); } }
private void FftStageN(int stageNr, WWComplex[] x, WWComplex[] y) { /* * stage0: 2つの入力データにバタフライ演算 (length=8の時) 4回 (nRepeat=4, nSubRepeat=2) * y[0] = x[0] + w_n^(0*4) * x[1] * y[1] = x[0] + w_n^(1*4) * x[1] * * y[2] = x[2] + w_n^(0*4) * x[3] * y[3] = x[2] + w_n^(1*4) * x[3] * * y[4] = x[4] + w_n^(0*4) * x[5] * y[5] = x[4] + w_n^(1*4) * x[5] * * y[6] = x[6] + w_n^(0*4) * x[7] * y[7] = x[6] + w_n^(1*4) * x[7] */ /* * stage1: 4つの入力データにバタフライ演算 (length=8の時) 2回 (nRepeat=2, nSubRepeat=4) * y[0] = x[0] + w_n^(0*2) * x[2] * y[1] = x[1] + w_n^(1*2) * x[3] * y[2] = x[0] + w_n^(2*2) * x[2] * y[3] = x[1] + w_n^(3*2) * x[3] * * y[4] = x[4] + w_n^(0*2) * x[6] * y[5] = x[5] + w_n^(1*2) * x[7] * y[6] = x[4] + w_n^(2*2) * x[6] * y[7] = x[5] + w_n^(3*2) * x[7] */ /* * stage2: 8つの入力データにバタフライ演算 (length=8の時) 1回 (nRepeat=1, nSubRepeat=8) * y[0] = x[0] + w_n^(0*1) * x[4] * y[1] = x[1] + w_n^(1*1) * x[5] * y[2] = x[2] + w_n^(2*1) * x[6] * y[3] = x[3] + w_n^(3*1) * x[7] * y[4] = x[0] + w_n^(4*1) * x[4] * y[5] = x[1] + w_n^(5*1) * x[5] * y[6] = x[2] + w_n^(6*1) * x[6] * y[7] = x[3] + w_n^(7*1) * x[7] */ /* * stageN: */ int nRepeat = Pow2(mNumStage - stageNr - 1); int nSubRepeat = mNumPoints / nRepeat; var t = WWComplex.Zero(); for (int i = 0; i < nRepeat; ++i) { int offsBase = i * nSubRepeat; bool allZero = true; for (int j = 0; j < nSubRepeat / 2; ++j) { int offs = offsBase + (j % (nSubRepeat / 2)); if (Double.Epsilon < x[offs].Magnitude()) { allZero = false; break; } if (Double.Epsilon < x[offs + nSubRepeat / 2].Magnitude()) { allZero = false; break; } } if (allZero) { for (int j = 0; j < nSubRepeat; ++j) { y[j + offsBase] = WWComplex.Zero(); } } else { for (int j = 0; j < nSubRepeat; ++j) { int offs = offsBase + (j % (nSubRepeat / 2)); var v1 = x[offs]; var v2 = WWComplex.Mul(mWn[j * nRepeat], x[offs + nSubRepeat / 2]); y[j + offsBase] = WWComplex.Add(v1, v2); } } } }