#ifndef SPROUT_RATIONAL_RATIONAL_HPP #define SPROUT_RATIONAL_RATIONAL_HPP #include #include #include #include #include #include #include namespace sprout { template class rational; namespace detail { struct rational_private_constructor_tag {}; template inline SPROUT_CONSTEXPR sprout::rational make_rational( typename sprout::detail::call_traits::param_type n, typename sprout::detail::call_traits::param_type d, sprout::detail::rational_private_constructor_tag ); } // namespace detail namespace detail { template class rational_impl { protected: typedef IntType int_type; typedef typename sprout::detail::call_traits::param_type param_type; protected: IntType num_; IntType den_; protected: SPROUT_CONSTEXPR rational_impl() : num_(0) , den_(1) {} rational_impl(rational_impl const&) = default; SPROUT_CONSTEXPR rational_impl(param_type n) : num_(n) , den_(1) {} SPROUT_CONSTEXPR rational_impl(param_type n, param_type d) : num_(n) , den_(d) {} SPROUT_CONSTEXPR rational_impl(param_type n, param_type d, param_type g) : num_(n / g) , den_(d / g) {} }; } // namespace detail // // rational // template class rational : private sprout::detail::rational_impl { static_assert(std::numeric_limits::is_specialized, "std::numeric_limits::is_specialized"); public: typedef IntType int_type; typedef typename sprout::detail::call_traits::param_type param_type; private: struct private_constructor_tag {}; typedef sprout::detail::rational_impl base_type; private: static SPROUT_CONSTEXPR IntType normalize_g_1(IntType den, IntType g) { return den / g < 0 ? -g : g; } static SPROUT_CONSTEXPR IntType normalize_g(IntType num, IntType den) { return den == 0 ? throw sprout::bad_rational() : num == 0 ? den : normalize_g_1(den, sprout::math::gcd(num, den)) ; } private: using base_type::num_; using base_type::den_; private: SPROUT_CONSTEXPR rational(param_type n, param_type d, private_constructor_tag) : base_type(n, d) {} public: SPROUT_CONSTEXPR rational() : base_type() {} rational(rational const&) = default; SPROUT_CONSTEXPR rational(param_type n) : base_type(n) {} SPROUT_CONSTEXPR rational(param_type n, param_type d) : base_type(n, d, normalize_g(n, d)) {} rational& operator=(rational const&) = default; rational& operator=(param_type n) { return assign(n, 1); } rational& assign(param_type n, param_type d) { rational temp(n, d); sprout::swap(temp, *this); return *this; } SPROUT_CONSTEXPR IntType numerator() const { return num_; } SPROUT_CONSTEXPR IntType denominator() const { return den_; } rational& operator+=(rational const& rhs) { IntType g = sprout::math::gcd(den_, rhs.den_); den_ /= g; num_ = num_ * (rhs.den_ / g) + rhs.num_ * den_; g = sprout::math::gcd(num_, g); num_ /= g; den_ *= rhs.den_ / g; return *this; } rational& operator-=(rational const& rhs) { IntType g = sprout::math::gcd(den_, rhs.den_); den_ /= g; num_ = num_ * (rhs.den_ / g) - rhs.num_ * den_; g = sprout::math::gcd(num_, g); num_ /= g; den_ *= rhs.den_ / g; return *this; } rational& operator*=(rational const& rhs) { IntType gcd1 = sprout::math::gcd(num_, rhs.den_); IntType gcd2 = sprout::math::gcd(rhs.num_, den_); num_ =(num_ / gcd1) * (rhs.num_ / gcd2); den_ =(den_ / gcd2) * (rhs.den_ / gcd1); return *this; } rational& operator/=(rational const& rhs) { if (rhs.num_ == IntType(0)) { throw bad_rational(); } if (num_ == IntType(0)) { return *this; } IntType gcd1 = sprout::math::gcd(num_, rhs.num_); IntType gcd2 = sprout::math::gcd(rhs.den_, den_); num_ =(num_ / gcd1) * (rhs.den_ / gcd2); den_ =(den_ / gcd2) * (rhs.num_ / gcd1); if (den_ < IntType(0)) { num_ = -num_; den_ = -den_; } return *this; } rational& operator+=(param_type rhs) { return *this += rational(rhs); } rational& operator-=(param_type rhs) { return *this -= rational(rhs); } rational& operator*=(param_type rhs) { return *this *= rational(rhs); } rational& operator/=(param_type rhs) { return *this /= rational(rhs); } rational& operator++() { num_ += den_; return *this; } rational& operator--() { num_ -= den_; return *this; } rational operator++(int) { rational result(*this); ++*this; return result; } rational operator--(int) { rational result(*this); --*this; return result; } SPROUT_CONSTEXPR bool operator!() const { return !num_; } SPROUT_CONSTEXPR operator bool() const { return num_ != 0; } public: friend sprout::rational sprout::detail::make_rational( typename sprout::detail::call_traits::param_type n, typename sprout::detail::call_traits::param_type d, sprout::detail::rational_private_constructor_tag ); }; namespace detail { template inline SPROUT_CONSTEXPR sprout::rational make_rational( typename sprout::detail::call_traits::param_type n, typename sprout::detail::call_traits::param_type d, sprout::detail::rational_private_constructor_tag ) { return sprout::rational( n, d, typename sprout::rational::private_constructor_tag() ); } } // namespace detail } // namespace sprout #endif // SPROUT_RATIONAL_RATIONAL_HPP