Sorashi
4/13/2017 - 8:38 AM

The base for a fraction class. Can be used to represent rational numbers precisely.

The base for a fraction class. Can be used to represent rational numbers precisely.

using System;
using System.Text.RegularExpressions;

public class Fraction
{
    private int denominator = 1;
    private int nominator = 0;

    public Fraction() {
    }

    public Fraction(int n, int d) {
        Nominator = n;
        Denominator = d;
    }

    public static Fraction One => new Fraction { nominator = 1, denominator = 1 };

    public static Fraction Zero => new Fraction { nominator = 0, denominator = 1 };

    public int Denominator {
        get {
            Shorten();
            return denominator;
        }
        set {
            if (value == 0)
                throw new DivideByZeroException("Fraction can not have a denominator with the value of 0");
            denominator = value;
            Shorten();
        }
    }

    public int Nominator {
        get {
            Shorten();
            return nominator;
        }
        set {
            nominator = value;
            Shorten();
        }
    }

    #region Operators

    public static implicit operator Fraction(int i) => new Fraction(i, 1);

    public static explicit operator double(Fraction f) => f.Nominator / (double)f.Denominator;

    public static explicit operator decimal(Fraction f) => f.Nominator / (decimal)f.Denominator;

    public static explicit operator float(Fraction f) => f.Nominator / (float)f.Denominator;

    public static Fraction operator -(Fraction f) {
        return new Fraction(-f.Nominator, f.Denominator);
    }

    public static Fraction operator -(Fraction f1, Fraction f2) {
        return f1 + (-f2);
    }

    public static bool operator !=(Fraction f1, Fraction f2) {
        return !(f1 == f2);
    }

    public static Fraction operator *(Fraction f1, Fraction f2) {
        return new Fraction {
            Nominator = f1.Nominator * f2.Nominator,
            Denominator = f1.Denominator * f2.Denominator
        };
    }

    public static Fraction operator /(Fraction f1, Fraction f2) {
        return f1 * f2.Inverse();
    }

    public static Fraction operator +(Fraction f1, Fraction f2) {
        return new Fraction() {
            Nominator = f1.Denominator * f2.Nominator + f1.Nominator * f2.Denominator,
            Denominator = f1.Denominator * f2.Denominator
        };
    }

    public static bool operator ==(Fraction f1, Fraction f2) {
        if (f1 == null && f2 == null) return true;
        if (f1 == null) return false;
        return f1.Equals(f2);
    }

    #endregion Operators

    public static Fraction Parse(string s) {
        var regex = new Regex(@"(?<nominator>-?\d+)(?:\/(?<denominator>-?\d+))?");
        var m = regex.Match(s);
        if (!m.Success)
            throw new FormatException();
        var n = int.Parse(m.Groups["nominator"].Value);
        var d = !string.IsNullOrEmpty(m.Groups["denominator"].Value) ? int.Parse(m.Groups["denominator"].Value) : 1;
        return new Fraction(n, d);
    }

    public static Fraction Pow(Fraction f, int exponent = 2) {
        return new Fraction((int)Math.Pow(f.Nominator, exponent), (int)Math.Pow(f.Denominator, exponent));
    }

    public static Fraction Sqrt(Fraction f) {
        if (!IsPerfectSquare(f.Nominator) || !IsPerfectSquare(f.Denominator)) throw new ArgumentException("The nominator or denominator are not perfect squares");
        return new Fraction((int)Math.Sqrt(f.Nominator), (int)Math.Sqrt(f.Denominator));
    }

    public override bool Equals(object obj) {
        if (ReferenceEquals(null, obj)) return false;
        if (ReferenceEquals(this, obj)) return true;
        if (obj.GetType() != GetType()) return false;
        return Equals((Fraction)obj);
    }

    public override int GetHashCode() {
        unchecked {
            return (Nominator * 397) ^ denominator;
        }
    }

    public Fraction Inverse() {
        return new Fraction(Denominator, Nominator);
    }

    public override string ToString() {
        return $"{Nominator}/{Denominator}";
    }

    protected bool Equals(Fraction other) {
        return Nominator == other.Nominator && Denominator == other.Denominator;
    }

    private static int Gcd(int a, int b) {
        while (true) {
            if (b == 0) return a;
            var temp = a;
            a = b;
            b = temp % b;
        }
    }

    private static bool IsPerfectSquare(double input) {
        // source http://stackoverflow.com/a/4886006/1697953
        var sqrt = Math.Sqrt(input);
        return Math.Abs(Math.Ceiling(sqrt) - Math.Floor(sqrt)) < Double.Epsilon;
    }

    private void Shorten() {
        var gcd = Gcd(nominator, denominator);
        nominator /= gcd;
        denominator /= gcd;
        if (denominator < 0 && nominator >= 0) {
            nominator = -nominator;
            denominator = -denominator;
        }
    }
}