// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // namespace ImageProcessorCore { using System; using System.Globalization; using System.Numerics; using System.Runtime.InteropServices; using System.Text; /// /// Represents a number that can be expressed as a fraction /// /// /// This is a very simplified implimentation of a rational number designed for use with /// metadata only. /// public struct Rational : IEquatable { /// /// Represents a rational object that is not a number. /// public static Rational Indeterminate = new Rational(0, 0); /// /// Represents a rational object that is equal to 0. /// public static Rational Zero = new Rational(0, 1); /// /// Represents a rational object that is equal to 1. /// public static Rational One = new Rational(1, 1); /// /// Represents a Rational object that is equal to negative infinity (-1, 0). /// public static readonly Rational NegativeInfinity = new Rational(-1, 0); /// /// Represents a Rational object that is equal to positive infinity (+1, 0). /// public static readonly Rational PositiveInfinity = new Rational(1, 0); /// /// Initializes a new instance of the struct. /// /// /// The number above the line in a vulgar fraction showing how many of the parts /// indicated by the denominator are taken. /// /// /// The number below the line in a vulgar fraction; a divisor. /// public Rational(uint numerator, uint denominator) : this() { this.Numerator = numerator; this.Denominator = denominator; this.Simplify(); } /// /// Initializes a new instance of the struct. /// /// /// The number above the line in a vulgar fraction showing how many of the parts /// indicated by the denominator are taken. /// /// /// The number below the line in a vulgar fraction; a divisor. /// public Rational(int numerator, int denominator) : this() { this.Numerator = numerator; this.Denominator = denominator; this.Simplify(); } /// /// Initializes a new instance of the struct. /// /// /// The number above the line in a vulgar fraction showing how many of the parts /// indicated by the denominator are taken. /// /// /// The number below the line in a vulgar fraction; a divisor. /// public Rational(BigInteger numerator, BigInteger denominator) : this() { this.Numerator = numerator; this.Denominator = denominator; this.Simplify(); } /// /// Initializes a new instance of the struct. /// /// The big integer to create the rational from. public Rational(BigInteger value) : this() { this.Numerator = value; this.Denominator = BigInteger.One; this.Simplify(); } /// /// Initializes a new instance of the struct. /// /// The double to create the rational from. public Rational(double value) : this() { if (double.IsNaN(value)) { this = Indeterminate; return; } else if (double.IsPositiveInfinity(value)) { this = PositiveInfinity; return; } else if (double.IsNegativeInfinity(value)) { this = NegativeInfinity; return; } this = Parse(value.ToString("R", CultureInfo.InvariantCulture)); } /// /// Gets the numerator of a number. /// public BigInteger Numerator { get; private set; } /// /// Gets the denominator of a number. /// public BigInteger Denominator { get; private set; } /// /// Gets a value indicating whether this instance is indeterminate. /// public bool IsIndeterminate => (this.Equals(Indeterminate)); /// /// Gets a value indicating whether this instance is an integer. /// public bool IsInteger => (this.Denominator == 1); /// /// Gets a value indicating whether this instance is equal to 0 /// public bool IsZero => (this.Equals(Zero)); /// /// Gets a value indicating whether this instance is equal to 1. /// public bool IsOne => (this.Equals(One)); /// /// Gets a value indicating whether this instance is equal to negative infinity (-1, 0). /// public bool IsNegativeInfinity => (this.Equals(NegativeInfinity)); /// /// Gets a value indicating whether this instance is equal to positive infinity (1, 0). /// public bool IsPositiveInfinity => (this.Equals(PositiveInfinity)); /// /// Converts a rational number to the nearest double. /// /// /// The . /// public double ToDouble() { if (this.IsIndeterminate) { return double.NaN; } if (this.IsPositiveInfinity) { return double.PositiveInfinity; } if (this.IsNegativeInfinity) { return double.NegativeInfinity; } if (this.IsInteger) { return (double)this.Numerator; } return (double)(this.Numerator / this.Denominator); } /// public override bool Equals(object obj) { if (obj is Rational) { return this.Equals((Rational)obj); } return false; } /// public bool Equals(Rational other) { if (this.Denominator == other.Denominator) { return this.Numerator == other.Numerator; } else if (this.Numerator == BigInteger.Zero && this.Denominator == BigInteger.Zero) { return other.Numerator == BigInteger.Zero && other.Denominator == BigInteger.Zero; } else if (other.Numerator == BigInteger.Zero && other.Denominator == BigInteger.Zero) { return this.Numerator == BigInteger.Zero && this.Denominator == BigInteger.Zero; } else { return (this.Numerator * other.Denominator) == (this.Denominator * other.Numerator); } } /// public override int GetHashCode() { unchecked { return ((int)this.Numerator * 397) ^ (int)this.Denominator; } } /// public override string ToString() { return this.ToString(CultureInfo.InvariantCulture); } /// /// Converts the numeric value of this instance to its equivalent string representation using /// the specified culture-specific format information. /// /// /// An object that supplies culture-specific formatting information. /// /// public string ToString(IFormatProvider provider) { if (this.IsIndeterminate) { return "[ Indeterminate ]"; } if (this.IsPositiveInfinity) { return "[ PositiveInfinity ]"; } if (this.IsNegativeInfinity) { return "[ NegativeInfinity ]"; } if (this.IsInteger) { return this.Numerator.ToString(provider); } StringBuilder sb = new StringBuilder(); sb.Append(this.Numerator.ToString("R", provider)); sb.Append("/"); sb.Append(this.Denominator.ToString("R", provider)); return sb.ToString(); } /// /// Simplifies the rational. /// private void Simplify() { if (this.IsIndeterminate) { return; } if (this.IsNegativeInfinity) { return; } if (this.IsPositiveInfinity) { return; } if (this.IsInteger) { return; } if (this.Numerator == BigInteger.Zero) { Denominator = BigInteger.One; return; } if (this.Numerator == this.Denominator) { this.Numerator = BigInteger.One; this.Denominator = BigInteger.One; return; } BigInteger gcd = BigInteger.GreatestCommonDivisor(this.Numerator, this.Denominator); if (gcd > BigInteger.One) { this.Numerator = this.Numerator / gcd; this.Denominator = this.Denominator / gcd; } } /// /// Converts the string representation of a number into its rational value /// /// A string that contains a number to convert. /// The internal static Rational Parse(string value) { int periodIndex = value.IndexOf("."); int eIndeix = value.IndexOf("E"); int slashIndex = value.IndexOf("/"); // An integer such as 7 if (periodIndex == -1 && eIndeix == -1 && slashIndex == -1) { return new Rational(BigInteger.Parse(value)); } // A fraction such as 3/7 if (periodIndex == -1 && eIndeix == -1 && slashIndex != -1) { return new Rational(BigInteger.Parse(value.Substring(0, slashIndex)), BigInteger.Parse(value.Substring(slashIndex + 1))); } // No scientific Notation such as 5.997 if (eIndeix == -1) { BigInteger n = BigInteger.Parse(value.Replace(".", "")); BigInteger d = (BigInteger)Math.Pow(10, value.Length - periodIndex - 1); return new Rational(n, d); } // Scientific notation such as 2.4556E-2 int characteristic = int.Parse(value.Substring(eIndeix + 1)); BigInteger ten = 10; BigInteger numerator = BigInteger.Parse(value.Substring(0, eIndeix).Replace(".", "")); BigInteger denominator = new BigInteger(Math.Pow(10, eIndeix - periodIndex - 1)); BigInteger charPower = BigInteger.Pow(ten, Math.Abs(characteristic)); if (characteristic > 0) { numerator = numerator * charPower; } else { denominator = denominator * charPower; } return new Rational(numerator, denominator); } } }