1use crate::coord::ranged1d::{
2 AsRangedCoord, KeyPointHint, NoDefaultFormatting, Ranged, ReversibleRanged, ValueFormatter,
3};
4use std::ops::Range;
5
6/// The trait indicates the coordinate is discrete
7/// This means we can bidirectionally map the range value to 0 to N
8/// in which N is the number of distinct values of the range.
9///
10/// This is useful since for a histgoram, this is an abstraction of bucket.
11pub trait DiscreteRanged
12where
13 Self: Ranged,
14{
15 /// Get the number of element in the range
16 /// Note: we assume that all the ranged discrete coordinate has finite value
17 ///
18 /// - **returns** The number of values in the range
19 fn size(&self) -> usize;
20
21 /// Map a value to the index
22 ///
23 /// Note: This function doesn't guareentee return None when the value is out of range.
24 /// The only way to confirm the value is in the range is to examing the return value isn't
25 /// larger than self.size.
26 ///
27 /// - `value`: The value to map
28 /// - **returns** The index of the value
29 fn index_of(&self, value: &Self::ValueType) -> Option<usize>;
30
31 /// Reverse map the index to the value
32 ///
33 /// Note: This function doesn't guareentee returning None when the index is out of range.
34 ///
35 /// - `value`: The index to map
36 /// - **returns** The value
37 // TODO: This doesn't follows rust's naming convention - however, this is a protential breaking
38 // change, so postpone the fix to the next major release
39 #[allow(clippy::wrong_self_convention)]
40 fn from_index(&self, index: usize) -> Option<Self::ValueType>;
41
42 /// Return a iterator that iterates over the all possible values
43 ///
44 /// - **returns** The value iterator
45 fn values(&self) -> DiscreteValueIter<'_, Self>
46 where
47 Self: Sized,
48 {
49 DiscreteValueIter(self, 0, self.size())
50 }
51
52 /// Returns the previous value in this range
53 ///
54 /// Normally, it's based on the `from_index` and `index_of` function. But for
55 /// some of the coord spec, it's possible that we value faster implementation.
56 /// If this is the case, we can impelemnet the type specific impl for the `previous`
57 /// and `next`.
58 ///
59 /// - `value`: The current value
60 /// - **returns**: The value piror to current value
61 fn previous(&self, value: &Self::ValueType) -> Option<Self::ValueType> {
62 if let Some(idx) = self.index_of(value) {
63 if idx > 0 {
64 return self.from_index(idx - 1);
65 }
66 }
67 None
68 }
69
70 /// Returns the next value in this range
71 ///
72 /// Normally, it's based on the `from_index` and `index_of` function. But for
73 /// some of the coord spec, it's possible that we value faster implementation.
74 /// If this is the case, we can impelemnet the type specific impl for the `previous`
75 /// and `next`.
76 ///
77 /// - `value`: The current value
78 /// - **returns**: The value next to current value
79 fn next(&self, value: &Self::ValueType) -> Option<Self::ValueType> {
80 if let Some(idx) = self.index_of(value) {
81 if idx + 1 < self.size() {
82 return self.from_index(idx + 1);
83 }
84 }
85 None
86 }
87}
88
89/// A `SegmentedCoord` is a decorator on any discrete coordinate specification.
90/// This decorator will convert the discrete coordiante in two ways:
91/// - Add an extra dummy element after all the values in origianl discrete coordinate
92/// - Logically each value `v` from original coordinate system is mapped into an segment `[v, v+1)` where `v+1` denotes the sucessor of the `v`
93/// - Introduce two types of values `SegmentValue::Exact(value)` which denotes the left end of value's segment and `SegmentValue::CenterOf(value)` which refers the center of the segment.
94/// This is used in histogram types, which uses a discrete coordinate as the buckets. The segmented coord always emits `CenterOf(value)` key points, thus it allows all the label and tick marks
95/// of the coordinate rendered in the middle of each segment.
96/// The coresponding trait [IntoSegmentedCoord](trait.IntoSegmentedCoord.html) is used to apply this decorator to coordinates.
97#[derive(Clone)]
98pub struct SegmentedCoord<D: DiscreteRanged>(D);
99
100/// The trait for types that can decorated by [SegmentedCoord](struct.SegmentedCoord.html) decorator.
101pub trait IntoSegmentedCoord: AsRangedCoord
102where
103 Self::CoordDescType: DiscreteRanged,
104{
105 /// Convert current ranged value into a segmented coordinate
106 fn into_segmented(self) -> SegmentedCoord<Self::CoordDescType> {
107 SegmentedCoord(self.into())
108 }
109}
110
111impl<R: AsRangedCoord> IntoSegmentedCoord for R where R::CoordDescType: DiscreteRanged {}
112
113/// The value that used by the segmented coordinate.
114#[derive(Clone, Debug)]
115pub enum SegmentValue<T> {
116 /// Means we are referring the exact position of value `T`
117 Exact(T),
118 /// Means we are referring the center of position `T` and the successor of `T`
119 CenterOf(T),
120 /// Referring the last dummy element
121 Last,
122}
123
124impl<T, D: DiscreteRanged + Ranged<ValueType = T>> ValueFormatter<SegmentValue<T>>
125 for SegmentedCoord<D>
126where
127 D: ValueFormatter<T>,
128{
129 fn format(value: &SegmentValue<T>) -> String {
130 match value {
131 SegmentValue::Exact(ref value: &T) => D::format(value),
132 SegmentValue::CenterOf(ref value: &T) => D::format(value),
133 _ => "".to_string(),
134 }
135 }
136}
137
138impl<D: DiscreteRanged> Ranged for SegmentedCoord<D> {
139 type FormatOption = NoDefaultFormatting;
140 type ValueType = SegmentValue<D::ValueType>;
141
142 fn map(&self, value: &Self::ValueType, limit: (i32, i32)) -> i32 {
143 let margin = ((limit.1 - limit.0) as f32 / self.0.size() as f32).round() as i32;
144
145 match value {
146 SegmentValue::Exact(coord) => self.0.map(coord, (limit.0, limit.1 - margin)),
147 SegmentValue::CenterOf(coord) => {
148 let left = self.0.map(coord, (limit.0, limit.1 - margin));
149 if let Some(idx) = self.0.index_of(coord) {
150 if idx + 1 < self.0.size() {
151 let right = self.0.map(
152 &self.0.from_index(idx + 1).unwrap(),
153 (limit.0, limit.1 - margin),
154 );
155 return (left + right) / 2;
156 }
157 }
158 left + margin / 2
159 }
160 SegmentValue::Last => limit.1,
161 }
162 }
163
164 fn key_points<HintType: KeyPointHint>(&self, hint: HintType) -> Vec<Self::ValueType> {
165 self.0
166 .key_points(hint)
167 .into_iter()
168 .map(SegmentValue::CenterOf)
169 .collect()
170 }
171
172 fn range(&self) -> Range<Self::ValueType> {
173 let range = self.0.range();
174 SegmentValue::Exact(range.start)..SegmentValue::Exact(range.end)
175 }
176}
177
178impl<D: DiscreteRanged> DiscreteRanged for SegmentedCoord<D> {
179 fn size(&self) -> usize {
180 self.0.size() + 1
181 }
182
183 fn index_of(&self, value: &Self::ValueType) -> Option<usize> {
184 match value {
185 SegmentValue::Exact(value: &::ValueType) => self.0.index_of(value),
186 SegmentValue::CenterOf(value: &::ValueType) => self.0.index_of(value),
187 SegmentValue::Last => Some(self.0.size()),
188 }
189 }
190
191 fn from_index(&self, idx: usize) -> Option<Self::ValueType> {
192 match idx {
193 idx: usize if idx < self.0.size() => self.0.from_index(idx).map(SegmentValue::Exact),
194 idx: usize if idx == self.0.size() => Some(SegmentValue::Last),
195 _ => None,
196 }
197 }
198}
199
200impl<T> From<T> for SegmentValue<T> {
201 fn from(this: T) -> SegmentValue<T> {
202 SegmentValue::Exact(this)
203 }
204}
205
206impl<DC: DiscreteRanged> ReversibleRanged for DC {
207 fn unmap(&self, input: i32, limit: (i32, i32)) -> Option<Self::ValueType> {
208 let idx: usize = (f64::from(input - limit.0) * (self.size() as f64) / f64::from(limit.1 - limit.0))
209 .floor() as usize;
210 self.from_index(idx)
211 }
212}
213
214/// The iterator that can be used to iterate all the values defined by a discrete coordinate
215pub struct DiscreteValueIter<'a, T: DiscreteRanged>(&'a T, usize, usize);
216
217impl<'a, T: DiscreteRanged> Iterator for DiscreteValueIter<'a, T> {
218 type Item = T::ValueType;
219 fn next(&mut self) -> Option<T::ValueType> {
220 if self.1 >= self.2 {
221 return None;
222 }
223 let idx: usize = self.1;
224 self.1 += 1;
225 self.0.from_index(idx)
226 }
227}
228
229#[cfg(test)]
230mod test {
231 use super::*;
232 #[test]
233 fn test_value_iter() {
234 let range: crate::coord::ranged1d::types::RangedCoordi32 = (-10..10).into();
235
236 let values: Vec<_> = range.values().collect();
237
238 assert_eq!(21, values.len());
239
240 for (expected, value) in (-10..=10).zip(values) {
241 assert_eq!(expected, value);
242 }
243 assert_eq!(range.next(&5), Some(6));
244 assert_eq!(range.next(&10), None);
245 assert_eq!(range.previous(&-10), None);
246 assert_eq!(range.previous(&10), Some(9));
247 }
248
249 #[test]
250 fn test_centric_coord() {
251 let coord = (0..10).into_segmented();
252
253 assert_eq!(coord.size(), 12);
254 for i in 0..=11 {
255 match coord.from_index(i as usize) {
256 Some(SegmentValue::Exact(value)) => assert_eq!(i, value),
257 Some(SegmentValue::Last) => assert_eq!(i, 11),
258 _ => panic!(),
259 }
260 }
261
262 for (kps, idx) in coord.key_points(20).into_iter().zip(0..) {
263 match kps {
264 SegmentValue::CenterOf(value) if value <= 10 => assert_eq!(value, idx),
265 _ => panic!(),
266 }
267 }
268
269 assert_eq!(coord.map(&SegmentValue::CenterOf(0), (0, 24)), 1);
270 assert_eq!(coord.map(&SegmentValue::Exact(0), (0, 24)), 0);
271 assert_eq!(coord.map(&SegmentValue::Exact(1), (0, 24)), 2);
272 }
273}
274