use crate::Result;
use std::fmt;
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Transform {
pub scale: f64,
pub offset: f64,
}
impl Transform {
pub fn direct(&self, n: i32) -> f64 {
self.scale * f64::from(n) + self.offset
}
pub fn inverse(&self, n: f64) -> Result<i32> {
self.inverse_with_rounding_mode(n, RoundingMode::Round)
}
pub(crate) fn inverse_with_rounding_mode(&self, n: f64, r: RoundingMode) -> Result<i32> {
use crate::Error;
fn round(n: f64, r: RoundingMode) -> f64 {
match r {
RoundingMode::Round => n.round(),
RoundingMode::Ceil => n.ceil(),
RoundingMode::Floor => n.floor(),
}
}
let n = round((n - self.offset) / self.scale, r);
if n > f64::from(i32::MAX) || n < f64::from(i32::MIN) {
Err(Error::InvalidInverseTransform {
n,
transform: *self,
})
} else {
Ok(n as i32)
}
}
}
impl Default for Transform {
fn default() -> Transform {
Transform {
scale: 0.001,
offset: 0.,
}
}
}
impl fmt::Display for Transform {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "`{} * x + {}`", self.scale, self.offset)
}
}
pub(crate) enum RoundingMode {
Round,
Ceil,
Floor,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn too_large() {
let transform = Transform::default();
let n = i32::MAX as f64 * transform.scale + 1.;
assert!(transform.inverse(n).is_err());
}
#[test]
fn too_small() {
let transform = Transform::default();
let n = i32::MIN as f64 * transform.scale - 1.;
assert!(transform.inverse(n).is_err());
}
}