1

Ok, I need to implement, among others, these two particular types: ComplexNumber<T> and Matrix<T>. T can be one of the following: a rational number, a real number or integers.

In the System namespace I have a good representation for real numbers (decimal), integer numbers (int or long). Numerics.ComplexNumber does not cut it becuase internally RealPart and ImaginaryPart are double and I can't afford, in this particular case, the representation error of this type.

Now, the probem is of course that there is no way to constraint the generic parameter T to mathematical "valid" types. That is, I can't do the following:

public struct ComplexNumber<T>
{
     readonly T realPart;
     readonly T imaginaryPart;

     public static ComplexNumber<T> Add(ComplexNumber<T> left, ComplexNumber<T> right)
     {
         return new ComplexNumber<T>(left.realPart + right.realPart, left.imaginaryPart + right.imaginaryPart); //Compile time error. No operator defined for T
     }
}

So I need a workaround. Although performance is not a goal in itself, I'd like the code to work reasonably well, but above all, I'd like it to be the most elegant solution possible. Right now I've come up with two possibilities:

A Numeric abstract base class similar to:

public abstract class Numeric
{
    protected abstract Numeric _Add(Numeric right);
    protected abstract Numeric _Subtract(Numeric right);

    public static Numeric Add(Numeric left, Numeric right) { return _Add(this, right); }
    public static Numeric Subtract(Numeric left, Numeric right) { return _Subtract(this, right);
}

Now I could do:

public sealed class RationalNumber: Numeric
{
     readonly long numerator, denominator;

     protected override Numeric _Add(Numeric right) { //Rational addition implementation }
}

And then Matrix<RationalNumber> or Matrix<ComplexNumber> would work.

The other option is to do it through an interface:

public INumeric
{
     INumeric Add(INumeric right);
     INumeric Subtract(INumeric right);
}

public struct RationalNumber: INumeric
{
     readonly long numerator, denominator;

     public static RationalNumber Add(RationalNumber left, RationalNumber right) { //Rationa add implementation }
     INumeric INumeric.Add(INumeric right) //explicit to not clog up the type and to convey the idea of a framework mechanism.
     {
          if (!(right is RationalNumber))
              throw new ArgumentException();

          Add(this, (RationalNumber)right);
     }

The interface option lets me implement RationalNumber as a struct wich is, IMHO, more consistent with how numeric types are implemented in the framework. Also the types themselves are pretty lightweight and will typically have short lives. The Numeric base class solution seems like a whole lot more heavyweight, but I'm not really an expert to exactly quantify the advantages of one over the other when it comes to:

  • Code qualitiy and future extensibility and maintenance
  • Performance and memory consumption (curiosity more than anything else)
  • A better solution?

If anyone can shed a little bit of light, I'd appreciate it.

InBetween
  • 32,319
  • 3
  • 50
  • 90
  • 5
    Why make it generic in the first place? Just fix it to a decimal number, surely that gives you everything you need? – DavidG Sep 15 '14 at 09:36
  • 2
    Decimals are not a good representation of rationals, except if the numerator is a factor of 10... – tolanj Sep 15 '14 at 09:39
  • INumeric Add(Numeric right); etc in your interface example all need to take INumeric parameters... – tolanj Sep 15 '14 at 09:39
  • @tolanj In what way are they not a good representation? – DavidG Sep 15 '14 at 09:40
  • And interface all the way, you can still have an abstract implementation (ie you can still use your Numeric as a : INumeric) but coding to the interface gives you flexiblity. However you really want to only be able to add rationals to rationals etc (perhaps with some inbuilt casts), ie you probably want an INumeric AND an IRational : INumeric with Numeric_Add(IRational right) AND Rational implementing that. – tolanj Sep 15 '14 at 09:44
  • Why not use an existing library? https://github.com/mathnet/mathnet-numerics contains rational numbers – samy Sep 15 '14 at 09:44
  • @DavidG: Because I need to support rational numbers. I could convert rational numbers to decimal, operate and then "reconvert" them back to rational numbers but that would be way too slow and not exact: the conversion back to rational could have conversion errors. Also, I have to support a complex type too. – InBetween Sep 15 '14 at 09:45
  • @DavidG 1/3 is perfectly representable as a rational and thus the complex number 2/3 + 1/3i is perfectly representable with his proposed ComplexNumber solution but would only be approximated by a ComplexNumber fully tied to a decimal backend. – tolanj Sep 15 '14 at 09:46
  • @tolanj typo. Fixed, thanks. – InBetween Sep 15 '14 at 09:51
  • If you want to stick with the `+` operator, see http://stackoverflow.com/a/5013910/412770 for 2 solutions: `dynamic` and expression-trees. – Ani Sep 15 '14 at 09:56
  • @tolanj what I had in mind is to implement INumeric explicitly and implement a public static method more accordingly typed to each class. I've updated the code in the question. – InBetween Sep 15 '14 at 09:56
  • The generalization to `INumeric` seems a bit skew to me. Some of the underlying types form a ring, some a field. The general approach seems valid. – Codor Sep 15 '14 at 09:57

1 Answers1

0

You can use extensions method to solve this problem:

public struct ComplexNumber<T>
{
    private readonly T _realPart;
    private readonly T _imaginaryPart;

    public ComplexNumber(T realPart, T imaginaryPart)
    {
        _realPart = realPart;
        _imaginaryPart = imaginaryPart;
    }

    public T RealPart
    {
        get
        {
            return _realPart;
        }
    }

    public T ImaginaryPart
    {
        get
        {
            return _imaginaryPart;
        }
    }

    public override string ToString()
    {
        return string.Format("({0}, {1})", RealPart, ImaginaryPart);
    }
}

public static class ComplexNumberExtensions
{
    public static ComplexNumber<int> Add(this ComplexNumber<int> self, ComplexNumber<int> other)
    {
        return new ComplexNumber<int>(self.RealPart + other.RealPart, self.ImaginaryPart + other.ImaginaryPart);
    }

    public static ComplexNumber<double> Add(this ComplexNumber<double> self, ComplexNumber<double> other)
    {
        return new ComplexNumber<double>(self.RealPart + other.RealPart, self.ImaginaryPart + other.ImaginaryPart);
    }

    // Add similar extension methods for each numeric type you need
}

Use it like this:

var first = new ComplexNumber<int>(1, 2);
var second = new ComplexNumber<int>(3, 4);
var result = first.Add(second);
Console.WriteLine(result);
RX_DID_RX
  • 4,113
  • 4
  • 17
  • 27