pub(crate) const BASE: u32 = 32;
pub(crate) const T_MIN: u32 = 1;
pub(crate) const T_MAX: u32 = 26;
pub(crate) const SKEW: u32 = 38;
pub(crate) const DAMP: u32 = 700;
pub(crate) const INITIAL_BIAS: u32 = 72;
const ALPHABET: &[u8; 32] = b"abcdefghijklmnopqrstuvwxyz012345";
pub(crate) fn adapt_bias(mut delta: u32, num_points: u32, first_time: bool) -> u32 {
delta = if first_time { delta / DAMP } else { delta / 2 };
delta += delta / num_points;
let mut k = 0u32;
let base_minus_tmin = BASE - T_MIN;
let threshold = (base_minus_tmin * T_MAX) / 2;
while delta > threshold {
delta /= base_minus_tmin;
k += BASE;
}
k + ((base_minus_tmin + 1) * delta) / (delta + SKEW)
}
pub(crate) fn encode_digit(d: u32) -> Option<char> {
if d < 32 {
Some(ALPHABET[d as usize] as char)
} else {
None
}
}
pub(crate) fn decode_digit(c: char) -> Option<u32> {
match c {
'a'..='z' => Some(c as u32 - 'a' as u32),
'A'..='Z' => Some(c as u32 - 'A' as u32), '0'..='5' => Some(c as u32 - '0' as u32 + 26),
_ => None,
}
}
pub(crate) fn threshold(k: u32, bias: u32) -> u32 {
if k <= bias + T_MIN {
T_MIN
} else if k >= bias + T_MAX {
T_MAX
} else {
k - bias
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_encode_digit() {
assert_eq!(encode_digit(0), Some('a'));
assert_eq!(encode_digit(25), Some('z'));
assert_eq!(encode_digit(26), Some('0'));
assert_eq!(encode_digit(31), Some('5'));
assert_eq!(encode_digit(32), None);
}
#[test]
fn test_decode_digit() {
assert_eq!(decode_digit('a'), Some(0));
assert_eq!(decode_digit('z'), Some(25));
assert_eq!(decode_digit('A'), Some(0));
assert_eq!(decode_digit('Z'), Some(25));
assert_eq!(decode_digit('0'), Some(26));
assert_eq!(decode_digit('5'), Some(31));
assert_eq!(decode_digit('6'), None);
assert_eq!(decode_digit('-'), None);
}
#[test]
fn test_roundtrip() {
for d in 0..32 {
let c = encode_digit(d).unwrap();
assert_eq!(decode_digit(c), Some(d));
}
}
#[test]
fn test_threshold() {
assert_eq!(threshold(1, 72), T_MIN);
assert_eq!(threshold(73, 72), T_MIN);
assert_eq!(threshold(100, 72), T_MAX);
assert_eq!(threshold(80, 72), 8);
}
#[test]
fn test_adapt_bias() {
let bias = adapt_bias(0, 1, true);
assert!(bias < BASE);
let bias1 = adapt_bias(1000, 1, true);
let bias2 = adapt_bias(1000, 1, false);
assert_ne!(bias1, bias2);
}
}