/* * BSD Licence: * Copyright (c) 2001, 2002 Ben Houston [ ben@exocortex.org ] * Exocortex Technologies [ www.exocortex.org ] * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * DAMAGE. */ using System; using System.Diagnostics; namespace Exocortex.DSP { // Comments? Questions? Bugs? Tell Ben Houston at ben@exocortex.org // Version: May 4, 2002 /// ///

A set of statistical utilities for complex number arrays

///
public class ComplexStats { //--------------------------------------------------------------------------------------------- private ComplexStats() { } //--------------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------------- /// /// Calculate the sum /// /// /// static public ComplexF Sum(ComplexF[] data) { Debug.Assert(data != null); return SumRecursion(data, 0, data.Length); } 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); } } /// /// Calculate the sum /// /// /// static public Complex Sum(Complex[] data) { Debug.Assert(data != null); return SumRecursion(data, 0, data.Length); } static private Complex SumRecursion(Complex[] 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) { Complex sum = Complex.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); } } //-------------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------------- /// /// Calculate the sum of squares /// /// /// static public ComplexF SumOfSquares(ComplexF[] data) { Debug.Assert(data != null); return SumOfSquaresRecursion(data, 0, data.Length); } static private ComplexF SumOfSquaresRecursion(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 sumOfSquares = ComplexF.Zero; for (int i = start; i < end; i++) { sumOfSquares += data[i] * data[i]; } return sumOfSquares; } else { int middle = (start + end) >> 1; return SumOfSquaresRecursion(data, start, middle) + SumOfSquaresRecursion(data, middle, end); } } /// /// Calculate the sum of squares /// /// /// static public Complex SumOfSquares(Complex[] data) { Debug.Assert(data != null); return SumOfSquaresRecursion(data, 0, data.Length); } static private Complex SumOfSquaresRecursion(Complex[] 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) { Complex sumOfSquares = Complex.Zero; for (int i = start; i < end; i++) { sumOfSquares += data[i] * data[i]; } return sumOfSquares; } else { int middle = (start + end) >> 1; return SumOfSquaresRecursion(data, start, middle) + SumOfSquaresRecursion(data, middle, end); } } //-------------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------------- /// /// Calculate the mean (average) /// /// /// static public ComplexF Mean(ComplexF[] data) { return ComplexStats.Sum(data) / data.Length; } /// /// Calculate the mean (average) /// /// /// static public Complex Mean(Complex[] data) { return ComplexStats.Sum(data) / data.Length; } /// /// Calculate the variance /// /// /// 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); } /// /// Calculate the variance /// /// /// static public Complex Variance(Complex[] 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); } /// /// Calculate the standard deviation /// /// /// 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)); } /// /// Calculate the standard deviation /// /// /// static public Complex StdDev(Complex[] data) { Debug.Assert(data != null); if (data.Length == 0) { throw new DivideByZeroException("length of data is zero"); } return ComplexMath.Sqrt(ComplexStats.Variance(data)); } //-------------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------------- /// /// Calculate the root mean squared (RMS) error between two sets of data. /// /// /// /// 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)); } 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); } } /// /// Calculate the root mean squared (RMS) error between two sets of data. /// /// /// /// static public double RMSError(Complex[] alpha, Complex[] beta) { Debug.Assert(alpha != null); Debug.Assert(beta != null); Debug.Assert(beta.Length == alpha.Length); return Math.Sqrt(SumOfSquaredErrorRecursion(alpha, beta, 0, alpha.Length)); } static private double SumOfSquaredErrorRecursion(Complex[] alpha, Complex[] 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) { double sumOfSquaredError = 0; for (int i = start; i < end; i++) { Complex 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); } } } }