initial
This commit is contained in:
commit
3cf5b5e652
4 changed files with 118 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
/target
|
||||||
|
ciphertext.txt
|
||||||
7
Cargo.lock
generated
Normal file
7
Cargo.lock
generated
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "substitution-helper"
|
||||||
|
version = "0.1.0"
|
||||||
6
Cargo.toml
Normal file
6
Cargo.toml
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
[package]
|
||||||
|
name = "substitution-helper"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
103
src/main.rs
Normal file
103
src/main.rs
Normal file
|
|
@ -0,0 +1,103 @@
|
||||||
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
|
env,
|
||||||
|
io::{self, Read, Write},
|
||||||
|
process,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut subst = HashMap::new();
|
||||||
|
for pair in env::args().skip(1) {
|
||||||
|
let &[from, to] = pair.as_bytes() else {
|
||||||
|
eprintln!("Usage: substitution-helper AB CD EF ...");
|
||||||
|
process::exit(1);
|
||||||
|
};
|
||||||
|
subst.insert(from.to_ascii_uppercase(), to.to_ascii_uppercase());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut inp = String::new();
|
||||||
|
io::stdin().read_to_string(&mut inp).unwrap();
|
||||||
|
let mut out = io::stdout().lock();
|
||||||
|
|
||||||
|
let mut byte_frequencies: HashMap<[char; 1], usize> = HashMap::new();
|
||||||
|
for c in inp.chars().filter(char::is_ascii_alphabetic) {
|
||||||
|
*byte_frequencies
|
||||||
|
.entry([c.to_ascii_uppercase()])
|
||||||
|
.or_default() += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("Letters:");
|
||||||
|
print_freqs(byte_frequencies, &subst);
|
||||||
|
println!();
|
||||||
|
|
||||||
|
let mut digram_freqs: HashMap<[char; 2], usize> = HashMap::new();
|
||||||
|
for slice in inp.as_bytes().windows(2) {
|
||||||
|
let [a, b] = slice else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
if !(a.is_ascii_alphabetic() && b.is_ascii_alphabetic()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
*digram_freqs.entry([*a as char, *b as char]).or_default() += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("Digrams:");
|
||||||
|
print_freqs(digram_freqs, &subst);
|
||||||
|
println!();
|
||||||
|
|
||||||
|
let mut trigram_freqs: HashMap<[char; 3], usize> = HashMap::new();
|
||||||
|
for slice in inp.as_bytes().windows(3) {
|
||||||
|
let [a, b, c] = slice else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
if !(a.is_ascii_alphabetic() && b.is_ascii_alphabetic() && c.is_ascii_alphabetic()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
*trigram_freqs
|
||||||
|
.entry([*a as char, *b as char, *c as char])
|
||||||
|
.or_default() += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("Trigrams:");
|
||||||
|
print_freqs(trigram_freqs, &subst);
|
||||||
|
println!();
|
||||||
|
|
||||||
|
for byte in inp.bytes() {
|
||||||
|
let new_byte = subst
|
||||||
|
.get(&byte.to_ascii_uppercase())
|
||||||
|
.copied()
|
||||||
|
.unwrap_or(byte.to_ascii_lowercase());
|
||||||
|
// .unwrap_or(b'.');
|
||||||
|
out.write_all(&[new_byte]).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_freqs<const N: usize>(freqs: HashMap<[char; N], usize>, subst: &HashMap<u8, u8>) {
|
||||||
|
let total_freqs: usize = freqs.values().sum();
|
||||||
|
|
||||||
|
let mut freqs: Vec<_> = freqs
|
||||||
|
.into_iter()
|
||||||
|
.filter(|(_, n)| *n > 1 && (*n as f64 / total_freqs as f64) > 0.001)
|
||||||
|
.collect();
|
||||||
|
freqs.sort_by_key(|(f, count)| (*count, *f));
|
||||||
|
|
||||||
|
for (c, count) in freqs.iter().rev() {
|
||||||
|
println!(
|
||||||
|
"{:>3}: {:4.1}% ({count:4})",
|
||||||
|
c.iter()
|
||||||
|
.map(|c| {
|
||||||
|
let Ok(b): Result<u8, _> = (*c).try_into() else {
|
||||||
|
panic!()
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(new) = subst.get(&b) {
|
||||||
|
(*new as char).to_ascii_uppercase()
|
||||||
|
} else {
|
||||||
|
(b as char).to_ascii_lowercase()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<String>(),
|
||||||
|
(*count as f32 / total_freqs as f32) * 100.
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue