diff --git a/DIRECTORY.md b/DIRECTORY.md index 3685ba5bf56..36c6563b9b6 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -46,6 +46,7 @@ * [Xor](https://github.com/TheAlgorithms/Rust/blob/master/src/ciphers/xor.rs) * Compression * [Run Length Encoding](https://github.com/TheAlgorithms/Rust/blob/master/src/compression/run_length_encoding.rs) + * [Move-To-Front Encoding](https://github.com/TheAlgorithms/Rust/blob/master/src/compression/move_to_front.rs) * Conversions * [Binary To Decimal](https://github.com/TheAlgorithms/Rust/blob/master/src/conversions/binary_to_decimal.rs) * [Binary To Hexadecimal](https://github.com/TheAlgorithms/Rust/blob/master/src/conversions/binary_to_hexadecimal.rs) diff --git a/src/compression/mod.rs b/src/compression/mod.rs index 7759b3ab8e4..7acbee56ec5 100644 --- a/src/compression/mod.rs +++ b/src/compression/mod.rs @@ -1,3 +1,5 @@ +mod move_to_front; mod run_length_encoding; +pub use self::move_to_front::{move_to_front_decode, move_to_front_encode}; pub use self::run_length_encoding::{run_length_decode, run_length_encode}; diff --git a/src/compression/move_to_front.rs b/src/compression/move_to_front.rs new file mode 100644 index 00000000000..fe38b02ef7c --- /dev/null +++ b/src/compression/move_to_front.rs @@ -0,0 +1,60 @@ +// https://en.wikipedia.org/wiki/Move-to-front_transform + +fn blank_char_table() -> Vec { + (0..=255).map(|ch| ch as u8 as char).collect() +} + +pub fn move_to_front_encode(text: &str) -> Vec { + let mut char_table = blank_char_table(); + let mut result = Vec::new(); + + for ch in text.chars() { + if let Some(position) = char_table.iter().position(|&x| x == ch) { + result.push(position as u8); + char_table.remove(position); + char_table.insert(0, ch); + } + } + + result +} + +pub fn move_to_front_decode(encoded: &[u8]) -> String { + let mut char_table = blank_char_table(); + let mut result = String::new(); + + for &pos in encoded { + let ch = char_table[pos as usize]; + result.push(ch); + char_table.remove(pos as usize); + char_table.insert(0, ch); + } + + result +} + +#[cfg(test)] +mod test { + use super::*; + + macro_rules! test_mtf { + ($($name:ident: ($text:expr, $encoded:expr),)*) => { + $( + #[test] + fn $name() { + assert_eq!(move_to_front_encode($text), $encoded); + assert_eq!(move_to_front_decode($encoded), $text); + } + )* + } + } + + test_mtf! { + empty: ("", &[]), + single_char: ("@", &[64]), + repeated_chars: ("aaba", &[97, 0, 98, 1]), + mixed_chars: ("aZ!", &[97, 91, 35]), + word: ("banana", &[98, 98, 110, 1, 1, 1]), + special_chars: ("\0\n\t", &[0, 10, 10]), + } +}