Calculations with Roman Numerals using C#

In a previous article, I wrote how to parse Roman Numerals using C#. This article will focus on how to calculate with the class intuitively. It will show how to implement implicit casting and the add and subtraction operator overloads. Fun stuff that's probably useful in other projects.

It would be great to do things like this:

RomanNumeral X = "X";
string result = X + "IV" + 8;

Implement implicit casting

The first thing we must implement is implicit casting. This will enable the class to interact with strings and integers without having to cast them to a RomanNumeral first. Let's look at the code:

public static implicit operator int(RomanNumeral r)
{
    return (r?.Number).GetValueOrDefault();
}

public static implicit operator RomanNumeral(int r)
{
    return new RomanNumeral(r);
}

public static implicit operator string(RomanNumeral r)
{
    return r?.ToString();
}

public static implicit operator RomanNumeral(string r)
{
    return Parse(r);
}

What are the drawbacks of this method? Well, the Parse might return a null when the string is not parsable (l33t is not a valid Roman Numeral). This might result in a null-reference exception. The constructor method will throw an exception when anything below 0 is passed. This is something you might want to work around.

Plus and minus operator overloading

Now let's implement the arithmetical operators +, -/* and % for the RomanNumeral class. Notice that negative numbers are not supported in this example. We'll default to 0 (or nulla as the Romans would call it).

public static RomanNumeral operator +(RomanNumeral r1, RomanNumeral r2)
{
    return r1?.Number + r2?.Number;
}

public static RomanNumeral operator -(RomanNumeral r1, RomanNumeral r2)
{
    var n = r1?.Number - r2?.Number;

    if (n < 0)
    {
        n = 0;
    }

    return new RomanNumeral(n.GetValueOrDefault());
}

public static RomanNumeral operator /(RomanNumeral r1, RomanNumeral r2)
{
    return r1?.Number / r2?.Number;
}

public static RomanNumeral operator *(RomanNumeral r1, RomanNumeral r2)
{
    return r1?.Number * r2?.Number;
}

public static RomanNumeral operator %(RomanNumeral r1, RomanNumeral r2)
{
    return r1?.Number % r2?.Number;
}

Because we've implemented implicit conversions first the class handles operation on strings and integers automagically:

RomanNumeral I = "I";
RomanNumeral IV = "IV";

int a = IV - 1;
int b = 4 - I;
int c = IV - "I";
int d = "IV" - I;
int e = IV - I;

Assert.AreEqual(3, a);
Assert.AreEqual(3, b);
Assert.AreEqual(3, c);
Assert.AreEqual(3, d);
Assert.AreEqual(3, e);

string f = IV - 1;
string g = 4 - I;
string h = IV - "I";
string i = "IV" - I;
string j = IV - I;

Assert.AreEqual("III", f);
Assert.AreEqual("III", g);
Assert.AreEqual("III", h);
Assert.AreEqual("III", i);
Assert.AreEqual("III", j);

RomanNumeral k = IV - 1;
RomanNumeral l = 4 - I;
RomanNumeral m = IV - "I";
RomanNumeral n = "IV" - I;
RomanNumeral o = IV - I;

Assert.AreEqual("III", k.ToString());
Assert.AreEqual("III", l.ToString());
Assert.AreEqual("III", m.ToString());
Assert.AreEqual("III", n.ToString());
Assert.AreEqual("III", o.ToString());

Beautiful, I'd say!

Comparison operators

Mathematical operations are one thing, but how about comparing Roman Numerals? Implementing an IComparable and IComparable<RomanNumeral> will allow sorting mechanisms to handle the class.

public int CompareTo(RomanNumeral other)
{
    if (object.ReferenceEquals(other, null))
    {
        return 1;
    }

    return Number.CompareTo(other.Number);
}

public int CompareTo(object obj)
{
    return CompareTo(obj as RomanNumeral);
}

protected static int Compare(RomanNumeral r1, RomanNumeral r2)
{
    if (object.ReferenceEquals(r1, r2))
    {
        return 0;
    }
    if (object.ReferenceEquals(r1, null))
    {
        return -1;
    }
    return r1.CompareTo(r2);
}

Notice how I'm not using the == or the != operators. This will prevent infinite loops when we implement these operators using the compare methods. The object.ReferenceEquals will do the job.

Implementing the operator overloads

Now let's implement the ==!=<<=>>= operators using the statis Compare method.

public static bool operator ==(RomanNumeral r1, RomanNumeral r2)
{
    return Compare(r1, r2) == 0;
}

public static bool operator !=(RomanNumeral r1, RomanNumeral r2)
{
    return Compare(r1, r2) != 0;
}

public static bool operator <(RomanNumeral r1, RomanNumeral r2)
{
    return (Compare(r1, r2) < 0);
}

public static bool operator >(RomanNumeral r1, RomanNumeral r2)
{
    return (Compare(r1, r2) > 0);
}

public static bool operator <=(RomanNumeral r1, RomanNumeral r2)
{
    return (Compare(r1, r2) <= 0);
}

public static bool operator >=(RomanNumeral r1, RomanNumeral r2)
{
    return (Compare(r1, r2) >= 0);
}

Implement equals

The equality method can also be implemented using the CompareTo method. Remember that the GetHashCode should be overridden as well.

public override bool Equals(object obj)
{
    return CompareTo(obj) == 0;
}

public override int GetHashCode()
{
    return this.Number.GetHashCode();
}

Summary

I’ve shown how one can do arithmetic using Roman Numerals. Implicit casting and operator overloading are very helpful in this case. You can download the class from GitHub and use it in your projects.

expand_less