optimal_binary/
lib.rs

1//! Utilities for working with binary optimizers.
2
3use num_traits::{pow, One, Zero};
4use std::ops::{Add, Div, Mul, RangeInclusive, Sub};
5
6#[cfg(feature = "serde")]
7use serde::{Deserialize, Serialize};
8
9/// Reduce innermost axis
10/// to numbers within range.
11/// Leftmost is least significant.
12///
13/// # Examples
14///
15/// ```
16/// use optimal_binary::ToRealLE;
17///
18/// // It returns lower bound for empty arrays:
19/// assert_eq!(ToRealLE::new(1.0..=2.0, 0).decode([]), 1.);
20///
21/// // It returns lower bound when all bits are false:
22/// assert_eq!(ToRealLE::new(0.0..=1.0, 1).decode([false]), 0.);
23/// assert_eq!(ToRealLE::new(1.0..=2.0, 2).decode([false, false]), 1.);
24///
25/// // It returns upper bound when all bits are true:
26/// assert_eq!(ToRealLE::new(0.0..=1.0, 1).decode([true]), 1.);
27/// assert_eq!(ToRealLE::new(1.0..=2.0, 2).decode([true, true]), 2.);
28///
29/// // It returns a number between lower and upper bound when some bits are true:
30/// assert_eq!(ToRealLE::new(1.0..=4.0, 2).decode([true, false]), 2.);
31/// assert_eq!(ToRealLE::new(1.0..=4.0, 2).decode([false, true]), 3.);
32/// ```
33#[derive(Clone, Debug, PartialEq, Eq)]
34#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
35#[cfg_attr(
36    feature = "serde",
37    serde(bound(deserialize = "T: One + Add<Output = T> + Deserialize<'de>"))
38)]
39pub struct ToRealLE<T> {
40    #[cfg_attr(feature = "serde", serde(skip))]
41    to_int: ToIntLE<T>,
42    start: T,
43    a: Option<T>,
44}
45
46impl<T> ToRealLE<T> {
47    pub fn new(range: RangeInclusive<T>, len: usize) -> Self
48    where
49        T: Copy + One + Add<Output = T> + Sub<Output = T> + Div<Output = T>,
50    {
51        let to_int = ToIntLE::new();
52        let (start, end) = range.into_inner();
53        Self {
54            a: if len > 0 {
55                Some((end - start) / (pow(to_int.two, len) - T::one()))
56            } else {
57                None
58            },
59            start,
60            to_int,
61        }
62    }
63
64    pub fn decode(&self, bits: impl IntoIterator<Item = bool>) -> T
65    where
66        T: Copy + Zero + One + Add<Output = T> + Mul<Output = T>,
67    {
68        match self.a {
69            Some(a) => a * self.to_int.decode(bits) + self.start,
70            None => self.start,
71        }
72    }
73}
74
75/// Reduce to base 10 integer representations of bits.
76/// Leftmost is least significant.
77///
78/// # Examples
79///
80/// ```
81/// use optimal_binary::ToIntLE;
82///
83/// let to_int_le: ToIntLE<u8> = ToIntLE::new();
84///
85/// // It returns 0 when empty:
86/// assert_eq!(to_int_le.decode([]), 0_u8);
87///
88/// // It returns the base 10 integer represented by binary bits:
89/// assert_eq!(to_int_le.decode([false]), 0_u8);
90/// assert_eq!(to_int_le.decode([false, false]), 0_u8);
91/// assert_eq!(to_int_le.decode([false, false, false]), 0_u8);
92/// assert_eq!(to_int_le.decode([true]), 1_u8);
93/// assert_eq!(to_int_le.decode([true, true]), 3_u8);
94/// assert_eq!(to_int_le.decode([true, true, true]), 7_u8);
95///
96/// // It treats leftmost as least significant:
97/// assert_eq!(to_int_le.decode([false, true]), 2_u8);
98/// assert_eq!(to_int_le.decode([false, false, true]), 4_u8);
99/// ```
100#[derive(Clone, Debug, PartialEq, Eq)]
101#[cfg_attr(feature = "serde", derive(Serialize))]
102pub struct ToIntLE<T> {
103    #[cfg_attr(feature = "serde", serde(skip))]
104    two: T,
105}
106
107impl<T> Default for ToIntLE<T>
108where
109    T: One + Add<Output = T>,
110{
111    fn default() -> Self {
112        Self::new()
113    }
114}
115
116#[cfg(feature = "serde")]
117impl<'de, T> Deserialize<'de> for ToIntLE<T>
118where
119    T: One + Add<Output = T>,
120{
121    fn deserialize<D>(_deserializer: D) -> Result<Self, D::Error>
122    where
123        D: serde::Deserializer<'de>,
124    {
125        Ok(Self::new())
126    }
127}
128
129impl<T> ToIntLE<T> {
130    pub fn new() -> Self
131    where
132        T: One + Add<Output = T>,
133    {
134        Self {
135            two: T::one() + T::one(),
136        }
137    }
138
139    pub fn decode(&self, bits: impl IntoIterator<Item = bool>) -> T
140    where
141        T: Copy + Zero + One + Add<Output = T> + Mul<Output = T>,
142    {
143        bits.into_iter()
144            .fold((T::zero(), T::one()), |(acc, a), b| {
145                (if b { acc + a } else { acc }, self.two * a)
146            })
147            .0
148    }
149}