Last active
June 9, 2021 03:23
-
-
Save sorashi/0224be5400f0f34527fb48bdf0a81f8c to your computer and use it in GitHub Desktop.
The base for a fraction class. Can be used to represent rational numbers precisely.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment