├── .gitignore ├── src ├── main.rs ├── cli.rs ├── chunk_type.rs ├── chunk.rs └── png.rs ├── Cargo.toml ├── README.md └── Cargo.lock /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | *.png -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | mod chunk; 2 | mod chunk_type; 3 | mod cli; 4 | mod png; 5 | 6 | use clap::Parser; 7 | use cli::{get_args, run}; 8 | 9 | pub type Error = Box; 10 | pub type Result = std::result::Result; 11 | 12 | fn main() { 13 | if let Err(e) = cli::get_args().and_then(cli::run) { 14 | eprintln!("{}", e); 15 | std::process::exit(1); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "pngme" 3 | version = "0.1.0" 4 | edition = "2021" 5 | authors = ["kriticalflare "] 6 | description = "A command line tool to hide messages in png files" 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | clap = { version = "4.4.6", features = ["derive"] } 11 | crc = "3.0.1" 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PNGme 2 | 3 | PNGme is my Rust implementation of the PNGme Project, which you can find more about at [this link](https://picklenerd.github.io/pngme_book/). It's a command line program that lets you hide secret messages in PNG files. 4 | 5 | You can find the latest release [here](https://github.com/kriticalflare/pngme/releases) 6 | 7 | ## Encode Message 8 | 9 | ### Usage 10 | 11 | ``` 12 | pngme encode [OUTPUT_FILE] 13 | ``` 14 | 15 | ### Arguments 16 | 17 | - ``: The path to the input PNG file. 18 | - ``: The type of chunk to be encoded. 19 | - ``: The message to be encoded. 20 | - `[OUTPUT_FILE]` (optional): The path where the encoded PNG will be written to. 21 | 22 | ## Decode Message 23 | 24 | ### Usage 25 | 26 | ``` 27 | pngme decode 28 | ``` 29 | 30 | ### Arguments 31 | 32 | - ``: The path to the input PNG file. 33 | - ``: The type of chunk to be decoded. 34 | 35 | ## Remove a Chunk 36 | 37 | ### Usage 38 | 39 | ``` 40 | pngme remove 41 | ``` 42 | 43 | ### Arguments 44 | 45 | - ``: The path to the input PNG file. 46 | - ``: The type of chunk to be removed. 47 | 48 | ## Print 49 | 50 | ### Usage 51 | 52 | ``` 53 | pngme print 54 | ``` 55 | 56 | ### Arguments 57 | 58 | - ``: The path to the input PNG file. 59 | -------------------------------------------------------------------------------- /src/cli.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | fs::File, 3 | io::{self, BufRead, BufReader, BufWriter, Write}, 4 | }; 5 | 6 | use crate::{chunk::Chunk, chunk_type::ChunkType, png::Png, Result}; 7 | use clap::{Parser, Subcommand}; 8 | 9 | use Command::*; 10 | 11 | fn open(filename: &str) -> Result> { 12 | match filename { 13 | "-" => Ok(Box::new(BufReader::new(io::stdin()))), 14 | _ => Ok(Box::new(BufReader::new(File::open(filename)?))), 15 | } 16 | } 17 | 18 | #[derive(Parser, Debug)] 19 | #[command( 20 | author, 21 | version, 22 | about = "A command line tool to hide messages in png files" 23 | )] 24 | #[command( 25 | help_template = "{author} {about-section}Version: {version} \n {usage-heading} {usage} \n {all-args} {tab}" 26 | )] 27 | pub struct CliArgs { 28 | #[clap(subcommand)] 29 | command: Command, 30 | } 31 | 32 | #[derive(Subcommand, Debug)] 33 | pub enum Command { 34 | Encode { 35 | #[arg(help = "Path to input png file")] 36 | file_path: String, 37 | 38 | #[arg(help = "Type of Chunk to be encoded")] 39 | chunk_type: String, 40 | 41 | #[arg(help = "Message to be encoded")] 42 | message: String, 43 | 44 | #[arg(help = "Path where the encoded png will be written to")] 45 | output_file: Option, 46 | }, 47 | 48 | Decode { 49 | #[arg(help = "Path to input png file")] 50 | file_path: String, 51 | 52 | #[arg(help = "Type of Chunk to be decoded")] 53 | chunk_type: String, 54 | }, 55 | 56 | Remove { 57 | #[arg(help = "Path to input png file")] 58 | file_path: String, 59 | 60 | #[arg(help = "Type of Chunk to be removed")] 61 | chunk_type: String, 62 | }, 63 | 64 | Print { 65 | #[arg(help = "Path to input png file")] 66 | file_path: String, 67 | }, 68 | } 69 | 70 | pub fn get_args() -> Result { 71 | let args = CliArgs::parse(); 72 | Ok(args.command) 73 | } 74 | 75 | pub fn run(command: Command) -> Result<()> { 76 | dbg!(&command); 77 | match command { 78 | Encode { 79 | file_path, 80 | chunk_type, 81 | message, 82 | output_file, 83 | } => { 84 | let chunk = Chunk::new( 85 | ChunkType::from( 86 | chunk_type 87 | .as_bytes() 88 | .try_into() 89 | .expect("chunk_type length is not equal to 4"), 90 | ), 91 | message.into_bytes(), 92 | ); 93 | let mut input = open(&file_path)?; 94 | let mut chunks: Vec = vec![]; 95 | input.read_to_end(&mut chunks)?; 96 | 97 | let mut input_png = Png::try_from(chunks.as_slice())?; 98 | input_png.append_chunk(chunk); 99 | if let Some(output_file_path) = output_file { 100 | let f = File::create(output_file_path)?; 101 | let mut f = BufWriter::new(f); 102 | f.write_all(input_png.as_bytes().as_slice())?; 103 | } 104 | } 105 | Decode { 106 | file_path, 107 | chunk_type, 108 | } => { 109 | let mut input = open(&file_path)?; 110 | let mut chunks: Vec = vec![]; 111 | input.read_to_end(&mut chunks)?; 112 | 113 | let input_png = Png::try_from(chunks.as_slice())?; 114 | match input_png.chunk_by_type(&chunk_type) { 115 | Some(chunk) => { 116 | println!("{}", chunk); 117 | } 118 | None => { 119 | return Err(format!("Chunk of type {} not found", chunk_type).into()); 120 | } 121 | } 122 | } 123 | Remove { 124 | file_path, 125 | chunk_type, 126 | } => { 127 | let mut input = open(&file_path)?; 128 | let mut chunks: Vec = vec![]; 129 | input.read_to_end(&mut chunks)?; 130 | 131 | let mut input_png = Png::try_from(chunks.as_slice())?; 132 | input_png.remove_chunk(&chunk_type)?; 133 | let f = File::create(file_path)?; 134 | let mut f = BufWriter::new(f); 135 | f.write_all(input_png.as_bytes().as_slice())?; 136 | } 137 | Print { file_path } => { 138 | let mut input = open(&file_path)?; 139 | let mut chunks: Vec = vec![]; 140 | input.read_to_end(&mut chunks)?; 141 | let input_png = Png::try_from(chunks.as_slice())?; 142 | println!("{}", input_png) 143 | } 144 | } 145 | Ok(()) 146 | } 147 | -------------------------------------------------------------------------------- /src/chunk_type.rs: -------------------------------------------------------------------------------- 1 | #[allow(unused)] 2 | use std::convert::TryFrom; 3 | use std::{fmt::Display, str::FromStr}; 4 | 5 | use crate::{Error, Result}; 6 | 7 | #[derive(PartialEq, Clone, Eq, Debug)] 8 | pub struct ChunkType([u8; 4]); 9 | 10 | impl FromStr for ChunkType { 11 | type Err = Error; 12 | 13 | fn from_str(s: &str) -> Result { 14 | let bytes = s.as_bytes(); 15 | if bytes.len() != 4 { 16 | return Err("input str length is not 4".into()); 17 | } 18 | let mut arr = [0; 4]; 19 | arr.copy_from_slice(bytes); 20 | 21 | if s.chars().any(|c| !c.is_ascii_alphabetic()) { 22 | return Err("input str has non alphabetic ascii character".into()); 23 | } 24 | 25 | Ok(ChunkType(arr)) 26 | } 27 | } 28 | 29 | impl TryFrom<[u8; 4]> for ChunkType { 30 | type Error = Error; 31 | 32 | fn try_from(value: [u8; 4]) -> Result { 33 | if value.iter().any(|byte| !byte.is_ascii_alphabetic()) { 34 | Err("value is not valid ascii alphabet".into()) 35 | } else { 36 | Ok(ChunkType(value)) 37 | } 38 | } 39 | } 40 | 41 | impl Display for ChunkType { 42 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 43 | write!( 44 | f, 45 | "{}", 46 | String::from_utf8(self.0.to_vec()) 47 | .expect("chunk_type should be valid utf-8") 48 | .to_string() 49 | ) 50 | } 51 | } 52 | 53 | impl ChunkType { 54 | pub fn from(bytes: [u8; 4]) -> Self { 55 | Self(bytes) 56 | } 57 | 58 | pub fn bytes(&self) -> [u8; 4] { 59 | self.0.to_owned() 60 | } 61 | 62 | fn is_valid(&self) -> bool { 63 | self.is_reserved_bit_valid() 64 | } 65 | 66 | fn is_critical(&self) -> bool { 67 | (self.0[0] >> 5 & 1) == 0 68 | } 69 | 70 | fn is_public(&self) -> bool { 71 | (self.0[1] >> 5 & 1) == 0 72 | } 73 | 74 | fn is_reserved_bit_valid(&self) -> bool { 75 | (self.0[2] >> 5 & 1) == 0 76 | } 77 | 78 | fn is_safe_to_copy(&self) -> bool { 79 | (self.0[3] >> 5 & 1) == 1 80 | } 81 | } 82 | 83 | #[cfg(test)] 84 | mod tests { 85 | use super::*; 86 | use std::convert::TryFrom; 87 | use std::str::FromStr; 88 | 89 | #[test] 90 | pub fn test_chunk_type_from_bytes() { 91 | let expected = [82, 117, 83, 116]; 92 | let actual = ChunkType::try_from([82, 117, 83, 116]).unwrap(); 93 | 94 | assert_eq!(expected, actual.bytes()); 95 | } 96 | 97 | #[test] 98 | pub fn test_chunk_type_from_str() { 99 | let expected = ChunkType::try_from([82, 117, 83, 116]).unwrap(); 100 | let actual = ChunkType::from_str("RuSt").unwrap(); 101 | assert_eq!(expected, actual); 102 | } 103 | 104 | #[test] 105 | pub fn test_chunk_type_is_critical() { 106 | let chunk = ChunkType::from_str("RuSt").unwrap(); 107 | assert!(chunk.is_critical()); 108 | } 109 | 110 | #[test] 111 | pub fn test_chunk_type_is_not_critical() { 112 | let chunk = ChunkType::from_str("ruSt").unwrap(); 113 | assert!(!chunk.is_critical()); 114 | } 115 | 116 | #[test] 117 | pub fn test_chunk_type_is_public() { 118 | let chunk = ChunkType::from_str("RUSt").unwrap(); 119 | assert!(chunk.is_public()); 120 | } 121 | 122 | #[test] 123 | pub fn test_chunk_type_is_not_public() { 124 | let chunk = ChunkType::from_str("RuSt").unwrap(); 125 | assert!(!chunk.is_public()); 126 | } 127 | 128 | #[test] 129 | pub fn test_chunk_type_is_reserved_bit_valid() { 130 | let chunk = ChunkType::from_str("RuSt").unwrap(); 131 | assert!(chunk.is_reserved_bit_valid()); 132 | } 133 | 134 | #[test] 135 | pub fn test_chunk_type_is_reserved_bit_invalid() { 136 | let chunk = ChunkType::from_str("Rust").unwrap(); 137 | assert!(!chunk.is_reserved_bit_valid()); 138 | } 139 | 140 | #[test] 141 | pub fn test_chunk_type_is_safe_to_copy() { 142 | let chunk = ChunkType::from_str("RuSt").unwrap(); 143 | assert!(chunk.is_safe_to_copy()); 144 | } 145 | 146 | #[test] 147 | pub fn test_chunk_type_is_unsafe_to_copy() { 148 | let chunk = ChunkType::from_str("RuST").unwrap(); 149 | assert!(!chunk.is_safe_to_copy()); 150 | } 151 | 152 | #[test] 153 | pub fn test_valid_chunk_is_valid() { 154 | let chunk = ChunkType::from_str("RuSt").unwrap(); 155 | assert!(chunk.is_valid()); 156 | } 157 | 158 | #[test] 159 | pub fn test_invalid_chunk_is_valid() { 160 | let chunk = ChunkType::from_str("Rust").unwrap(); 161 | assert!(!chunk.is_valid()); 162 | 163 | let chunk = ChunkType::from_str("Ru1t"); 164 | assert!(chunk.is_err()); 165 | } 166 | 167 | #[test] 168 | pub fn test_chunk_type_string() { 169 | let chunk = ChunkType::from_str("RuSt").unwrap(); 170 | assert_eq!(&chunk.to_string(), "RuSt"); 171 | } 172 | 173 | #[test] 174 | pub fn test_chunk_type_trait_impls() { 175 | let chunk_type_1: ChunkType = TryFrom::try_from([82, 117, 83, 116]).unwrap(); 176 | let chunk_type_2: ChunkType = FromStr::from_str("RuSt").unwrap(); 177 | let _chunk_string = format!("{}", chunk_type_1); 178 | let _are_chunks_equal = chunk_type_1 == chunk_type_2; 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "anstream" 7 | version = "0.6.4" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44" 10 | dependencies = [ 11 | "anstyle", 12 | "anstyle-parse", 13 | "anstyle-query", 14 | "anstyle-wincon", 15 | "colorchoice", 16 | "utf8parse", 17 | ] 18 | 19 | [[package]] 20 | name = "anstyle" 21 | version = "1.0.4" 22 | source = "registry+https://github.com/rust-lang/crates.io-index" 23 | checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" 24 | 25 | [[package]] 26 | name = "anstyle-parse" 27 | version = "0.2.2" 28 | source = "registry+https://github.com/rust-lang/crates.io-index" 29 | checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140" 30 | dependencies = [ 31 | "utf8parse", 32 | ] 33 | 34 | [[package]] 35 | name = "anstyle-query" 36 | version = "1.0.0" 37 | source = "registry+https://github.com/rust-lang/crates.io-index" 38 | checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" 39 | dependencies = [ 40 | "windows-sys", 41 | ] 42 | 43 | [[package]] 44 | name = "anstyle-wincon" 45 | version = "3.0.1" 46 | source = "registry+https://github.com/rust-lang/crates.io-index" 47 | checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" 48 | dependencies = [ 49 | "anstyle", 50 | "windows-sys", 51 | ] 52 | 53 | [[package]] 54 | name = "clap" 55 | version = "4.4.6" 56 | source = "registry+https://github.com/rust-lang/crates.io-index" 57 | checksum = "d04704f56c2cde07f43e8e2c154b43f216dc5c92fc98ada720177362f953b956" 58 | dependencies = [ 59 | "clap_builder", 60 | "clap_derive", 61 | ] 62 | 63 | [[package]] 64 | name = "clap_builder" 65 | version = "4.4.6" 66 | source = "registry+https://github.com/rust-lang/crates.io-index" 67 | checksum = "0e231faeaca65ebd1ea3c737966bf858971cd38c3849107aa3ea7de90a804e45" 68 | dependencies = [ 69 | "anstream", 70 | "anstyle", 71 | "clap_lex", 72 | "strsim", 73 | ] 74 | 75 | [[package]] 76 | name = "clap_derive" 77 | version = "4.4.2" 78 | source = "registry+https://github.com/rust-lang/crates.io-index" 79 | checksum = "0862016ff20d69b84ef8247369fabf5c008a7417002411897d40ee1f4532b873" 80 | dependencies = [ 81 | "heck", 82 | "proc-macro2", 83 | "quote", 84 | "syn", 85 | ] 86 | 87 | [[package]] 88 | name = "clap_lex" 89 | version = "0.5.1" 90 | source = "registry+https://github.com/rust-lang/crates.io-index" 91 | checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961" 92 | 93 | [[package]] 94 | name = "colorchoice" 95 | version = "1.0.0" 96 | source = "registry+https://github.com/rust-lang/crates.io-index" 97 | checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" 98 | 99 | [[package]] 100 | name = "crc" 101 | version = "3.0.1" 102 | source = "registry+https://github.com/rust-lang/crates.io-index" 103 | checksum = "86ec7a15cbe22e59248fc7eadb1907dab5ba09372595da4d73dd805ed4417dfe" 104 | dependencies = [ 105 | "crc-catalog", 106 | ] 107 | 108 | [[package]] 109 | name = "crc-catalog" 110 | version = "2.2.0" 111 | source = "registry+https://github.com/rust-lang/crates.io-index" 112 | checksum = "9cace84e55f07e7301bae1c519df89cdad8cc3cd868413d3fdbdeca9ff3db484" 113 | 114 | [[package]] 115 | name = "heck" 116 | version = "0.4.1" 117 | source = "registry+https://github.com/rust-lang/crates.io-index" 118 | checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" 119 | 120 | [[package]] 121 | name = "pngme" 122 | version = "0.1.0" 123 | dependencies = [ 124 | "clap", 125 | "crc", 126 | ] 127 | 128 | [[package]] 129 | name = "proc-macro2" 130 | version = "1.0.67" 131 | source = "registry+https://github.com/rust-lang/crates.io-index" 132 | checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328" 133 | dependencies = [ 134 | "unicode-ident", 135 | ] 136 | 137 | [[package]] 138 | name = "quote" 139 | version = "1.0.33" 140 | source = "registry+https://github.com/rust-lang/crates.io-index" 141 | checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" 142 | dependencies = [ 143 | "proc-macro2", 144 | ] 145 | 146 | [[package]] 147 | name = "strsim" 148 | version = "0.10.0" 149 | source = "registry+https://github.com/rust-lang/crates.io-index" 150 | checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" 151 | 152 | [[package]] 153 | name = "syn" 154 | version = "2.0.37" 155 | source = "registry+https://github.com/rust-lang/crates.io-index" 156 | checksum = "7303ef2c05cd654186cb250d29049a24840ca25d2747c25c0381c8d9e2f582e8" 157 | dependencies = [ 158 | "proc-macro2", 159 | "quote", 160 | "unicode-ident", 161 | ] 162 | 163 | [[package]] 164 | name = "unicode-ident" 165 | version = "1.0.12" 166 | source = "registry+https://github.com/rust-lang/crates.io-index" 167 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 168 | 169 | [[package]] 170 | name = "utf8parse" 171 | version = "0.2.1" 172 | source = "registry+https://github.com/rust-lang/crates.io-index" 173 | checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" 174 | 175 | [[package]] 176 | name = "windows-sys" 177 | version = "0.48.0" 178 | source = "registry+https://github.com/rust-lang/crates.io-index" 179 | checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" 180 | dependencies = [ 181 | "windows-targets", 182 | ] 183 | 184 | [[package]] 185 | name = "windows-targets" 186 | version = "0.48.5" 187 | source = "registry+https://github.com/rust-lang/crates.io-index" 188 | checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" 189 | dependencies = [ 190 | "windows_aarch64_gnullvm", 191 | "windows_aarch64_msvc", 192 | "windows_i686_gnu", 193 | "windows_i686_msvc", 194 | "windows_x86_64_gnu", 195 | "windows_x86_64_gnullvm", 196 | "windows_x86_64_msvc", 197 | ] 198 | 199 | [[package]] 200 | name = "windows_aarch64_gnullvm" 201 | version = "0.48.5" 202 | source = "registry+https://github.com/rust-lang/crates.io-index" 203 | checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" 204 | 205 | [[package]] 206 | name = "windows_aarch64_msvc" 207 | version = "0.48.5" 208 | source = "registry+https://github.com/rust-lang/crates.io-index" 209 | checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" 210 | 211 | [[package]] 212 | name = "windows_i686_gnu" 213 | version = "0.48.5" 214 | source = "registry+https://github.com/rust-lang/crates.io-index" 215 | checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" 216 | 217 | [[package]] 218 | name = "windows_i686_msvc" 219 | version = "0.48.5" 220 | source = "registry+https://github.com/rust-lang/crates.io-index" 221 | checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" 222 | 223 | [[package]] 224 | name = "windows_x86_64_gnu" 225 | version = "0.48.5" 226 | source = "registry+https://github.com/rust-lang/crates.io-index" 227 | checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" 228 | 229 | [[package]] 230 | name = "windows_x86_64_gnullvm" 231 | version = "0.48.5" 232 | source = "registry+https://github.com/rust-lang/crates.io-index" 233 | checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" 234 | 235 | [[package]] 236 | name = "windows_x86_64_msvc" 237 | version = "0.48.5" 238 | source = "registry+https://github.com/rust-lang/crates.io-index" 239 | checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" 240 | -------------------------------------------------------------------------------- /src/chunk.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::Display; 2 | 3 | use crate::{chunk_type::ChunkType, Error, Result}; 4 | use crc::{Crc, CRC_32_ISO_HDLC}; 5 | 6 | pub const CRC32: Crc = Crc::::new(&CRC_32_ISO_HDLC); 7 | 8 | #[derive(Debug, Clone)] 9 | pub struct Chunk { 10 | length: u32, 11 | chunk_type: ChunkType, 12 | data: Vec, 13 | crc: u32, 14 | } 15 | 16 | impl Display for Chunk { 17 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 18 | write!(f, "{}", self.data_as_string().unwrap_or_default()) 19 | } 20 | } 21 | 22 | impl TryFrom<&[u8]> for Chunk { 23 | type Error = Error; 24 | 25 | fn try_from(value: &[u8]) -> Result { 26 | let length = 27 | u32::from_be_bytes(value[0..4].try_into().expect("chunk with incorrect length")); 28 | // println!("length: {:?}", length); 29 | 30 | let chunk_type = ChunkType::from( 31 | value[4..8] 32 | .try_into() 33 | .expect("chunk_type with incorrect length"), 34 | ); 35 | // println!("chunk_type: {}", chunk_type); 36 | 37 | let mut data: Vec = Vec::new(); 38 | data.extend_from_slice(&value[8..(8 + length) as usize]); 39 | 40 | let crc = u32::from_be_bytes( 41 | value[(8 + length) as usize..] 42 | .try_into() 43 | .expect("chunk crc with incorrect length"), 44 | ); 45 | // println!("crc: {}", crc); 46 | 47 | let mut crc_bytes = Vec::from(chunk_type.bytes()); 48 | crc_bytes.extend(&data); 49 | 50 | let computed_crc = CRC32.checksum(&crc_bytes); 51 | 52 | if computed_crc != crc { 53 | Err("Invalid CRC")? 54 | } 55 | 56 | Ok(Chunk { 57 | length, 58 | chunk_type, 59 | data, 60 | crc, 61 | }) 62 | } 63 | } 64 | 65 | impl Chunk { 66 | pub fn new(chunk_type: ChunkType, chunk_data: Vec) -> Self { 67 | let mut crc_bytes = Vec::from(chunk_type.bytes()); 68 | crc_bytes.extend(&chunk_data); 69 | 70 | Chunk { 71 | length: chunk_data.len() as u32, 72 | chunk_type, 73 | crc: CRC32.checksum(&crc_bytes), 74 | data: chunk_data, 75 | } 76 | } 77 | 78 | pub fn length(&self) -> u32 { 79 | self.length 80 | } 81 | 82 | pub fn chunk_type(&self) -> &ChunkType { 83 | &self.chunk_type 84 | } 85 | 86 | pub fn crc(&self) -> u32 { 87 | self.crc 88 | } 89 | 90 | pub fn data_as_string(&self) -> Option { 91 | match String::from_utf8(self.data.clone()) { 92 | Ok(data_string) => Some(data_string), 93 | Err(_) => None, 94 | } 95 | } 96 | 97 | pub fn as_bytes(&self) -> Vec { 98 | let mut bytes = Vec::new(); 99 | bytes.extend(u32::to_be_bytes(self.length())); 100 | bytes.extend(self.chunk_type().bytes()); 101 | bytes.extend(&self.data); 102 | bytes.extend(u32::to_be_bytes(self.crc())); 103 | 104 | bytes 105 | } 106 | } 107 | 108 | #[cfg(test)] 109 | mod tests { 110 | use super::*; 111 | use crate::chunk_type::ChunkType; 112 | use std::str::FromStr; 113 | 114 | fn testing_chunk() -> Chunk { 115 | let data_length: u32 = 42; 116 | let chunk_type = "RuSt".as_bytes(); 117 | let message_bytes = "This is where your secret message will be!".as_bytes(); 118 | let crc: u32 = 2882656334; 119 | 120 | let chunk_data: Vec = data_length 121 | .to_be_bytes() 122 | .iter() 123 | .chain(chunk_type.iter()) 124 | .chain(message_bytes.iter()) 125 | .chain(crc.to_be_bytes().iter()) 126 | .copied() 127 | .collect(); 128 | 129 | Chunk::try_from(chunk_data.as_ref()).unwrap() 130 | } 131 | 132 | #[test] 133 | fn test_new_chunk() { 134 | let chunk_type = ChunkType::from_str("RuSt").unwrap(); 135 | let data = "This is where your secret message will be!" 136 | .as_bytes() 137 | .to_vec(); 138 | let chunk = Chunk::new(chunk_type, data); 139 | assert_eq!(chunk.length(), 42); 140 | assert_eq!(chunk.crc(), 2882656334); 141 | } 142 | 143 | #[test] 144 | fn test_chunk_length() { 145 | let chunk = testing_chunk(); 146 | assert_eq!(chunk.length(), 42); 147 | } 148 | 149 | #[test] 150 | fn test_chunk_type() { 151 | let chunk = testing_chunk(); 152 | assert_eq!(chunk.chunk_type().to_string(), String::from("RuSt")); 153 | } 154 | 155 | #[test] 156 | fn test_chunk_string() { 157 | let chunk = testing_chunk(); 158 | let chunk_string = chunk.data_as_string().unwrap(); 159 | let expected_chunk_string = String::from("This is where your secret message will be!"); 160 | assert_eq!(chunk_string, expected_chunk_string); 161 | } 162 | 163 | #[test] 164 | fn test_chunk_crc() { 165 | let chunk = testing_chunk(); 166 | assert_eq!(chunk.crc(), 2882656334); 167 | } 168 | 169 | #[test] 170 | fn test_valid_chunk_from_bytes() { 171 | let data_length: u32 = 42; 172 | let chunk_type = "RuSt".as_bytes(); 173 | let message_bytes = "This is where your secret message will be!".as_bytes(); 174 | let crc: u32 = 2882656334; 175 | 176 | let chunk_data: Vec = data_length 177 | .to_be_bytes() 178 | .iter() 179 | .chain(chunk_type.iter()) 180 | .chain(message_bytes.iter()) 181 | .chain(crc.to_be_bytes().iter()) 182 | .copied() 183 | .collect(); 184 | 185 | let chunk = Chunk::try_from(chunk_data.as_ref()).unwrap(); 186 | 187 | let chunk_string = chunk.data_as_string().unwrap(); 188 | let expected_chunk_string = String::from("This is where your secret message will be!"); 189 | 190 | assert_eq!(chunk.length(), 42); 191 | assert_eq!(chunk.chunk_type().to_string(), String::from("RuSt")); 192 | assert_eq!(chunk_string, expected_chunk_string); 193 | assert_eq!(chunk.crc(), 2882656334); 194 | } 195 | 196 | #[test] 197 | fn test_invalid_chunk_from_bytes() { 198 | let data_length: u32 = 42; 199 | let chunk_type = "RuSt".as_bytes(); 200 | let message_bytes = "This is where your secret message will be!".as_bytes(); 201 | let crc: u32 = 2882656333; 202 | 203 | let chunk_data: Vec = data_length 204 | .to_be_bytes() 205 | .iter() 206 | .chain(chunk_type.iter()) 207 | .chain(message_bytes.iter()) 208 | .chain(crc.to_be_bytes().iter()) 209 | .copied() 210 | .collect(); 211 | 212 | let chunk = Chunk::try_from(chunk_data.as_ref()); 213 | assert!(chunk.is_err()); 214 | } 215 | 216 | #[test] 217 | pub fn test_chunk_trait_impls() { 218 | let data_length: u32 = 42; 219 | let chunk_type = "RuSt".as_bytes(); 220 | let message_bytes = "This is where your secret message will be!".as_bytes(); 221 | let crc: u32 = 2882656334; 222 | 223 | let chunk_data: Vec = data_length 224 | .to_be_bytes() 225 | .iter() 226 | .chain(chunk_type.iter()) 227 | .chain(message_bytes.iter()) 228 | .chain(crc.to_be_bytes().iter()) 229 | .copied() 230 | .collect(); 231 | 232 | let chunk: Chunk = TryFrom::try_from(chunk_data.as_ref()).unwrap(); 233 | 234 | let _chunk_string = format!("{}", chunk); 235 | } 236 | } 237 | -------------------------------------------------------------------------------- /src/png.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::Display; 2 | use std::io::{BufReader, Read}; 3 | 4 | use crate::chunk::Chunk; 5 | use crate::chunk_type::ChunkType; 6 | use crate::{Error, Result}; 7 | 8 | pub struct Png { 9 | chunks: Vec, 10 | } 11 | 12 | impl Png { 13 | const STANDARD_HEADER: [u8; 8] = [137, 80, 78, 71, 13, 10, 26, 10]; 14 | 15 | pub fn from_chunks(chunks: Vec) -> Png { 16 | Png { chunks } 17 | } 18 | pub fn append_chunk(&mut self, chunk: Chunk) { 19 | self.chunks.push(chunk); 20 | } 21 | pub fn remove_chunk(&mut self, chunk_type: &str) -> Result { 22 | let bytes = chunk_type.as_bytes(); 23 | 24 | let index = self 25 | .chunks 26 | .iter() 27 | .position(|chunk| chunk.chunk_type().bytes() == bytes); 28 | 29 | match index { 30 | Some(i) => { 31 | let removed_element = self.chunks.remove(i); 32 | Ok(removed_element) 33 | } 34 | None => Err("No element matches the condition")?, 35 | } 36 | } 37 | pub fn header(&self) -> &[u8; 8] { 38 | &Self::STANDARD_HEADER 39 | } 40 | pub fn chunks(&self) -> &[Chunk] { 41 | &self.chunks 42 | } 43 | pub fn chunk_by_type(&self, chunk_type: &str) -> Option<&Chunk> { 44 | let bytes = chunk_type.as_bytes(); 45 | self.chunks 46 | .iter() 47 | .find(|chunk| chunk.chunk_type().bytes() == bytes) 48 | } 49 | pub fn as_bytes(&self) -> Vec { 50 | let chunk_bytes: Vec = self 51 | .chunks 52 | .iter() 53 | .flat_map(|chunk| chunk.as_bytes()) 54 | .collect(); 55 | 56 | Self::STANDARD_HEADER 57 | .iter() 58 | .chain(chunk_bytes.iter()) 59 | .copied() 60 | .collect() 61 | } 62 | } 63 | 64 | impl TryFrom<&[u8]> for Png { 65 | type Error = Error; 66 | 67 | fn try_from(value: &[u8]) -> Result { 68 | let mut chunks: Vec = Vec::new(); 69 | let mut offset = 0; 70 | if value[offset..Self::STANDARD_HEADER.len()] != Self::STANDARD_HEADER { 71 | return Err("Invalid Header".into()); 72 | } 73 | offset += 8; 74 | if value.len() < offset { 75 | return Err("Not enough bytes".into()); 76 | } 77 | 78 | loop { 79 | let length = u32::from_be_bytes(value[offset..offset + 4].try_into()?) as usize; 80 | let read_until = offset + length + 4 + 4 + 4; 81 | let chunk_bytes = value[offset..read_until].to_vec(); 82 | let chunk = Chunk::try_from(chunk_bytes.as_slice())?; 83 | chunks.push(chunk); 84 | offset = read_until; 85 | if offset == value.len() { 86 | break; 87 | } 88 | } 89 | 90 | Ok(Self { chunks }) 91 | } 92 | } 93 | 94 | impl Display for Png { 95 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 96 | let png_string = String::from_iter( 97 | self.chunks 98 | .iter() 99 | .map(|c| c.data_as_string().unwrap_or_default()), 100 | ); 101 | write!(f, "{}", png_string) 102 | } 103 | } 104 | 105 | #[cfg(test)] 106 | mod tests { 107 | use super::*; 108 | use crate::chunk::Chunk; 109 | use crate::chunk_type::ChunkType; 110 | use std::convert::TryFrom; 111 | use std::str::FromStr; 112 | 113 | fn testing_chunks() -> Vec { 114 | let mut chunks = Vec::new(); 115 | 116 | chunks.push(chunk_from_strings("FrSt", "I am the first chunk").unwrap()); 117 | chunks.push(chunk_from_strings("miDl", "I am another chunk").unwrap()); 118 | chunks.push(chunk_from_strings("LASt", "I am the last chunk").unwrap()); 119 | 120 | chunks 121 | } 122 | 123 | fn testing_png() -> Png { 124 | let chunks = testing_chunks(); 125 | Png::from_chunks(chunks) 126 | } 127 | 128 | fn chunk_from_strings(chunk_type: &str, data: &str) -> Result { 129 | use std::str::FromStr; 130 | 131 | let chunk_type = ChunkType::from_str(chunk_type)?; 132 | let data: Vec = data.bytes().collect(); 133 | 134 | Ok(Chunk::new(chunk_type, data)) 135 | } 136 | 137 | #[test] 138 | fn test_from_chunks() { 139 | let chunks = testing_chunks(); 140 | let png = Png::from_chunks(chunks); 141 | 142 | assert_eq!(png.chunks().len(), 3); 143 | } 144 | 145 | #[test] 146 | fn test_valid_from_bytes() { 147 | let chunk_bytes: Vec = testing_chunks() 148 | .into_iter() 149 | .flat_map(|chunk| chunk.as_bytes()) 150 | .collect(); 151 | 152 | let bytes: Vec = Png::STANDARD_HEADER 153 | .iter() 154 | .chain(chunk_bytes.iter()) 155 | .copied() 156 | .collect(); 157 | 158 | let png = Png::try_from(bytes.as_ref()); 159 | 160 | assert!(png.is_ok()); 161 | } 162 | 163 | #[test] 164 | fn test_invalid_header() { 165 | let chunk_bytes: Vec = testing_chunks() 166 | .into_iter() 167 | .flat_map(|chunk| chunk.as_bytes()) 168 | .collect(); 169 | 170 | let bytes: Vec = [13, 80, 78, 71, 13, 10, 26, 10] 171 | .iter() 172 | .chain(chunk_bytes.iter()) 173 | .copied() 174 | .collect(); 175 | 176 | let png = Png::try_from(bytes.as_ref()); 177 | 178 | assert!(png.is_err()); 179 | } 180 | 181 | #[test] 182 | fn test_invalid_chunk() { 183 | let mut chunk_bytes: Vec = testing_chunks() 184 | .into_iter() 185 | .flat_map(|chunk| chunk.as_bytes()) 186 | .collect(); 187 | 188 | #[rustfmt::skip] 189 | let mut bad_chunk = vec![ 190 | 0, 0, 0, 5, // length 191 | 32, 117, 83, 116, // Chunk Type (bad) 192 | 65, 64, 65, 66, 67, // Data 193 | 1, 2, 3, 4, 5 // CRC (bad) 194 | ]; 195 | 196 | chunk_bytes.append(&mut bad_chunk); 197 | 198 | let png = Png::try_from(chunk_bytes.as_ref()); 199 | 200 | assert!(png.is_err()); 201 | } 202 | 203 | #[test] 204 | fn test_list_chunks() { 205 | let png = testing_png(); 206 | let chunks = png.chunks(); 207 | assert_eq!(chunks.len(), 3); 208 | } 209 | 210 | #[test] 211 | fn test_chunk_by_type() { 212 | let png = testing_png(); 213 | let chunk = png.chunk_by_type("FrSt").unwrap(); 214 | assert_eq!(&chunk.chunk_type().to_string(), "FrSt"); 215 | assert_eq!(&chunk.data_as_string().unwrap(), "I am the first chunk"); 216 | } 217 | 218 | #[test] 219 | fn test_append_chunk() { 220 | let mut png = testing_png(); 221 | png.append_chunk(chunk_from_strings("TeSt", "Message").unwrap()); 222 | let chunk = png.chunk_by_type("TeSt").unwrap(); 223 | assert_eq!(&chunk.chunk_type().to_string(), "TeSt"); 224 | assert_eq!(&chunk.data_as_string().unwrap(), "Message"); 225 | } 226 | 227 | #[test] 228 | fn test_remove_chunk() { 229 | let mut png = testing_png(); 230 | png.append_chunk(chunk_from_strings("TeSt", "Message").unwrap()); 231 | png.remove_chunk("TeSt").unwrap(); 232 | let chunk = png.chunk_by_type("TeSt"); 233 | assert!(chunk.is_none()); 234 | } 235 | 236 | #[test] 237 | fn test_png_from_image_file() { 238 | let png = Png::try_from(&PNG_FILE[..]); 239 | assert!(png.is_ok()); 240 | } 241 | 242 | #[test] 243 | fn test_as_bytes() { 244 | let png = Png::try_from(&PNG_FILE[..]).unwrap(); 245 | let actual = png.as_bytes(); 246 | let expected: Vec = PNG_FILE.to_vec(); 247 | assert_eq!(actual, expected); 248 | } 249 | 250 | #[test] 251 | fn test_png_trait_impls() { 252 | let chunk_bytes: Vec = testing_chunks() 253 | .into_iter() 254 | .flat_map(|chunk| chunk.as_bytes()) 255 | .collect(); 256 | 257 | let bytes: Vec = Png::STANDARD_HEADER 258 | .iter() 259 | .chain(chunk_bytes.iter()) 260 | .copied() 261 | .collect(); 262 | 263 | let png: Png = TryFrom::try_from(bytes.as_ref()).unwrap(); 264 | 265 | let _png_string = format!("{}", png); 266 | } 267 | 268 | // This is the raw bytes for a shrunken version of the `dice.png` image on Wikipedia 269 | const PNG_FILE: [u8; 4803] = [ 270 | 137, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13, 73, 72, 68, 82, 0, 0, 0, 50, 0, 0, 0, 50, 8, 271 | 6, 0, 0, 0, 30, 63, 136, 177, 0, 0, 0, 1, 115, 82, 71, 66, 0, 174, 206, 28, 233, 0, 0, 0, 272 | 4, 103, 65, 77, 65, 0, 0, 177, 143, 11, 252, 97, 5, 0, 0, 0, 9, 112, 72, 89, 115, 0, 0, 14, 273 | 194, 0, 0, 14, 194, 1, 21, 40, 74, 128, 0, 0, 18, 73, 73, 68, 65, 84, 104, 67, 237, 153, 274 | 121, 112, 28, 213, 157, 199, 95, 223, 215, 76, 207, 61, 26, 29, 163, 251, 180, 101, 27, 275 | 217, 194, 14, 24, 18, 192, 78, 124, 4, 19, 108, 47, 46, 28, 2, 235, 64, 128, 164, 216, 10, 276 | 144, 132, 10, 187, 97, 23, 66, 150, 224, 101, 33, 241, 134, 56, 164, 8, 149, 133, 80, 132, 277 | 212, 134, 36, 101, 112, 184, 109, 108, 3, 190, 176, 177, 124, 201, 146, 172, 123, 164, 153, 278 | 209, 104, 206, 158, 190, 207, 125, 35, 15, 174, 0, 78, 226, 107, 107, 255, 225, 83, 154, 279 | 105, 169, 231, 245, 175, 223, 183, 127, 239, 119, 188, 17, 248, 140, 207, 248, 191, 1, 41, 280 | 31, 207, 138, 95, 253, 202, 64, 28, 199, 193, 186, 186, 28, 36, 22, 67, 204, 53, 107, 72, 281 | 167, 252, 209, 255, 59, 127, 87, 200, 252, 249, 25, 140, 36, 211, 141, 149, 149, 204, 21, 282 | 126, 63, 223, 205, 113, 118, 101, 32, 96, 187, 250, 250, 204, 62, 219, 86, 79, 204, 154, 283 | 229, 236, 227, 121, 170, 231, 222, 123, 171, 236, 242, 37, 23, 196, 236, 253, 179, 177, 97, 284 | 97, 152, 253, 142, 239, 59, 124, 130, 73, 20, 155, 66, 77, 226, 15, 66, 63, 248, 187, 182, 285 | 255, 170, 144, 133, 11, 139, 136, 219, 93, 104, 232, 234, 178, 239, 88, 186, 20, 95, 223, 286 | 211, 35, 241, 83, 83, 110, 98, 122, 90, 22, 178, 89, 32, 48, 140, 68, 121, 60, 56, 151, 287 | 201, 232, 169, 66, 193, 217, 124, 240, 32, 247, 235, 98, 177, 69, 43, 95, 126, 206, 224, 288 | 219, 113, 242, 6, 249, 134, 197, 152, 138, 221, 128, 34, 232, 60, 138, 164, 124, 125, 118, 289 | 223, 177, 154, 234, 154, 15, 163, 222, 232, 214, 42, 79, 85, 223, 61, 161, 123, 204, 242, 290 | 240, 79, 113, 70, 33, 87, 95, 157, 32, 114, 57, 97, 89, 103, 167, 252, 111, 179, 102, 113, 291 | 205, 24, 70, 40, 111, 188, 17, 183, 17, 4, 101, 56, 206, 139, 88, 22, 158, 33, 8, 17, 165, 292 | 105, 18, 27, 30, 182, 210, 46, 23, 194, 20, 139, 214, 179, 137, 132, 231, 191, 38, 38, 26, 293 | 140, 178, 153, 179, 98, 233, 177, 165, 96, 215, 248, 174, 106, 79, 214, 115, 243, 114, 124, 294 | 249, 183, 166, 205, 105, 29, 135, 180, 122, 90, 107, 194, 174, 176, 132, 122, 80, 185, 183, 295 | 216, 59, 21, 166, 195, 127, 226, 66, 220, 230, 7, 107, 31, 204, 148, 47, 253, 24, 88, 249, 296 | 120, 154, 245, 235, 227, 30, 65, 16, 190, 169, 105, 234, 109, 166, 233, 240, 3, 3, 154, 60, 297 | 50, 34, 186, 38, 38, 178, 240, 40, 244, 231, 114, 138, 104, 24, 42, 238, 114, 49, 142, 36, 298 | 97, 208, 3, 168, 77, 146, 24, 176, 44, 164, 197, 231, 179, 147, 0, 60, 218, 95, 40, 60, 90, 299 | 182, 246, 183, 193, 63, 192, 81, 103, 220, 89, 184, 88, 94, 188, 9, 232, 96, 225, 170, 186, 300 | 85, 254, 110, 119, 55, 63, 150, 31, 3, 83, 106, 82, 200, 129, 124, 225, 150, 198, 91, 170, 301 | 71, 147, 163, 129, 162, 93, 92, 16, 147, 70, 67, 95, 186, 247, 11, 7, 247, 252, 226, 128, 302 | 84, 54, 113, 26, 180, 124, 156, 97, 221, 186, 137, 202, 88, 172, 240, 160, 32, 104, 55, 303 | 240, 60, 234, 13, 6, 17, 111, 91, 27, 168, 96, 89, 196, 193, 48, 102, 202, 239, 199, 41, 304 | 154, 54, 213, 234, 106, 194, 148, 36, 85, 86, 20, 197, 90, 177, 130, 10, 111, 218, 20, 108, 305 | 111, 106, 34, 188, 8, 226, 220, 118, 233, 165, 133, 202, 178, 185, 191, 9, 249, 62, 73, 16, 306 | 99, 196, 114, 46, 199, 253, 136, 113, 152, 121, 56, 134, 219, 203, 91, 151, 251, 90, 131, 307 | 173, 204, 135, 242, 193, 209, 183, 210, 111, 247, 158, 72, 157, 16, 239, 223, 121, 255, 308 | 228, 174, 248, 174, 244, 120, 113, 220, 144, 29, 109, 137, 37, 17, 255, 186, 117, 116, 171, 309 | 167, 108, 230, 52, 167, 61, 114, 215, 93, 147, 1, 77, 19, 126, 154, 201, 152, 11, 108, 27, 310 | 1, 245, 245, 118, 133, 166, 233, 70, 99, 35, 111, 173, 92, 201, 176, 138, 146, 113, 119, 311 | 117, 5, 107, 91, 224, 115, 207, 102, 109, 69, 85, 29, 195, 182, 109, 103, 245, 234, 112, 312 | 120, 209, 162, 144, 107, 104, 40, 79, 136, 34, 234, 5, 192, 18, 106, 107, 31, 218, 63, 56, 313 | 248, 88, 217, 242, 167, 121, 226, 196, 19, 88, 229, 116, 229, 173, 205, 106, 243, 35, 189, 314 | 74, 175, 116, 68, 59, 50, 129, 88, 208, 181, 162, 197, 110, 137, 189, 60, 190, 39, 183, 39, 315 | 105, 58, 166, 217, 68, 54, 249, 18, 118, 34, 155, 52, 146, 169, 38, 186, 137, 169, 97, 107, 316 | 60, 134, 104, 206, 41, 18, 197, 248, 45, 27, 111, 233, 217, 178, 113, 75, 217, 226, 95, 317 | 120, 36, 16, 156, 254, 6, 207, 203, 43, 1, 112, 128, 3, 127, 250, 251, 145, 201, 84, 10, 318 | 164, 187, 186, 48, 230, 248, 241, 156, 134, 97, 1, 184, 246, 253, 83, 71, 143, 106, 147, 319 | 197, 162, 173, 148, 198, 192, 203, 236, 23, 95, 156, 74, 253, 252, 231, 137, 137, 201, 73, 320 | 21, 116, 116, 144, 117, 12, 163, 173, 103, 24, 33, 116, 202, 234, 153, 145, 37, 249, 82, 321 | 66, 35, 30, 32, 0, 193, 123, 113, 47, 179, 130, 88, 209, 101, 91, 14, 249, 31, 67, 143, 322 | 245, 252, 41, 241, 167, 49, 152, 226, 103, 178, 84, 5, 30, 246, 47, 171, 88, 214, 54, 219, 323 | 53, 187, 50, 202, 69, 137, 13, 13, 27, 60, 130, 89, 52, 70, 114, 35, 95, 59, 54, 117, 172, 324 | 102, 198, 88, 153, 25, 143, 60, 188, 113, 130, 220, 181, 99, 228, 251, 147, 19, 182, 203, 325 | 231, 179, 88, 77, 51, 16, 28, 215, 185, 138, 10, 221, 115, 252, 184, 204, 13, 12, 152, 124, 326 | 91, 91, 88, 234, 235, 43, 10, 146, 100, 153, 80, 196, 105, 178, 89, 221, 60, 118, 76, 87, 327 | 37, 137, 144, 88, 214, 164, 198, 198, 12, 85, 85, 237, 241, 145, 145, 95, 28, 47, 15, 249, 328 | 24, 179, 142, 205, 66, 134, 226, 67, 255, 176, 44, 188, 108, 201, 170, 200, 42, 223, 72, 329 | 124, 196, 55, 169, 78, 40, 115, 61, 243, 176, 16, 8, 97, 35, 218, 72, 177, 60, 20, 100, 330 | 141, 76, 113, 65, 100, 1, 231, 35, 125, 252, 246, 233, 237, 147, 136, 131, 16, 41, 109, 74, 331 | 62, 152, 59, 168, 166, 244, 84, 182, 227, 225, 142, 35, 195, 155, 134, 103, 198, 206, 120, 332 | 132, 197, 113, 250, 115, 139, 162, 161, 75, 46, 9, 90, 93, 93, 94, 230, 202, 43, 189, 158, 333 | 75, 46, 97, 104, 93, 71, 249, 247, 223, 23, 92, 146, 100, 218, 153, 140, 162, 228, 243, 58, 334 | 20, 113, 202, 99, 37, 49, 56, 14, 208, 150, 22, 156, 102, 89, 133, 133, 158, 196, 62, 248, 335 | 192, 56, 89, 40, 96, 138, 105, 162, 95, 250, 198, 55, 82, 212, 204, 29, 62, 1, 237, 208, 336 | 148, 102, 104, 45, 181, 238, 90, 130, 161, 24, 100, 84, 25, 29, 242, 123, 2, 169, 31, 95, 337 | 253, 227, 134, 111, 54, 127, 179, 141, 4, 36, 122, 185, 239, 242, 192, 179, 93, 207, 94, 338 | 222, 66, 183, 114, 56, 77, 104, 221, 77, 221, 64, 210, 37, 117, 75, 124, 75, 220, 141, 185, 339 | 253, 77, 92, 83, 0, 215, 240, 107, 169, 28, 85, 81, 54, 123, 74, 72, 119, 55, 237, 84, 87, 340 | 147, 228, 146, 37, 84, 21, 77, 227, 106, 125, 61, 137, 85, 85, 185, 157, 171, 174, 138, 20, 341 | 87, 172, 8, 76, 235, 186, 101, 196, 98, 34, 92, 78, 167, 124, 81, 122, 143, 70, 201, 224, 342 | 205, 55, 7, 219, 159, 120, 162, 181, 125, 225, 66, 134, 96, 89, 45, 140, 162, 122, 21, 142, 343 | 155, 181, 209, 40, 118, 249, 229, 151, 131, 51, 6, 189, 199, 246, 32, 173, 104, 107, 213, 344 | 51, 71, 127, 133, 110, 60, 254, 168, 166, 80, 114, 41, 221, 98, 9, 33, 161, 31, 200, 30, 345 | 72, 233, 166, 110, 109, 104, 219, 80, 183, 124, 246, 114, 255, 92, 207, 92, 31, 131, 49, 346 | 152, 155, 113, 179, 87, 133, 174, 234, 40, 168, 5, 13, 198, 212, 248, 101, 254, 203, 248, 347 | 251, 219, 238, 191, 154, 23, 249, 165, 101, 179, 167, 132, 56, 14, 33, 230, 114, 224, 173, 348 | 98, 81, 48, 231, 207, 87, 67, 161, 144, 200, 45, 92, 168, 133, 154, 154, 21, 239, 250, 245, 349 | 46, 238, 139, 95, 12, 73, 193, 32, 30, 44, 9, 40, 121, 3, 94, 130, 132, 195, 152, 63, 16, 350 | 32, 89, 158, 119, 3, 4, 193, 205, 169, 41, 113, 2, 86, 252, 10, 152, 178, 9, 89, 118, 201, 351 | 125, 125, 31, 207, 136, 31, 209, 70, 183, 233, 181, 116, 237, 20, 137, 81, 102, 17, 17, 352 | 180, 90, 190, 174, 106, 76, 25, 147, 87, 190, 186, 242, 221, 199, 7, 31, 239, 45, 153, 127, 353 | 101, 228, 149, 196, 75, 189, 47, 197, 119, 102, 119, 78, 201, 178, 140, 13, 77, 13, 17, 354 | 240, 188, 63, 140, 134, 91, 10, 122, 193, 126, 35, 253, 70, 2, 122, 21, 33, 77, 242, 154, 355 | 89, 189, 179, 102, 60, 63, 19, 35, 207, 61, 247, 99, 176, 118, 237, 247, 196, 190, 62, 227, 356 | 202, 124, 30, 115, 226, 113, 90, 178, 109, 29, 141, 197, 76, 224, 114, 153, 152, 101, 249, 357 | 68, 130, 64, 184, 120, 92, 23, 76, 19, 192, 100, 101, 57, 178, 44, 106, 195, 195, 138, 180, 358 | 99, 71, 62, 181, 111, 95, 49, 79, 16, 56, 21, 141, 210, 65, 89, 230, 166, 124, 62, 178, 359 | 208, 217, 137, 191, 240, 234, 171, 143, 137, 37, 251, 127, 201, 129, 159, 31, 112, 208, 91, 360 | 208, 236, 120, 110, 188, 19, 78, 78, 171, 34, 170, 194, 162, 38, 138, 49, 37, 86, 48, 17, 361 | 211, 65, 80, 4, 25, 144, 6, 196, 215, 18, 175, 37, 60, 136, 135, 242, 96, 158, 144, 37, 91, 362 | 212, 137, 68, 239, 73, 65, 42, 228, 97, 125, 35, 34, 120, 36, 244, 86, 250, 173, 84, 76, 363 | 143, 9, 13, 190, 134, 215, 7, 158, 26, 80, 79, 63, 181, 198, 70, 247, 135, 110, 55, 183, 364 | 47, 159, 39, 52, 195, 192, 236, 201, 184, 91, 178, 29, 132, 237, 239, 231, 243, 166, 137, 365 | 219, 30, 143, 75, 10, 4, 48, 174, 180, 186, 74, 66, 146, 73, 61, 55, 48, 160, 79, 29, 57, 366 | 162, 64, 111, 81, 21, 62, 31, 211, 112, 224, 128, 0, 45, 57, 136, 207, 231, 156, 240, 251, 367 | 209, 233, 196, 162, 69, 204, 208, 220, 185, 243, 7, 111, 188, 241, 142, 125, 115, 230, 60, 368 | 56, 186, 120, 241, 166, 158, 175, 126, 245, 215, 191, 124, 54, 188, 250, 250, 163, 150, 369 | 136, 37, 198, 243, 58, 208, 38, 31, 91, 240, 88, 199, 134, 138, 13, 45, 51, 19, 41, 247, 370 | 26, 176, 182, 16, 152, 134, 225, 74, 70, 70, 119, 158, 220, 121, 98, 215, 244, 187, 67, 371 | 131, 250, 80, 50, 138, 214, 18, 180, 66, 219, 240, 60, 112, 4, 7, 232, 150, 62, 211, 22, 372 | 157, 22, 178, 98, 133, 79, 139, 70, 185, 231, 224, 122, 85, 112, 28, 65, 49, 20, 5, 193, 373 | 128, 27, 5, 136, 201, 194, 95, 1, 73, 226, 118, 125, 61, 55, 227, 70, 184, 148, 16, 151, 374 | 139, 38, 151, 45, 243, 84, 52, 53, 241, 145, 198, 70, 46, 136, 162, 238, 100, 83, 19, 151, 375 | 89, 48, 91, 97, 174, 236, 221, 124, 162, 237, 95, 230, 63, 82, 48, 140, 63, 7, 127, 246, 376 | 179, 237, 174, 123, 239, 221, 236, 106, 107, 187, 193, 242, 251, 215, 194, 181, 184, 150, 377 | 116, 216, 245, 183, 29, 140, 214, 60, 243, 155, 8, 251, 237, 183, 24, 180, 133, 173, 4, 94, 378 | 198, 75, 148, 108, 99, 0, 67, 230, 146, 115, 35, 215, 179, 95, 89, 232, 118, 220, 236, 31, 379 | 166, 255, 248, 193, 33, 227, 80, 220, 102, 109, 228, 10, 247, 226, 150, 102, 190, 169, 122, 380 | 64, 29, 136, 31, 87, 122, 135, 3, 172, 255, 85, 193, 45, 40, 51, 115, 42, 189, 125, 196, 381 | 59, 239, 8, 196, 193, 131, 194, 227, 4, 97, 111, 48, 12, 32, 153, 150, 198, 1, 135, 70, 41, 382 | 10, 203, 160, 168, 83, 106, 67, 192, 107, 175, 141, 103, 68, 209, 177, 174, 189, 214, 91, 383 | 113, 247, 221, 117, 209, 151, 94, 18, 114, 219, 183, 43, 146, 63, 27, 47, 94, 129, 188, 384 | 131, 205, 51, 123, 76, 76, 203, 26, 66, 60, 46, 185, 59, 59, 67, 26, 207, 51, 234, 225, 385 | 195, 150, 146, 201, 20, 36, 93, 71, 58, 159, 124, 178, 5, 239, 232, 160, 83, 47, 190, 56, 386 | 170, 109, 219, 38, 90, 233, 180, 40, 96, 182, 246, 208, 237, 128, 58, 142, 102, 138, 152, 387 | 137, 33, 141, 68, 99, 144, 7, 110, 238, 164, 62, 152, 172, 164, 43, 185, 90, 62, 202, 142, 388 | 43, 49, 51, 99, 166, 69, 5, 168, 70, 220, 152, 44, 216, 156, 115, 4, 107, 196, 158, 176, 389 | 150, 90, 185, 210, 220, 63, 214, 107, 61, 247, 220, 163, 246, 157, 119, 62, 56, 172, 170, 390 | 214, 210, 174, 46, 169, 209, 182, 89, 171, 169, 201, 34, 109, 27, 55, 3, 1, 189, 228, 13, 391 | 12, 118, 37, 142, 32, 96, 72, 52, 26, 96, 230, 207, 119, 113, 233, 93, 187, 148, 249, 67, 392 | 207, 103, 175, 95, 148, 229, 42, 208, 56, 110, 226, 14, 7, 221, 197, 186, 87, 173, 170, 393 | 246, 223, 116, 147, 207, 80, 20, 144, 233, 233, 81, 115, 177, 216, 168, 86, 200, 231, 82, 394 | 91, 255, 60, 37, 31, 63, 78, 163, 165, 204, 82, 81, 17, 69, 57, 46, 196, 202, 154, 26, 237, 395 | 47, 72, 137, 203, 58, 42, 84, 83, 3, 9, 37, 145, 181, 41, 219, 232, 244, 117, 70, 191, 59, 396 | 247, 187, 205, 181, 108, 173, 111, 66, 140, 57, 7, 181, 15, 199, 178, 78, 86, 118, 56, 112, 397 | 8, 68, 193, 211, 206, 50, 39, 117, 106, 230, 103, 232, 126, 71, 70, 28, 176, 119, 175, 184, 398 | 82, 81, 82, 155, 220, 110, 7, 182, 41, 150, 19, 10, 217, 156, 162, 216, 142, 199, 131, 57, 399 | 7, 14, 184, 166, 222, 124, 51, 158, 231, 216, 48, 254, 45, 250, 5, 208, 97, 28, 180, 189, 400 | 119, 220, 81, 169, 140, 140, 168, 169, 67, 135, 80, 28, 118, 149, 40, 92, 119, 48, 242, 401 | 129, 146, 136, 235, 83, 3, 125, 100, 198, 40, 80, 168, 155, 38, 93, 40, 153, 246, 113, 145, 402 | 162, 37, 74, 150, 36, 203, 22, 86, 93, 221, 66, 187, 92, 6, 217, 223, 63, 86, 116, 185, 403 | 220, 255, 61, 123, 56, 111, 93, 122, 105, 123, 66, 77, 100, 100, 216, 5, 57, 186, 19, 244, 404 | 177, 190, 196, 152, 52, 38, 103, 204, 140, 2, 219, 149, 105, 224, 1, 111, 130, 106, 240, 405 | 71, 176, 4, 100, 203, 83, 158, 225, 140, 109, 252, 209, 163, 6, 218, 223, 47, 173, 74, 36, 406 | 236, 239, 195, 70, 176, 209, 229, 178, 105, 69, 193, 149, 96, 80, 128, 85, 159, 115, 158, 407 | 121, 166, 247, 100, 211, 228, 54, 254, 22, 240, 91, 87, 77, 125, 125, 1, 46, 110, 67, 76, 408 | 165, 100, 181, 80, 80, 249, 89, 237, 97, 250, 158, 187, 90, 167, 38, 134, 241, 212, 182, 409 | 157, 90, 5, 23, 84, 113, 158, 23, 148, 209, 209, 156, 157, 207, 91, 208, 252, 76, 57, 178, 410 | 44, 203, 193, 88, 214, 75, 93, 119, 93, 21, 182, 99, 199, 176, 232, 247, 71, 199, 228, 254, 411 | 201, 231, 175, 39, 8, 169, 88, 116, 137, 166, 104, 176, 4, 103, 87, 195, 14, 139, 211, 57, 412 | 124, 212, 28, 125, 239, 29, 250, 157, 159, 128, 26, 176, 27, 92, 6, 123, 229, 79, 112, 70, 413 | 33, 37, 38, 39, 29, 112, 242, 164, 94, 151, 203, 233, 107, 17, 196, 252, 146, 162, 80, 17, 414 | 69, 49, 208, 92, 174, 128, 236, 222, 61, 17, 91, 51, 182, 209, 95, 137, 38, 81, 234, 11, 415 | 95, 152, 175, 239, 216, 17, 71, 114, 57, 219, 211, 209, 206, 140, 30, 222, 231, 35, 121, 416 | 207, 9, 174, 162, 198, 9, 119, 206, 99, 1, 172, 166, 217, 147, 39, 149, 210, 26, 46, 41, 417 | 112, 16, 4, 129, 217, 2, 232, 208, 213, 85, 183, 222, 90, 23, 186, 238, 186, 80, 241, 216, 418 | 49, 49, 253, 200, 35, 99, 150, 105, 88, 155, 190, 138, 40, 158, 96, 107, 235, 81, 249, 104, 419 | 82, 176, 133, 124, 3, 219, 160, 232, 168, 254, 186, 43, 236, 250, 195, 239, 189, 191, 31, 420 | 1, 29, 51, 117, 236, 83, 252, 85, 33, 127, 73, 44, 166, 226, 197, 34, 230, 73, 165, 116, 421 | 118, 207, 30, 65, 175, 30, 216, 106, 71, 14, 63, 245, 71, 28, 238, 184, 136, 186, 186, 32, 422 | 16, 69, 197, 128, 79, 152, 20, 197, 230, 220, 225, 195, 73, 34, 18, 25, 131, 129, 238, 227, 423 | 124, 62, 66, 153, 156, 212, 74, 117, 116, 166, 29, 128, 197, 40, 242, 208, 67, 237, 54, 424 | 203, 18, 177, 7, 30, 56, 214, 118, 251, 237, 85, 145, 47, 127, 57, 156, 221, 177, 35, 59, 425 | 254, 192, 3, 195, 165, 49, 133, 182, 138, 223, 30, 184, 115, 241, 208, 239, 10, 191, 83, 426 | 116, 68, 47, 176, 62, 118, 124, 159, 119, 95, 10, 180, 130, 191, 185, 221, 61, 43, 33, 159, 427 | 164, 103, 205, 26, 119, 58, 22, 123, 29, 166, 104, 152, 45, 49, 184, 113, 68, 17, 19, 69, 428 | 81, 21, 69, 73, 251, 208, 161, 2, 225, 243, 25, 92, 107, 171, 7, 135, 207, 31, 175, 169, 429 | 161, 201, 207, 127, 62, 168, 188, 253, 118, 138, 241, 249, 176, 89, 63, 249, 73, 187, 9, 430 | 171, 234, 209, 251, 238, 59, 225, 140, 143, 155, 149, 87, 92, 225, 206, 111, 219, 150, 179, 431 | 224, 22, 179, 228, 50, 184, 228, 222, 186, 244, 131, 15, 126, 80, 190, 213, 89, 115, 198, 432 | 54, 226, 239, 225, 11, 6, 97, 173, 193, 101, 56, 121, 80, 18, 81, 122, 97, 60, 143, 243, 433 | 107, 214, 132, 176, 182, 54, 212, 82, 20, 19, 198, 139, 94, 138, 133, 218, 187, 239, 174, 434 | 175, 95, 189, 58, 20, 90, 183, 174, 82, 30, 24, 80, 196, 158, 158, 188, 184, 123, 119, 218, 435 | 56, 118, 76, 54, 114, 57, 45, 243, 230, 155, 226, 71, 34, 74, 227, 161, 223, 230, 140, 215, 436 | 213, 157, 243, 3, 62, 47, 33, 117, 79, 63, 109, 194, 62, 127, 212, 129, 2, 240, 138, 10, 437 | 30, 161, 105, 162, 116, 103, 189, 189, 189, 2, 95, 176, 32, 232, 192, 138, 138, 219, 54, 438 | 128, 159, 121, 89, 184, 39, 246, 120, 60, 0, 73, 38, 101, 27, 246, 247, 189, 223, 251, 222, 439 | 208, 192, 15, 127, 56, 10, 163, 189, 180, 220, 28, 19, 195, 78, 149, 0, 40, 194, 134, 127, 440 | 67, 59, 225, 180, 215, 27, 158, 57, 119, 14, 156, 151, 144, 18, 168, 207, 215, 79, 194, 441 | 229, 4, 120, 158, 67, 171, 171, 253, 40, 12, 106, 244, 248, 241, 52, 26, 10, 209, 48, 173, 442 | 178, 37, 97, 38, 12, 232, 195, 207, 63, 15, 246, 111, 220, 40, 76, 190, 240, 66, 178, 52, 443 | 241, 211, 79, 190, 140, 1, 253, 169, 116, 118, 182, 216, 110, 55, 13, 133, 0, 139, 166, 41, 444 | 27, 65, 218, 78, 221, 229, 236, 57, 111, 33, 222, 197, 139, 79, 98, 46, 23, 101, 166, 211, 445 | 130, 204, 48, 52, 213, 210, 226, 194, 182, 109, 75, 128, 222, 222, 60, 30, 137, 48, 182, 446 | 105, 218, 102, 42, 149, 23, 223, 123, 239, 112, 126, 235, 214, 126, 56, 115, 184, 155, 181, 447 | 49, 3, 216, 164, 1, 44, 194, 116, 108, 24, 66, 14, 162, 100, 179, 2, 74, 146, 46, 35, 16, 448 | 240, 193, 212, 129, 27, 11, 22, 116, 56, 24, 86, 87, 190, 205, 89, 115, 222, 66, 248, 134, 449 | 134, 221, 184, 203, 133, 160, 176, 29, 197, 85, 181, 32, 93, 123, 109, 19, 198, 113, 56, 450 | 76, 173, 8, 60, 79, 58, 112, 99, 3, 39, 78, 26, 154, 200, 42, 184, 17, 77, 161, 74, 115, 451 | 18, 85, 218, 83, 152, 210, 62, 137, 200, 29, 73, 120, 204, 32, 106, 141, 5, 35, 95, 155, 452 | 152, 72, 83, 215, 92, 227, 37, 130, 65, 26, 182, 214, 184, 229, 241, 4, 202, 183, 57, 107, 453 | 206, 91, 72, 221, 186, 117, 211, 182, 36, 237, 135, 171, 11, 33, 211, 105, 25, 8, 130, 233, 454 | 192, 174, 18, 134, 62, 1, 60, 30, 94, 175, 143, 70, 5, 218, 110, 53, 57, 164, 217, 32, 156, 455 | 8, 32, 17, 15, 20, 198, 184, 56, 63, 25, 12, 68, 73, 148, 162, 25, 7, 163, 176, 36, 198, 456 | 215, 215, 125, 251, 219, 100, 195, 186, 117, 46, 242, 43, 95, 153, 5, 14, 28, 0, 48, 89, 457 | 204, 52, 130, 231, 194, 121, 11, 41, 129, 209, 244, 78, 88, 27, 48, 204, 237, 198, 168, 87, 458 | 94, 25, 66, 39, 39, 101, 130, 32, 200, 172, 92, 240, 209, 193, 48, 237, 225, 195, 148, 3, 459 | 23, 60, 77, 226, 128, 192, 79, 197, 52, 67, 177, 0, 22, 76, 48, 255, 63, 159, 2, 237, 143, 460 | 63, 25, 114, 224, 78, 217, 3, 27, 203, 82, 66, 112, 98, 49, 219, 171, 105, 195, 112, 82, 461 | 123, 103, 6, 159, 3, 23, 36, 132, 175, 173, 125, 155, 173, 175, 247, 25, 55, 222, 216, 140, 462 | 193, 218, 128, 195, 45, 57, 92, 239, 56, 229, 128, 124, 78, 17, 92, 150, 172, 20, 77, 203, 463 | 4, 162, 172, 2, 203, 23, 6, 145, 53, 55, 129, 180, 37, 1, 39, 18, 114, 234, 102, 207, 6, 464 | 245, 157, 179, 144, 182, 16, 109, 143, 61, 252, 48, 114, 244, 206, 59, 13, 233, 229, 151, 465 | 199, 12, 138, 59, 238, 13, 133, 142, 150, 111, 113, 214, 124, 172, 251, 61, 87, 54, 239, 466 | 221, 91, 188, 167, 165, 165, 70, 108, 107, 91, 196, 91, 150, 11, 238, 113, 125, 208, 35, 467 | 130, 110, 131, 162, 141, 57, 180, 56, 62, 84, 176, 128, 201, 192, 64, 183, 23, 253, 251, 468 | 79, 209, 186, 207, 95, 131, 96, 48, 206, 193, 142, 189, 2, 223, 217, 129, 107, 71, 142, 56, 469 | 202, 190, 189, 114, 118, 100, 132, 177, 68, 145, 160, 28, 116, 178, 224, 10, 220, 183, 240, 470 | 245, 151, 39, 202, 183, 56, 107, 46, 200, 35, 37, 40, 175, 119, 179, 127, 251, 246, 65, 471 | 216, 166, 136, 136, 105, 230, 73, 216, 182, 48, 56, 206, 185, 1, 149, 38, 109, 34, 69, 0, 472 | 68, 37, 112, 151, 201, 51, 46, 196, 235, 245, 130, 72, 62, 27, 175, 116, 180, 216, 200, 93, 473 | 119, 29, 46, 110, 222, 124, 130, 211, 245, 73, 158, 162, 122, 85, 69, 235, 21, 60, 193, 474 | 187, 174, 217, 246, 242, 254, 178, 233, 115, 226, 188, 90, 148, 79, 210, 251, 207, 255, 475 | 116, 159, 62, 81, 184, 19, 70, 11, 234, 80, 20, 111, 134, 195, 60, 206, 48, 104, 236, 181, 476 | 215, 142, 42, 154, 233, 241, 132, 106, 27, 96, 102, 85, 26, 106, 3, 147, 198, 192, 128, 12, 477 | 247, 202, 32, 151, 203, 169, 176, 88, 18, 104, 117, 0, 55, 42, 232, 151, 212, 193, 226, 99, 478 | 221, 91, 182, 168, 101, 147, 231, 204, 5, 123, 164, 68, 98, 137, 217, 151, 239, 50, 77, 479 | 186, 38, 226, 118, 7, 131, 22, 157, 205, 198, 64, 42, 53, 138, 226, 184, 26, 207, 142, 169, 480 | 177, 236, 168, 234, 214, 11, 83, 114, 54, 75, 88, 176, 63, 43, 125, 93, 79, 44, 158, 231, 481 | 29, 92, 133, 251, 226, 183, 226, 152, 121, 179, 123, 248, 66, 68, 148, 184, 160, 24, 249, 482 | 136, 213, 171, 167, 252, 73, 100, 250, 50, 33, 156, 54, 29, 148, 32, 152, 34, 106, 2, 10, 483 | 134, 12, 124, 80, 84, 182, 152, 213, 13, 89, 142, 155, 34, 161, 133, 48, 163, 48, 143, 245, 484 | 102, 190, 232, 15, 89, 221, 115, 48, 166, 186, 129, 19, 197, 119, 51, 28, 71, 29, 124, 250, 485 | 233, 222, 67, 101, 115, 231, 197, 69, 241, 136, 203, 53, 87, 164, 233, 38, 212, 100, 220, 486 | 214, 160, 255, 68, 234, 253, 217, 251, 133, 253, 149, 239, 201, 241, 89, 19, 118, 246, 203, 487 | 8, 170, 174, 197, 11, 206, 215, 235, 133, 220, 210, 8, 22, 139, 136, 182, 78, 34, 100, 42, 488 | 245, 190, 20, 10, 205, 65, 116, 29, 135, 29, 175, 81, 218, 112, 93, 16, 23, 69, 72, 101, 489 | 229, 170, 66, 125, 253, 98, 148, 97, 106, 57, 199, 169, 114, 83, 244, 124, 70, 161, 128, 490 | 144, 119, 103, 5, 169, 210, 180, 141, 0, 131, 54, 182, 222, 84, 215, 214, 182, 161, 154, 491 | 97, 170, 243, 83, 83, 253, 125, 178, 60, 97, 37, 147, 251, 164, 202, 202, 175, 85, 200, 492 | 114, 162, 244, 61, 210, 5, 113, 81, 132, 68, 34, 221, 73, 191, 191, 182, 199, 182, 9, 119, 493 | 125, 253, 218, 234, 182, 182, 127, 108, 174, 170, 186, 50, 72, 146, 36, 12, 19, 6, 71, 81, 494 | 2, 27, 30, 126, 110, 228, 228, 201, 95, 14, 10, 194, 158, 130, 101, 77, 72, 154, 150, 22, 495 | 51, 153, 253, 162, 219, 29, 133, 159, 115, 159, 250, 199, 205, 185, 114, 81, 132, 4, 131, 496 | 221, 48, 126, 145, 223, 217, 118, 226, 164, 40, 190, 145, 79, 38, 255, 71, 50, 140, 62, 53, 497 | 16, 168, 12, 180, 180, 220, 218, 54, 111, 222, 191, 206, 225, 249, 74, 70, 146, 222, 205, 498 | 97, 88, 222, 36, 8, 3, 54, 185, 53, 180, 170, 90, 158, 193, 193, 173, 25, 28, 15, 164, 203, 499 | 166, 206, 155, 139, 34, 164, 4, 236, 243, 246, 5, 2, 85, 239, 40, 202, 184, 38, 73, 199, 500 | 28, 73, 74, 85, 21, 139, 66, 64, 215, 227, 176, 37, 59, 146, 55, 140, 140, 205, 243, 159, 501 | 139, 70, 34, 183, 55, 55, 52, 60, 216, 78, 211, 181, 20, 220, 146, 104, 150, 149, 217, 227, 502 | 241, 116, 15, 150, 205, 156, 55, 23, 77, 72, 36, 178, 82, 15, 133, 22, 252, 134, 101, 185, 503 | 247, 29, 39, 51, 230, 118, 47, 176, 162, 209, 175, 211, 154, 150, 74, 143, 142, 62, 57, 40, 504 | 203, 163, 170, 101, 105, 6, 77, 243, 48, 57, 84, 243, 44, 91, 231, 166, 40, 125, 75, 32, 505 | 176, 251, 135, 179, 103, 127, 255, 130, 99, 228, 162, 20, 196, 143, 16, 197, 30, 48, 52, 506 | 244, 84, 71, 60, 222, 119, 27, 65, 44, 90, 226, 114, 117, 213, 38, 147, 59, 50, 211, 211, 507 | 175, 36, 16, 196, 134, 37, 4, 40, 240, 149, 231, 184, 250, 233, 64, 160, 117, 31, 207, 43, 508 | 111, 116, 119, 255, 254, 99, 223, 79, 157, 47, 23, 85, 200, 71, 12, 15, 255, 136, 207, 100, 509 | 62, 104, 86, 20, 171, 73, 81, 116, 151, 97, 12, 11, 4, 225, 100, 9, 194, 158, 166, 105, 39, 510 | 227, 118, 219, 185, 57, 115, 98, 23, 84, 0, 63, 227, 51, 62, 227, 92, 0, 224, 127, 1, 208, 511 | 202, 28, 31, 66, 176, 235, 16, 0, 0, 0, 3, 82, 117, 83, 116, 104, 101, 121, 158, 176, 245, 512 | 160, 0, 0, 0, 0, 73, 69, 78, 68, 174, 66, 96, 130, 513 | ]; 514 | } 515 | --------------------------------------------------------------------------------