// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // namespace ImageProcessorCore { using System; using System.Globalization; using System.Text; /// /// Represents a number that can be expressed as a fraction /// /// /// This is a very simplified implementation of a rational number designed for use with metadata only. /// internal struct LongRational : IEquatable { /// /// 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 LongRational(long numerator, long denominator) : this(numerator, denominator, false) { } /// /// 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. /// /// /// Whether to attempt to simplify the fractional parts. /// public LongRational(long numerator, long denominator, bool simplify) : this() { this.Numerator = numerator; this.Denominator = denominator; if (simplify) { this.Simplify(); } } /// /// Initializes a new instance of the struct. /// /// The to create the instance from. /// Whether to use the best possible precision when parsing the value. public LongRational(double value, bool bestPrecision) : this() { if (double.IsNaN(value)) { this.Numerator = this.Denominator = 0; return; } if (double.IsPositiveInfinity(value)) { this.Numerator = 1; this.Denominator = 0; return; } if (double.IsNegativeInfinity(value)) { this.Numerator = -1; this.Denominator = 0; return; } this.Numerator = 1; this.Denominator = 1; double val = Math.Abs(value); double df = this.Numerator / (double)this.Denominator; double epsilon = bestPrecision ? double.Epsilon : .000001; while (Math.Abs(df - val) > epsilon) { if (df < val) { this.Numerator++; } else { this.Denominator++; this.Numerator = (int)(val * this.Denominator); } df = this.Numerator / (double)this.Denominator; } if (value < 0.0) { this.Numerator *= -1; } this.Simplify(); } /// /// Gets the numerator of a number. /// public long Numerator { get; private set; } /// /// Gets the denominator of a number. /// public long Denominator { get; private set; } /// /// Gets a value indicating whether this instance is indeterminate. /// public bool IsIndeterminate { get { if (this.Denominator != 0) { return false; } return this.Numerator == 0; } } /// /// Gets a value indicating whether this instance is an integer (n, 1) /// public bool IsInteger => this.Denominator == 1; /// /// Gets a value indicating whether this instance is equal to negative infinity (-1, 0) /// public bool IsNegativeInfinity { get { if (this.Denominator != 0) { return false; } return this.Numerator == -1; } } /// /// Gets a value indicating whether this instance is equal to positive infinity (1, 0) /// public bool IsPositiveInfinity { get { if (this.Denominator != 0) { return false; } return this.Numerator == 1; } } /// /// Gets a value indicating whether this instance is equal to 0 (0, 1) /// public bool IsZero { get { if (this.Denominator != 1) { return false; } return this.Numerator == 0; } } /// public bool Equals(LongRational other) { if (this.Denominator == other.Denominator) { return this.Numerator == other.Numerator; } if (this.Numerator == 0 && this.Denominator == 0) { return other.Numerator == 0 && other.Denominator == 0; } if (other.Numerator == 0 && other.Denominator == 0) { return this.Numerator == 0 && this.Denominator == 0; } return (this.Numerator * other.Denominator) == (this.Denominator * other.Numerator); } /// public override int GetHashCode() { return this.GetHashCode(this); } /// 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. /// /// The public string ToString(IFormatProvider provider) { if (this.IsIndeterminate) { return "[ Indeterminate ]"; } if (this.IsPositiveInfinity) { return "[ PositiveInfinity ]"; } if (this.IsNegativeInfinity) { return "[ NegativeInfinity ]"; } if (this.IsZero) { return "0"; } 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(); } /// /// Finds the greatest common divisor of two values. /// /// The first value /// The second value /// The private static long GreatestCommonDivisor(long left, long right) { return right == 0 ? left : GreatestCommonDivisor(right, left % right); } /// /// Simplifies the /// private void Simplify() { if (this.IsIndeterminate) { return; } if (this.IsNegativeInfinity) { return; } if (this.IsPositiveInfinity) { return; } if (this.IsInteger) { return; } if (this.IsZero) { return; } if (this.Numerator == 0) { this.Denominator = 0; return; } if (this.Numerator == this.Denominator) { this.Numerator = 1; this.Denominator = 1; } long gcd = GreatestCommonDivisor(Math.Abs(this.Numerator), Math.Abs(this.Denominator)); if (gcd > 1) { this.Numerator = this.Numerator / gcd; this.Denominator = this.Denominator / gcd; } } /// /// Returns the hash code for this instance. /// /// /// The instance of to return the hash code for. /// /// /// A 32-bit signed integer that is the hash code for this instance. /// private int GetHashCode(LongRational rational) { return ((rational.Numerator * 397) ^ rational.Denominator).GetHashCode(); } } }