| /** @file |
| |
| Scaled integral values. |
| |
| In many situations it is desirable to define scaling factors or base units (a "metric"). This template |
| enables this to be done in a type and scaling safe manner where the defined factors carry their scaling |
| information as part of the type. |
| |
| @section license License |
| |
| Licensed to the Apache Software Foundation (ASF) under one |
| or more contributor license agreements. See the NOTICE file |
| distributed with this work for additional information |
| regarding copyright ownership. The ASF licenses this file |
| to you under the Apache License, Version 2.0 (the |
| "License"); you may not use this file except in compliance |
| with the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| */ |
| |
| #pragma once |
| |
| #include <cstdint> |
| #include <ratio> |
| #include <ostream> |
| #include <type_traits> |
| #include "tscore/BufferWriter.h" |
| |
| namespace tag |
| { |
| struct generic; |
| } |
| |
| namespace ts |
| { |
| template <intmax_t N, typename C, typename T> class Scalar; |
| |
| namespace detail |
| { |
| // @internal - although these conversion methods look bulky, in practice they compile down to |
| // very small amounts of code due to the conditions being compile time constant - the non-taken |
| // clauses are dead code and eliminated by the compiler. |
| |
| // The general case where neither N nor S are a multiple of the other seems a bit long but this |
| // minimizes the risk of integer overflow. I need to validate that under -O2 the compiler will |
| // only do 1 division to get both the quotient and remainder for (n/N) and (n%N). In cases where |
| // N,S are powers of 2 I have verified recent GNU compilers will optimize to bit operations. |
| |
| /// Convert a count @a c that is scale @s S to the corresponding count for scale @c N |
| template <intmax_t N, intmax_t S, typename C> |
| C |
| scale_conversion_round_up(C c) |
| { |
| typedef std::ratio<N, S> R; |
| if (N == S) { |
| return c; |
| } else if (R::den == 1) { |
| return c / R::num + (0 != c % R::num); // N is a multiple of S. |
| } else if (R::num == 1) { |
| return c * R::den; // S is a multiple of N. |
| } else { |
| return (c / R::num) * R::den + ((c % R::num) * R::den) / R::num + (0 != (c % R::num)); |
| } |
| } |
| |
| /// Convert a count @a c that is scale @s S to scale @c N |
| template <intmax_t N, intmax_t S, typename C> |
| C |
| scale_conversion_round_down(C c) |
| { |
| typedef std::ratio<N, S> R; |
| if (N == S) { |
| return c; |
| } else if (R::den == 1) { |
| return c / R::num; // N = k S |
| } else if (R::num == 1) { |
| return c * R::den; // S = k N |
| } else { |
| return (c / R::num) * R::den + ((c % R::num) * R::den) / R::num; |
| } |
| } |
| |
| /* Helper classes for @c Scalar |
| |
| These wrap values to capture extra information for @c Scalar methods. This includes whether to |
| round up or down when converting and, when the wrapped data is also a @c Scalar, the scale. |
| |
| These are not intended for direct use but by the @c round_up and @c round_down free functions |
| which capture the information about the argument and construct an instance of one of these |
| classes to pass it on to a @c Scalar method. |
| |
| Scale conversions between @c Scalar instances are handled in these classes via the templated |
| methods @c scale_conversion_round_up and @c scale_conversion_round_down. |
| |
| Conversions between scales and types for the scalar helpers is done inside the helper classes |
| and a user type conversion operator exists so the helper can be converted by the compiler to |
| the correct type. For the units bases conversion this is done in @c Scalar because the |
| generality of the needed conversion is too broad to be easily used. It can be done but there is |
| some ugliness due to the fact that in some cases two user conversions which is difficult to |
| deal with. I have tried it both ways and overall this seems a cleaner implementation. |
| |
| Much of this is driven by the fact that the assignment operator, in some cases, can not be |
| templated and therefore to have a nice interface for assignment this split is needed. |
| |
| Note - the key point is the actual conversion is not done when the wrapper instance is created |
| but when the wrapper instance is assigned. That is what enables the conversion to be done in |
| the context of the destination, which is not otherwise possible. |
| */ |
| |
| // Unit value, to be rounded up. |
| template <typename C> struct scalar_unit_round_up_t { |
| C _n; |
| template <intmax_t N, typename I> |
| constexpr I |
| scale() const |
| { |
| return static_cast<I>(_n / N + (0 != (_n % N))); |
| } |
| }; |
| // Unit value, to be rounded down. |
| template <typename C> struct scalar_unit_round_down_t { |
| C _n; |
| // template <typename I> operator scalar_unit_round_down_t<I>() { return {static_cast<I>(_n)}; } |
| template <intmax_t N, typename I> |
| constexpr I |
| scale() const |
| { |
| return static_cast<I>(_n / N); |
| } |
| }; |
| // Scalar value, to be rounded up. |
| template <intmax_t N, typename C, typename T> struct scalar_round_up_t { |
| C _n; |
| template <intmax_t S, typename I> constexpr operator Scalar<S, I, T>() const |
| { |
| return Scalar<S, I, T>(scale_conversion_round_up<S, N>(_n)); |
| } |
| }; |
| // Scalar value, to be rounded down. |
| template <intmax_t N, typename C, typename T> struct scalar_round_down_t { |
| C _n; |
| template <intmax_t S, typename I> constexpr operator Scalar<S, I, T>() const |
| { |
| return Scalar<S, I, T>(scale_conversion_round_down<S, N>(_n)); |
| } |
| }; |
| } // namespace detail |
| |
| /// Mark a unit value to be scaled, rounding down. |
| template <typename C> |
| constexpr detail::scalar_unit_round_up_t<C> |
| round_up(C n) |
| { |
| return {n}; |
| } |
| |
| /// Mark a @c Scalar value to be scaled, rounding up. |
| template <intmax_t N, typename C, typename T> |
| constexpr detail::scalar_round_up_t<N, C, T> |
| round_up(Scalar<N, C, T> v) |
| { |
| return {v.count()}; |
| } |
| |
| /// Mark a unit value to be scaled, rounding down. |
| template <typename C> |
| constexpr detail::scalar_unit_round_down_t<C> |
| round_down(C n) |
| { |
| return {n}; |
| } |
| |
| /// Mark a @c Scalar value, to be rounded down. |
| template <intmax_t N, typename C, typename T> |
| constexpr detail::scalar_round_down_t<N, C, T> |
| round_down(Scalar<N, C, T> v) |
| { |
| return {v.count()}; |
| } |
| |
| /** A class to hold scaled values. |
| |
| Instances of this class have a @a count and a @a scale. The "value" of the instance is @a |
| count * @a scale. The scale is stored in the compiler in the class symbol table and so only |
| the count is a run time value. An instance with a large scale can be assign to an instance |
| with a smaller scale and the conversion is done automatically. Conversions from a smaller to |
| larger scale must be explicit using @c round_up and @c round_down. This prevents |
| inadvertent changes in value. Because the scales are not the same these conversions can be |
| lossy and the two conversions determine whether, in such a case, the result should be rounded |
| up or down to the nearest scale value. |
| |
| @a N sets the scale. @a C is the type used to hold the count, which is in units of @a N. |
| |
| @a T is a "tag" type which is used only to distinguish the base metric for the scale. Scalar |
| types that have different tags are not interoperable although they can be converted manually by |
| converting to units and then explicitly constructing a new Scalar instance. This is by |
| design. This can be ignored - if not specified then it defaults to a "generic" tag. The type can |
| be (and usually is) defined in name only). |
| |
| @note This is modeled somewhat on @c std::chrono and serves a similar function for different |
| and simpler cases (where the ratio is always an integer, never a fraction). |
| |
| @see round_up |
| @see round_down |
| */ |
| template <intmax_t N, typename C = int, typename T = tag::generic> class Scalar |
| { |
| typedef Scalar self; ///< Self reference type. |
| |
| public: |
| /// Scaling factor - make it external accessible. |
| constexpr static intmax_t SCALE = N; |
| typedef C Counter; ///< Type used to hold the count. |
| typedef T Tag; ///< Make tag accessible. |
| |
| static_assert(N > 0, "The scaling factor (1st template argument) must be a positive integer"); |
| static_assert(std::is_integral<C>::value, "The counter type (2nd template argument) must be an integral type"); |
| |
| constexpr Scalar(); ///< Default constructor. |
| ///< Construct to have @a n scaled units. |
| explicit constexpr Scalar(Counter n); |
| /// Copy constructor. |
| constexpr Scalar(self const &that); /// Copy constructor. |
| /// Copy constructor for same scale. |
| template <typename I> constexpr Scalar(Scalar<N, I, T> const &that); |
| /// Direct conversion constructor. |
| /// @note Requires that @c S be an integer multiple of @c SCALE. |
| template <intmax_t S, typename I> constexpr Scalar(Scalar<S, I, T> const &that); |
| /// Conversion constructor. |
| constexpr Scalar(detail::scalar_round_up_t<N, C, T> const &that); |
| /// Conversion constructor. |
| constexpr Scalar(detail::scalar_round_down_t<N, C, T> const &that); |
| /// Conversion constructor. |
| template <typename I> constexpr Scalar(detail::scalar_unit_round_up_t<I> v); |
| /// Conversion constructor. |
| template <typename I> constexpr Scalar(detail::scalar_unit_round_down_t<I> v); |
| |
| /// Assignment operator. |
| /// The value @a that is scaled appropriately. |
| /// @note Requires the scale of @a that be an integer multiple of the scale of @a this. If this isn't the case then |
| /// the @c round_up or @c round_down must be used to indicate the rounding direction. |
| template <intmax_t S, typename I> self &operator=(Scalar<S, I, T> const &that); |
| /// Assignment from same scale. |
| self &operator=(self const &that); |
| // Conversion assignments. |
| template <typename I> self &operator=(detail::scalar_unit_round_up_t<I> n); |
| template <typename I> self &operator=(detail::scalar_unit_round_down_t<I> n); |
| self &operator =(detail::scalar_round_up_t<N, C, T> v); |
| self &operator =(detail::scalar_round_down_t<N, C, T> v); |
| |
| /// Direct assignment. |
| /// The count is set to @a n. |
| self &assign(Counter n); |
| /// The value @a that is scaled appropriately. |
| /// @note Requires the scale of @a that be an integer multiple of the scale of @a this. If this isn't the case then |
| /// the @c round_up or @c round_down must be used to indicate the rounding direction. |
| template <intmax_t S, typename I> self &assign(Scalar<S, I, T> const &that); |
| // Conversion assignments. |
| template <typename I> self &assign(detail::scalar_unit_round_up_t<I> n); |
| template <typename I> self &assign(detail::scalar_unit_round_down_t<I> n); |
| self &assign(detail::scalar_round_up_t<N, C, T> v); |
| self &assign(detail::scalar_round_down_t<N, C, T> v); |
| |
| /// The number of scale units. |
| constexpr Counter count() const; |
| /// The scaled value. |
| constexpr intmax_t value() const; |
| /// User conversion to scaled value. |
| constexpr operator intmax_t() const; |
| |
| /// Addition operator. |
| /// The value is scaled from @a that to @a this. |
| /// @note Requires the scale of @a that be an integer multiple of the scale of @a this. If this isn't the case then |
| /// the @c scale_up or @c scale_down casts must be used to indicate the rounding direction. |
| self &operator+=(self const &that); |
| template <intmax_t S, typename I> self &operator+=(Scalar<S, I, T> const &that); |
| template <typename I> self &operator+=(detail::scalar_unit_round_up_t<I> n); |
| template <typename I> self &operator+=(detail::scalar_unit_round_down_t<I> n); |
| self &operator+=(detail::scalar_round_up_t<N, C, T> v); |
| self &operator+=(detail::scalar_round_down_t<N, C, T> v); |
| |
| /// Increment - increase count by 1. |
| self &operator++(); |
| /// Increment - increase count by 1. |
| self operator++(int); |
| /// Decrement - decrease count by 1. |
| self &operator--(); |
| /// Decrement - decrease count by 1. |
| self operator--(int); |
| /// Increment by @a n. |
| self &inc(Counter n); |
| /// Decrement by @a n. |
| self &dec(Counter n); |
| |
| /// Subtraction operator. |
| /// The value is scaled from @a that to @a this. |
| /// @note Requires the scale of @a that be an integer multiple of the scale of @a this. If this isn't the case then |
| /// the @c scale_up or @c scale_down casts must be used to indicate the rounding direction. |
| self &operator-=(self const &that); |
| template <intmax_t S, typename I> self &operator-=(Scalar<S, I, T> const &that); |
| template <typename I> self &operator-=(detail::scalar_unit_round_up_t<I> n); |
| template <typename I> self &operator-=(detail::scalar_unit_round_down_t<I> n); |
| self &operator-=(detail::scalar_round_up_t<N, C, T> v); |
| self &operator-=(detail::scalar_round_down_t<N, C, T> v); |
| |
| /// Multiplication - multiple the count by @a n. |
| self &operator*=(C n); |
| |
| /// Division - divide (rounding down) the count by @a n. |
| self &operator/=(C n); |
| |
| /// Utility overload of the function operator to create instances at the same scale. |
| self operator()(Counter n) const; |
| |
| /// Return a value at the same scale with a count increased by @a n. |
| self plus(Counter n) const; |
| |
| /// Return a value at the same scale with a count decreased by @a n. |
| self minus(Counter n) const; |
| |
| /// Run time access to the scale (template arg @a N). |
| static constexpr intmax_t scale(); |
| |
| protected: |
| Counter _n; ///< Number of scale units. |
| }; |
| |
| template <intmax_t N, typename C, typename T> constexpr Scalar<N, C, T>::Scalar() : _n() {} |
| template <intmax_t N, typename C, typename T> constexpr Scalar<N, C, T>::Scalar(Counter n) : _n(n) {} |
| template <intmax_t N, typename C, typename T> constexpr Scalar<N, C, T>::Scalar(self const &that) : _n(that._n) {} |
| template <intmax_t N, typename C, typename T> |
| template <typename I> |
| constexpr Scalar<N, C, T>::Scalar(Scalar<N, I, T> const &that) : _n(static_cast<C>(that.count())) |
| { |
| } |
| template <intmax_t N, typename C, typename T> |
| template <intmax_t S, typename I> |
| constexpr Scalar<N, C, T>::Scalar(Scalar<S, I, T> const &that) : _n(std::ratio<S, N>::num * that.count()) |
| { |
| static_assert(std::ratio<S, N>::den == 1, |
| "Construction not permitted - target scale is not an integral multiple of source scale."); |
| } |
| template <intmax_t N, typename C, typename T> |
| constexpr Scalar<N, C, T>::Scalar(detail::scalar_round_up_t<N, C, T> const &v) : _n(v._n) |
| { |
| } |
| template <intmax_t N, typename C, typename T> |
| constexpr Scalar<N, C, T>::Scalar(detail::scalar_round_down_t<N, C, T> const &v) : _n(v._n) |
| { |
| } |
| template <intmax_t N, typename C, typename T> |
| template <typename I> |
| constexpr Scalar<N, C, T>::Scalar(detail::scalar_unit_round_up_t<I> v) : _n(v.template scale<N, C>()) |
| { |
| } |
| template <intmax_t N, typename C, typename T> |
| template <typename I> |
| constexpr Scalar<N, C, T>::Scalar(detail::scalar_unit_round_down_t<I> v) : _n(v.template scale<N, C>()) |
| { |
| } |
| |
| template <intmax_t N, typename C, typename T> |
| constexpr auto |
| Scalar<N, C, T>::count() const -> Counter |
| { |
| return _n; |
| } |
| |
| template <intmax_t N, typename C, typename T> |
| constexpr intmax_t |
| Scalar<N, C, T>::value() const |
| { |
| return _n * SCALE; |
| } |
| |
| template <intmax_t N, typename C, typename T> constexpr Scalar<N, C, T>::operator intmax_t() const |
| { |
| return _n * SCALE; |
| } |
| |
| template <intmax_t N, typename C, typename T> |
| inline auto |
| Scalar<N, C, T>::assign(Counter n) -> self & |
| { |
| _n = n; |
| return *this; |
| } |
| |
| template <intmax_t N, typename C, typename T> |
| inline auto |
| Scalar<N, C, T>::operator=(self const &that) -> self & |
| { |
| _n = that._n; |
| return *this; |
| } |
| |
| template <intmax_t N, typename C, typename T> |
| inline auto |
| Scalar<N, C, T>::operator=(detail::scalar_round_up_t<N, C, T> v) -> self & |
| { |
| _n = v._n; |
| return *this; |
| } |
| template <intmax_t N, typename C, typename T> |
| inline auto |
| Scalar<N, C, T>::assign(detail::scalar_round_up_t<N, C, T> v) -> self & |
| { |
| _n = v._n; |
| return *this; |
| } |
| template <intmax_t N, typename C, typename T> |
| inline auto |
| Scalar<N, C, T>::operator=(detail::scalar_round_down_t<N, C, T> v) -> self & |
| { |
| _n = v._n; |
| return *this; |
| } |
| template <intmax_t N, typename C, typename T> |
| inline auto |
| Scalar<N, C, T>::assign(detail::scalar_round_down_t<N, C, T> v) -> self & |
| { |
| _n = v._n; |
| return *this; |
| } |
| template <intmax_t N, typename C, typename T> |
| template <typename I> |
| inline auto |
| Scalar<N, C, T>::operator=(detail::scalar_unit_round_up_t<I> v) -> self & |
| { |
| _n = v.template scale<N, C>(); |
| return *this; |
| } |
| template <intmax_t N, typename C, typename T> |
| template <typename I> |
| inline auto |
| Scalar<N, C, T>::assign(detail::scalar_unit_round_up_t<I> v) -> self & |
| { |
| _n = v.template scale<N, C>(); |
| return *this; |
| } |
| template <intmax_t N, typename C, typename T> |
| template <typename I> |
| inline auto |
| Scalar<N, C, T>::operator=(detail::scalar_unit_round_down_t<I> v) -> self & |
| { |
| _n = v.template scale<N, C>(); |
| return *this; |
| } |
| template <intmax_t N, typename C, typename T> |
| template <typename I> |
| inline auto |
| Scalar<N, C, T>::assign(detail::scalar_unit_round_down_t<I> v) -> self & |
| { |
| _n = v.template scale<N, C>(); |
| return *this; |
| } |
| template <intmax_t N, typename C, typename T> |
| template <intmax_t S, typename I> |
| auto |
| Scalar<N, C, T>::operator=(Scalar<S, I, T> const &that) -> self & |
| { |
| typedef std::ratio<S, N> R; |
| static_assert(R::den == 1, "Assignment not permitted - target scale is not an integral multiple of source scale."); |
| _n = that.count() * R::num; |
| return *this; |
| } |
| template <intmax_t N, typename C, typename T> |
| template <intmax_t S, typename I> |
| auto |
| Scalar<N, C, T>::assign(Scalar<S, I, T> const &that) -> self & |
| { |
| typedef std::ratio<S, N> R; |
| static_assert(R::den == 1, "Assignment not permitted - target scale is not an integral multiple of source scale."); |
| _n = that.count() * R::num; |
| return *this; |
| } |
| |
| template <intmax_t N, typename C, typename T> |
| constexpr inline intmax_t |
| Scalar<N, C, T>::scale() |
| { |
| return SCALE; |
| } |
| |
| // --- Compare operators |
| // These optimize nicely because if R::num or R::den is 1 the compiler will drop it. |
| |
| template <intmax_t N, typename C1, intmax_t S, typename I, typename T> |
| bool |
| operator<(Scalar<N, C1, T> const &lhs, Scalar<S, I, T> const &rhs) |
| { |
| typedef std::ratio<N, S> R; |
| return lhs.count() * R::num < rhs.count() * R::den; |
| } |
| |
| template <intmax_t N, typename C1, intmax_t S, typename I, typename T> |
| bool |
| operator==(Scalar<N, C1, T> const &lhs, Scalar<S, I, T> const &rhs) |
| { |
| typedef std::ratio<N, S> R; |
| return lhs.count() * R::num == rhs.count() * R::den; |
| } |
| |
| template <intmax_t N, typename C1, intmax_t S, typename I, typename T> |
| bool |
| operator<=(Scalar<N, C1, T> const &lhs, Scalar<S, I, T> const &rhs) |
| { |
| typedef std::ratio<N, S> R; |
| return lhs.count() * R::num <= rhs.count() * R::den; |
| } |
| |
| // Derived compares. |
| template <intmax_t N, typename C, intmax_t S, typename I, typename T> |
| bool |
| operator>(Scalar<N, C, T> const &lhs, Scalar<S, I, T> const &rhs) |
| { |
| return rhs < lhs; |
| } |
| |
| template <intmax_t N, typename C, intmax_t S, typename I, typename T> |
| bool |
| operator>=(Scalar<N, C, T> const &lhs, Scalar<S, I, T> const &rhs) |
| { |
| return rhs <= lhs; |
| } |
| |
| // Arithmetic operators |
| template <intmax_t N, typename C, typename T> |
| auto |
| Scalar<N, C, T>::operator+=(self const &that) -> self & |
| { |
| _n += that._n; |
| return *this; |
| } |
| |
| template <intmax_t N, typename C, typename T> |
| template <intmax_t S, typename I> |
| auto |
| Scalar<N, C, T>::operator+=(Scalar<S, I, T> const &that) -> self & |
| { |
| typedef std::ratio<S, N> R; |
| static_assert(R::den == 1, "Addition not permitted - target scale is not an integral multiple of source scale."); |
| _n += that.count() * R::num; |
| return *this; |
| } |
| |
| template <intmax_t N, typename C, typename T> |
| template <typename I> |
| auto |
| Scalar<N, C, T>::operator+=(detail::scalar_unit_round_up_t<I> v) -> self & |
| { |
| _n += v.template scale<N, C>(); |
| return *this; |
| } |
| |
| template <intmax_t N, typename C, typename T> |
| template <typename I> |
| auto |
| Scalar<N, C, T>::operator+=(detail::scalar_unit_round_down_t<I> v) -> self & |
| { |
| _n += v.template scale<N, C>(); |
| return *this; |
| } |
| template <intmax_t N, typename C, typename T> |
| auto |
| Scalar<N, C, T>::operator+=(detail::scalar_round_up_t<N, C, T> v) -> self & |
| { |
| _n += v._n; |
| return *this; |
| } |
| template <intmax_t N, typename C, typename T> |
| auto |
| Scalar<N, C, T>::operator+=(detail::scalar_round_down_t<N, C, T> v) -> self & |
| { |
| _n += v._n; |
| return *this; |
| } |
| |
| template <intmax_t N, typename C, intmax_t S, typename I, typename T> |
| auto |
| operator+(Scalar<N, C, T> lhs, Scalar<S, I, T> const &rhs) -> typename std::common_type<Scalar<N, C, T>, Scalar<S, I, T>>::type |
| { |
| return typename std::common_type<Scalar<N, C, T>, Scalar<S, I, T>>::type(lhs) += rhs; |
| } |
| |
| template <intmax_t N, typename C, typename T> |
| Scalar<N, C, T> |
| operator+(Scalar<N, C, T> const &lhs, Scalar<N, C, T> const &rhs) |
| { |
| return Scalar<N, C, T>(lhs) += rhs; |
| } |
| |
| template <intmax_t N, typename C, typename T, typename I> |
| Scalar<N, C, T> |
| operator+(detail::scalar_unit_round_up_t<I> lhs, Scalar<N, C, T> const &rhs) |
| { |
| return Scalar<N, C, T>(rhs) += lhs; |
| } |
| |
| template <intmax_t N, typename C, typename T, typename I> |
| Scalar<N, C, T> |
| operator+(Scalar<N, C, T> const &lhs, detail::scalar_unit_round_up_t<I> rhs) |
| { |
| return Scalar<N, C, T>(lhs) += rhs; |
| } |
| |
| template <intmax_t N, typename C, typename T, typename I> |
| Scalar<N, C, T> |
| operator+(detail::scalar_unit_round_down_t<I> lhs, Scalar<N, C, T> const &rhs) |
| { |
| return Scalar<N, C, T>(rhs) += lhs; |
| } |
| template <intmax_t N, typename C, typename T, typename I> |
| Scalar<N, C, T> |
| operator+(Scalar<N, C, T> const &lhs, detail::scalar_unit_round_down_t<I> rhs) |
| { |
| return Scalar<N, C, T>(lhs) += rhs; |
| } |
| template <intmax_t N, typename C, typename T> |
| Scalar<N, C, T> |
| operator+(detail::scalar_round_up_t<N, C, T> lhs, Scalar<N, C, T> const &rhs) |
| { |
| return Scalar<N, C, T>(rhs) += lhs._n; |
| } |
| template <intmax_t N, typename C, typename T> |
| Scalar<N, C, T> |
| operator+(Scalar<N, C, T> const &lhs, detail::scalar_round_up_t<N, C, T> rhs) |
| { |
| return Scalar<N, C, T>(lhs) += rhs._n; |
| } |
| template <intmax_t N, typename C, typename T> |
| Scalar<N, C, T> |
| operator+(detail::scalar_round_down_t<N, C, T> lhs, Scalar<N, C, T> const &rhs) |
| { |
| return Scalar<N, C, T>(rhs) += lhs._n; |
| } |
| template <intmax_t N, typename C, typename T> |
| Scalar<N, C, T> |
| operator+(Scalar<N, C, T> const &lhs, detail::scalar_round_down_t<N, C, T> rhs) |
| { |
| return Scalar<N, C, T>(lhs) += rhs._n; |
| } |
| |
| template <intmax_t N, typename C, typename T> |
| auto |
| Scalar<N, C, T>::operator-=(self const &that) -> self & |
| { |
| _n -= that._n; |
| return *this; |
| } |
| |
| template <intmax_t N, typename C, typename T> |
| template <intmax_t S, typename I> |
| auto |
| Scalar<N, C, T>::operator-=(Scalar<S, I, T> const &that) -> self & |
| { |
| typedef std::ratio<S, N> R; |
| static_assert(R::den == 1, "Subtraction not permitted - target scale is not an integral multiple of source scale."); |
| _n -= that.count() * R::num; |
| return *this; |
| } |
| |
| template <intmax_t N, typename C, typename T> |
| template <typename I> |
| auto |
| Scalar<N, C, T>::operator-=(detail::scalar_unit_round_up_t<I> v) -> self & |
| { |
| _n -= v.template scale<N, C>(); |
| return *this; |
| } |
| template <intmax_t N, typename C, typename T> |
| template <typename I> |
| auto |
| Scalar<N, C, T>::operator-=(detail::scalar_unit_round_down_t<I> v) -> self & |
| { |
| _n -= v.template scale<N, C>(); |
| return *this; |
| } |
| template <intmax_t N, typename C, typename T> |
| auto |
| Scalar<N, C, T>::operator-=(detail::scalar_round_up_t<N, C, T> v) -> self & |
| { |
| _n -= v._n; |
| return *this; |
| } |
| template <intmax_t N, typename C, typename T> |
| auto |
| Scalar<N, C, T>::operator-=(detail::scalar_round_down_t<N, C, T> v) -> self & |
| { |
| _n -= v._n; |
| return *this; |
| } |
| |
| template <intmax_t N, typename C, intmax_t S, typename I, typename T> |
| auto |
| operator-(Scalar<N, C, T> lhs, Scalar<S, I, T> const &rhs) -> typename std::common_type<Scalar<N, C, T>, Scalar<S, I, T>>::type |
| { |
| return typename std::common_type<Scalar<N, C, T>, Scalar<S, I, T>>::type(lhs) -= rhs; |
| } |
| |
| template <intmax_t N, typename C, typename T> |
| Scalar<N, C, T> |
| operator-(Scalar<N, C, T> const &lhs, Scalar<N, C, T> const &rhs) |
| { |
| return Scalar<N, C, T>(lhs) -= rhs; |
| } |
| |
| template <intmax_t N, typename C, typename T, typename I> |
| Scalar<N, C, T> |
| operator-(detail::scalar_unit_round_up_t<I> lhs, Scalar<N, C, T> const &rhs) |
| { |
| return Scalar<N, C, T>(lhs.template scale<N, C>()) -= rhs; |
| } |
| |
| template <intmax_t N, typename C, typename T, typename I> |
| Scalar<N, C, T> |
| operator-(Scalar<N, C, T> const &lhs, detail::scalar_unit_round_up_t<I> rhs) |
| { |
| return Scalar<N, C, T>(lhs) -= rhs; |
| } |
| |
| template <intmax_t N, typename C, typename T, typename I> |
| Scalar<N, C, T> |
| operator-(detail::scalar_unit_round_down_t<I> lhs, Scalar<N, C, T> const &rhs) |
| { |
| return Scalar<N, C, T>(lhs.template scale<N, C>()) -= rhs; |
| } |
| |
| template <intmax_t N, typename C, typename T, typename I> |
| Scalar<N, C, T> |
| operator-(Scalar<N, C, T> const &lhs, detail::scalar_unit_round_down_t<I> rhs) |
| { |
| return Scalar<N, C, T>(lhs) -= rhs; |
| } |
| |
| template <intmax_t N, typename C, typename T> |
| Scalar<N, C, T> |
| operator-(detail::scalar_round_up_t<N, C, T> lhs, Scalar<N, C, T> const &rhs) |
| { |
| return Scalar<N, C, T>(lhs._n) -= rhs; |
| } |
| |
| template <intmax_t N, typename C, typename T> |
| Scalar<N, C, T> |
| operator-(Scalar<N, C, T> const &lhs, detail::scalar_round_up_t<N, C, T> rhs) |
| { |
| return Scalar<N, C, T>(lhs) -= rhs._n; |
| } |
| |
| template <intmax_t N, typename C, typename T> |
| Scalar<N, C, T> |
| operator-(detail::scalar_round_down_t<N, C, T> lhs, Scalar<N, C, T> const &rhs) |
| { |
| return Scalar<N, C, T>(lhs._n) -= rhs; |
| } |
| |
| template <intmax_t N, typename C, typename T> |
| Scalar<N, C, T> |
| operator-(Scalar<N, C, T> const &lhs, detail::scalar_round_down_t<N, C, T> rhs) |
| { |
| return Scalar<N, C, T>(lhs) -= rhs._n; |
| } |
| |
| template <intmax_t N, typename C, typename T> |
| auto |
| Scalar<N, C, T>::operator++() -> self & |
| { |
| ++_n; |
| return *this; |
| } |
| |
| template <intmax_t N, typename C, typename T> |
| auto |
| Scalar<N, C, T>::operator++(int) -> self |
| { |
| self zret(*this); |
| ++_n; |
| return zret; |
| } |
| |
| template <intmax_t N, typename C, typename T> |
| auto |
| Scalar<N, C, T>::operator--() -> self & |
| { |
| --_n; |
| return *this; |
| } |
| |
| template <intmax_t N, typename C, typename T> |
| auto |
| Scalar<N, C, T>::operator--(int) -> self |
| { |
| self zret(*this); |
| --_n; |
| return zret; |
| } |
| |
| template <intmax_t N, typename C, typename T> |
| auto |
| Scalar<N, C, T>::inc(Counter n) -> self & |
| { |
| _n += n; |
| return *this; |
| } |
| |
| template <intmax_t N, typename C, typename T> |
| auto |
| Scalar<N, C, T>::dec(Counter n) -> self & |
| { |
| _n -= n; |
| return *this; |
| } |
| |
| template <intmax_t N, typename C, typename T> |
| auto |
| Scalar<N, C, T>::operator*=(C n) -> self & |
| { |
| _n *= n; |
| return *this; |
| } |
| |
| template <intmax_t N, typename C, typename T> |
| Scalar<N, C, T> |
| operator*(Scalar<N, C, T> const &lhs, C n) |
| { |
| return Scalar<N, C, T>(lhs) *= n; |
| } |
| template <intmax_t N, typename C, typename T> |
| Scalar<N, C, T> |
| operator*(C n, Scalar<N, C, T> const &rhs) |
| { |
| return Scalar<N, C, T>(rhs) *= n; |
| } |
| template <intmax_t N, typename C, typename T> |
| Scalar<N, C, T> |
| operator*(Scalar<N, C, T> const &lhs, int n) |
| { |
| return Scalar<N, C, T>(lhs) *= n; |
| } |
| template <intmax_t N, typename C, typename T> |
| Scalar<N, C, T> |
| operator*(int n, Scalar<N, C, T> const &rhs) |
| { |
| return Scalar<N, C, T>(rhs) *= n; |
| } |
| template <intmax_t N> |
| Scalar<N, int> |
| operator*(Scalar<N, int> const &lhs, int n) |
| { |
| return Scalar<N, int>(lhs) *= n; |
| } |
| template <intmax_t N> |
| Scalar<N, int> |
| operator*(int n, Scalar<N, int> const &rhs) |
| { |
| return Scalar<N, int>(rhs) *= n; |
| } |
| |
| template <intmax_t N, typename C, typename T> |
| auto |
| Scalar<N, C, T>::operator/=(C n) -> self & |
| { |
| _n /= n; |
| return *this; |
| } |
| |
| template <intmax_t N, typename C, intmax_t S, typename I, typename T> |
| auto |
| operator/(Scalar<N, C, T> lhs, Scalar<S, I, T> rhs) -> typename std::common_type<C, I>::type |
| { |
| using R = std::ratio<N, S>; |
| return (lhs.count() * R::num) / (rhs.count() * R::den); |
| } |
| |
| template <intmax_t N, typename C, typename T, typename I> |
| Scalar<N, C, T> |
| operator/(Scalar<N, C, T> lhs, I n) |
| { |
| static_assert(std::is_integral<I>::value, "Scalar division only support integral types."); |
| return Scalar<N, C, T>(lhs) /= n; |
| } |
| |
| template <intmax_t N, typename C, typename T> |
| auto |
| Scalar<N, C, T>::operator()(Counter n) const -> self |
| { |
| return self{n}; |
| } |
| |
| template <intmax_t N, typename C, typename T> |
| auto |
| Scalar<N, C, T>::plus(Counter n) const -> self |
| { |
| return {_n + n}; |
| } |
| |
| template <intmax_t N, typename C, typename T> |
| auto |
| Scalar<N, C, T>::minus(Counter n) const -> self |
| { |
| return {_n - n}; |
| } |
| |
| template <intmax_t N, typename C> |
| C |
| round_up(C value) |
| { |
| return N * detail::scale_conversion_round_up<N, 1>(value); |
| } |
| |
| template <intmax_t N, typename C> |
| C |
| round_down(C value) |
| { |
| return N * detail::scale_conversion_round_down<N, 1>(value); |
| } |
| |
| namespace detail |
| { |
| // These classes exist only to create distinguishable overloads. |
| struct tag_label_A { |
| }; |
| struct tag_label_B : public tag_label_A { |
| }; |
| // The purpose is to print a label for a tagged type only if the tag class defines a member that |
| // is the label. This creates a base function that always works and does nothing. The second |
| // function creates an overload if the tag class has a member named 'label' that has an stream IO |
| // output operator. When invoked with a second argument of B then the second overload exists and |
| // is used, otherwise only the first exists and that is used. The critical technology is the use |
| // of 'auto' and 'decltype' which effectively checks if the code inside 'decltype' compiles. |
| template <typename T> |
| inline std::ostream & |
| tag_label(std::ostream &s, tag_label_A const &) |
| { |
| return s; |
| } |
| template <typename T> |
| inline BufferWriter & |
| tag_label(BufferWriter &w, BWFSpec const &, tag_label_A const &) |
| { |
| return w; |
| } |
| template <typename T> |
| inline auto |
| tag_label(std::ostream &s, tag_label_B const &) -> decltype(s << T::label, s) |
| { |
| return s << T::label; |
| } |
| template <typename T> |
| inline auto |
| tag_label(BufferWriter &w, BWFSpec const &spec, tag_label_B const &) -> decltype(bwformat(w, spec, T::label), w) |
| { |
| return bwformat(w, spec, T::label); |
| } |
| } // namespace detail |
| |
| template <intmax_t N, typename C, typename T> |
| BufferWriter & |
| bwformat(BufferWriter &w, BWFSpec const &spec, Scalar<N, C, T> const &x) |
| { |
| static constexpr ts::detail::tag_label_B b{}; |
| bwformat(w, spec, x.value()); |
| return ts::detail::tag_label<T>(w, spec, b); |
| } |
| |
| } // namespace ts |
| |
| namespace std |
| { |
| template <intmax_t N, typename C, typename T> |
| ostream & |
| operator<<(ostream &s, ts::Scalar<N, C, T> const &x) |
| { |
| static ts::detail::tag_label_B b; // Can't be const or the compiler gets upset. |
| s << x.value(); |
| return ts::detail::tag_label<T>(s, b); |
| } |
| |
| /// Compute common type of two scalars. |
| /// In `std` to overload the base definition. This yields a type that has the common type of the |
| /// counter type and a scale that is the GCF of the input scales. |
| template <intmax_t N, typename C, intmax_t S, typename I, typename T> struct common_type<ts::Scalar<N, C, T>, ts::Scalar<S, I, T>> { |
| typedef std::ratio<N, S> R; |
| typedef ts::Scalar<N / R::num, typename common_type<C, I>::type, T> type; |
| }; |
| } // namespace std |