#pragma once
#include "range.h"
namespace DHE {
namespace Sigmoid {
static constexpr auto sigmoid_range = Range{-1.f, 1.f};
static constexpr auto proportion_range = Range{0.f, 1.f};
/**
 * Applies an inverse sigmoid function to the input.
 * 
 * The curvature determines the shape and intensity of the transfer function.
 * A positive curvature applies an inverted S-shaped transfer function.
 * A curvature of 0 applies a linear transfer function.
 * A negative curvature applies an S-shaped transfer function.
 * 
 * Before the function is applied:
 * 
 * - The input is clamped to the range [-1.0, 1.0].*
- The curvature is clamped to the range [0.0001, 0.9999].*
* @param input the input to the inverse sigmoid function
 * @param curvature the intensity and direction of the curvature
 * @return the sigmoid function result
 */
inline auto inverse(float input, float curvature) -> float {
  static constexpr auto precision = 1e-4f;
  static constexpr auto max_curvature = 1.0f - precision;
  static constexpr auto curvature_range = Range{-max_curvature, max_curvature};
  curvature = curvature_range.clamp(curvature);
  input = sigmoid_range.clamp(input);
  return (input - input * curvature) /
         (curvature - std::abs(input) * 2.0f * curvature + 1.0f);
}
/**
 * Applies a sigmoid function to the input.
 *
 * The curvature determines the shape and intensity of the transfer function.
 * A positive curvature applies an S-shaped transfer function.
 * A curvature of 0 applies a linear transfer function.
 * A negative curvature applies an inverted S-shaped transfer function.
 * 
 * Before the function is applied:
 * 
 * - The input is clamped to the range [-1.0, 1.0].*
- The curvature is clamped to the range [0.0001, 0.9999].*
* @param input the input to the sigmoid function
 * @param curvature the intensity and direction of the curvature
 * @return the sigmoid function result
 */
inline auto curve(float input, float curvature) -> float {
  return inverse(input, -curvature);
}
/**
 * Applies a gentle S-shaped transfer function to map an input in the range
 * [0.0, 1.0] to an output in the range [-1.0, 1.0]. The transfer function makes
 * the output more sensitive to changes in inputs near 0.5 and less sensitive to
 * changes near 0.0 and 1.0.
 *
 * This function is intended to translate DHE-Modules CURVE knob rotation to a
 * curvature value suitable to pass to the curve(), inverse(), j_taper(), and
 * s_taper() functions.
 *
 * @param input the value to map to a curvature
 * @return the curvature
 */
inline auto curvature(float input) -> float {
  // This curvature creates a gentle S curve, increasing sensitivity in the
  // middle of the input range and decreasing sensitivity toward the extremes.
  static constexpr auto gentle_s = 0.65f;
  auto scaled = sigmoid_range.scale(input);
  return curve(scaled, gentle_s);
}
/**
 * Applies a J-shaped transfer function to the input.
 * 
 * The curvature determines the shape and intensity of the taper.
 * A positive curvature applies a J-taper.
 * A curvature of 0 applies a linear taper.
 * A negative curvature applies an inverted J-taper.
 * 
 * Before the function is applied:
 * 
 * - The input is clamped to the range [0.0, 1.0].*
- The curvature is clamped to the range [0.0001, 0.9999].*
* @param input the input to the taper function
 * @param curvature the intensity and direction of the taper
 * @return the taper function result
 */
inline auto j_taper(float input, float curvature) -> float {
  return inverse(proportion_range.clamp(input), curvature);
}
/**
 * Applies an S-shaped transfer function to the input.
 *
 * The curvature determines the shape and intensity of the taper.
 * A positive curvature applies an S-taper.
 * A curvature of 0 applies a linear taper.
 * A negative curvature applies an inverted S-taper.
 * 
 * Before the function is applied:
 * 
 * - The input is clamped to the range [0.0, 1.0].*
- The curvature is clamped to the range [0.0001, 0.9999].*
* @param input the input to the taper function
 * @param curvature the intensity and direction of the taper
 * @return the taper function result
 */
inline auto s_taper(float input, float curvature) -> float {
  const auto scaled = sigmoid_range.scale(input);
  const auto tapered = curve(scaled, curvature);
  return sigmoid_range.normalize(tapered);
}
inline auto taper(float input, float curvature, bool is_s) -> float {
  return is_s ? s_taper(input, curvature) : j_taper(input, curvature);
}
} // namespace Sigmoid
} // namespace DHE