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