#pragma once
#include <algorithm>
#include <cmath>

namespace schneide
{
class Interval
{
public:
  double min;
  double max;

  static Interval unit()
  {
    return {0.0, 1.0};
  }

  Interval();

  Interval(double left, double right) : min(left), max(right)
  {
  }

  double toRelative(double e) const
  {
    return (e - min) / length();
  }

  double fromRelative(double lambda) const
  {
    return min + lambda * length();
  }

  template <typename Function>
  Interval map(Function&& f) const
  {
    return {f(min), f(max)};
  }

  /** Create a new interval, that when iterated over from 0..N-1, produces
   * evenly sized steps where 0 maps to min and N-1 maps to max.
   */
  Interval over(std::size_t N) const
  {
    auto highest = static_cast<double>(N - 1);
    return Interval(min, (max - min) / highest + min);
  }

  double length() const
  {
    return max - min;
  }

  double center() const
  {
    return (min + max) * 0.5;
  }

  Interval move(double rhs) const
  {
    return {min + rhs, max + rhs};
  }

  Interval intersect(Interval const& rhs) const
  {
    return {std::max(min, rhs.min), std::min(max, rhs.max)};
  }
};

inline bool operator==(Interval const& lhs, Interval const& rhs)
{
  return lhs.min == rhs.min && lhs.max == rhs.max;
}

inline bool operator!=(Interval const& lhs, Interval const& rhs)
{
  return !(lhs == rhs);
}
}  // namespace schneide
