#![no_std]
use core::ops::{Add, Div, Mul, Range, Sub};
use num_traits::{CheckedAdd, CheckedDiv, CheckedMul, CheckedSub};
pub trait MapRange: Sized {
#[must_use]
fn map_range(self, from: Range<Self>, to: Range<Self>) -> Self;
}
pub trait CheckedMapRange: Sized {
#[must_use]
fn checked_map_range(self, from: Range<Self>, to: Range<Self>) -> Option<Self>;
}
impl<T> MapRange for T
where
T: Copy + Add<Output = Self> + Sub<Output = Self> + Mul<Output = Self> + Div<Output = Self>,
{
fn map_range(self, from: Range<Self>, to: Range<Self>) -> Self {
to.start + (self - from.start) * (to.end - to.start) / (from.end - from.start)
}
}
impl<T> CheckedMapRange for T
where
T: CheckedAdd<Output = Self>
+ CheckedSub<Output = Self>
+ CheckedMul<Output = Self>
+ CheckedDiv<Output = Self>,
{
fn checked_map_range(self, from: Range<Self>, to: Range<Self>) -> Option<Self> {
to.start.checked_add(
&self
.checked_sub(&from.start)?
.checked_mul(&to.end.checked_sub(&to.start)?)?
.checked_div(&from.end.checked_sub(&from.start)?)?,
)
}
}
#[cfg(test)]
mod tests {
use crate::MapRange;
#[test]
fn test_f32_map() {
assert!((5.0_f32.map_range(0.0..10.0, 0.0..20.0) - 10.0).abs() <= f32::EPSILON);
assert!((5.0_f32.map_range(0.0..10.0, 10.0..0.0) - 5.0).abs() <= f32::EPSILON);
}
#[test]
fn test_i32_map() {
assert_eq!(5_i32.map_range(0..10, -10..10), 0);
}
}