#![allow(missing_docs, unused)]
use crate::{is, isize_up, nz, unwrap, usize_up};
#[doc = crate::_tags!(quant)]
#[doc = crate::_doc_location!("num/quant")]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Scale<T>(T);
macro_rules! impl_scale {
() => {
impl_scale![sint: i8:i16, i16:i32, i32:i64, i64:i128, isize:isize_up, i128:i128];
impl_scale![uint: u8:u16, u16:u32, u32:u64, u64:u128, usize:usize_up, u128:u128];
};
( // signed integers specific methods and optimizations.
//
// $T: the signed type
// $UP: the signed upscaled type
sint: $( $T:ty : $UP:ty ),+ $(,)?) => { $( impl_scale!(@sint: $T:$UP); )+ };
(@sint: $T:ty : $UP:ty ) => {
impl Scale<$T> {
pub const fn scale(self, imin: $T, imax: $T, omin: $T, omax: $T) -> $T {
if imax == imin { return omin; }
let irange = (imax as $UP).abs_diff(imin as $UP);
let orange = (omax as $UP).abs_diff(omin as $UP);
let v = self.0 as $UP;
let shift = irange.trailing_zeros();
if irange == (1 << shift) { let scaled = (v.abs_diff(imin as $UP).wrapping_mul(orange) >> shift) as $T;
is![self.0 < imin, omin.wrapping_sub(scaled), omin.wrapping_add(scaled)]
} else { let scaled = (v.abs_diff(imin as $UP).wrapping_mul(orange) / irange) as $T;
is![self.0 < imin, omin.wrapping_sub(scaled), omin.wrapping_add(scaled)]
}
}
impl_scale! {@shared: $T:$UP }
}
};
( // unsigned integers specific methods and optimizations.
uint: $( $T:ty : $UP:ty ),+ $(,)?) => { $( impl_scale!(@uint: $T : $UP); )+ };
(@uint: $T:ty : $UP:ty) => {
impl Scale<$T> {
pub const fn scale(self, imin: $T, imax: $T, omin: $T, omax: $T) -> $T {
if imax == imin { return omin; }
let v = self.0 as $UP;
let (imin, imax) = (imin as $UP, imax as $UP);
let (omin, omax) = (omin as $UP, omax as $UP);
let divisor = imax.wrapping_sub(imin);
let shift = divisor.trailing_zeros() as $UP;
if divisor == (1 << shift) { (v.wrapping_sub(imin)
.wrapping_mul(omax.wrapping_sub(omin)) >> shift + omin) as $T
} else { ((v - imin) * (omax - omin) / divisor + omin) as $T
}
}
impl_scale! {@shared: $T:$UP }
}
};
( // shared methods
shared: $( $T:ty : $UP:ty ),+ $(,)?) => { $( impl_scale!(@shared: $T : $UP); )+ };
(@shared: $T:ty : $UP:ty) => {
pub const fn scale_sat(self, imin: $T, imax: $T, omin: $T, omax: $T) -> $T {
let v = self.0;
let scaled = (v - imin).saturating_mul(omax - omin) / (imax - imin);
scaled.saturating_add(omin)
}
pub const fn scale_wrap(self, imin: $T, imax: $T, omin: $T, omax: $T) -> $T {
let v = self.0 as $UP;
let (imin, imax) = (imin as $UP, imax as $UP);
let (omin, omax) = (omin as $UP, omax as $UP);
((v - imin) * (omax - omin) / (imax - imin) + omin) as $T
}
};
}
impl_scale!();
#[cfg(test)]
mod tests {
use super::Scale;
#[test]
fn uint_scale() {
assert_eq![Scale(32u8).scale(0, 32, 0, 64), 64];
assert_eq![Scale(32u8).scale(0, 64, 0, 128), 64];
assert_eq![Scale(32u8).scale(0, 32, 0, 16), 16];
assert_eq![Scale(31u8).scale(0, 31, 0, 62), 62];
assert_eq![Scale(31u8).scale(0, 63, 0, 126), 62];
}
#[test]
fn sint_scale() {
}
#[test]
fn scale_wrap() {}
}