mirror of
				https://github.com/furyfire/trueskill.git
				synced 2025-11-04 10:12:28 +01:00 
			
		
		
		
	Initial version of Moserware.Skills TrueSkill calculator to go along with my Computing Your Skill blog post
This commit is contained in:
		
							
								
								
									
										240
									
								
								Skills/Numerics/GaussianDistribution.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										240
									
								
								Skills/Numerics/GaussianDistribution.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,240 @@
 | 
			
		||||
using System;
 | 
			
		||||
 | 
			
		||||
namespace Moserware.Numerics
 | 
			
		||||
{
 | 
			
		||||
    public class GaussianDistribution
 | 
			
		||||
    {
 | 
			
		||||
        // Intentionally, we're not going to derive related things, but set them all at once
 | 
			
		||||
        // to get around some NaN issues
 | 
			
		||||
 | 
			
		||||
        private GaussianDistribution()
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public GaussianDistribution(double mean, double standardDeviation)
 | 
			
		||||
        {
 | 
			
		||||
            Mean = mean;
 | 
			
		||||
            StandardDeviation = standardDeviation;
 | 
			
		||||
            Variance = Square(StandardDeviation);
 | 
			
		||||
            Precision = 1.0/Variance;
 | 
			
		||||
            PrecisionMean = Precision*Mean;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public double Mean { get; private set; }
 | 
			
		||||
        public double StandardDeviation { get; private set; }
 | 
			
		||||
 | 
			
		||||
        // Precision and PrecisionMean are used because they make multiplying and dividing simpler
 | 
			
		||||
        // (the the accompanying math paper for more details)
 | 
			
		||||
 | 
			
		||||
        public double Precision { get; private set; }
 | 
			
		||||
 | 
			
		||||
        public double PrecisionMean { get; private set; }
 | 
			
		||||
 | 
			
		||||
        private double Variance { get; set; }
 | 
			
		||||
 | 
			
		||||
        public double NormalizationConstant
 | 
			
		||||
        {
 | 
			
		||||
            get
 | 
			
		||||
            {
 | 
			
		||||
                // Great derivation of this is at http://www.astro.psu.edu/~mce/A451_2/A451/downloads/notes0.pdf
 | 
			
		||||
                return 1.0/(Math.Sqrt(2*Math.PI)*StandardDeviation);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public GaussianDistribution Clone()
 | 
			
		||||
        {
 | 
			
		||||
            var result = new GaussianDistribution();
 | 
			
		||||
            result.Mean = Mean;
 | 
			
		||||
            result.StandardDeviation = StandardDeviation;
 | 
			
		||||
            result.Variance = Variance;
 | 
			
		||||
            result.Precision = Precision;
 | 
			
		||||
            result.PrecisionMean = PrecisionMean;
 | 
			
		||||
            return result;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static GaussianDistribution FromPrecisionMean(double precisionMean, double precision)
 | 
			
		||||
        {
 | 
			
		||||
            var gaussianDistribution = new GaussianDistribution();
 | 
			
		||||
            gaussianDistribution.Precision = precision;
 | 
			
		||||
            gaussianDistribution.PrecisionMean = precisionMean;
 | 
			
		||||
            gaussianDistribution.Variance = 1.0/precision;
 | 
			
		||||
            gaussianDistribution.StandardDeviation = Math.Sqrt(gaussianDistribution.Variance);
 | 
			
		||||
            gaussianDistribution.Mean = gaussianDistribution.PrecisionMean/gaussianDistribution.Precision;
 | 
			
		||||
            return gaussianDistribution;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Although we could use equations from // For details, see http://www.tina-vision.net/tina-knoppix/tina-memo/2003-003.pdf
 | 
			
		||||
        // for multiplication, the precision mean ones are easier to write :)
 | 
			
		||||
        public static GaussianDistribution operator *(GaussianDistribution left, GaussianDistribution right)
 | 
			
		||||
        {
 | 
			
		||||
            return FromPrecisionMean(left.PrecisionMean + right.PrecisionMean, left.Precision + right.Precision);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// Computes the absolute difference between two Gaussians
 | 
			
		||||
        public static double AbsoluteDifference(GaussianDistribution left, GaussianDistribution right)
 | 
			
		||||
        {
 | 
			
		||||
            return Math.Max(
 | 
			
		||||
                Math.Abs(left.PrecisionMean - right.PrecisionMean),
 | 
			
		||||
                Math.Sqrt(Math.Abs(left.Precision - right.Precision)));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// Computes the absolute difference between two Gaussians
 | 
			
		||||
        public static double operator -(GaussianDistribution left, GaussianDistribution right)
 | 
			
		||||
        {
 | 
			
		||||
            return AbsoluteDifference(left, right);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static double LogProductNormalization(GaussianDistribution left, GaussianDistribution right)
 | 
			
		||||
        {
 | 
			
		||||
            if ((left.Precision == 0) || (right.Precision == 0))
 | 
			
		||||
            {
 | 
			
		||||
                return 0;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            double varianceSum = left.Variance + right.Variance;
 | 
			
		||||
            double meanDifference = left.Mean - right.Mean;
 | 
			
		||||
 | 
			
		||||
            double logSqrt2Pi = Math.Log(Math.Sqrt(2*Math.PI));
 | 
			
		||||
            return -logSqrt2Pi - (Math.Log(varianceSum)/2.0) - (Square(meanDifference)/(2.0*varianceSum));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public static GaussianDistribution operator /(GaussianDistribution numerator, GaussianDistribution denominator)
 | 
			
		||||
        {
 | 
			
		||||
            return FromPrecisionMean(numerator.PrecisionMean - denominator.PrecisionMean,
 | 
			
		||||
                                     numerator.Precision - denominator.Precision);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static double LogRatioNormalization(GaussianDistribution numerator, GaussianDistribution denominator)
 | 
			
		||||
        {
 | 
			
		||||
            if ((numerator.Precision == 0) || (denominator.Precision == 0))
 | 
			
		||||
            {
 | 
			
		||||
                return 0;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            double varianceDifference = denominator.Variance - numerator.Variance;
 | 
			
		||||
            double meanDifference = numerator.Mean - denominator.Mean;
 | 
			
		||||
 | 
			
		||||
            double logSqrt2Pi = Math.Log(Math.Sqrt(2*Math.PI));
 | 
			
		||||
 | 
			
		||||
            return Math.Log(denominator.Variance) + logSqrt2Pi - Math.Log(varianceDifference)/2.0 +
 | 
			
		||||
                   Square(meanDifference)/(2*varianceDifference);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private static double Square(double x)
 | 
			
		||||
        {
 | 
			
		||||
            return x*x;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static double At(double x)
 | 
			
		||||
        {
 | 
			
		||||
            return At(x, 0, 1);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static double At(double x, double mean, double standardDeviation)
 | 
			
		||||
        {
 | 
			
		||||
            // See http://mathworld.wolfram.com/NormalDistribution.html
 | 
			
		||||
            //                1              -(x-mean)^2 / (2*stdDev^2)
 | 
			
		||||
            // P(x) = ------------------- * e
 | 
			
		||||
            //        stdDev * sqrt(2*pi)
 | 
			
		||||
 | 
			
		||||
            double multiplier = 1.0/(standardDeviation*Math.Sqrt(2*Math.PI));
 | 
			
		||||
            double expPart = Math.Exp((-1.0*Math.Pow(x - mean, 2.0))/(2*(standardDeviation*standardDeviation)));
 | 
			
		||||
            double result = multiplier*expPart;
 | 
			
		||||
            return result;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static double CumulativeTo(double x, double mean, double standardDeviation)
 | 
			
		||||
        {
 | 
			
		||||
            double invsqrt2 = -0.707106781186547524400844362104;
 | 
			
		||||
            double result = ErrorFunctionCumulativeTo(invsqrt2*x);
 | 
			
		||||
            return 0.5*result;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static double CumulativeTo(double x)
 | 
			
		||||
        {
 | 
			
		||||
            return CumulativeTo(x, 0, 1);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private static double ErrorFunctionCumulativeTo(double x)
 | 
			
		||||
        {
 | 
			
		||||
            // Derived from page 265 of Numerical Recipes 3rd Edition            
 | 
			
		||||
            double z = Math.Abs(x);
 | 
			
		||||
 | 
			
		||||
            double t = 2.0/(2.0 + z);
 | 
			
		||||
            double ty = 4*t - 2;
 | 
			
		||||
 | 
			
		||||
            double[] coefficients = {
 | 
			
		||||
                                        -1.3026537197817094, 6.4196979235649026e-1,
 | 
			
		||||
                                        1.9476473204185836e-2, -9.561514786808631e-3, -9.46595344482036e-4,
 | 
			
		||||
                                        3.66839497852761e-4, 4.2523324806907e-5, -2.0278578112534e-5,
 | 
			
		||||
                                        -1.624290004647e-6, 1.303655835580e-6, 1.5626441722e-8, -8.5238095915e-8,
 | 
			
		||||
                                        6.529054439e-9, 5.059343495e-9, -9.91364156e-10, -2.27365122e-10,
 | 
			
		||||
                                        9.6467911e-11, 2.394038e-12, -6.886027e-12, 8.94487e-13, 3.13092e-13,
 | 
			
		||||
                                        -1.12708e-13, 3.81e-16, 7.106e-15, -1.523e-15, -9.4e-17, 1.21e-16, -2.8e-17
 | 
			
		||||
                                    };
 | 
			
		||||
 | 
			
		||||
            int ncof = coefficients.Length;
 | 
			
		||||
            double d = 0.0;
 | 
			
		||||
            double dd = 0.0;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            for (int j = ncof - 1; j > 0; j--)
 | 
			
		||||
            {
 | 
			
		||||
                double tmp = d;
 | 
			
		||||
                d = ty*d - dd + coefficients[j];
 | 
			
		||||
                dd = tmp;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            double ans = t*Math.Exp(-z*z + 0.5*(coefficients[0] + ty*d) - dd);
 | 
			
		||||
            return x >= 0.0 ? ans : (2.0 - ans);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        private static double InverseErrorFunctionCumulativeTo(double p)
 | 
			
		||||
        {
 | 
			
		||||
            // From page 265 of numerical recipes                       
 | 
			
		||||
 | 
			
		||||
            if (p >= 2.0)
 | 
			
		||||
            {
 | 
			
		||||
                return -100;
 | 
			
		||||
            }
 | 
			
		||||
            if (p <= 0.0)
 | 
			
		||||
            {
 | 
			
		||||
                return 100;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            double pp = (p < 1.0) ? p : 2 - p;
 | 
			
		||||
            double t = Math.Sqrt(-2*Math.Log(pp/2.0)); // Initial guess
 | 
			
		||||
            double x = -0.70711*((2.30753 + t*0.27061)/(1.0 + t*(0.99229 + t*0.04481)) - t);
 | 
			
		||||
 | 
			
		||||
            for (int j = 0; j < 2; j++)
 | 
			
		||||
            {
 | 
			
		||||
                double err = ErrorFunctionCumulativeTo(x) - pp;
 | 
			
		||||
                x += err/(1.12837916709551257*Math.Exp(-(x*x)) - x*err); // Halley                
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return p < 1.0 ? x : -x;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static double InverseCumulativeTo(double x, double mean, double standardDeviation)
 | 
			
		||||
        {
 | 
			
		||||
            // From numerical recipes, page 320
 | 
			
		||||
            return mean - Math.Sqrt(2)*standardDeviation*InverseErrorFunctionCumulativeTo(2*x);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static double InverseCumulativeTo(double x)
 | 
			
		||||
        {
 | 
			
		||||
            return InverseCumulativeTo(x, 0, 1);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public override string ToString()
 | 
			
		||||
        {
 | 
			
		||||
            // Debug help
 | 
			
		||||
            return String.Format("μ={0:0.0000}, σ={1:0.0000}",
 | 
			
		||||
                                 Mean,
 | 
			
		||||
                                 StandardDeviation);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										520
									
								
								Skills/Numerics/Matrix.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										520
									
								
								Skills/Numerics/Matrix.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,520 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
 | 
			
		||||
namespace Moserware.Numerics
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Represents an MxN matrix with double precision values.
 | 
			
		||||
    /// </summary>    
 | 
			
		||||
    internal class Matrix
 | 
			
		||||
    {
 | 
			
		||||
        protected double[][] _MatrixRowValues;
 | 
			
		||||
        // Note: some properties like Determinant, Inverse, etc are properties instead
 | 
			
		||||
        // of methods to make the syntax look nicer even though this sort of goes against
 | 
			
		||||
        // Framework Design Guidelines that properties should be "cheap" since it could take
 | 
			
		||||
        // a long time to compute these properties if the matrices are "big."
 | 
			
		||||
 | 
			
		||||
        protected Matrix()
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public Matrix(int rows, int columns, params double[] allRowValues)
 | 
			
		||||
        {
 | 
			
		||||
            Rows = rows;
 | 
			
		||||
            Columns = columns;
 | 
			
		||||
 | 
			
		||||
            _MatrixRowValues = new double[rows][];
 | 
			
		||||
 | 
			
		||||
            int currentIndex = 0;
 | 
			
		||||
            for (int currentRow = 0; currentRow < Rows; currentRow++)
 | 
			
		||||
            {
 | 
			
		||||
                _MatrixRowValues[currentRow] = new double[Columns];
 | 
			
		||||
 | 
			
		||||
                for (int currentColumn = 0; currentColumn < Columns; currentColumn++)
 | 
			
		||||
                {
 | 
			
		||||
                    if ((allRowValues != null) && (currentIndex < allRowValues.Length))
 | 
			
		||||
                    {
 | 
			
		||||
                        _MatrixRowValues[currentRow][currentColumn] = allRowValues[currentIndex++];
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public Matrix(double[][] rowValues)
 | 
			
		||||
        {
 | 
			
		||||
            if (!rowValues.All(row => row.Length == rowValues[0].Length))
 | 
			
		||||
            {
 | 
			
		||||
                throw new ArgumentException("All rows must be the same length!");
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            Rows = rowValues.Length;
 | 
			
		||||
            Columns = rowValues[0].Length;
 | 
			
		||||
            _MatrixRowValues = rowValues;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected Matrix(int rows, int columns, double[][] matrixRowValues)
 | 
			
		||||
        {
 | 
			
		||||
            Rows = rows;
 | 
			
		||||
            Columns = columns;
 | 
			
		||||
            _MatrixRowValues = matrixRowValues;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public Matrix(int rows, int columns, IEnumerable<IEnumerable<double>> columnValues)
 | 
			
		||||
            : this(rows, columns)
 | 
			
		||||
        {
 | 
			
		||||
            int columnIndex = 0;
 | 
			
		||||
 | 
			
		||||
            foreach (var currentColumn in columnValues)
 | 
			
		||||
            {
 | 
			
		||||
                int rowIndex = 0;
 | 
			
		||||
                foreach (double currentColumnValue in currentColumn)
 | 
			
		||||
                {
 | 
			
		||||
                    _MatrixRowValues[rowIndex++][columnIndex] = currentColumnValue;
 | 
			
		||||
                }
 | 
			
		||||
                columnIndex++;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public int Rows { get; protected set; }
 | 
			
		||||
        public int Columns { get; protected set; }
 | 
			
		||||
 | 
			
		||||
        public double this[int row, int column]
 | 
			
		||||
        {
 | 
			
		||||
            get { return _MatrixRowValues[row][column]; }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public Matrix Transpose
 | 
			
		||||
        {
 | 
			
		||||
            get
 | 
			
		||||
            {
 | 
			
		||||
                // Just flip everything 
 | 
			
		||||
                var transposeMatrix = new double[Columns][];
 | 
			
		||||
                for (int currentRowTransposeMatrix = 0;
 | 
			
		||||
                     currentRowTransposeMatrix < Columns;
 | 
			
		||||
                     currentRowTransposeMatrix++)
 | 
			
		||||
                {
 | 
			
		||||
                    var transposeMatrixCurrentRowColumnValues = new double[Rows];
 | 
			
		||||
                    transposeMatrix[currentRowTransposeMatrix] = transposeMatrixCurrentRowColumnValues;
 | 
			
		||||
 | 
			
		||||
                    for (int currentColumnTransposeMatrix = 0;
 | 
			
		||||
                         currentColumnTransposeMatrix < Rows;
 | 
			
		||||
                         currentColumnTransposeMatrix++)
 | 
			
		||||
                    {
 | 
			
		||||
                        transposeMatrixCurrentRowColumnValues[currentColumnTransposeMatrix] =
 | 
			
		||||
                            _MatrixRowValues[currentColumnTransposeMatrix][currentRowTransposeMatrix];
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                return new Matrix(Columns, Rows, transposeMatrix);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private bool IsSquare
 | 
			
		||||
        {
 | 
			
		||||
            get { return (Rows == Columns) && Rows > 0; }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public double Determinant
 | 
			
		||||
        {
 | 
			
		||||
            get
 | 
			
		||||
            {
 | 
			
		||||
                // Basic argument checking
 | 
			
		||||
                if (!IsSquare)
 | 
			
		||||
                {
 | 
			
		||||
                    throw new NotSupportedException("Matrix must be square!");
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (Rows == 1)
 | 
			
		||||
                {
 | 
			
		||||
                    // Really happy path :)
 | 
			
		||||
                    return _MatrixRowValues[0][0];
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (Rows == 2)
 | 
			
		||||
                {
 | 
			
		||||
                    // Happy path!
 | 
			
		||||
                    // Given:
 | 
			
		||||
                    // | a b |
 | 
			
		||||
                    // | c d |
 | 
			
		||||
                    // The determinant is ad - bc
 | 
			
		||||
                    double a = _MatrixRowValues[0][0];
 | 
			
		||||
                    double b = _MatrixRowValues[0][1];
 | 
			
		||||
                    double c = _MatrixRowValues[1][0];
 | 
			
		||||
                    double d = _MatrixRowValues[1][1];
 | 
			
		||||
                    return a*d - b*c;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // I use the Laplace expansion here since it's straightforward to implement.
 | 
			
		||||
                // It's O(n^2) and my implementation is especially poor performing, but the
 | 
			
		||||
                // core idea is there. Perhaps I should replace it with a better algorithm
 | 
			
		||||
                // later.
 | 
			
		||||
                // See http://en.wikipedia.org/wiki/Laplace_expansion for details
 | 
			
		||||
 | 
			
		||||
                double result = 0.0;
 | 
			
		||||
 | 
			
		||||
                // I expand along the first row
 | 
			
		||||
                for (int currentColumn = 0; currentColumn < Columns; currentColumn++)
 | 
			
		||||
                {
 | 
			
		||||
                    double firstRowColValue = _MatrixRowValues[0][currentColumn];
 | 
			
		||||
                    double cofactor = GetCofactor(0, currentColumn);
 | 
			
		||||
                    double itemToAdd = firstRowColValue*cofactor;
 | 
			
		||||
                    result += itemToAdd;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                return result;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public Matrix Adjugate
 | 
			
		||||
        {
 | 
			
		||||
            get
 | 
			
		||||
            {
 | 
			
		||||
                if (!IsSquare)
 | 
			
		||||
                {
 | 
			
		||||
                    throw new ArgumentException("Matrix must be square!");
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // See http://en.wikipedia.org/wiki/Adjugate_matrix
 | 
			
		||||
                if (Rows == 2)
 | 
			
		||||
                {
 | 
			
		||||
                    // Happy path!
 | 
			
		||||
                    // Adjugate of:
 | 
			
		||||
                    // | a b |
 | 
			
		||||
                    // | c d |
 | 
			
		||||
                    // is
 | 
			
		||||
                    // | d -b |
 | 
			
		||||
                    // | -c a |
 | 
			
		||||
 | 
			
		||||
                    double a = _MatrixRowValues[0][0];
 | 
			
		||||
                    double b = _MatrixRowValues[0][1];
 | 
			
		||||
                    double c = _MatrixRowValues[1][0];
 | 
			
		||||
                    double d = _MatrixRowValues[1][1];
 | 
			
		||||
 | 
			
		||||
                    return new SquareMatrix(d, -b,
 | 
			
		||||
                                            -c, a);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // The idea is that it's the transpose of the cofactors                
 | 
			
		||||
                var result = new double[Columns][];
 | 
			
		||||
 | 
			
		||||
                for (int currentColumn = 0; currentColumn < Columns; currentColumn++)
 | 
			
		||||
                {
 | 
			
		||||
                    result[currentColumn] = new double[Rows];
 | 
			
		||||
 | 
			
		||||
                    for (int currentRow = 0; currentRow < Rows; currentRow++)
 | 
			
		||||
                    {
 | 
			
		||||
                        result[currentColumn][currentRow] = GetCofactor(currentRow, currentColumn);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                return new Matrix(result);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public Matrix Inverse
 | 
			
		||||
        {
 | 
			
		||||
            get
 | 
			
		||||
            {
 | 
			
		||||
                if ((Rows == 1) && (Columns == 1))
 | 
			
		||||
                {
 | 
			
		||||
                    return new SquareMatrix(1.0/_MatrixRowValues[0][0]);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // Take the simple approach:
 | 
			
		||||
                // http://en.wikipedia.org/wiki/Cramer%27s_rule#Finding_inverse_matrix
 | 
			
		||||
                return (1.0/Determinant)*Adjugate;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static Matrix operator *(double scalarValue, Matrix matrix)
 | 
			
		||||
        {
 | 
			
		||||
            int rows = matrix.Rows;
 | 
			
		||||
            int columns = matrix.Columns;
 | 
			
		||||
            var newValues = new double[rows][];
 | 
			
		||||
 | 
			
		||||
            for (int currentRow = 0; currentRow < rows; currentRow++)
 | 
			
		||||
            {
 | 
			
		||||
                var newRowColumnValues = new double[columns];
 | 
			
		||||
                newValues[currentRow] = newRowColumnValues;
 | 
			
		||||
 | 
			
		||||
                for (int currentColumn = 0; currentColumn < columns; currentColumn++)
 | 
			
		||||
                {
 | 
			
		||||
                    newRowColumnValues[currentColumn] = scalarValue*matrix._MatrixRowValues[currentRow][currentColumn];
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return new Matrix(rows, columns, newValues);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static Matrix operator +(Matrix left, Matrix right)
 | 
			
		||||
        {
 | 
			
		||||
            if ((left.Rows != right.Rows) || (left.Columns != right.Columns))
 | 
			
		||||
            {
 | 
			
		||||
                throw new ArgumentException("Matrices must be of the same size");
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // simple addition of each item
 | 
			
		||||
 | 
			
		||||
            var resultMatrix = new double[left.Rows][];
 | 
			
		||||
 | 
			
		||||
            for (int currentRow = 0; currentRow < left.Rows; currentRow++)
 | 
			
		||||
            {
 | 
			
		||||
                var rowColumnValues = new double[right.Columns];
 | 
			
		||||
                resultMatrix[currentRow] = rowColumnValues;
 | 
			
		||||
                for (int currentColumn = 0; currentColumn < right.Columns; currentColumn++)
 | 
			
		||||
                {
 | 
			
		||||
                    rowColumnValues[currentColumn] = left._MatrixRowValues[currentRow][currentColumn]
 | 
			
		||||
                                                     +
 | 
			
		||||
                                                     right._MatrixRowValues[currentRow][currentColumn];
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return new Matrix(left.Rows, right.Columns, resultMatrix);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static Matrix operator *(Matrix left, Matrix right)
 | 
			
		||||
        {
 | 
			
		||||
            // Just your standard matrix multiplication.
 | 
			
		||||
            // See http://en.wikipedia.org/wiki/Matrix_multiplication for details
 | 
			
		||||
 | 
			
		||||
            if (left.Columns != right.Rows)
 | 
			
		||||
            {
 | 
			
		||||
                throw new ArgumentException("The width of the left matrix must match the height of the right matrix",
 | 
			
		||||
                                            "right");
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            int resultRows = left.Rows;
 | 
			
		||||
            int resultColumns = right.Columns;
 | 
			
		||||
 | 
			
		||||
            var resultMatrix = new double[resultRows][];
 | 
			
		||||
 | 
			
		||||
            for (int currentRow = 0; currentRow < resultRows; currentRow++)
 | 
			
		||||
            {
 | 
			
		||||
                resultMatrix[currentRow] = new double[resultColumns];
 | 
			
		||||
 | 
			
		||||
                for (int currentColumn = 0; currentColumn < resultColumns; currentColumn++)
 | 
			
		||||
                {
 | 
			
		||||
                    double productValue = 0;
 | 
			
		||||
 | 
			
		||||
                    for (int vectorIndex = 0; vectorIndex < left.Columns; vectorIndex++)
 | 
			
		||||
                    {
 | 
			
		||||
                        double leftValue = left._MatrixRowValues[currentRow][vectorIndex];
 | 
			
		||||
                        double rightValue = right._MatrixRowValues[vectorIndex][currentColumn];
 | 
			
		||||
                        double vectorIndexProduct = leftValue*rightValue;
 | 
			
		||||
                        productValue += vectorIndexProduct;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    resultMatrix[currentRow][currentColumn] = productValue;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return new Matrix(resultRows, resultColumns, resultMatrix);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private Matrix GetMinorMatrix(int rowToRemove, int columnToRemove)
 | 
			
		||||
        {
 | 
			
		||||
            // See http://en.wikipedia.org/wiki/Minor_(linear_algebra)
 | 
			
		||||
 | 
			
		||||
            // I'm going to use a horribly naïve algorithm... because I can :)
 | 
			
		||||
            var result = new double[Rows - 1][];
 | 
			
		||||
            int resultRow = 0;
 | 
			
		||||
 | 
			
		||||
            for (int currentRow = 0; currentRow < Rows; currentRow++)
 | 
			
		||||
            {
 | 
			
		||||
                if (currentRow == rowToRemove)
 | 
			
		||||
                {
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                result[resultRow] = new double[Columns - 1];
 | 
			
		||||
 | 
			
		||||
                int resultColumn = 0;
 | 
			
		||||
 | 
			
		||||
                for (int currentColumn = 0; currentColumn < Columns; currentColumn++)
 | 
			
		||||
                {
 | 
			
		||||
                    if (currentColumn == columnToRemove)
 | 
			
		||||
                    {
 | 
			
		||||
                        continue;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    result[resultRow][resultColumn] = _MatrixRowValues[currentRow][currentColumn];
 | 
			
		||||
                    resultColumn++;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                resultRow++;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return new Matrix(Rows - 1, Columns - 1, result);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private double GetCofactor(int rowToRemove, int columnToRemove)
 | 
			
		||||
        {
 | 
			
		||||
            // See http://en.wikipedia.org/wiki/Cofactor_(linear_algebra) for details
 | 
			
		||||
            // REVIEW: should things be reversed since I'm 0 indexed?
 | 
			
		||||
            int sum = rowToRemove + columnToRemove;
 | 
			
		||||
            bool isEven = (sum%2 == 0);
 | 
			
		||||
 | 
			
		||||
            if (isEven)
 | 
			
		||||
            {
 | 
			
		||||
                return GetMinorMatrix(rowToRemove, columnToRemove).Determinant;
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                return -1.0*GetMinorMatrix(rowToRemove, columnToRemove).Determinant;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Equality stuff
 | 
			
		||||
        // See http://msdn.microsoft.com/en-us/library/ms173147.aspx
 | 
			
		||||
 | 
			
		||||
        public static bool operator ==(Matrix a, Matrix b)
 | 
			
		||||
        {
 | 
			
		||||
            // If both are null, or both are same instance, return true.
 | 
			
		||||
            if (ReferenceEquals(a, b))
 | 
			
		||||
            {
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // If one is null, but not both, return false.
 | 
			
		||||
            if (((object) a == null) || ((object) b == null))
 | 
			
		||||
            {
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if ((a.Rows != b.Rows) || (a.Columns != b.Columns))
 | 
			
		||||
            {
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            const double errorTolerance = 0.0000000000001;
 | 
			
		||||
 | 
			
		||||
            for (int currentRow = 0; currentRow < a.Rows; currentRow++)
 | 
			
		||||
            {
 | 
			
		||||
                for (int currentColumn = 0; currentColumn < a.Columns; currentColumn++)
 | 
			
		||||
                {
 | 
			
		||||
                    double delta =
 | 
			
		||||
                        Math.Abs(a._MatrixRowValues[currentRow][currentColumn] -
 | 
			
		||||
                                 b._MatrixRowValues[currentRow][currentColumn]);
 | 
			
		||||
 | 
			
		||||
                    if (delta > errorTolerance)
 | 
			
		||||
                    {
 | 
			
		||||
                        return false;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static bool operator !=(Matrix a, Matrix b)
 | 
			
		||||
        {
 | 
			
		||||
            return !(a == b);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override int GetHashCode()
 | 
			
		||||
        {
 | 
			
		||||
            double result = Rows;
 | 
			
		||||
            result += 2*Columns;
 | 
			
		||||
 | 
			
		||||
            unchecked
 | 
			
		||||
            {
 | 
			
		||||
                for (int currentRow = 0; currentRow < Rows; currentRow++)
 | 
			
		||||
                {
 | 
			
		||||
                    bool eventRow = (currentRow%2) == 0;
 | 
			
		||||
                    double multiplier = eventRow ? 1.0 : 2.0;
 | 
			
		||||
 | 
			
		||||
                    for (int currentColumn = 0; currentColumn < Columns; currentColumn++)
 | 
			
		||||
                    {
 | 
			
		||||
                        result += multiplier*_MatrixRowValues[currentRow][currentColumn];
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Ok, now convert that double to an int
 | 
			
		||||
            byte[] resultBytes = BitConverter.GetBytes(result);
 | 
			
		||||
 | 
			
		||||
            var finalBytes = new byte[4];
 | 
			
		||||
            for (int i = 0; i < 4; i++)
 | 
			
		||||
            {
 | 
			
		||||
                finalBytes[i] = (byte) (resultBytes[i] ^ resultBytes[i + 4]);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            int hashCode = BitConverter.ToInt32(finalBytes, 0);
 | 
			
		||||
            return hashCode;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public override bool Equals(object obj)
 | 
			
		||||
        {
 | 
			
		||||
            var other = obj as Matrix;
 | 
			
		||||
            if (other == null)
 | 
			
		||||
            {
 | 
			
		||||
                return base.Equals(obj);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return this == other;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    internal class DiagonalMatrix : Matrix
 | 
			
		||||
    {
 | 
			
		||||
        public DiagonalMatrix(IList<double> diagonalValues)
 | 
			
		||||
            : base(diagonalValues.Count, diagonalValues.Count)
 | 
			
		||||
        {
 | 
			
		||||
            for (int i = 0; i < diagonalValues.Count; i++)
 | 
			
		||||
            {
 | 
			
		||||
                _MatrixRowValues[i][i] = diagonalValues[i];
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    internal class Vector : Matrix
 | 
			
		||||
    {
 | 
			
		||||
        public Vector(IList<double> vectorValues)
 | 
			
		||||
            : base(vectorValues.Count, 1, new IEnumerable<double>[] {vectorValues})
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    internal class SquareMatrix : Matrix
 | 
			
		||||
    {
 | 
			
		||||
        public SquareMatrix(params double[] allValues)
 | 
			
		||||
        {
 | 
			
		||||
            Rows = (int) Math.Sqrt(allValues.Length);
 | 
			
		||||
            Columns = Rows;
 | 
			
		||||
 | 
			
		||||
            int allValuesIndex = 0;
 | 
			
		||||
 | 
			
		||||
            _MatrixRowValues = new double[Rows][];
 | 
			
		||||
            for (int currentRow = 0; currentRow < Rows; currentRow++)
 | 
			
		||||
            {
 | 
			
		||||
                var currentRowValues = new double[Columns];
 | 
			
		||||
                _MatrixRowValues[currentRow] = currentRowValues;
 | 
			
		||||
 | 
			
		||||
                for (int currentColumn = 0; currentColumn < Columns; currentColumn++)
 | 
			
		||||
                {
 | 
			
		||||
                    currentRowValues[currentColumn] = allValues[allValuesIndex++];
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    internal class IdentityMatrix : DiagonalMatrix
 | 
			
		||||
    {
 | 
			
		||||
        public IdentityMatrix(int rows)
 | 
			
		||||
            : base(CreateDiagonal(rows))
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private static double[] CreateDiagonal(int rows)
 | 
			
		||||
        {
 | 
			
		||||
            var result = new double[rows];
 | 
			
		||||
            for (int i = 0; i < rows; i++)
 | 
			
		||||
            {
 | 
			
		||||
                result[i] = 1.0;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return result;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										49
									
								
								Skills/Numerics/Range.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								Skills/Numerics/Range.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,49 @@
 | 
			
		||||
using System;
 | 
			
		||||
 | 
			
		||||
namespace Moserware.Skills.Numerics
 | 
			
		||||
{
 | 
			
		||||
    // The whole purpose of this class is to make the code for the SkillCalculator(s)
 | 
			
		||||
    // look a little cleaner
 | 
			
		||||
 | 
			
		||||
    public abstract class Range<T> where T : Range<T>, new()
 | 
			
		||||
    {
 | 
			
		||||
        private static readonly T _Instance = new T();
 | 
			
		||||
 | 
			
		||||
        protected Range(int min, int max)
 | 
			
		||||
        {
 | 
			
		||||
            if (min > max)
 | 
			
		||||
            {
 | 
			
		||||
                throw new ArgumentOutOfRangeException();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            Min = min;
 | 
			
		||||
            Max = max;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public int Min { get; private set; }
 | 
			
		||||
        public int Max { get; private set; }
 | 
			
		||||
        protected abstract T Create(int min, int max);
 | 
			
		||||
 | 
			
		||||
        // REVIEW: It's probably bad form to have access statics via a derived class, but the syntax looks better :-)
 | 
			
		||||
 | 
			
		||||
        public static T Inclusive(int min, int max)
 | 
			
		||||
        {
 | 
			
		||||
            return _Instance.Create(min, max);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static T Exactly(int value)
 | 
			
		||||
        {
 | 
			
		||||
            return _Instance.Create(value, value);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static T AtLeast(int minimumValue)
 | 
			
		||||
        {
 | 
			
		||||
            return _Instance.Create(minimumValue, int.MaxValue);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public bool IsInRange(int value)
 | 
			
		||||
        {
 | 
			
		||||
            return (Min <= value) && (value <= Max);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user