Program Listing for File range.hpp

Return to documentation for file (rcppsw/math/range.hpp)

#pragma once

/*******************************************************************************
 * Includes
 ******************************************************************************/
#include <cmath>
#include <iosfwd>
#include <string>

#include "rcppsw/er/client.hpp"
#include "rcppsw/rcppsw.hpp"
#include "rcppsw/utils/string_utils.hpp"
#include "rcppsw/er/stringizable.hpp"

/*******************************************************************************
 * Namespaces/Decls
 ******************************************************************************/
namespace rcppsw::math {

/*******************************************************************************
 * Class Definitions
 ******************************************************************************/
template <typename T>
class range final : public er::client<range<T>>, er::stringizable {
 public:
  range(void) noexcept : ER_CLIENT_INIT() {}
  range(const T& lb, const T& ub) noexcept
      : ER_CLIENT_INIT(),
        m_initialized(true),
        m_lb(lb),
        m_ub(ub),
        m_span(m_ub - m_lb) {}

  RCPPSW_NODISCARD T lb(void) const { return m_lb; }

  RCPPSW_NODISCARD T ub(void) const { return m_ub; }

  RCPPSW_NODISCARD T span(void) const { return m_span; }

  void lb(const T& lb) {
    ER_ASSERT(m_initialized, "Range not initialized");
    ER_ASSERT(lb < m_ub,
              "New lower bound (%s) >= upper bound (%s)",
              rcppsw::to_string(lb).c_str(),
              rcppsw::to_string(m_ub).c_str());
    m_lb = lb;
    m_span = m_ub;
    m_span -= m_lb;
  }

  void ub(const T& ub) {
    ER_ASSERT(m_initialized, "Range not initialized");
    ER_ASSERT(ub > m_lb,
              "New upper bound (%s) <= lower bound (%s)",
              rcppsw::to_string(ub).c_str(),
              rcppsw::to_string(m_lb).c_str());

    m_ub = ub;
    m_span = m_ub;
    m_span -= m_lb;
  }

  void set(const T& lb, const T& ub) {
    ER_ASSERT(lb < ub,
              "New lower bound (%s) >= New upper bound (%s)",
              rcppsw::to_string(m_lb).c_str(),
              rcppsw::to_string(m_ub).c_str());
    m_initialized = true;
    m_lb = lb;
    m_ub = ub;
    m_span = m_ub;
    m_span -= m_lb;
  }

  template <typename U = T, RCPPSW_SFINAE_DECLDEF(!std::is_floating_point<U>::value)>
  bool contains(const T& value) const {
    ER_ASSERT(m_initialized, "Range not initialized");
    return value >= m_lb && value <= m_ub;
  }

  template <typename U = T, RCPPSW_SFINAE_DECLDEF(std::is_floating_point<U>::value)>
  bool contains(const T& value) const {
    ER_ASSERT(m_initialized, "Range not initialized");
    return value >= m_lb && value <= m_ub;
  }

  bool contains(const range<T>& other) const {
    return this->contains(other.m_lb) && this->contains(other.m_ub);
  }

  bool overlaps_with(const range<T>& other) const {
    return this->contains(other.m_lb) || this->contains(other.m_ub) ||
           other.contains(this->m_lb) || other.contains(this->m_ub);
  }

  RCPPSW_NODISCARD RCPPSW_PURE T wrap_value(T value) const {
    ER_ASSERT(m_initialized, "Range not initialized");

    while (value > m_ub) {
      value -= m_span;
    }
    while (value < m_lb) {
      value += m_span;
    }
    return value;
  }

  T center(void) const { return (m_lb + m_ub) / 2; }


  std::string to_str(void) const override {
    return "[" + rcppsw::to_string(m_lb) + "-" + rcppsw::to_string(m_ub) + "]";
  }

  friend std::ostream& operator<<(std::ostream& stream, const range& c_range) {
    stream << c_range.to_str();
    return stream;
  }
  range translate(const T& value) const {
    return range(m_lb + value, m_ub + value);
  }
  range shrink(const T& value) const {
    return range(m_lb + value, m_ub - value);
  }

  range recenter(const T&value) const {
    T span = this->span();
    return range(static_cast<T>(value - span / 2.0),
                 static_cast<T>(value + span / 2.0));
  }

  friend std::istream& operator>>(std::istream& is, range& r) {
    T values[2] = { T(), T() };
    utils::parse_values<T>(is, 2, values, ':');
    r.set(values[0], values[1]);
    return is;
  }

 private:
  /* clang-format off */
  bool m_initialized{false};
  T    m_lb{};
  T    m_ub{};
  T    m_span{};
  /* clang-format on */
};

using rangei = range<int>;

using rangez = range<size_t>;

using ranged = range<double>;

/*******************************************************************************
 * Macros
 ******************************************************************************/
#define RCPPSW_MATH_RANGE_DIRECT_CONV2FLT(prefix)                         \
  static inline ranged prefix##range2drange(const range##prefix& other) { \
    return ranged(other.lb(), other.lb());                                \
  }

#define RCPPSW_MATH_RANGE_SCALED_CONV2FLT(prefix)                       \
  static inline ranged prefix##range2drange(const range##prefix& other, \
                                            double scale) {             \
    return ranged(other.lb() * scale, other.ub() * scale);              \
  }

#define RCPPSW_MATH_RANGE_CONV2DISC(dest_prefix, dest_type)                \
  static inline range##dest_prefix drange2##dest_prefix##range(            \
      const range##dest_prefix& other, double scale) {                     \
    return range##dest_prefix(static_cast<dest_type>(other.lb() / scale),  \
                              static_cast<dest_type>(other.ub() / scale)); \
  }

/*******************************************************************************
 * Free Functions
 ******************************************************************************/
RCPPSW_MATH_RANGE_DIRECT_CONV2FLT(z);
RCPPSW_MATH_RANGE_DIRECT_CONV2FLT(i);
RCPPSW_MATH_RANGE_SCALED_CONV2FLT(z);
RCPPSW_MATH_RANGE_SCALED_CONV2FLT(i);
RCPPSW_MATH_RANGE_CONV2DISC(z, size_t);

} /* namespace rcppsw::math */