1use std::{
2 cmp::Ordering,
3 fmt::{self, Display, Formatter},
4};
5
6use derive_more::Constructor;
7use getset::{Getters, MutGetters, Setters};
8
9#[derive(Constructor, Debug, Default, Clone, Getters, Setters)]
11pub struct Point {
12 #[getset(get = "pub", set = "pub")]
13 number: f64,
14 #[getset(get = "pub", set = "pub")]
15 mark: String,
16}
17
18impl Display for Point {
19 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
20 writeln!(f, "Point:\t{}\t{}", self.number, self.mark)
21 }
22}
23
24#[derive(Clone, Constructor, Debug, Default, Getters, MutGetters, Setters)]
26pub struct Tier {
27 #[getset(get = "pub", set = "pub")]
28 name: String,
29 #[getset(get = "pub")]
30 xmin: f64,
31 #[getset(get = "pub")]
32 xmax: f64,
33 #[getset(get = "pub", get_mut = "pub", set = "pub")]
34 points: Vec<Point>,
35}
36
37impl Tier {
38 pub fn set_xmin(&mut self, xmin: f64, warn: Option<bool>) {
39 if warn.is_some_and(|b| b) {
40 let min_point = self
41 .points
42 .iter()
43 .filter_map(|point| {
44 point
45 .number
46 .partial_cmp(&f64::INFINITY)
47 .map(|_| point.number)
48 })
49 .min_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Greater)); if min_point.is_some_and(|min| xmin > min) {
52 eprintln!(
53 "Warning: Tier `{}` has a minimum point of {} but the set xmin is {}",
54 self.name,
55 min_point.unwrap_or_default(),
56 xmin
57 );
58 }
59 }
60
61 self.xmin = xmin;
62 }
63
64 pub fn set_xmax(&mut self, xmax: f64, warn: Option<bool>) {
65 if warn.is_some_and(|b| b) {
66 let max_point = self
67 .points
68 .iter()
69 .filter_map(|point| {
70 point
71 .number
72 .partial_cmp(&f64::INFINITY)
73 .map(|_| point.number)
74 })
75 .max_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Less)); if max_point.is_some_and(|max| xmax < max) {
78 eprintln!(
79 "Warning: Tier `{}` has a maximum point of {} but the set xmax is {}",
80 self.name,
81 max_point.unwrap_or_default(),
82 xmax
83 );
84 }
85 }
86
87 self.xmax = xmax;
88 }
89
90 #[must_use]
91 pub fn get_size(&self) -> usize {
92 self.points.len()
93 }
94
95 pub fn push_point<W: Into<Option<bool>> + Copy>(&mut self, point: Point, warn: W) {
103 if warn.into().unwrap_or_default() {
104 if point.number < self.xmin {
105 eprintln!(
106 "Warning: Tier `{}` has a number of {} but the tier has an xmin of {}",
107 self.name, point.number, self.xmin
108 );
109 }
110 if point.number > self.xmax {
111 eprintln!(
112 "Warning: Tier `{}` has a number of {} but the tier has an xmax of {}",
113 self.name, point.number, self.xmax
114 );
115 }
116 }
117
118 self.points.push(point);
119
120 self.reorder();
121 }
122
123 pub fn push_points<W: Into<Option<bool>> + Copy>(&mut self, points: Vec<Point>, warn: W) {
131 if warn.into().unwrap_or_default() {
132 for point in &points {
133 if point.number < self.xmin {
134 eprintln!(
135 "Warning: Tier `{}` has a number of {} but the tier has an xmin of {}",
136 self.name, point.number, self.xmin
137 );
138 }
139 if point.number > self.xmax {
140 eprintln!(
141 "Warning: Tier `{}` has a number of {} but the tier has an xmax of {}",
142 self.name, point.number, self.xmax
143 );
144 }
145 }
146 }
147
148 self.points.extend(points);
149
150 self.reorder();
151 }
152
153 #[must_use]
159 pub fn check_overlaps(&self) -> Option<Vec<(u64, u64)>> {
160 let mut overlaps: Vec<(u64, u64)> = Vec::new();
161 for (i, point) in self.points.iter().enumerate() {
162 for (j, other_point) in self.points.iter().enumerate() {
163 #[allow(clippy::float_cmp)]
164 if i != j && point.number == other_point.number {
165 overlaps.push((i as u64, j as u64));
166 }
167 }
168 }
169 if overlaps.is_empty() {
170 None
171 } else {
172 Some(overlaps)
173 }
174 }
175
176 pub fn reorder(&mut self) {
178 self.points
179 .sort_by(|a, b| a.number.partial_cmp(&b.number).unwrap_or(Ordering::Equal));
180 }
181}
182
183impl Display for Tier {
184 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
185 write!(
186 f,
187 "PointTier {}:
188 xmin: {}
189 xmax: {}
190 point count: {}",
191 self.name,
192 self.xmin,
193 self.xmax,
194 self.points.len()
195 )
196 }
197}
198
199#[cfg(test)]
200#[allow(clippy::float_cmp)]
201mod test_point {
202 #[test]
203 fn test_point() {
204 use crate::point::Point;
205
206 let point = Point::new(1.0, "test".to_string());
207 assert_eq!(point.number(), &1.0);
208 assert_eq!(point.mark(), "test");
209 assert_eq!(point.to_string(), "Point:\t1\ttest\n");
210 }
211}
212
213#[cfg(test)]
214#[allow(clippy::float_cmp)]
215mod test_point_tier {
216 #[test]
217 fn set_xmin() {
218 use crate::point::Tier;
219
220 let mut tier = Tier::new("test".to_string(), 0.0, 10.0, vec![]);
221 tier.set_xmin(5.0, Some(true));
222 assert_eq!(tier.xmin(), &5.0);
223 }
224
225 #[test]
226 fn set_xmax() {
227 use crate::point::Tier;
228
229 let mut tier = Tier::new("test".to_string(), 0.0, 10.0, vec![]);
230 tier.set_xmax(5.0, Some(true));
231 assert_eq!(tier.xmax(), &5.0);
232 }
233
234 #[test]
235 fn get_size() {
236 use crate::point::Tier;
237
238 let tier = Tier::new("test".to_string(), 0.0, 10.0, vec![]);
239 assert_eq!(tier.get_size(), 0);
240 }
241
242 #[test]
243 fn push_point() {
244 use crate::point::{Point, Tier};
245
246 let mut tier = Tier::new("test".to_string(), 0.0, 10.0, vec![]);
247 tier.push_point(Point::new(5.0, "test".to_string()), true);
248 assert_eq!(tier.get_size(), 1);
249 }
250
251 #[test]
252 fn push_points() {
253 use crate::point::{Point, Tier};
254
255 let mut tier = Tier::new("test".to_string(), 0.0, 10.0, vec![]);
256 tier.push_points(vec![Point::new(5.0, "test".to_string())], true);
257 assert_eq!(tier.get_size(), 1);
258 }
259
260 #[test]
261 fn check_overlaps() {
262 use crate::point::{Point, Tier};
263
264 let mut tier = Tier::new("test".to_string(), 0.0, 10.0, vec![]);
265 tier.push_points(
266 vec![
267 Point::new(5.0, "test".to_string()),
268 Point::new(5.0, "test".to_string()),
269 ],
270 true,
271 );
272 assert_eq!(tier.check_overlaps(), Some(vec![(0, 1), (1, 0)]));
273 }
274
275 #[test]
276 fn reorder() {
277 use crate::point::{Point, Tier};
278
279 let mut tier = Tier::new(
280 "test".to_string(),
281 0.0,
282 10.0,
283 vec![
284 Point::new(5.0, "test".to_string()),
285 Point::new(3.0, "test".to_string()),
286 ],
287 );
288 tier.reorder();
289 assert_eq!(tier.points()[0].number(), &3.0);
290 assert_eq!(tier.points()[1].number(), &5.0);
291 }
292}