//
// 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();
}
}
}