use alloc::string::String;
const BASE: u32 = 36;
const TMIN: u32 = 1;
const TMAX: u32 = 26;
const SKEW: u32 = 38;
const DAMP: u32 = 700;
const INITIAL_BIAS: u32 = 72;
const INITIAL_N: u32 = 128;
const DELIMITER: char = '-';
pub fn encode(input: &str) -> Result<String, ()> {
let input_len = input.chars().count() as u32;
let mut output: String = input.chars().filter(char::is_ascii).collect();
let b = output.chars().count() as u32;
let mut h = b;
if b > 0 {
output.push(DELIMITER);
}
let mut n = INITIAL_N;
let mut delta: u32 = 0;
let mut bias = INITIAL_BIAS;
while h < input_len {
let m = input.chars().map(u32::from).filter(|&c| c >= n).min().ok_or(())?;
delta = (m - n).checked_mul(h + 1).and_then(|d| delta.checked_add(d)).ok_or(())?;
n = m;
for c in input.chars().map(u32::from) {
if c < n {
delta = delta.checked_add(1).ok_or(())?;
} else if c == n {
let mut q = delta;
let mut k = BASE;
loop {
let t = threshold(k, bias);
if q < t {
break;
}
output.push(encode_digit(t + (q - t) % (BASE - t)));
q = (q - t) / (BASE - t);
k += BASE;
}
output.push(encode_digit(q));
bias = adapt(delta, h + 1, h == b);
delta = 0;
h += 1;
}
}
delta = delta.checked_add(1).ok_or(())?;
n += 1;
}
Ok(output)
}
fn threshold(k: u32, bias: u32) -> u32 {
if k <= bias + TMIN {
TMIN
} else if k >= bias + TMAX {
TMAX
} else {
k - bias
}
}
fn adapt(delta: u32, num_points: u32, first_time: bool) -> u32 {
let mut delta = if first_time { delta / DAMP } else { delta / 2 };
delta += delta / num_points;
let mut k = 0;
while delta > ((BASE - TMIN) * TMAX) / 2 {
delta /= BASE - TMIN;
k += BASE;
}
k + (((BASE - TMIN + 1) * delta) / (delta + SKEW))
}
fn encode_digit(d: u32) -> char {
debug_assert!(d < BASE);
let c = d + 22 + if d < 26 { 75 } else { 0 };
c as u8 as char
}