Crypto Square
1. Readme
加密广场
实现,用于组成称为方形代码的加密信息的经典方法.
给定英文文本,输出该文本的加密编码版本。
首先,输入被规范化:
- 从英文文本中删除空格和标点符号,并且消息是朝下的.
然后,
- 规范化字符被分成行。当使用插入的换行符打印时,这些行自自然然形成类似矩形的样子。
例如,句子
"If man was meant to stay on the ground, god would have given us roots."
规范化为:
"ifmanwasmeanttostayonthegroundgodwouldhavegivenusroots"
明文应该组织成一个矩形。矩形的大小(r x c
)应该根据消息的长度来决定c >= r
和c - r <= 1
,这里的c
是列数和r
是行数.
我们的标准化文本长度为 54 个字符,用c = 8
和r = 7
指示矩形:
"ifmanwas"
"meanttos"
"tayonthe"
"groundgo"
"dwouldha"
"vegivenu"
"sroots "
通过向下(第一行第一个,拼接第二行第一个),读取从左到右的列来获得编码消息.
上面的消息编码为:
"imtgdvsfearwermayoogoanouuiontnnlvtwttddesaohghnsseoau"
根据输出的,矩形块编码文本的大小(r X c)
,表明有c
块r
长度的编码字串,以空格分隔。对于那些n
位字符,但少于规定的长度的,每个尾添一个空格。
"imtgdvs fearwer mayoogo anouuio ntnnlvt wttddes aohghn sseoau "
请注意,如果我们要堆叠这些,我们可以直观地,将密文解码回原始消息: (第一行第一个,拼接第二行第一个...)
"imtgdvs"
"fearwer"
"mayoogo"
"anouuio"
"ntnnlvt"
"wttddes"
"aohghn "
"sseoau "
资源
J Dalbey 的编程实践问题http://users.csc.calpoly.edu/~jdalbey/103/Projects/ProgrammingPractice.html
2. 开始你的表演
pub fn encrypt(input: &str) -> String { unimplemented!("Encrypt {:?} using a square code", input) }
3. 测试代码查看
# #![allow(unused_variables)] #fn main() { fn test(input: &str, output: &str) { assert_eq!(&encrypt(input), output); } #[test] fn test_empty_input() { test("", "") } #[test] //#[ignore] fn test_encrypt_also_decrypts_square() { // note that you only get the exact input back if: // 1. no punctuation // 2. even spacing // 3. all lowercase // 4. square input let example = "lime anda coco anut"; assert_eq!(example, &encrypt(&encrypt(example))); } #[test] //#[ignore] fn test_example() { test( "If man was meant to stay on the ground, god would have given us roots.", "imtgdvs fearwer mayoogo anouuio ntnnlvt wttddes aohghn sseoau ", ) } #[test] //#[ignore] fn test_empty_last_line() { test("congratulate", "crl oaa ntt gue") } #[test] //#[ignore] fn test_spaces_are_reorganized() { test("abet", "ae bt"); test("a bet", "ae bt"); test(" a b e t ", "ae bt"); } #[test] //#[ignore] fn test_everything_becomes_lowercase() { test("caSe", "cs ae"); test("cAsE", "cs ae"); test("CASE", "cs ae"); } #[test] //#[ignore] fn test_long() { test( r#" We choose to go to the moon. We choose to go to the moon in this decade and do the other things, not because they are easy, but because they are hard, because that goal will serve to organize and measure the best of our energies and skills, because that challenge is one that we are willing to accept, one we are unwilling to postpone, and one which we intend to win, and the others, too. -- John F. Kennedy, 12 September 1962 "#, &(String::from("womdbudlmecsgwdwob enooetbsenaotioihe ") + "cwotcbeeaeunolnnnr henhaecrsrsealeaf1 ocieucavugetciwnk9 " + "ohnosauerithcnhde6 sotteusteehaegitn2 eohhtseotsatptchn " + "tsiehetohatwtohee oesrethrenceopwod gtdtyhagbdhanoety " + "ooehaetaesaresih1 tgcirygnsklewtne2 ooaneaoitilweptrs " + "ttdgerazoleiaoese hoesaeleflnlrnntp etanshwaosgleedot " + "mhnoyainubeiuatoe oedtbrldreinnnojm "), ) } #}
4. 答案
# #![allow(unused_variables)] #fn main() { extern crate itertools; use itertools::Itertools; /// Encrypt the input string using square cryptography pub fn encrypt(input: &str) -> String { let prepared = prepare(input); if prepared.len() == 0 { return String::new(); } let (cols, rows) = dimensions(prepared.len()); let mut output = String::with_capacity(input.len()); for chunk_iterator in SquareIndexer::new(rows, cols).chunks(cols).into_iter() { for ch_idx in chunk_iterator { if ch_idx < prepared.len() { output.push(prepared[ch_idx]); } } output.push(' '); } // we know there's one extra space at the end output.pop(); output } /// Construct a vector of characters from the given input. /// /// Constrain it to the allowed chars: lowercase ascii letters. /// We construct a vector here because the length of the input /// matters when constructing the output, so we need to know /// how many input chars there are. We could treat it as a stream /// and just stream twice, but collecting it into a vector works /// equally well and might be a bit faster. fn prepare(input: &str) -> Vec<char> { let mut output = Vec::with_capacity(input.len()); output.extend( input .chars() .filter(|&c| c.is_ascii() && !c.is_whitespace() && !c.is_ascii_punctuation()) .map(|c| c.to_ascii_lowercase()), ); // add space padding to the end such that the actual string returned // forms a perfect rectangle let (r, c) = dimensions(output.len()); let padding_qty = (r * c) - output.len(); for _ in 0..padding_qty { output.push(' '); } output.shrink_to_fit(); output } /// Get the dimensions of the appropriate bounding rectangle for this encryption /// /// To find `(rows, cols)` such that `cols >= rows && cols - rows <= 1`, we find /// the least square greater than or equal to the message length. Its square root /// is the cols. If the message length is a perfect square, `rows` is the same. /// Otherwise, it is one less. fn dimensions(length: usize) -> (usize, usize) { let cols = (length as f64).sqrt().ceil() as usize; let rows = if cols * cols == length { cols } else { cols - 1 }; (rows, cols) } /// Iterator over the indices of the appropriate chars of the output. /// /// For a (2, 3) (r, c) grid, yields (0, 3, 1, 4, 2, 5). /// Does no bounds checking or space insertion: that's handled elsewhere. #[derive(Debug)] struct SquareIndexer { rows: usize, cols: usize, cur_row: usize, cur_col: usize, max_value: usize, } impl SquareIndexer { fn new(rows: usize, cols: usize) -> SquareIndexer { SquareIndexer { rows: rows, cols: cols, cur_row: 0, cur_col: 0, max_value: rows * cols, } } } impl Iterator for SquareIndexer { type Item = usize; fn next(&mut self) -> Option<usize> { let value = self.cur_row + (self.cur_col * self.rows); let output = if value < self.max_value && self.cur_row < self.rows { Some(value) } else { None }; // now increment internal state to next value self.cur_col += 1; if self.cur_col >= self.cols { self.cur_col = 0; self.cur_row += 1; } output } } #}