Search code examples
c#genericsinterfacemath

Creating a Math library using Generics in C#


Is there any feasible way of using generics to create a Math library that does not depend on the base type chosen to store data?

In other words, let's assume I want to write a Fraction class. The fraction can be represented by two ints or two doubles or whatnot. The important thing is that the basic four arithmetic operations are well defined. So, I would like to be able to write Fraction<int> frac = new Fraction<int>(1,2) and/or Fraction<double> frac = new Fraction<double>(0.1, 1.0).

Unfortunately there is no interface representing the four basic operations (+,-,*,/). Has anybody found a workable, feasible way of implementing this?


Solution

  • Here is a way to abstract out the operators that is relatively painless.

        abstract class MathProvider<T>
        {
            public abstract T Divide(T a, T b);
            public abstract T Multiply(T a, T b);
            public abstract T Add(T a, T b);
            public abstract T Negate(T a);
            public virtual T Subtract(T a, T b)
            {
                return Add(a, Negate(b));
            }
        }
    
        class DoubleMathProvider : MathProvider<double>
        {
            public override double Divide(double a, double b)
            {
                return a / b;
            }
    
            public override double Multiply(double a, double b)
            {
                return a * b;
            }
    
            public override double Add(double a, double b)
            {
                return a + b;
            }
    
            public override double Negate(double a)
            {
                return -a;
            }
        }
    
        class IntMathProvider : MathProvider<int>
        {
            public override int Divide(int a, int b)
            {
                return a / b;
            }
    
            public override int Multiply(int a, int b)
            {
                return a * b;
            }
    
            public override int Add(int a, int b)
            {
                return a + b;
            }
    
            public override int Negate(int a)
            {
                return -a;
            }
        }
    
        class Fraction<T>
        {
            static MathProvider<T> _math;
            // Notice this is a type constructor.  It gets run the first time a
            // variable of a specific type is declared for use.
            // Having _math static reduces overhead.
            static Fraction()
            {
                // This part of the code might be cleaner by once
                // using reflection and finding all the implementors of
                // MathProvider and assigning the instance by the one that
                // matches T.
                if (typeof(T) == typeof(double))
                    _math = new DoubleMathProvider() as MathProvider<T>;
                else if (typeof(T) == typeof(int))
                    _math = new IntMathProvider() as MathProvider<T>;
                // ... assign other options here.
    
                if (_math == null)
                    throw new InvalidOperationException(
                        "Type " + typeof(T).ToString() + " is not supported by Fraction.");
            }
    
            // Immutable impementations are better.
            public T Numerator { get; private set; }
            public T Denominator { get; private set; }
    
            public Fraction(T numerator, T denominator)
            {
                // We would want this to be reduced to simpilest terms.
                // For that we would need GCD, abs, and remainder operations
                // defined for each math provider.
                Numerator = numerator;
                Denominator = denominator;
            }
    
            public static Fraction<T> operator +(Fraction<T> a, Fraction<T> b)
            {
                return new Fraction<T>(
                    _math.Add(
                      _math.Multiply(a.Numerator, b.Denominator),
                      _math.Multiply(b.Numerator, a.Denominator)),
                    _math.Multiply(a.Denominator, b.Denominator));
            }
    
            public static Fraction<T> operator -(Fraction<T> a, Fraction<T> b)
            {
                return new Fraction<T>(
                    _math.Subtract(
                      _math.Multiply(a.Numerator, b.Denominator),
                      _math.Multiply(b.Numerator, a.Denominator)),
                    _math.Multiply(a.Denominator, b.Denominator));
            }
    
            public static Fraction<T> operator /(Fraction<T> a, Fraction<T> b)
            {
                return new Fraction<T>(
                    _math.Multiply(a.Numerator, b.Denominator),
                    _math.Multiply(a.Denominator, b.Numerator));
            }
    
            // ... other operators would follow.
        }
    

    If you fail to implement a type that you use, you will get a failure at runtime instead of at compile time (that is bad). The definition of the MathProvider<T> implementations is always going to be the same (also bad). I would suggest that you just avoid doing this in C# and use F# or some other language better suited to this level of abstraction.

    Edit: Fixed definitions of add and subtract for Fraction<T>. Another interesting and simple thing to do is implement a MathProvider that operates on an abstract syntax tree. This idea immediately points to doing things like automatic differentiation: http://conal.net/papers/beautiful-differentiation/