ndarray_histogram/quantile/
interpolate.rs1use num_traits::{Float, FromPrimitive, NumOps, ToPrimitive};
3
4fn float_quantile_index<F: Float>(q: F, len: usize) -> F {
5 q * (F::from(len - 1).unwrap())
6}
7
8fn float_quantile_index_fraction<F: Float>(q: F, len: usize) -> F {
13 float_quantile_index(q, len).fract()
14}
15
16pub(crate) fn lower_index<F: Float>(q: F, len: usize) -> usize {
18 float_quantile_index(q, len).floor().to_usize().unwrap()
19}
20
21pub(crate) fn higher_index<F: Float>(q: F, len: usize) -> usize {
23 float_quantile_index(q, len).ceil().to_usize().unwrap()
24}
25
26pub trait Interpolate<T> {
30 #[doc(hidden)]
33 fn needs_lower<F: Float>(q: F, len: usize) -> bool;
34
35 #[doc(hidden)]
38 fn needs_higher<F: Float>(q: F, len: usize) -> bool;
39
40 #[doc(hidden)]
45 fn interpolate<F: Float>(lower: Option<T>, higher: Option<T>, q: F, len: usize) -> T;
46
47 private_decl! {}
48}
49
50pub struct Higher;
52pub struct Lower;
54pub struct Nearest;
56pub struct Midpoint;
58pub struct Linear;
62
63impl<T> Interpolate<T> for Higher {
64 fn needs_lower<F: Float>(_q: F, _len: usize) -> bool {
65 false
66 }
67 fn needs_higher<F: Float>(_q: F, _len: usize) -> bool {
68 true
69 }
70 fn interpolate<F: Float>(_lower: Option<T>, higher: Option<T>, _q: F, _len: usize) -> T {
71 higher.unwrap()
72 }
73 private_impl! {}
74}
75
76impl<T> Interpolate<T> for Lower {
77 fn needs_lower<F: Float>(_q: F, _len: usize) -> bool {
78 true
79 }
80 fn needs_higher<F: Float>(_q: F, _len: usize) -> bool {
81 false
82 }
83 fn interpolate<F: Float>(lower: Option<T>, _higher: Option<T>, _q: F, _len: usize) -> T {
84 lower.unwrap()
85 }
86 private_impl! {}
87}
88
89impl<T> Interpolate<T> for Nearest {
90 fn needs_lower<F: Float>(q: F, len: usize) -> bool {
91 float_quantile_index_fraction(q, len) < F::from(0.5).unwrap()
92 }
93 fn needs_higher<F: Float>(q: F, len: usize) -> bool {
94 !<Self as Interpolate<T>>::needs_lower(q, len)
95 }
96 fn interpolate<F: Float>(lower: Option<T>, higher: Option<T>, q: F, len: usize) -> T {
97 if <Self as Interpolate<T>>::needs_lower(q, len) {
98 lower.unwrap()
99 } else {
100 higher.unwrap()
101 }
102 }
103 private_impl! {}
104}
105
106impl<T> Interpolate<T> for Midpoint
107where
108 T: NumOps + Clone + FromPrimitive,
109{
110 fn needs_lower<F: Float>(_q: F, _len: usize) -> bool {
111 true
112 }
113 fn needs_higher<F: Float>(_q: F, _len: usize) -> bool {
114 true
115 }
116 fn interpolate<F: Float>(lower: Option<T>, higher: Option<T>, _q: F, _len: usize) -> T {
117 let denom = T::from_u8(2).unwrap();
118 let lower = lower.unwrap();
119 let higher = higher.unwrap();
120 lower.clone() + (higher - lower) / denom
121 }
122 private_impl! {}
123}
124
125impl<T> Interpolate<T> for Linear
126where
127 T: NumOps + Clone + FromPrimitive + ToPrimitive,
128{
129 fn needs_lower<F: Float>(_q: F, _len: usize) -> bool {
130 true
131 }
132 fn needs_higher<F: Float>(_q: F, _len: usize) -> bool {
133 true
134 }
135 fn interpolate<F: Float>(lower: Option<T>, higher: Option<T>, q: F, len: usize) -> T {
136 let fraction = float_quantile_index_fraction(q, len).to_f64().unwrap();
137 let lower = lower.unwrap();
138 let higher = higher.unwrap();
139 let lower_f64 = lower.to_f64().unwrap();
140 let higher_f64 = higher.to_f64().unwrap();
141 lower + T::from_f64(fraction * (higher_f64 - lower_f64)).unwrap()
142 }
143 private_impl! {}
144}