use crate::domain::text::mapping::convert_ru_en_bidirectional;
const LATIN_BIJECTIVE: &str = "qwertyuiop[]asdfghjkl;'zxcvbnm,.`QWERTYUIOP{}ASDFGHJKL:\"ZXCVBNM<>~";
fn xorshift64(seed: &mut u64) -> u64 {
let mut x = *seed;
x ^= x << 13;
x ^= x >> 7;
x ^= x << 17;
*seed = x;
x
}
fn gen_string(seed: &mut u64, alphabet: &[char], max_len: usize) -> String {
let modulus = u64::try_from(max_len + 1).unwrap_or(u64::MAX);
let len_u64 = xorshift64(seed) % modulus;
let len = usize::try_from(len_u64).unwrap_or(0);
let mut out = String::with_capacity(len);
for _ in 0..len {
let alpha_len = u64::try_from(alphabet.len()).unwrap_or(u64::MAX);
let idx_u64 = xorshift64(seed) % alpha_len;
let idx = usize::try_from(idx_u64).unwrap_or(0);
out.push(alphabet[idx]);
}
out
}
#[test]
fn mapping_roundtrip_latin_only_is_identity_on_double_convert() {
let alphabet: Vec<char> = LATIN_BIJECTIVE
.chars()
.filter(char::is_ascii_alphabetic)
.collect();
let mut seed = 0xD1A5_3EED_5EED_1234u64;
for _ in 0..2000 {
let s = gen_string(&mut seed, &alphabet, 64);
let t = convert_ru_en_bidirectional(&s);
let u = convert_ru_en_bidirectional(&t);
assert_eq!(u, s, "latin roundtrip failed: s={s:?} t={t:?} u={u:?}");
}
}
#[test]
fn mapping_roundtrip_cyrillic_only_is_identity_on_double_convert() {
let alphabet: Vec<char> = "йцукенгшщзфывапролячсмитьЙЦУКЕНГШЩЗФЫВАПРОЛЯЧСМИТЬ"
.chars()
.collect();
let mut seed = 0xBADC_0FFE_EE12_3456u64;
for _ in 0..2000 {
let s = gen_string(&mut seed, &alphabet, 64);
let t = convert_ru_en_bidirectional(&s);
let u = convert_ru_en_bidirectional(&t);
assert_eq!(u, s, "cyr roundtrip failed: s={s:?} t={t:?} u={u:?}");
}
}
#[test]
fn punctuation_rules_apply_only_in_en_to_ru_mode() {
assert_eq!(convert_ru_en_bidirectional("a/"), "ф.");
assert_eq!(convert_ru_en_bidirectional("a?"), "ф,");
assert_eq!(convert_ru_en_bidirectional("/а"), "/f");
}
#[test]
fn ampersand_prefers_latin_direction_when_present() {
assert_eq!(convert_ru_en_bidirectional("a&"), "ф?");
assert_eq!(convert_ru_en_bidirectional("я?"), "z&");
}