1use crate::Result;
2use std::fmt;
3
4#[derive(Clone, Copy, Debug, PartialEq)]
6pub struct Transform {
7 pub scale: f64,
9
10 pub offset: f64,
12}
13
14impl Transform {
15 pub fn direct(&self, n: i32) -> f64 {
25 self.scale * f64::from(n) + self.offset
26 }
27
28 pub fn inverse(&self, n: f64) -> Result<i32> {
40 self.inverse_with_rounding_mode(n, RoundingMode::Round)
41 }
42
43 pub(crate) fn inverse_with_rounding_mode(&self, n: f64, r: RoundingMode) -> Result<i32> {
44 use crate::Error;
45
46 fn round(n: f64, r: RoundingMode) -> f64 {
47 match r {
48 RoundingMode::Round => n.round(),
49 RoundingMode::Ceil => n.ceil(),
50 RoundingMode::Floor => n.floor(),
51 }
52 }
53
54 let n = round((n - self.offset) / self.scale, r);
55
56 if n > f64::from(i32::MAX) || n < f64::from(i32::MIN) {
57 Err(Error::InvalidInverseTransform {
58 n,
59 transform: *self,
60 })
61 } else {
62 Ok(n as i32)
63 }
64 }
65}
66
67impl Default for Transform {
68 fn default() -> Transform {
69 Transform {
70 scale: 0.001,
71 offset: 0.,
72 }
73 }
74}
75
76impl fmt::Display for Transform {
77 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
78 write!(f, "`{} * x + {}`", self.scale, self.offset)
79 }
80}
81
82pub(crate) enum RoundingMode {
83 Round,
84 Ceil,
85 Floor,
86}
87
88#[cfg(test)]
89mod tests {
90 use super::*;
91
92 #[test]
93 fn too_large() {
94 let transform = Transform::default();
95 let n = i32::MAX as f64 * transform.scale + 1.;
96 assert!(transform.inverse(n).is_err());
97 }
98
99 #[test]
100 fn too_small() {
101 let transform = Transform::default();
102 let n = i32::MIN as f64 * transform.scale - 1.;
103 assert!(transform.inverse(n).is_err());
104 }
105}