1use crate::coord::ranged1d::{
2 AsRangedCoord, DiscreteRanged, KeyPointHint, NoDefaultFormatting, Ranged, ValueFormatter,
3};
4use std::ops::Range;
5
6/// Describe a value for a nested coordinate
7#[derive(PartialEq, Eq, Clone, Debug)]
8pub enum NestedValue<C, V> {
9 /// Category value
10 Category(C),
11 /// One exact nested value
12 Value(C, V),
13}
14
15impl<C, V> NestedValue<C, V> {
16 /// Get the category of current nest value
17 pub fn category(&self) -> &C {
18 match self {
19 NestedValue::Category(cat) => cat,
20 NestedValue::Value(cat, _) => cat,
21 }
22 }
23 /// Get the nested value from this value
24 pub fn nested_value(&self) -> Option<&V> {
25 match self {
26 NestedValue::Category(_) => None,
27 NestedValue::Value(_, val) => Some(val),
28 }
29 }
30}
31
32impl<C, V> From<(C, V)> for NestedValue<C, V> {
33 fn from((cat, val): (C, V)) -> NestedValue<C, V> {
34 NestedValue::Value(cat, val)
35 }
36}
37
38impl<C, V> From<C> for NestedValue<C, V> {
39 fn from(cat: C) -> NestedValue<C, V> {
40 NestedValue::Category(cat)
41 }
42}
43
44/// A nested coordinate spec which is a discrete coordinate on the top level and
45/// for each value in discrete value, there is a secondary coordinate system.
46/// And the value is defined as a tuple of primary coordinate value and secondary
47/// coordinate value
48pub struct NestedRange<Primary: DiscreteRanged, Secondary: Ranged> {
49 primary: Primary,
50 secondary: Vec<Secondary>,
51}
52
53impl<PT, ST, P, S> ValueFormatter<NestedValue<PT, ST>> for NestedRange<P, S>
54where
55 P: Ranged<ValueType = PT> + DiscreteRanged,
56 S: Ranged<ValueType = ST>,
57 P: ValueFormatter<PT>,
58 S: ValueFormatter<ST>,
59{
60 fn format(value: &NestedValue<PT, ST>) -> String {
61 match value {
62 NestedValue::Category(cat) => P::format(cat),
63 NestedValue::Value(_, val) => S::format(val),
64 }
65 }
66}
67
68impl<P: DiscreteRanged, S: Ranged> Ranged for NestedRange<P, S> {
69 type FormatOption = NoDefaultFormatting;
70 type ValueType = NestedValue<P::ValueType, S::ValueType>;
71
72 fn range(&self) -> Range<Self::ValueType> {
73 let primary_range = self.primary.range();
74
75 let secondary_left = self.secondary[0].range().start;
76 let secondary_right = self.secondary[self.primary.size() - 1].range().end;
77
78 NestedValue::Value(primary_range.start, secondary_left)
79 ..NestedValue::Value(primary_range.end, secondary_right)
80 }
81
82 fn map(&self, value: &Self::ValueType, limit: (i32, i32)) -> i32 {
83 let idx = self.primary.index_of(value.category()).unwrap_or(0);
84 let total = self.primary.size();
85
86 let bucket_size = (limit.1 - limit.0) / total as i32;
87 let mut residual = (limit.1 - limit.0) % total as i32;
88
89 if residual < 0 {
90 residual += total as i32;
91 }
92
93 let s_left = limit.0 + bucket_size * idx as i32 + residual.min(idx as i32);
94 let s_right = s_left + bucket_size + if (residual as usize) < idx { 1 } else { 0 };
95
96 if let Some(secondary_value) = value.nested_value() {
97 self.secondary[idx].map(secondary_value, (s_left, s_right))
98 } else {
99 (s_left + s_right) / 2
100 }
101 }
102
103 fn key_points<Hint: KeyPointHint>(&self, hint: Hint) -> Vec<Self::ValueType> {
104 if !hint.weight().allow_light_points() || hint.max_num_points() < self.primary.size() * 2 {
105 self.primary
106 .key_points(hint)
107 .into_iter()
108 .map(NestedValue::Category)
109 .collect()
110 } else {
111 let secondary_size =
112 (hint.max_num_points() - self.primary.size()) / self.primary.size();
113 self.primary
114 .values()
115 .enumerate()
116 .flat_map(|(idx, val)| {
117 std::iter::once(NestedValue::Category(val)).chain(
118 self.secondary[idx]
119 .key_points(secondary_size)
120 .into_iter()
121 .map(move |v| {
122 NestedValue::Value(self.primary.from_index(idx).unwrap(), v)
123 }),
124 )
125 })
126 .collect()
127 }
128 }
129}
130
131impl<P: DiscreteRanged, S: DiscreteRanged> DiscreteRanged for NestedRange<P, S> {
132 fn size(&self) -> usize {
133 self.secondary.iter().map(|x| x.size()).sum::<usize>()
134 }
135
136 fn index_of(&self, value: &Self::ValueType) -> Option<usize> {
137 let p_idx = self.primary.index_of(value.category())?;
138 let s_idx = self.secondary[p_idx].index_of(value.nested_value()?)?;
139 Some(
140 s_idx
141 + self.secondary[..p_idx]
142 .iter()
143 .map(|x| x.size())
144 .sum::<usize>(),
145 )
146 }
147
148 fn from_index(&self, mut index: usize) -> Option<Self::ValueType> {
149 for (p_idx, snd) in self.secondary.iter().enumerate() {
150 if snd.size() > index {
151 return Some(NestedValue::Value(
152 self.primary.from_index(p_idx).unwrap(),
153 snd.from_index(index).unwrap(),
154 ));
155 }
156 index -= snd.size();
157 }
158 None
159 }
160}
161
162/// Used to build a nested coordinate system.
163pub trait BuildNestedCoord: AsRangedCoord
164where
165 Self::CoordDescType: DiscreteRanged,
166{
167 /// Builds a nested coordinate system.
168 fn nested_coord<S: AsRangedCoord>(
169 self,
170 builder: impl Fn(<Self::CoordDescType as Ranged>::ValueType) -> S,
171 ) -> NestedRange<Self::CoordDescType, S::CoordDescType> {
172 let primary: Self::CoordDescType = self.into();
173 assert!(primary.size() > 0);
174
175 let secondary = primary
176 .values()
177 .map(|value| builder(value).into())
178 .collect();
179
180 NestedRange { primary, secondary }
181 }
182}
183
184impl<T: AsRangedCoord> BuildNestedCoord for T where T::CoordDescType: DiscreteRanged {}
185
186#[cfg(test)]
187mod test {
188 use super::*;
189
190 #[test]
191 fn test_nested_coord() {
192 let coord = (0..10).nested_coord(|x| 0..(x + 1));
193
194 let range = coord.range();
195
196 assert_eq!(NestedValue::Value(0, 0)..NestedValue::Value(10, 11), range);
197 assert_eq!(coord.map(&NestedValue::Category(0), (0, 1100)), 50);
198 assert_eq!(coord.map(&NestedValue::Value(0, 0), (0, 1100)), 0);
199 assert_eq!(coord.map(&NestedValue::Value(5, 4), (0, 1100)), 567);
200
201 assert_eq!(coord.size(), (2 + 12) * 11 / 2);
202 assert_eq!(coord.index_of(&NestedValue::Value(5, 4)), Some(24));
203 assert_eq!(coord.from_index(24), Some(NestedValue::Value(5, 4)));
204 }
205}
206