use core::cmp::Ordering;
use core::fmt;
use core::marker::PhantomData;
use core::ops::{Div, Index, Sub};
use num_traits::FromPrimitive;
use num_traits::identities::Zero;
use num_traits::real::Real;
#[cfg(feature = "std")]
use std::error::Error;
use core::fmt::Debug;
use super::{Chain, Signal};
pub trait SortedChain: Chain {
fn strict_upper_bound_clamped(&self, element: Self::Output, min: usize, max: usize) -> usize
where
Self::Output: PartialOrd + Copy,
{
let mut pointer = min;
let mut dist = max - min;
while dist > 0 {
let step = dist / 2;
let sample = pointer + step;
if element >= self.eval(sample) {
pointer = sample + 1;
dist -= step + 1;
} else {
dist = step;
}
}
pointer
}
fn strict_upper_bound(&self, element: Self::Output) -> usize
where
Self::Output: PartialOrd + Copy,
{
self.strict_upper_bound_clamped(element, 0, self.len())
}
fn upper_border(&self, element: Self::Output) -> (usize, usize, Self::Output)
where
Self::Output: PartialOrd
+ Sub<Output = Self::Output>
+ Div<Output = Self::Output>
+ Zero
+ Copy
+ Debug,
{
let max_index = self.strict_upper_bound(element);
if self.len() == max_index {
let max_index = self.len() - 1;
let min_index = max_index - 1;
return (
min_index,
max_index,
self.linear_factor(min_index, max_index, element),
);
}
if max_index == 0 {
let max_index = 1;
let min_index = 0;
return (
min_index,
max_index,
self.linear_factor(min_index, max_index, element),
);
}
(
max_index - 1,
max_index,
self.linear_factor_unchecked(max_index - 1, max_index, element),
)
}
fn linear_factor_unchecked(
&self,
min_index: usize,
max_index: usize,
element: Self::Output,
) -> Self::Output
where
Self::Output: Sub<Output = Self::Output> + Div<Output = Self::Output> + Copy,
{
let max = self.eval(max_index);
let min = self.eval(min_index);
(element - min) / (max - min)
}
fn linear_factor(
&self,
min_index: usize,
max_index: usize,
element: Self::Output,
) -> Self::Output
where
Self::Output: Sub<Output = Self::Output> + Div<Output = Self::Output> + Zero + Copy,
{
let max = self.eval(max_index);
let min = self.eval(min_index);
let div = max - min;
if div.is_zero() {
return div;
}
(element - min) / div
}
}
#[derive(Debug, Copy, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct Sorted<C>(C);
impl<C> Sorted<C>
where
C: Chain,
C::Output: PartialOrd,
{
pub fn new(col: C) -> Result<Self, NotSorted> {
if col.is_empty() {
return Ok(Sorted(col));
}
let mut last = col.eval(0);
for i in 1..col.len() {
let current = col.eval(i);
match last.partial_cmp(¤t) {
None | Some(Ordering::Greater) => return Err(NotSorted { index: i }),
_ => {
last = current;
}
}
}
Ok(Sorted(col))
}
}
impl<C> Sorted<C> {
pub const fn new_unchecked(col: C) -> Self {
Sorted(col)
}
}
impl<C> Signal<usize> for Sorted<C>
where
C: Signal<usize>,
{
type Output = C::Output;
fn eval(&self, input: usize) -> Self::Output {
self.0.eval(input)
}
}
impl<C> Chain for Sorted<C>
where
C: Chain,
{
fn len(&self) -> usize {
self.0.len()
}
}
impl<C: Chain> SortedChain for Sorted<C> {}
impl<C, Idx> Index<Idx> for Sorted<C>
where
C: Index<Idx>,
{
type Output = C::Output;
fn index(&self, index: Idx) -> &Self::Output {
self.0.index(index)
}
}
#[derive(Debug, Copy, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct NotSorted {
index: usize,
}
impl NotSorted {
pub fn new(index: usize) -> Self {
NotSorted { index }
}
}
impl fmt::Display for NotSorted {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"Given knots are not sorted. From index {} to {} we found decreasing values.",
self.index,
self.index + 1
)
}
}
#[cfg(feature = "std")]
impl Error for NotSorted {}
#[derive(Debug, Copy, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct Equidistant<R = f64> {
len: usize,
step: R,
offset: R,
}
impl<R> Equidistant<R>
where
R: Real + FromPrimitive,
{
pub fn normalized(len: usize) -> Self {
Equidistant {
len,
step: R::from_usize(len - 1).unwrap().recip(),
offset: R::zero(),
}
}
pub fn new(len: usize, start: R, end: R) -> Self {
Equidistant {
len,
step: (end - start) / R::from_usize(len - 1).unwrap(),
offset: start,
}
}
pub fn step(len: usize, start: R, step: R) -> Self {
Equidistant {
len,
step,
offset: start,
}
}
}
impl<R> Signal<usize> for Equidistant<R>
where
R: Real + FromPrimitive,
{
type Output = R;
fn eval(&self, input: usize) -> R {
self.step * R::from_usize(input).unwrap() + self.offset
}
}
impl<R> Chain for Equidistant<R>
where
R: Real + FromPrimitive,
{
fn len(&self) -> usize {
self.len
}
}
impl<R> SortedChain for Equidistant<R>
where
R: Real + FromPrimitive,
{
fn strict_upper_bound(&self, element: Self::Output) -> usize
where
Self::Output: PartialOrd + Copy,
{
if element < self.offset {
return 0;
}
let scaled = (element - self.offset) / self.step;
let min_index = scaled.floor().to_usize().unwrap();
self.len().min(min_index + 1)
}
fn strict_upper_bound_clamped(&self, element: Self::Output, min: usize, max: usize) -> usize
where
Self::Output: PartialOrd + Copy,
{
if element < self.eval(min) {
return min;
}
let scaled = (element - self.offset) / self.step;
let min_index = scaled.floor().to_usize().unwrap();
max.min(min_index + 1)
}
fn upper_border(&self, element: R) -> (usize, usize, R) {
let scaled = (element - self.offset) / self.step;
if element < self.offset {
return (0, 1, scaled);
}
let min_index = scaled.floor().to_usize().unwrap();
let max_index = scaled.ceil().to_usize().unwrap();
if max_index >= self.len {
return (
self.len - 2,
self.len - 1,
scaled - R::from_usize(self.len - 2).unwrap(),
);
}
let factor = scaled.fract();
(min_index, max_index, factor)
}
}
#[derive(Debug, Copy, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct ConstEquidistant<R , const N: usize>(PhantomData<*const R>);
impl<R, const N: usize> ConstEquidistant<R, N> {
pub const fn new() -> Self {
ConstEquidistant(PhantomData)
}
}
impl<R, const N: usize> Default for ConstEquidistant<R, N> {
fn default() -> Self {
Self(Default::default())
}
}
impl<R, const N: usize> Signal<usize> for ConstEquidistant<R, N>
where
R: Real + FromPrimitive,
{
type Output = R;
fn eval(&self, input: usize) -> R {
R::from_usize(input).unwrap() / R::from_usize(N - 1).unwrap()
}
}
impl<R, const N: usize> Chain for ConstEquidistant<R, N>
where
R: Real + FromPrimitive,
{
fn len(&self) -> usize {
N
}
}
impl<R, const N: usize> SortedChain for ConstEquidistant<R, N>
where
R: Real + FromPrimitive,
{
fn strict_upper_bound(&self, element: Self::Output) -> usize
where
Self::Output: PartialOrd + Copy,
{
if element < R::zero() {
return 0;
}
let scaled = element * R::from_usize(N - 1).unwrap();
let min_index = scaled.floor().to_usize().unwrap();
self.len().min(min_index + 1)
}
fn strict_upper_bound_clamped(&self, element: Self::Output, min: usize, max: usize) -> usize
where
Self::Output: PartialOrd + Copy,
{
if element < self.eval(min) {
return min;
}
let scaled = element * R::from_usize(N - 1).unwrap();
let min_index = scaled.floor().to_usize().unwrap();
max.min(min_index + 1)
}
fn upper_border(&self, element: R) -> (usize, usize, R)
where
R: PartialOrd + Sub<Output = R> + Div<Output = R> + Copy + Debug,
{
let scaled = element * R::from_usize(N - 1).unwrap();
if element < R::zero() {
return (0, 1, scaled);
}
let min_index = scaled.floor().to_usize().unwrap();
let max_index = scaled.ceil().to_usize().unwrap();
if max_index >= N {
return (N - 2, N - 1, scaled - R::from_usize(N - 2).unwrap());
}
let factor = scaled.fract();
(min_index, max_index, factor)
}
}