static private ComplexF SumRecursion( ComplexF[] data, int start, int end ) { Debug.Assert( 0 <= start, "start = " + start ); Debug.Assert( start < end, "start = " + start + " and end = " + end ); Debug.Assert( end <= data.Length, "end = " + end + " and data.Length = " + data.Length ); if( ( end - start ) <= 1000 ) { ComplexF sum = ComplexF.Zero; for( int i = start; i < end; i ++ ) { sum += data[ i ]; } return sum; } else { int middle = ( start + end ) >> 1; return SumRecursion( data, start, middle ) + SumRecursion( data, middle, end ); } }
static private void UnlockBufferCF( ref ComplexF[] buffer ) { Debug.Assert( _bufferCF == buffer ); Debug.Assert( _bufferCFLocked == true ); _bufferCFLocked = false; buffer = null; }
//--------------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------------- /// <summary> /// Calculate the sum /// </summary> /// <param name="data"></param> /// <returns></returns> static public ComplexF Sum( ComplexF[] data ) { Debug.Assert( data != null ); return SumRecursion( data, 0, data.Length ); }
//-------------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------------- /// <summary> /// Calculate the root mean squared (RMS) error between two sets of data. /// </summary> /// <param name="alpha"></param> /// <param name="beta"></param> /// <returns></returns> static public float RMSError( ComplexF[] alpha, ComplexF[] beta ) { Debug.Assert( alpha != null ); Debug.Assert( beta != null ); Debug.Assert( beta.Length == alpha.Length ); return (float) Math.Sqrt( SumOfSquaredErrorRecursion( alpha, beta, 0, alpha.Length ) ); }
/// <summary> /// Calculate the variance /// </summary> /// <param name="data"></param> /// <returns></returns> static public ComplexF Variance( ComplexF[] data ) { Debug.Assert( data != null ); if( data.Length == 0 ) { throw new DivideByZeroException( "length of data is zero" ); } return ComplexStats.SumOfSquares( data ) / data.Length - ComplexStats.Sum( data ); }
static private void LockWorkspaceF( int length, ref ComplexF[] workspace ) { Debug.Assert( _workspaceFLocked == false ); _workspaceFLocked = true; if( length >= _workspaceF.Length ) { _workspaceF = new ComplexF[ length ]; } workspace = _workspaceF; }
/// <summary> /// Scale and offset the elements in the array so that the /// overall range is [0, 1] /// </summary> /// <param name="array"></param> static public void Normalize( ComplexF[] array ) { float min = 0, max = 0; GetLengthRange( array, ref min, ref max ); Scale( array, ( 1 / ( max - min ) ) ); Offset( array, ( - min / ( max - min ) ) ); }
/// <summary> /// Divide each element in lhs array with corresponding element in rhs array and /// put product in result array /// </summary> /// <param name="lhs"></param> /// <param name="rhs"></param> /// <param name="result"></param> static public void Divide( ComplexF[] lhs, ComplexF[] rhs, ComplexF[] result ) { Debug.Assert( lhs != null ); Debug.Assert( rhs != null ); Debug.Assert( result != null ); Debug.Assert( lhs.Length == rhs.Length ); Debug.Assert( lhs.Length == result.Length ); ComplexF zero = ComplexF.Zero; int length = lhs.Length; for( int i = 0; i < length; i ++ ) { if( rhs[i] != zero ) { result[i] = lhs[i] / rhs[i]; } else { result[i] = zero; } } }
static private void UnlockWorkspaceF( ref ComplexF[] workspace ) { Debug.Assert( _workspaceF == workspace ); Debug.Assert( _workspaceFLocked == true ); _workspaceFLocked = false; workspace = null; }
/// <summary> /// Create a complex number based on an existing complex number /// </summary> /// <param name="c"></param> public ComplexF( ComplexF c ) { this.Re = c.Re; this.Im = c.Im; }
//----------------------------------------------------------------------------------- //----------------------------------------------------------------------------------- /// <summary> /// Determine whether two complex numbers are almost (i.e. within the tolerance) equivalent. /// </summary> /// <param name="a"></param> /// <param name="b"></param> /// <param name="tolerance"></param> /// <returns></returns> static public bool IsEqual( ComplexF a, ComplexF b, float tolerance ) { return ( Math.Abs( a.Re - b.Re ) < tolerance ) && ( Math.Abs( a.Im - b.Im ) < tolerance ); }
/// <summary> /// Compute a 1D fast Fourier transform of a dataset of complex numbers. /// </summary> /// <param name="data"></param> /// <param name="direction"></param> public static void FFT( ComplexF[] data, FourierDirection direction ) { if( data == null ) { throw new ArgumentNullException( "data" ); } Fourier.FFT( data, data.Length, direction ); }
/// <summary> /// Compute a 1D fast Fourier transform of a dataset of complex numbers. /// </summary> /// <param name="data"></param> /// <param name="length"></param> /// <param name="direction"></param> public static void FFT_Quick( ComplexF[] data, int length, FourierDirection direction ) { /*if( data == null ) { throw new ArgumentNullException( "data" ); } if( data.Length < length ) { throw new ArgumentOutOfRangeException( "length", length, "must be at least as large as 'data.Length' parameter" ); } if( Fourier.IsPowerOf2( length ) == false ) { throw new ArgumentOutOfRangeException( "length", length, "must be a power of 2" ); } Fourier.SyncLookupTableLength( length );*/ int ln = Fourier.Log2( length ); // reorder array Fourier.ReorderArray( data ); // successive doubling int N = 1; int signIndex = ( direction == FourierDirection.Forward ) ? 0 : 1; for( int level = 1; level <= ln; level ++ ) { int M = N; N <<= 1; float[] uRLookup = _uRLookupF[ level, signIndex ]; float[] uILookup = _uILookupF[ level, signIndex ]; for( int j = 0; j < M; j ++ ) { float uR = uRLookup[j]; float uI = uILookup[j]; for( int even = j; even < length; even += N ) { int odd = even + M; float r = data[ odd ].Re; float i = data[ odd ].Im; float odduR = r * uR - i * uI; float odduI = r * uI + i * uR; r = data[ even ].Re; i = data[ even ].Im; data[ even ].Re = r + odduR; data[ even ].Im = i + odduI; data[ odd ].Re = r - odduR; data[ odd ].Im = i - odduI; } } } }
static private void Swap( ref ComplexF a, ref ComplexF b ) { ComplexF temp = a; a = b; b = temp; }
private static void LinearFFT_Quick( ComplexF[] data, int start, int inc, int length, FourierDirection direction ) { /*Debug.Assert( data != null ); Debug.Assert( start >= 0 ); Debug.Assert( inc >= 1 ); Debug.Assert( length >= 1 ); Debug.Assert( ( start + inc * ( length - 1 ) ) < data.Length ); */ // copy to buffer ComplexF[] buffer = null; LockBufferCF( length, ref buffer ); int j = start; for( int i = 0; i < length; i ++ ) { buffer[ i ] = data[ j ]; j += inc; } FFT( buffer, length, direction ); // copy from buffer j = start; for( int i = 0; i < length; i ++ ) { data[ j ] = buffer[ i ]; j += inc; } UnlockBufferCF( ref buffer ); }
/// <summary> /// Multiply each element in lhs array with corresponding element in rhs array and /// put product in result array /// </summary> /// <param name="lhs"></param> /// <param name="rhs"></param> /// <param name="result"></param> static public void Multiply( ComplexF[] lhs, ComplexF[] rhs, ComplexF[] result ) { Debug.Assert( lhs != null ); Debug.Assert( rhs != null ); Debug.Assert( result != null ); Debug.Assert( lhs.Length == rhs.Length ); Debug.Assert( lhs.Length == result.Length ); int length = lhs.Length; for( int i = 0; i < length; i ++ ) { result[i] = lhs[i] * rhs[i]; } }
/// <summary> /// Divide each element in target array with corresponding element in rhs array /// </summary> /// <param name="target"></param> /// <param name="rhs"></param> static public void Divide( ComplexF[] target, ComplexF[] rhs ) { ComplexArray.Divide( target, rhs, target ); }
/// <summary> /// Shift (offset) the elements in the array /// </summary> /// <param name="array"></param> /// <param name="offset"></param> static public void Shift( ComplexF[] array, int offset ) { Debug.Assert( array != null ); Debug.Assert( offset >= 0 ); Debug.Assert( offset < array.Length ); if( offset == 0 ) { return; } int length = array.Length; ComplexF[] workspace = null; ComplexArray.LockWorkspaceF( length, ref workspace ); for( int i = 0; i < length; i ++ ) { workspace[ ( i + offset ) % length ] = array[ i ]; } for( int i = 0; i < length; i ++ ) { array[ i ] = workspace[ i ]; } ComplexArray.UnlockWorkspaceF( ref workspace ); }
/// <summary> /// Copy an array /// </summary> /// <param name="dest"></param> /// <param name="source"></param> static public void Copy( ComplexF[] dest, ComplexF[] source ) { Debug.Assert( dest != null ); Debug.Assert( source != null ); Debug.Assert( dest.Length == source.Length ); for( int i = 0; i < dest.Length; i ++ ) { dest[i] = source[i]; } }
/// <summary> /// Get the range of element lengths /// </summary> /// <param name="array"></param> /// <param name="minimum"></param> /// <param name="maximum"></param> static public void GetLengthRange( ComplexF[] array, ref float minimum, ref float maximum ) { minimum = +float.MaxValue; maximum = -float.MaxValue; for( int i = 0; i < array.Length; i ++ ) { float temp = array[i].GetModulus(); minimum = Math.Min( temp, minimum ); maximum = Math.Max( temp, maximum ); } }
/// <summary> /// Invert each element in the array /// </summary> /// <param name="array"></param> static public void Invert( ComplexF[] array ) { for( int i = 0; i < array.Length; i ++ ) { array[i] = ((ComplexF) 1 ) / array[i]; } }
/// <summary> /// Determine whether the elements in the two arrays are the same /// </summary> /// <param name="array1"></param> /// <param name="array2"></param> /// <param name="tolerance"></param> /// <returns></returns> static public bool IsEqual( ComplexF[] array1, ComplexF[] array2, float tolerance ) { if ( array1.Length != array2.Length ) { return false; } for( int i = 0; i < array1.Length; i ++ ) { if( ComplexF.IsEqual( array1[i], array2[i], tolerance ) == false ) { return false; } } return true; }
//-------------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------------- /// <summary> /// Calculate the mean (average) /// </summary> /// <param name="data"></param> /// <returns></returns> static public ComplexF Mean( ComplexF[] data ) { return ComplexStats.Sum( data ) / data.Length; }
/// <summary> /// Add a specific value to each element in the array /// </summary> /// <param name="array"></param> /// <param name="offset"></param> static public void Offset( ComplexF[] array, ComplexF offset ) { int length = array.Length; for( int i = 0; i < length; i ++ ) { array[i] += offset; } }
/// <summary> /// Calculate the standard deviation /// </summary> /// <param name="data"></param> /// <returns></returns> static public ComplexF StdDev( ComplexF[] data ) { Debug.Assert( data != null ); if( data.Length == 0 ) { throw new DivideByZeroException( "length of data is zero" ); } return ComplexMath.Sqrt( ComplexStats.Variance( data ) ); }
/// <summary> /// Multiply each element in the array by a specific value /// </summary> /// <param name="array"></param> /// <param name="scale"></param> static public void Scale( ComplexF[] array, ComplexF scale ) { Debug.Assert( array != null ); int length = array.Length; for( int i = 0; i < length; i ++ ) { array[i] *= scale; } }
static private float SumOfSquaredErrorRecursion( ComplexF[] alpha, ComplexF[] beta, int start, int end ) { Debug.Assert( 0 <= start, "start = " + start ); Debug.Assert( start < end, "start = " + start + " and end = " + end ); Debug.Assert( end <= alpha.Length, "end = " + end + " and alpha.Length = " + alpha.Length ); Debug.Assert( beta.Length == alpha.Length ); if( ( end - start ) <= 1000 ) { float sumOfSquaredError = 0; for( int i = start; i < end; i ++ ) { ComplexF delta = beta[ i ] - alpha[ i ]; sumOfSquaredError += ( delta.Re * delta.Re ) + ( delta.Im * delta.Im ); } return sumOfSquaredError; } else { int middle = ( start + end ) >> 1; return SumOfSquaredErrorRecursion( alpha, beta, start, middle ) + SumOfSquaredErrorRecursion( alpha, beta, middle, end ); } }
/// <summary> /// Multiply each element in the array by a specific value /// </summary> /// <param name="array"></param> /// <param name="scale"></param> /// <param name="start"></param> /// <param name="length"></param> static public void Scale( ComplexF[] array, ComplexF scale, int start, int length ) { Debug.Assert( array != null ); Debug.Assert( start >= 0 ); Debug.Assert( length >= 0 ); Debug.Assert( ( start + length ) < array.Length ); for( int i = 0; i < length; i ++ ) { array[i + start] *= scale; } }
/// <summary> /// Multiply each element in target array with corresponding element in rhs array /// </summary> /// <param name="target"></param> /// <param name="rhs"></param> static public void Multiply( ComplexF[] target, ComplexF[] rhs ) { ComplexArray.Multiply( target, rhs, target ); }
static private void LockBufferCF( int length, ref ComplexF[] buffer ) { Debug.Assert( length >= 0 ); Debug.Assert( _bufferCFLocked == false ); _bufferCFLocked = true; if( length != _bufferCF.Length ) { _bufferCF = new ComplexF[ length ]; } buffer = _bufferCF; }