///////////////////////////////////////////////////////////////////// // = NMatrix // // A linear algebra library for scientific computation in Ruby. // NMatrix is part of SciRuby. // // NMatrix was originally inspired by and derived from NArray, by // Masahiro Tanaka: http://narray.rubyforge.org // // == Copyright Information // // SciRuby is Copyright (c) 2010 - 2014, Ruby Science Foundation // NMatrix is Copyright (c) 2012 - 2014, John Woods and the Ruby Science Foundation // // Please see LICENSE.txt for additional copyright notices. // // == Contributing // // By contributing source code to SciRuby, you agree to be bound by // our Contributor Agreement: // // * https://github.com/SciRuby/sciruby/wiki/Contributor-Agreement // // == rational.h // // Functions and classes for dealing with rational numbers. #ifndef RATIONAL_H #define RATIONAL_H /* * Standard Includes */ #include #include #include /* * Project Includes */ #include "types.h" #include "util/util.h" /* * Macros */ /* * Types */ namespace nm { template class Rational; typedef Rational Rational32; typedef Rational Rational64; typedef Rational Rational128; /* * Data */ /* * Classes and Functions */ template class Rational { public: // The numerator and denominator of the rational number. Type n; Type d; /* * Default constructor. */ inline Rational(Type num = 0, Type den = 1) : n(num), d(den) {} /* * Copy constructors. */ template inline Rational(const Rational& other) : n(other.n), d(other.d) {} template ::value>::type> inline Rational(const Complex& other) : n(0), d(1) { rb_raise(rb_eNotImpError, "cannot convert from complex to rational"); } Rational(const RubyObject& other); /* * Rational inverse function -- creates a copy, but inverted. */ inline Rational inverse() const { return Rational(this->d, this->n); } /* * Binary operator definitions for varous types. */ ////////////////////////////////// // Rational-Rational Operations // ////////////////////////////////// template inline Rational operator+(const Rational& other) const { Rational result((this->n * other.d) + (other.n * this->d), this->d * other.d); long simplify = gcf(result.n, result.d); result.n /= simplify; result.d /= simplify; return result; } template inline Rational& operator+=(const Rational& other) { this->n = (this->n * other.d) + (other.n * this->d); this->d = this->d * other.d; long simplify = gcf(this->n, this->d); this->n /= simplify; this->d /= simplify; return *this; } template inline Rational operator-(const Rational& other) const { Rational result((this->n * other.d) - (other.n * this->d), this->d * other.d); long simplify = gcf(result.n, result.d); result.n /= simplify; result.d /= simplify; return result; } template inline Rational& operator-=(const Rational& other) { this->n = (this->n * other.d) - (other.n * this->d); this->d = this->d * other.d; long simplify = gcf(this->n, this->d); this->n /= simplify; this->d /= simplify; return *this; } template inline Rational operator*(const Rational& other) const { int g1 = gcf(this->n, other.d); int g2 = gcf(this->d, other.n); return Rational((this->n / g1) * (other.n / g2), (this->d / g2) * (other.d / g1)); } template inline Rational& operator*=(const Rational& other) { int g1 = gcf(this->n, other.d); int g2 = gcf(this->d, other.n); this->n = (this->n / g1) * (other.n / g2); this->d = (this->d / g2) * (other.d / g1); return *this; } template inline Rational operator/(const Rational& other) const { return *this * Rational(other.d, other.n); } template inline Rational operator/=(const Rational& other) { *this *= Rational(other.d, other.n); return *this; } template inline Rational operator%(const Rational& other) const { long floor_div = (this->n * other.n) / (this->d * other.d); Rational prod = other * Rational(floor_div, 1); return Rational(this->n, other.n) - prod; } template inline bool operator<(const Rational& other) const { return (this->n * other.d) < (other.n * this->d); } template inline bool operator>(const Rational& other) const { return (this->n * other.d) > (other.n * this->d); } template inline bool operator==(const Rational& other) const { return (this->n == other.n) && (this->d == other.d); } template inline bool operator!=(const Rational& other) const { return !(*this == other); } template inline bool operator<=(const Rational& other) const { return (*this < other) || (*this == other); } template inline bool operator>=(const Rational& other) const { return (*this > other) || (*this == other); } template inline operator Rational () const { return Rational((OtherType)this->n, (OtherType)this->d); } //////////////////////////////// // Rational-Native Operations // //////////////////////////////// template ::value>::type> inline Rational operator+(const IntType& other) const { return *this + Rational(other); } template ::value>::type> inline Rational operator-(const IntType& other) const { return *this - Rational(other); } template ::value>::type> inline Rational operator*(const IntType& other) const { return *this * Rational(other); } template ::value>::type> inline Rational operator/(const IntType& other) const { return *this / Rational(other); } template ::value>::type> inline Rational operator%(const IntType& other) const { return *this % Rational(other); } template ::value>::type> inline bool operator<(const IntType& other) const { return *this < Rational(other); } template ::value>::type> inline bool operator>(const IntType& other) const { return *this > Rational(other); } template ::value>::type> inline bool operator==(const IntType& other) const { return *this == Rational(other); } template ::value>::type> inline bool operator!=(const IntType& other) const { return *this != Rational(other); } template ::value>::type> inline bool operator<=(const IntType& other) const { return *this <= Rational(other); } template ::value>::type> inline bool operator>=(const IntType& other) const { return *this >= Rational(other); } template ::value>::type> inline operator NumType () const { return (NumType)this->n / (NumType)this->d; } /* * Special casting operator for Complex numbers. */ template ::value>::type> inline operator Rational () const { return Rational(((FloatType)this->n) / ((FloatType)this->d)); } }; // Negative operator template ::value>::type> inline Rational operator-(const Rational& rhs) { return Rational(-rhs.n, rhs.d); } //////////////////////////////// // Native-Rational Operations // //////////////////////////////// /* * Integer Math */ template ::value>::type> inline Rational operator+(const IntType& left, const Rational& right) { return Rational(left) + right; } template ::value>::type> inline Rational operator-(const IntType& left, const Rational& right) { return Rational(left) - right; } template ::value>::type> inline Rational operator*(const IntType& left, const Rational& right) { return Rational(left) * right; } template ::value>::type> inline Rational operator/(const IntType& left, const Rational& right) { return Rational(left) / right; } /* * Floating Point Math */ template ::value>::type> inline FloatType operator+(const FloatType& left, const Rational& right) { return left + (FloatType)right; } template ::value>::type> inline FloatType operator-(const FloatType& left, const Rational& right) { return left - (FloatType)right; } template ::value>::type> inline FloatType operator*(const FloatType& left, const Rational& right) { return left * (FloatType)right; } template ::value>::type> inline FloatType operator/(const FloatType& left, const Rational& right) { return left / (FloatType)right; } /* * Comparisons */ template ::value>::type> inline bool operator<(const NativeType left, const Rational& right) { //return Rational(left) < right; return (left * right.d) < right.n; } template ::value>::type> inline bool operator>(const NativeType left, const Rational& right) { //return Rational(left) > right; return (left * right.d) > right.n; } template inline bool operator==(const typename std::enable_if::value, IntType>::type left, const Rational& right) { //return Rational(left) == right; return (left * right.d) == right.n; } template inline bool operator==(const typename std::enable_if::value, FloatType>::type left, const Rational& right) { //return Rational(left) == right; return FP_EQUAL(left, ((FloatType)right)); } template ::value>::type> inline bool operator!=(const NativeType left, const Rational& right) { //return Rational(left) != right; return !(left == right); } template ::value>::type> inline bool operator<=(const NativeType left, const Rational& right) { //return Rational(left) <= right; return (left < right) or (left == right); } template ::value>::type> inline bool operator>=(const NativeType left, const Rational& right) { //return Rational(left) >= right; return (left > right) or (left == right); } template inline std::ostream& operator<<(std::ostream& out, const Rational& rhs) { out << rhs.n << "/" << rhs.d << std::flush; return out; } } // end of namespace nm namespace std { template ::value>::type> nm::Rational abs(const nm::Rational& value) { if (value.n >= 0) return value; return nm::Rational(-value.n, value.d); } template ::value>::type> nm::Rational sqrt(const nm::Rational& value) { nm::Rational result(std::sqrt(value.n), std::sqrt(value.d)); if (value * value == result) return result; else rb_raise(rb_eArgError, "square root of the given rational is not rational"); } } #endif // RATIONAL_H