use std::{i32, u32, u64, f64};
use std::iter::{Iterator, IntoIterator};
pub fn smallest_power_of_two<N>(n : N) -> usize
where N : Into<u64>
{
let n64 : u64 = n.into();
let mut r = 1_u64;
let mut log_two = 0;
while r < n64
{
r <<= 1;
log_two += 1;
}
log_two
}
pub fn bits_required<N>(n : N) -> usize
where N : Into<u64>
{
smallest_power_of_two(n.into() + 1)
}
#[derive(Clone, PartialEq, Debug)]
pub struct IntegerDataRange {
pub low : i64,
pub high : i64,
pub bits_required : usize
}
impl IntegerDataRange {
pub fn new<I>(low_i : I, high_i : I) -> Self
where I : Into<i64>
{
let low = low_i.into();
let high = high_i.into();
IntegerDataRange {
low,
high,
bits_required : bits_required((high - low) as u64)
}
}
pub fn from_i32<I>(points : &[I]) -> Self
where
for<'a> &'a I: IntoIterator<Item = &'a i32>,
{
let mut low = i32::MAX;
let mut high = i32::MIN;
for point in points.iter() {
for coordinate in point.into_iter().map(|c| *c) {
if coordinate > high { high = coordinate; }
if coordinate < low { low = coordinate; }
}
}
Self::new(low, high)
}
pub fn from_u32<I>(points : &[I]) -> Self
where
for<'a> &'a I: IntoIterator<Item = &'a u32>,
{
let mut low = u32::MAX as i64;
let mut high = 0_i64;
for point in points.iter() {
for coordinate in point.into_iter().map(|c| *c as i64) {
if coordinate > high { high = coordinate; }
if coordinate < low { low = coordinate; }
}
}
Self::new(low, high)
}
pub fn range(&self) -> u32 { (self.high - self.low) as u32 }
pub fn normalize<I>(&self, coordinate : I) -> u32
where I : Into<i64>
{
(coordinate.into() - self.low) as u32
}
pub fn compress<I>(&self, coordinate : I, bits_allocated : usize) -> u32
where I : Into<i64>
{
let uncompressed = self.normalize(coordinate);
if bits_allocated < self.bits_required { uncompressed >> (self.bits_required - bits_allocated) }
else { uncompressed }
}
}
#[derive(Clone, PartialEq, Debug)]
pub struct FloatDataRange {
pub low : f64,
pub high : f64,
pub scale : f64,
pub bits_required : usize
}
impl FloatDataRange {
pub fn new(low : f64, high : f64, scale : f64) -> Self {
let scaled_range = ((high - low) * scale).ceil() as u32;
FloatDataRange {
low,
high,
scale,
bits_required : bits_required(scaled_range)
}
}
pub fn from_f64<I>(points : &[I], scale : f64) -> Self
where
for<'a> &'a I: IntoIterator<Item = &'a f64>,
{
let mut low = f64::MAX;
let mut high = f64::MIN;
for point in points.iter() {
for coordinate in point.into_iter().map(|c| *c) {
if coordinate > high { high = coordinate; }
if coordinate < low { low = coordinate; }
}
}
FloatDataRange::new(low, high, scale)
}
pub fn range(&self) -> f64 { self.high - self.low }
pub fn normalize(&self, coordinate : f64) -> u32 {
((coordinate - self.low) * self.scale).ceil() as u32
}
pub fn compress(&self, coordinate : f64, bits_allocated : usize) -> u32 {
let uncompressed = self.normalize(coordinate);
if bits_allocated < self.bits_required { uncompressed >> (self.bits_required - bits_allocated) }
else { uncompressed }
}
}
#[cfg(test)]
mod tests {
#[allow(unused_imports)]
use spectral::prelude::*;
use super::{IntegerDataRange, FloatDataRange};
#[test]
fn from_i32() {
let points = test_points_i32();
let actual_range = IntegerDataRange::from_i32(&points);
let expected_range = IntegerDataRange::new(-100, 100);
asserting("Range correct").that(&actual_range).is_equal_to(expected_range);
}
#[test]
fn normalize_i32() {
let points = test_points_i32();
let range = IntegerDataRange::from_i32(&points);
let actual_normalized = range.normalize(-16);
let expected_normalized = 84;
asserting("Normalized value correct").that(&actual_normalized).is_equal_to(expected_normalized);
}
#[test]
fn compress_i32() {
let points = test_points_i32();
let range = IntegerDataRange::from_i32(&points);
let actual_compressed = range.compress(-16, 7);
let expected_compressed = 42;
asserting("Compressed value correct").that(&actual_compressed).is_equal_to(expected_compressed);
}
#[test]
fn smallest_power_of_two() {
let in_out_pairs = vec![(0_u64,0), (1,0), (2,1), (3,2), (4,2), (5,3), (6,3), (7,3), (8,3), (9,4)];
for (n, expected) in in_out_pairs {
let actual = super::smallest_power_of_two(n);
asserting(&format!("n = {}, actual = {}, expected = {}", n, actual, expected)).that(&actual).is_equal_to(expected);
}
}
#[test]
fn from_f64() {
let points = test_points_f64();
let actual_range = FloatDataRange::from_f64(&points, 10.0);
let expected_range = FloatDataRange::new(-100.0, 100.19, 10.0);
asserting("Range correct").that(&actual_range).is_equal_to(expected_range);
}
#[test]
fn normalize_f64() {
let points = test_points_f64();
let range = FloatDataRange::from_f64(&points, 10.0);
let actual_normalized = range.normalize(-16.0);
let expected_normalized = 840;
asserting("Normalized value correct").that(&actual_normalized).is_equal_to(expected_normalized);
}
#[test]
fn compress_f64() {
let points = test_points_f64();
let range = FloatDataRange::from_f64(&points, 10.0);
let actual_compressed = range.compress(-16.0, 7);
let expected_compressed = 52;
asserting("Compressed value correct").that(&actual_compressed).is_equal_to(expected_compressed);
}
fn test_points_i32() -> Vec<Vec<i32>> {
vec![
vec![-10, 5, 3],
vec![-4, 20, 100],
vec![42, -100, 0]
]
}
fn test_points_f64() -> Vec<Vec<f64>> {
vec![
vec![-10.5, 5.27, 3.66],
vec![-4.802, 20.2, 100.19],
vec![42.0, -100.0, 0.0]
]
}
}