├── .gitignore ├── CHANGELOG ├── Manifest ├── README.rdoc ├── Rakefile ├── lib ├── passgen.rb └── passgen │ ├── probabilities.rb │ └── strength_analyzer.rb ├── passgen.gemspec ├── rails └── init.rb └── spec ├── passgen └── strength_analyzer_spec.rb └── passgen_spec.rb /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .ruby-version 3 | -------------------------------------------------------------------------------- /CHANGELOG: -------------------------------------------------------------------------------- 1 | v1.1.2 Added contributors 2 | 3 | v1.1.1 Removed malicious dependency 4 | 5 | v1.0.1 Corrected syntax error with ruby1.9.2 6 | 7 | v1.0.0. Added password strength analyzer 8 | 9 | v0.9.1. Had not added new files in 0.9.0 version 10 | 11 | v0.9.0. Added pronounceable passwords 12 | 13 | v0.1.3. Updated help 14 | 15 | v0.1.2. Moved to rubygems.org 16 | 17 | v0.1.1. First public release -------------------------------------------------------------------------------- /Manifest: -------------------------------------------------------------------------------- 1 | CHANGELOG 2 | Manifest 3 | README.rdoc 4 | Rakefile 5 | rails/init.rb 6 | lib/passgen.rb 7 | lib/passgen/probabilities.rb 8 | lib/passgen/strength_analyzer.rb 9 | passgen.gemspec 10 | spec/passgen/strength_analyzer_spec.rb 11 | spec/passgen_spec.rb 12 | -------------------------------------------------------------------------------- /README.rdoc: -------------------------------------------------------------------------------- 1 | = Passgen 2 | 3 | Ruby gem for generating passwords quickly and easily. Although it is 4 | suitable for use within Rails it has no Rails dependencies and can be used in 5 | non-Rails applications as well. 6 | 7 | It can generate passwords including lower case, upper case, digits and symbols and also 8 | pronounceable passwords. 9 | 10 | Since 1.0.0 you can also analyze the quality of a password, both as a numeric score 11 | between 0 and 100 and as a complexity ranking. 12 | 13 | The algorithm used exists in a number of variations for different languages, if if anyone 14 | knows its origin and would like to include credit to the original author, please get in 15 | touch via GitHub. 16 | 17 | == Install 18 | 19 | gem install passgen 20 | 21 | == Usage 22 | 23 | The usage could not be easier. Just require and call the generate method: 24 | 25 | >> require 'rubygems' 26 | >> require 'passgen' 27 | >> Passgen::generate 28 | => "zLWCeS3xC9" 29 | 30 | You check the strength of a password by calling analyze: 31 | 32 | >> info = Passgen::analyze("zLWCeS3xC9") 33 | => # 34 | >> info.score 35 | => 78 36 | >> info.complexity 37 | => "Strong" 38 | 39 | == Examples 40 | 41 | >> Passgen::generate 42 | => "zLWCeS3xC9" 43 | 44 | >> Passgen::generate(:length => 20) 45 | => "6lCcHvkuEW6OuzAtkoAs" 46 | 47 | >> Passgen::generate(:symbols => true) 48 | => "gr)$6bIym1" 49 | 50 | >> Passgen::generate(:lowercase => :only) 51 | => "ysbwuxbcea" 52 | 53 | >> Passgen::generate(:number => 3) 54 | => ["REdOigTkdI", "PQu8DsV9WZ", "qptKLbw8YQ"] 55 | 56 | >> Passgen::generate(:seed => 5) 57 | => "JoV9M2qjiK" 58 | >> Passgen::generate(:seed => 5) # Will generate same password again 59 | => "JoV9M2qjiK" 60 | 61 | >> Passgen::generate(:pronounceable => true) # Pronounceable, mixed case password 62 | => "ActeodEuRT" 63 | >> Passgen::generate(:pronounceable => true, :lowercase => :only) # Pronounceable lower case 64 | => "terysolang" 65 | >> Passgen::generate(:pronounceable => true, :uppercase => :only) # Pronounceable upper case 66 | => "ACTOPECHEI" 67 | >> Passgen::generate(:pronounceable => true, :digits_before => 3) # Pad with digits in front 68 | => "886uRApLIN" 69 | >> Passgen::generate(:pronounceable => true, :digits_before => 3) # Pad with digits at the end 70 | => "uRAPLIN886" 71 | 72 | == Options: 73 | 74 | === :lowercase => true/false/:only 75 | * true - Use lowercase letters in the generated password. 76 | * false - Do not use lowercase letters in the generated password. 77 | * :only - Only use lowercase letters in the generated password. 78 | 79 | === :uppercase => true/false/:only 80 | * true - Use uppercase letters in the generated password. 81 | * false - Do not use uppercase letters in the generated password. 82 | * :only - Only use uppercase letters in the generated password. 83 | 84 | === :digits => true/false/:only 85 | * true - Use digits in the generated password. 86 | * false - Do not use digits in the generated password. 87 | * :only - Only use digits in the generated password. 88 | 89 | === :symbols => true/false/:only/:list 90 | * true - Use symbols in the generated password. 91 | * false - Do not use symbols in the generated password. 92 | * :only - Only use symbols in the generated password. 93 | * :list - A string with the symbols to use. Not implemented yet. 94 | 95 | === :number => integer 96 | Number of passwords to generate. If >1 the result is an Array. 97 | 98 | === :length => integer/range 99 | The number of characters in the generated passwords. A range results in passwords 100 | lengths within the given range. 101 | 102 | === :seed => integer/:default 103 | Set the srand seed to the given integer prior to generating the passwords. 104 | 105 | === :pronounceable => true/false 106 | * true - Generate pronounceable passwords 107 | * false - No effect 108 | 109 | === :digits_after => true/number (Only in combination with :pronounceable) 110 | * Pads the pronounceable password with number digits at the end. Defaults to 2 if true is passed. 111 | 112 | === :digits_before => true/number (Only in combination with :pronounceable) 113 | * Pads the pronounceable password with number digits in front. Defaults to 2 if true is passed. 114 | 115 | === Default values: 116 | 117 | :lowercase => true 118 | 119 | :uppercase => true 120 | 121 | :digits => true 122 | 123 | :symbols => false 124 | 125 | :pronounceable => Not implemented yet. 126 | 127 | :number => 1 128 | 129 | :length => 10 130 | 131 | :seed => nil 132 | 133 | :pronounceable => false 134 | 135 | :digits_after => 2 (Only in combination with pronounceable) 136 | 137 | :digits_before => 2 (Only in combination with pronounceable) 138 | 139 | == Copyright and license 140 | 141 | Permission is hereby granted, free of charge, to any person obtaining 142 | a copy of this software and associated documentation files (the 143 | "Software"), to deal in the Software without restriction, including 144 | without limitation the rights to use, copy, modify, merge, publish, 145 | distribute, sublicense, and/or sell copies of the Software, and to 146 | permit persons to whom the Software is furnished to do so, subject to 147 | the following conditions: 148 | 149 | The above copyright notice and this permission notice shall be 150 | included in all copies or substantial portions of the Software. 151 | 152 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 153 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 154 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 155 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 156 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 157 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 158 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 159 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # Rakefile 2 | require 'rubygems' 3 | require 'rake' 4 | require 'echoe' 5 | 6 | Echoe.new('passgen', '1.0.1') do |p| 7 | p.description = "A password generation gem for Ruby and Rails applications." 8 | p.url = "http://github.com/cryptice/passgen" 9 | p.author = "Erik Lindblad" 10 | p.email = "erik@l2c.se" 11 | p.ignore_pattern = ["tmp/*", "script/*", "nbproject/*"] 12 | p.development_dependencies = [] 13 | end 14 | 15 | Dir["#{File.dirname(__FILE__)}/tasks/*.rake"].sort.each { |ext| load ext } 16 | -------------------------------------------------------------------------------- /lib/passgen.rb: -------------------------------------------------------------------------------- 1 | #= Passgen 2 | # 3 | # Ruby gem for generating passwords quickly and easily. Although it is 4 | # suitable for use within Rails it has no Rails dependencies and can be used in 5 | # non-Rails applications as well. 6 | # 7 | #== Install 8 | # 9 | # gem install cryptice-passgen --source http://gems.github.com 10 | # 11 | #== Usage 12 | # 13 | # The usage could not be easier. Just require and call the generate method: 14 | # 15 | # >> require 'rubygems' 16 | # >> require 'passgen' 17 | # >> Passgen::generate 18 | # => "zLWCeS3xC9" 19 | # 20 | # == Examples 21 | # 22 | # >> Passgen::generate 23 | # => "zLWCeS3xC9" 24 | # 25 | # >> Passgen::generate(length: 20) 26 | # => "6lCcHvkuEW6OuzAtkoAs" 27 | # 28 | # >> Passgen::generate(symbols: true) 29 | # => "gr)$6bIym1" 30 | # 31 | # >> Passgen::generate(lowercase: :only) 32 | # => "ysbwuxbcea" 33 | # 34 | # >> Passgen::generate(number: 3) 35 | # => ["REdOigTkdI", "PQu8DsV9WZ", "qptKLbw8YQ"] 36 | # 37 | # >> Passgen::generate(seed: 5) 38 | # => "JoV9M2qjiK" 39 | # >> Passgen::generate(seed: 5) # Will generate same password again 40 | # => "JoV9M2qjiK" 41 | # 42 | # >> Passgen::generate(seed: :default) # Will set random seed... 43 | # => "SI8QDBdV98" 44 | # >> Passgen::generate(seed: :default) # and hence give different password 45 | # => "tHHU5HLBAn" 46 | # 47 | # == Options: 48 | # 49 | # === :lowercase => true/false/:only 50 | # * true - Use lowercase letters in the generated password. 51 | # * false - Do not use lowercase letters in the generated password. 52 | # * :only - Only use lowercase letters in the generated password. 53 | # 54 | # === :uppercase => true/false/:only 55 | # * true - Use uppercase letters in the generated password. 56 | # * false - Do not use uppercase letters in the generated password. 57 | # * :only - Only use uppercase letters in the generated password. 58 | # 59 | # === :digits => true/false/:only 60 | # * true - Use digits in the generated password. 61 | # * false - Do not use digits in the generated password. 62 | # * :only - Only use digits in the generated password. 63 | # 64 | # === :symbols => true/false/:only/:list 65 | # * true - Use symbols in the generated password. 66 | # * false - Do not use symbols in the generated password. 67 | # * :only - Only use symbols in the generated password. 68 | # * :list - A string with the symbols to use. Not implemented yet. 69 | # 70 | # === :pronounceable => true/false 71 | # Not implmented yet. 72 | # 73 | # === :number => integer 74 | # Number of passwords to generate. If >1 the result is an Array. 75 | # 76 | # === :length => integer/range 77 | # The number of characters in the generated passwords. A range results in passwords 78 | # lengths within the given range. 79 | # 80 | # === :seed => integer/:default 81 | # Set the srand seed to the given integer prior to generating the passwords. 82 | # 83 | # === Default values: 84 | # 85 | # lowercase: true 86 | # 87 | # uppercase: true 88 | # 89 | # digits: true 90 | # 91 | # symbols: false 92 | # 93 | # pronounceable: Not implemented yet. 94 | # 95 | # number: 1 96 | # 97 | # length: 10 98 | # 99 | # seed: nil 100 | # 101 | # == Copyright and license 102 | # 103 | # Copyright (c) 2009 Erik Lindblad 104 | # 105 | # Permission is hereby granted, free of charge, to any person obtaining 106 | # a copy of this software and associated documentation files (the 107 | # "Software"), to deal in the Software without restriction, including 108 | # without limitation the rights to use, copy, modify, merge, publish, 109 | # distribute, sublicense, and/or sell copies of the Software, and to 110 | # permit persons to whom the Software is furnished to do so, subject to 111 | # the following conditions: 112 | # 113 | # The above copyright notice and this permission notice shall be 114 | # included in all copies or substantial portions of the Software. 115 | # 116 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 117 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 118 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 119 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 120 | # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 121 | # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 122 | # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 123 | 124 | require 'digest' 125 | require 'passgen/probabilities' 126 | require 'passgen/strength_analyzer' 127 | 128 | module Passgen 129 | 130 | VERSION = "1.0.0" 131 | 132 | DEFAULT_PARAMS = { 133 | number: 1, 134 | length: 10, 135 | lowercase: true, 136 | uppercase: true, 137 | digits: true, 138 | symbols: false, 139 | pronounceable: false 140 | } 141 | 142 | def self.default_seed 143 | Digest::MD5.hexdigest("#{rand}#{Time.now}#{Process.object_id}").to_i(16) 144 | end 145 | 146 | def self.generate(params = {}) 147 | set_options(params) 148 | tokens = valid_tokens 149 | set_seed 150 | 151 | if n == 1 152 | generate_one(tokens) 153 | else 154 | Array.new(n) { |i| generate_one(tokens) } 155 | end 156 | end 157 | 158 | def self.analyze(pw) 159 | Passgen::StrengthAnalyzer.analyze(pw) 160 | end 161 | 162 | private 163 | 164 | def self.alphabet(index) 165 | if use_lowercase? && !use_uppercase? 166 | LOWERCASE_TOKENS[index] 167 | elsif use_uppercase? && !use_lowercase? 168 | UPPERCASE_TOKENS[index] 169 | else 170 | tmp = LOWERCASE_TOKENS[index] 171 | tmp.upcase! if rand > 0.5 172 | tmp 173 | end 174 | end 175 | 176 | def self.generate_one(tokens) 177 | if @options[:pronounceable] 178 | generate_pronounceable 179 | else 180 | Array.new(password_length) { tokens[rand(tokens.size)] }.join 181 | end 182 | end 183 | 184 | def self.generate_pronounceable 185 | password = "" 186 | 187 | # Append digits in front 188 | digits_prefix = if @options[:digits_before] 189 | @options[:length] -= @options[:digits_before] 190 | Array.new(@options[:digits_before]) { DIGIT_TOKENS[rand(DIGIT_TOKENS.size)] }.join 191 | else 192 | "" 193 | end 194 | 195 | # Append digits at the end 196 | digits_suffix = if @options[:digits_after] 197 | @options[:length] -= @options[:digits_after] 198 | Array.new(@options[:digits_after]) { DIGIT_TOKENS[rand(DIGIT_TOKENS.size)] }.join 199 | else 200 | "" 201 | end 202 | 203 | # Find a random starting point. 204 | found_start = false 205 | ranno = rand * SIGMA # random number [0,1[ weighed by sum of frequencies 206 | sum = 0; 207 | N_LETTERS.times do |c1| 208 | N_LETTERS.times do |c2| 209 | N_LETTERS.times do |c3| 210 | sum += P[c1][c2][c3] 211 | if sum > ranno 212 | password << alphabet(c1) 213 | password << alphabet(c2) 214 | password << alphabet(c3) 215 | found_start = true 216 | break 217 | end 218 | end 219 | break if found_start 220 | end 221 | break if found_start 222 | end 223 | 224 | # Do a random walk. 225 | (3...@options[:length]).each do |nchar| 226 | c1 = LETTER_INDEXES[password[nchar - 2..nchar - 2]] 227 | c2 = LETTER_INDEXES[password[nchar - 1..nchar - 1]] 228 | sum = 0 229 | N_LETTERS.times { |c3| sum += P[c1][c2][c3] } 230 | break if sum == 0 231 | ranno = rand * sum 232 | sum = 0 233 | N_LETTERS.times do |c3| 234 | sum += P[c1][c2][c3] 235 | if sum > ranno 236 | password << alphabet(c3) 237 | break 238 | end 239 | end 240 | end 241 | digits_prefix + password + digits_suffix 242 | end 243 | 244 | def self.password_length 245 | if @options[:length].is_a?(Range) 246 | tmp = @options[:length].to_a 247 | tmp[rand(tmp.size)] 248 | else 249 | @options[:length].to_i 250 | end 251 | end 252 | 253 | def self.n 254 | @options[:number] 255 | end 256 | 257 | def self.set_options(params) 258 | if params[:lowercase] == :only 259 | params[:uppercase] = false 260 | params[:digits] = false 261 | end 262 | 263 | if params[:uppercase] == :only 264 | params[:lowercase] = false 265 | params[:digits] = false 266 | end 267 | 268 | if params[:digits] == :only 269 | params[:lowercase] = false 270 | params[:uppercase] = false 271 | end 272 | 273 | if params[:symbols] == :only 274 | params[:lowercase] = false 275 | params[:uppercase] = false 276 | params[:digits] = false 277 | params[:symbols] = true 278 | end 279 | 280 | if params[:digits_before] == true 281 | params[:digits_before] = 2 282 | end 283 | 284 | if params[:digits_after] == true 285 | params[:digits_after] = 2 286 | end 287 | 288 | @options = DEFAULT_PARAMS.merge(params) 289 | end 290 | 291 | def self.set_seed 292 | if @options[:seed] 293 | if @options[:seed] == :default 294 | srand(default_seed) 295 | else 296 | srand(@options[:seed]) 297 | end 298 | end 299 | end 300 | 301 | def self.symbol_tokens 302 | %w{! @ # $ % & / ( ) + ? *} 303 | end 304 | 305 | def self.use_lowercase? 306 | @options[:lowercase] 307 | end 308 | 309 | def self.use_uppercase? 310 | @options[:uppercase] 311 | end 312 | 313 | def self.use_digits? 314 | @options[:digits] 315 | end 316 | 317 | def self.use_symbols? 318 | @options[:symbols] 319 | end 320 | 321 | def self.valid_tokens 322 | tmp = [] 323 | tmp += LOWERCASE_TOKENS if use_lowercase? 324 | tmp += UPPERCASE_TOKENS if use_uppercase? 325 | tmp += DIGIT_TOKENS if use_digits? 326 | tmp += symbol_tokens if use_symbols? 327 | tmp 328 | end 329 | end -------------------------------------------------------------------------------- /lib/passgen/probabilities.rb: -------------------------------------------------------------------------------- 1 | module Passgen 2 | 3 | P = [ 4 | [ 5 | [2,0,3,0,0,0,1,0,0,0,0,1,1,1,0,0,0,3,2,0,0,0,0,0,0,0], # A A 6 | [37,25,2,5,38,0,0,2,46,1,0,304,0,2,49,0,0,24,24,0,19,0,0,0,14,0], # A B 7 | [26,1,64,2,107,0,1,94,67,0,173,13,5,1,35,1,13,32,3,114,23,0,0,0,45,0], # A C 8 | [35,7,3,43,116,6,3,8,75,14,1,16,25,3,44,3,1,35,20,1,10,25,9,0,18,0], # A D 9 | [2,0,2,1,0,1,3,0,0,0,0,10,0,2,3,0,0,12,6,0,2,0,0,0,0,0], # A E 10 | [5,0,0,0,14,50,2,0,3,0,2,5,0,2,7,0,0,5,1,39,1,0,0,0,1,0], # A F 11 | [30,1,0,1,182,0,42,5,30,0,0,7,9,42,51,3,0,24,3,0,21,0,3,0,3,0], # A G 12 | [12,0,0,0,20,0,0,0,3,0,0,5,4,2,13,0,0,2,0,0,1,0,0,0,0,0], # A H 13 | [2,0,10,26,2,1,10,0,2,1,2,87,13,144,0,2,0,93,30,23,0,3,1,0,0,0], # A I 14 | [4,0,0,0,3,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0], # A J 15 | [11,0,1,1,98,1,0,1,15,0,0,3,0,0,5,1,0,3,0,1,2,0,3,0,8,0], # A K 16 | [78,20,34,45,124,21,24,5,109,0,28,237,31,3,53,23,0,7,16,69,29,26,5,0,26,2], # A L 17 | [70,57,1,1,98,3,0,1,68,0,0,3,38,2,43,69,0,3,14,3,12,0,2,0,14,0], # A M 18 | [114,6,156,359,103,8,146,12,141,2,57,4,0,89,61,1,4,1,124,443,29,6,1,3,28,9], # A N 19 | [0,0,1,0,0,0,0,0,0,0,0,3,1,0,0,0,0,3,2,2,2,0,0,0,0,0], # A O 20 | [29,3,0,1,59,1,0,86,25,0,1,14,1,1,37,94,0,9,22,30,8,0,0,0,9,0], # A P 21 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,14,0,0,0,0,0], # A Q 22 | [124,64,101,233,115,12,47,5,188,3,61,55,68,34,46,25,6,94,48,189,5,22,5,1,172,2], # A R 23 | [19,3,32,0,71,0,1,81,49,0,22,3,19,2,19,34,4,0,152,211,12,0,1,0,17,1], # A S 24 | [50,3,41,2,863,4,0,144,352,0,5,14,6,3,144,0,0,60,13,106,57,1,5,0,8,5], # A T 25 | [0,5,23,35,5,5,38,1,0,1,3,33,4,23,0,4,1,35,52,56,0,1,0,7,0,1], # A U 26 | [35,0,0,1,108,0,0,0,49,0,0,1,0,0,19,0,0,0,0,0,3,1,0,0,6,0], # A V 27 | [30,10,0,4,3,6,2,2,2,0,10,13,4,15,3,0,0,6,3,5,0,0,0,0,2,0], # A W 28 | [3,0,0,0,4,0,0,0,22,0,0,1,0,0,7,2,0,0,1,1,0,0,3,0,3,0], # A X 29 | [11,8,1,5,16,5,1,2,2,0,0,10,7,4,13,1,0,3,5,7,3,0,5,0,0,0], # A Y 30 | [10,0,0,1,22,0,0,0,10,0,0,0,0,0,7,0,0,0,0,2,2,0,0,0,4,11] # A Z 31 | ], 32 | [ 33 | [0,17,74,11,1,2,19,4,8,0,10,68,7,73,1,7,0,110,54,55,9,1,3,1,12,1], # B A 34 | [7,0,0,0,16,0,0,0,10,0,0,24,0,0,9,0,0,2,3,0,2,0,0,0,14,0], # B B 35 | [2,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0], # B C 36 | [2,0,0,0,2,0,0,0,2,0,0,0,0,0,3,0,0,1,0,0,3,0,0,0,0,0], # B D 37 | [51,1,14,34,18,11,16,7,9,0,1,85,5,48,2,2,2,199,36,41,0,4,5,1,6,2], # B E 38 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0], # B F 39 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # B G 40 | [0,0,0,0,1,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,1,0,0,0,0,0], # B H 41 | [34,8,22,21,8,3,9,1,0,3,1,50,7,45,16,4,2,29,22,59,4,4,0,0,0,3], # B I 42 | [0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0], # B J 43 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # B K 44 | [57,0,0,0,519,0,0,0,35,0,0,0,0,0,47,0,0,0,0,0,32,1,0,0,3,0], # B L 45 | [0,0,0,0,1,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0], # B M 46 | [1,0,0,0,2,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0], # B N 47 | [62,7,4,21,3,2,9,3,8,1,1,46,8,63,58,2,0,55,15,20,46,6,17,10,19,0], # B O 48 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0], # B P 49 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # B Q 50 | [110,0,0,0,77,0,0,0,100,0,0,0,0,0,78,0,0,0,0,0,28,0,0,0,10,0], # B R 51 | [0,0,6,0,16,0,0,0,7,0,0,0,0,0,12,0,0,0,0,27,2,0,0,0,0,0], # B S 52 | [1,0,0,0,3,1,0,0,0,0,0,4,0,0,1,0,0,3,0,0,0,0,0,0,0,0], # B T 53 | [0,3,21,16,3,5,14,0,12,1,2,52,7,20,2,0,1,104,44,54,0,0,0,3,1,5], # B U 54 | [0,0,0,0,3,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # B V 55 | [0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # B W 56 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # B X 57 | [1,0,0,0,3,0,1,2,0,0,0,4,0,0,0,3,0,6,8,3,0,0,2,0,0,2], # B Y 58 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] # B Z 59 | ], 60 | [ 61 | [1,47,17,33,1,3,4,5,7,1,3,120,40,120,1,59,1,171,60,150,19,20,1,0,5,0], # C A 62 | [0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0], # C B 63 | [23,0,0,0,22,0,0,5,13,0,0,13,0,0,26,0,0,7,0,0,27,0,0,0,0,0], # C C 64 | [1,0,1,0,1,0,0,0,0,0,0,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0], # C D 65 | [23,6,4,17,6,6,1,2,13,0,0,50,12,109,7,43,0,76,63,22,1,0,4,0,2,1], # C E 66 | [2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # C F 67 | [0,0,0,0,1,0,0,0,2,0,0,0,0,0,2,0,0,4,1,0,1,0,0,0,0,0], # C G 68 | [165,10,2,3,176,4,3,1,141,0,0,26,20,16,102,1,0,63,8,10,44,0,13,0,20,0], # C H 69 | [76,15,8,33,24,16,3,0,0,0,0,38,5,45,50,28,0,29,38,71,6,8,0,0,0,0], # C I 70 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # C J 71 | [17,16,2,3,90,4,1,7,20,1,1,45,8,8,12,9,0,3,32,6,6,0,13,0,22,0], # C K 72 | [95,0,0,0,84,0,0,0,50,0,0,0,0,0,54,0,0,0,0,0,34,0,0,0,3,0], # C L 73 | [1,0,0,0,2,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0], # C M 74 | [2,0,0,0,1,0,0,0,4,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0], # C N 75 | [33,16,40,22,14,10,11,12,9,1,1,101,218,421,24,56,2,129,37,40,86,22,25,4,4,2], # C O 76 | [1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0], # C P 77 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,13,0,0,0,0,0], # C Q 78 | [101,0,0,0,112,0,0,0,75,0,0,0,0,0,88,0,0,0,0,1,41,0,0,0,25,0], # C R 79 | [0,0,0,0,0,0,0,0,3,0,0,0,0,1,2,0,0,0,1,2,0,0,0,0,0,0], # C S 80 | [44,0,0,0,12,2,0,0,113,0,0,0,2,0,94,0,0,46,0,0,42,0,1,0,3,0], # C T 81 | [3,12,2,6,6,6,0,0,8,0,0,102,42,10,9,15,0,72,51,41,1,0,0,0,0,0], # C U 82 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # C V 83 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # C W 84 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # C X 85 | [5,1,20,0,0,0,1,0,0,0,0,3,0,2,2,4,0,3,2,9,0,0,0,0,0,0], # C Y 86 | [2,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] # C Z 87 | ], 88 | [ 89 | [0,7,16,7,1,2,13,6,18,0,3,54,23,59,0,10,0,31,6,40,8,13,3,0,32,3], # D A 90 | [9,0,0,0,7,0,0,0,3,0,0,2,0,0,8,0,0,1,0,0,8,0,0,0,2,0], # D B 91 | [5,0,0,0,0,0,0,2,0,0,0,2,0,0,3,0,0,0,0,0,2,0,0,0,0,0], # D C 92 | [8,0,0,0,30,0,0,3,19,0,0,38,0,0,4,0,0,4,0,0,1,0,0,0,16,0], # D D 93 | [34,37,82,14,17,41,11,4,5,2,0,88,62,170,14,40,4,183,99,39,6,20,16,6,1,2], # D E 94 | [6,0,0,0,0,0,0,0,6,0,0,2,0,0,5,0,0,2,0,0,4,0,0,0,0,0], # D F 95 | [4,0,0,0,73,0,0,0,2,0,1,1,1,0,0,0,0,1,0,0,2,0,1,0,3,0], # D G 96 | [8,0,0,0,9,0,0,0,4,0,0,0,0,0,10,0,0,0,0,0,0,0,0,0,0,0], # D H 97 | [100,10,104,12,33,26,31,1,1,0,1,22,22,65,57,15,0,20,138,53,20,31,1,6,0,1], # D I 98 | [4,0,0,0,2,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,7,0,0,0,0,0], # D J 99 | [0,0,0,0,1,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # D K 100 | [9,0,0,0,79,0,0,0,12,0,0,0,0,0,7,0,0,0,0,0,1,0,0,0,3,0], # D L 101 | [13,0,0,0,3,0,0,0,21,0,0,0,0,0,11,0,0,0,0,0,1,0,0,0,0,0], # D M 102 | [7,0,0,0,9,0,0,0,3,0,0,0,0,0,1,0,0,0,0,6,0,0,0,0,0,0], # D N 103 | [1,5,21,10,6,3,20,1,3,0,0,30,38,54,17,7,0,39,11,10,30,5,54,5,1,3], # D O 104 | [6,0,0,0,1,0,0,1,3,0,0,1,0,0,7,0,0,1,0,0,0,0,0,0,0,0], # D P 105 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0], # D Q 106 | [74,0,0,0,47,0,0,0,53,0,0,0,0,0,80,0,0,0,0,0,22,0,0,0,8,0], # D R 107 | [1,0,3,0,10,0,0,9,5,0,1,3,10,0,16,8,0,0,0,31,1,0,2,0,0,0], # D S 108 | [3,0,0,0,1,0,0,6,1,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0], # D T 109 | [10,7,52,2,5,3,4,0,2,0,1,33,14,15,5,11,1,19,15,8,1,0,0,0,0,1], # D U 110 | [3,0,0,0,13,0,0,0,7,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0], # D V 111 | [19,0,0,0,10,0,0,0,19,0,0,0,0,0,8,0,0,2,0,0,0,0,0,0,2,0], # D W 112 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # D X 113 | [4,2,1,2,3,1,2,0,1,0,1,4,4,12,0,0,0,0,8,1,0,0,1,0,0,0], # D Y 114 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0] # D Z 115 | ], 116 | [ 117 | [0,39,34,110,0,12,13,3,0,0,50,68,38,71,0,13,1,117,80,112,28,19,7,0,0,1], # E A 118 | [32,5,0,0,31,0,0,0,8,0,0,6,0,0,28,0,0,32,2,3,29,0,0,0,4,0], # E B 119 | [33,0,9,2,51,0,0,39,49,0,47,26,0,0,59,0,0,35,2,206,42,0,0,0,2,0], # E C 120 | [29,7,1,16,45,5,22,3,88,0,0,8,9,4,24,2,0,27,8,4,27,0,7,0,13,0], # E D 121 | [2,4,13,63,1,6,1,4,10,0,19,23,13,66,1,42,0,43,9,34,1,4,6,0,0,8], # E E 122 | [14,0,1,2,36,33,0,0,22,0,0,15,0,0,24,0,0,14,1,13,35,0,0,0,5,0], # E F 123 | [48,1,0,0,36,1,15,2,38,0,0,7,4,4,26,0,0,38,0,0,19,0,0,0,4,0], # E G 124 | [14,0,0,0,24,0,0,0,6,0,0,0,1,0,18,0,0,4,0,0,4,0,0,0,3,0], # E H 125 | [8,0,5,13,2,1,42,0,1,1,2,13,7,59,1,1,0,10,25,22,0,7,0,0,0,2], # E I 126 | [4,0,0,0,4,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,3,0,0,0,0,0], # E J 127 | [2,1,0,1,6,0,0,0,4,0,0,0,0,1,1,0,0,0,2,3,0,0,0,0,1,0], # E K 128 | [76,7,6,57,131,19,7,3,125,0,4,238,22,1,48,15,0,4,27,26,17,19,2,0,7,0], # E L 129 | [87,53,1,0,84,0,0,0,102,0,0,3,8,8,56,64,0,0,4,0,19,0,1,0,8,0], # E M 130 | [78,17,68,159,128,8,35,14,96,2,2,4,5,54,57,3,2,9,127,624,33,10,8,0,11,16], # E N 131 | [0,0,8,10,0,6,7,1,2,0,0,23,10,38,0,16,0,14,6,4,41,3,2,2,0,1], # E O 132 | [26,1,1,0,27,0,0,32,45,0,0,21,1,0,35,9,0,35,10,65,13,0,2,0,3,0], # E P 133 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,59,0,0,0,0,0], # E Q 134 | [217,57,66,22,190,41,70,13,200,3,14,40,134,117,113,42,2,123,167,135,23,58,22,1,123,1], # E R 135 | [17,7,74,6,58,1,3,25,82,0,3,6,17,5,34,52,7,0,222,278,18,2,1,0,6,0], # E S 136 | [78,3,19,0,129,4,0,93,105,0,1,3,2,2,50,1,0,73,5,113,17,0,4,0,32,4], # E T 137 | [0,4,7,6,1,0,4,0,0,0,2,3,17,4,0,15,0,46,20,18,0,2,1,0,0,0], # E U 138 | [29,0,0,0,121,0,0,0,56,0,0,0,0,0,26,0,0,2,1,0,2,2,0,0,3,1], # E V 139 | [33,4,3,4,16,2,0,5,24,0,0,3,3,3,23,2,0,3,15,4,0,0,1,0,2,0], # E W 140 | [29,0,43,0,20,0,0,14,21,0,0,0,0,0,15,78,1,0,0,72,12,0,0,1,2,0], # E X 141 | [7,3,1,4,25,2,0,2,0,0,1,4,6,4,4,1,0,2,3,0,0,1,4,0,0,0], # E Y 142 | [1,0,0,0,9,0,0,0,1,0,0,0,0,0,4,0,0,1,0,0,1,1,0,0,2,3] # E Z 143 | ], 144 | [ 145 | [1,10,39,5,2,1,1,3,18,0,2,35,10,27,0,0,0,36,13,18,10,0,2,3,4,1], # F A 146 | [2,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # F B 147 | [1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # F C 148 | [1,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0], # F D 149 | [18,5,24,6,12,0,2,0,6,0,1,25,6,18,2,0,0,114,17,15,4,2,2,0,1,0], # F E 150 | [10,2,0,0,51,0,0,2,45,0,0,21,4,0,13,0,0,9,7,0,7,0,0,0,8,0], # F F 151 | [1,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # F G 152 | [2,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # F H 153 | [9,9,58,18,42,7,11,0,0,0,0,29,2,53,0,0,0,40,41,18,0,2,0,10,0,3], # F I 154 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0], # F J 155 | [2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # F K 156 | [64,0,0,0,50,0,0,0,21,0,0,0,0,0,60,0,0,0,0,0,42,0,0,0,15,0], # F L 157 | [6,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # F M 158 | [0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # F N 159 | [5,1,8,2,1,0,7,0,6,0,0,34,1,8,32,2,0,165,5,0,25,1,2,7,1,0], # F O 160 | [0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # F P 161 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # F Q 162 | [64,0,0,0,66,0,0,0,35,0,0,0,0,0,35,0,0,0,0,0,11,0,0,0,3,0], # F R 163 | [1,0,0,0,2,0,0,2,0,0,1,0,0,0,1,1,0,0,0,2,0,0,0,0,0,0], # F S 164 | [1,1,1,0,19,0,0,3,1,0,0,0,1,0,3,0,0,1,9,0,0,0,4,0,8,0], # F T 165 | [0,0,4,2,1,0,9,0,0,2,0,119,7,24,0,0,0,28,31,6,0,0,0,0,0,2], # F U 166 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # F V 167 | [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # F W 168 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # F X 169 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # F Y 170 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] # F Z 171 | ], 172 | [ 173 | [0,20,5,11,3,2,11,3,13,0,0,68,24,60,1,5,0,63,23,68,15,8,5,0,2,5], # G A 174 | [4,0,0,0,1,0,0,0,3,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0], # G B 175 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # G C 176 | [2,0,0,0,1,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0], # G D 177 | [23,3,2,4,12,1,1,3,4,0,0,32,8,141,39,4,0,96,29,33,1,1,4,0,5,0], # G E 178 | [0,0,0,0,1,0,0,0,3,0,0,0,0,0,0,0,0,1,0,0,3,0,0,0,0,0], # G F 179 | [8,0,0,0,20,0,0,1,60,0,0,24,0,0,3,1,0,6,4,0,0,0,0,0,12,0], # G G 180 | [18,4,1,1,12,2,1,1,2,0,1,4,0,3,12,1,0,1,3,153,2,0,3,0,1,0], # G H 181 | [23,21,16,6,7,2,9,0,0,0,0,24,7,103,17,1,0,10,26,19,3,10,0,0,0,1], # G I 182 | [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # G J 183 | [0,0,0,0,0,0,0,0,2,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0], # G K 184 | [49,0,0,0,73,0,0,0,25,0,0,0,0,0,38,0,0,0,0,0,13,0,0,0,17,0], # G L 185 | [23,0,0,0,12,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,3,0,0,0,1,0], # G M 186 | [26,1,0,0,28,0,0,0,20,0,0,0,0,0,26,2,0,0,0,1,7,0,0,0,0,0], # G N 187 | [6,4,3,16,6,1,10,1,5,0,0,22,1,49,20,3,0,34,12,23,16,7,5,0,1,0], # G O 188 | [0,0,0,0,1,0,0,0,3,0,0,2,0,0,2,0,0,0,0,0,0,0,0,0,0,0], # G P 189 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # G Q 190 | [216,0,0,0,97,0,0,0,43,0,0,0,0,0,50,0,0,0,0,0,14,0,0,0,3,0], # G R 191 | [2,2,0,0,0,0,0,2,2,0,1,1,0,0,2,1,0,0,0,18,0,0,1,0,0,0], # G S 192 | [2,0,0,0,0,0,0,8,3,0,0,0,0,0,17,0,0,1,0,0,0,0,0,0,0,0], # G T 193 | [28,1,1,0,49,1,1,0,41,0,0,26,15,24,2,0,0,14,22,6,0,0,0,0,3,1], # G U 194 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # G V 195 | [5,0,0,0,2,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,1,0], # G W 196 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # G X 197 | [1,0,0,0,0,0,0,0,0,0,0,0,7,3,0,6,0,5,0,0,0,0,0,0,0,0], # G Y 198 | [2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] # G Z 199 | ], 200 | [ 201 | [2,26,15,20,6,8,22,3,31,0,11,90,66,171,3,25,0,142,30,49,20,11,20,0,13,8], # H A 202 | [4,0,0,0,3,0,0,0,1,0,0,2,0,0,12,0,0,2,0,0,4,0,0,0,1,0], # H B 203 | [1,0,0,0,0,0,0,1,0,0,0,0,0,0,2,0,0,1,0,0,0,0,0,0,0,0], # H C 204 | [2,0,0,0,0,0,0,0,1,0,0,0,0,0,2,0,0,4,0,0,0,0,0,0,0,0], # H D 205 | [123,5,22,33,37,5,3,0,27,0,0,87,65,86,17,7,1,311,57,42,11,11,14,8,11,2], # H E 206 | [2,0,0,0,0,0,0,0,3,0,0,0,0,0,2,0,0,0,0,0,10,0,0,0,0,0], # H F 207 | [1,0,0,0,1,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0], # H G 208 | [1,0,0,0,2,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0], # H H 209 | [22,22,56,15,23,6,19,0,0,1,1,73,20,79,17,41,0,36,53,39,3,11,0,0,0,6], # H I 210 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # H J 211 | [0,0,0,0,1,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0], # H K 212 | [5,0,0,0,11,0,0,0,8,0,0,0,0,0,22,0,0,1,0,0,1,0,0,0,1,0], # H L 213 | [21,0,0,0,15,0,0,0,6,0,0,0,1,0,7,0,0,0,2,0,1,0,0,0,0,0], # H M 214 | [3,0,0,0,8,0,0,0,9,0,0,0,0,1,3,0,0,0,4,0,2,0,0,0,0,0], # H N 215 | [13,18,13,25,17,5,13,0,7,1,4,101,62,62,44,29,0,130,45,33,81,8,28,0,6,2], # H O 216 | [3,0,0,0,0,0,0,0,2,0,0,1,0,0,1,0,0,0,0,0,1,0,0,0,0,0], # H P 217 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0], # H Q 218 | [20,0,0,0,23,0,0,0,40,0,0,1,0,0,72,0,0,0,0,0,13,0,0,0,3,0], # H R 219 | [3,0,1,0,0,0,0,2,1,0,0,0,0,0,3,0,0,0,0,5,0,0,0,0,0,0], # H S 220 | [3,0,2,1,21,9,1,7,5,0,0,1,4,3,4,1,0,2,7,1,1,0,3,0,6,0], # H T 221 | [3,13,7,6,3,5,12,1,0,0,0,7,37,26,0,3,0,37,24,15,0,0,0,2,2,1], # H U 222 | [0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # H V 223 | [17,0,0,0,5,0,0,2,5,0,0,0,0,0,9,0,0,0,0,0,0,0,0,0,0,0], # H W 224 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # H X 225 | [5,1,1,39,1,0,3,0,1,0,0,13,9,0,0,25,0,9,29,9,0,0,0,1,0,0], # H Y 226 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] # H Z 227 | ], 228 | [ 229 | [0,33,20,8,1,0,17,5,1,0,2,169,20,230,0,3,0,30,13,91,0,1,1,2,0,1], # I A 230 | [11,19,0,0,38,0,0,0,22,0,0,131,1,2,10,0,0,20,1,0,23,0,0,0,2,0], # I B 231 | [161,0,3,0,113,0,0,62,113,0,142,15,0,4,46,0,0,12,5,53,42,0,0,0,7,0], # I C 232 | [51,2,0,31,232,0,30,0,46,1,0,5,1,8,10,1,0,1,10,5,11,0,7,0,9,0], # I D 233 | [0,1,17,6,1,16,11,1,0,0,1,52,4,70,0,1,0,66,18,50,7,17,6,0,0,2], # I E 234 | [7,0,0,0,31,45,0,0,27,0,0,9,0,1,10,0,0,2,0,24,10,0,0,0,71,0], # I F 235 | [48,0,0,0,41,0,30,147,30,0,0,4,15,57,20,1,0,23,3,1,15,0,1,0,2,2], # I G 236 | [1,0,0,0,2,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # I H 237 | [1,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # I I 238 | [3,0,0,0,2,0,0,0,1,0,0,0,0,0,2,0,0,0,0,0,1,0,0,0,0,0], # I J 239 | [6,0,0,0,17,0,0,0,3,0,1,0,0,0,3,0,0,0,0,1,2,0,0,0,1,0], # I K 240 | [60,10,6,36,106,6,5,7,90,0,13,253,14,0,24,1,0,1,10,31,6,6,5,0,10,0], # I L 241 | [76,26,0,0,94,1,0,1,53,0,0,1,38,1,30,133,0,1,8,0,17,0,0,0,2,0], # I M 242 | [212,12,143,168,396,83,435,26,94,8,43,9,6,44,70,3,10,2,139,205,35,46,4,4,15,1], # I N 243 | [2,2,20,10,1,0,9,0,0,0,0,28,12,604,0,8,0,25,13,24,139,3,2,3,0,1], # I O 244 | [20,5,0,0,26,2,0,16,16,1,0,33,6,0,13,39,0,5,19,28,5,0,1,0,1,0], # I P 245 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,0,0,0,0,0], # I Q 246 | [41,2,39,24,106,7,9,0,19,0,11,20,24,1,24,8,0,39,11,31,3,5,8,0,10,0], # I R 247 | [35,5,71,4,110,4,2,189,56,1,13,12,93,5,55,33,3,6,85,271,4,1,1,0,8,0], # I S 248 | [136,1,34,1,184,5,0,77,158,0,1,4,6,5,70,1,0,31,2,105,72,0,1,0,142,19], # I T 249 | [0,0,1,0,0,0,0,0,0,0,0,1,121,1,0,0,0,1,19,0,0,0,0,0,0,0], # I U 250 | [57,0,0,0,292,0,0,0,37,0,0,0,0,0,12,0,0,1,0,0,3,0,0,0,2,0], # I V 251 | [3,0,0,0,0,0,0,0,2,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0], # I W 252 | [1,0,0,0,2,1,1,0,3,0,0,0,0,0,4,0,0,0,0,9,1,0,0,0,1,0], # I X 253 | [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # I Y 254 | [9,0,0,0,13,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,1,1,0,0,0,16] # I Z 255 | ], 256 | [ 257 | [0,2,32,1,1,0,3,3,2,0,3,1,8,17,0,2,0,5,2,0,2,3,2,1,1,2], # J A 258 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # J B 259 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # J C 260 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # J D 261 | [4,0,24,1,1,3,0,1,0,2,0,2,0,6,2,0,0,11,9,5,0,0,6,0,0,0], # J E 262 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # J F 263 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # J G 264 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # J H 265 | [0,1,0,0,0,1,4,0,0,0,0,2,4,3,0,0,0,0,0,4,0,1,0,0,0,0], # J I 266 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # J J 267 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # J K 268 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # J L 269 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # J M 270 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # J N 271 | [4,2,6,0,3,0,3,12,10,0,1,6,0,5,0,0,0,10,10,1,13,4,2,0,7,0], # J O 272 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # J P 273 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # J Q 274 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # J R 275 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # J S 276 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # J T 277 | [3,3,0,19,0,0,8,0,2,2,2,8,5,24,0,1,0,15,9,5,0,1,0,2,0,0], # J U 278 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # J V 279 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # J W 280 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # J X 281 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # J Y 282 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] # J Z 283 | ], 284 | [ 285 | [0,3,0,6,1,2,8,2,1,1,1,9,4,13,2,3,0,18,4,17,2,1,2,1,5,2], # K A 286 | [3,0,0,0,3,0,0,0,2,0,0,0,0,0,11,0,0,1,0,0,1,0,0,0,0,0], # K B 287 | [2,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0], # K C 288 | [3,0,0,0,1,0,0,0,0,0,0,0,0,0,2,0,0,1,0,0,0,0,0,0,0,0], # K D 289 | [4,3,0,7,28,3,3,2,1,0,0,20,5,55,3,3,0,59,18,56,2,1,4,0,27,0], # K E 290 | [1,0,0,0,1,0,0,0,1,0,0,0,0,0,3,0,0,0,0,0,3,0,0,0,0,0], # K F 291 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0], # K G 292 | [9,0,0,0,2,0,0,0,0,0,0,0,1,0,8,0,0,1,0,1,0,0,0,0,0,0], # K H 293 | [5,2,3,9,15,1,1,0,0,0,1,10,10,87,2,4,0,11,15,13,0,2,2,0,0,0], # K I 294 | [2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # K J 295 | [1,0,0,0,1,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0], # K K 296 | [15,0,0,0,46,0,0,0,13,0,0,0,0,0,3,0,0,0,0,0,1,0,0,0,2,0], # K L 297 | [13,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # K M 298 | [5,0,0,0,11,0,0,0,10,0,0,0,0,0,24,0,0,0,0,0,8,0,0,0,0,0], # K N 299 | [1,1,2,3,2,4,0,2,1,0,1,3,1,7,1,2,0,6,2,1,7,4,5,2,0,0], # K O 300 | [2,0,0,0,0,0,0,0,4,0,0,4,0,0,5,0,0,0,0,0,0,0,0,0,0,0], # K P 301 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # K Q 302 | [10,0,0,0,3,0,0,0,3,0,0,0,0,0,6,0,0,0,0,0,5,0,0,0,2,0], # K R 303 | [2,2,1,0,1,0,1,9,5,0,1,0,4,0,8,3,0,0,0,11,4,0,1,0,1,0], # K S 304 | [3,0,0,0,0,0,0,2,3,0,0,0,0,0,5,0,0,2,0,0,0,0,0,0,0,0], # K T 305 | [0,0,0,2,0,0,0,1,0,0,0,5,1,1,0,8,0,2,1,1,0,0,1,0,1,0], # K U 306 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # K V 307 | [9,0,0,0,4,0,0,1,2,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0], # K W 308 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # K X 309 | [2,0,0,0,1,0,0,1,0,1,0,4,0,0,2,0,0,2,1,0,1,0,3,0,0,0], # K Y 310 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] # K Z 311 | ], 312 | [ 313 | [1,46,84,43,3,2,46,9,52,0,10,3,64,242,4,23,1,157,92,210,45,21,23,9,42,11], # L A 314 | [12,0,0,0,17,0,0,0,3,0,0,2,0,0,13,0,0,4,0,0,4,0,0,0,2,0], # L B 315 | [9,0,0,0,6,0,0,12,4,0,0,1,1,0,19,0,0,2,0,1,7,0,0,0,2,0], # L C 316 | [2,3,2,0,41,4,0,1,16,0,0,1,2,3,13,1,0,8,9,2,3,0,5,0,3,0], # L D 317 | [94,25,75,44,36,13,55,9,26,1,1,9,55,121,22,22,0,77,84,115,12,29,14,30,75,1], # L E 318 | [9,1,0,0,4,1,1,1,12,0,0,1,0,0,7,0,0,8,1,2,8,0,1,0,0,0], # L F 319 | [16,0,0,0,12,0,0,0,10,0,0,0,0,0,6,0,0,6,0,0,0,0,0,0,0,0], # L G 320 | [7,0,0,0,6,0,0,0,2,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0], # L H 321 | [82,33,140,26,43,37,73,0,0,1,6,11,46,238,50,40,13,5,90,127,12,36,0,3,0,7], # L I 322 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0], # L J 323 | [7,0,0,0,4,0,0,3,9,0,0,2,0,1,2,0,0,0,3,0,0,0,3,0,8,0], # L K 324 | [128,12,2,4,169,7,2,4,152,1,0,0,7,0,100,2,0,1,10,2,41,0,7,0,53,0], # L L 325 | [27,0,0,2,11,0,0,2,9,0,0,0,1,0,13,0,0,0,4,0,3,0,0,0,3,0], # L M 326 | [0,0,0,0,2,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,3,0,0,0,0,0], # L N 327 | [23,23,65,15,7,4,132,3,32,0,2,7,29,69,50,36,11,74,33,53,66,16,80,1,12,1], # L O 328 | [11,0,0,0,3,1,0,21,5,0,0,0,1,0,6,0,0,3,1,4,0,0,0,0,1,0], # L P 329 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # L Q 330 | [2,0,0,0,1,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,2,0,0,0,6,0], # L R 331 | [7,1,0,0,16,0,0,8,23,0,1,0,1,0,20,3,0,0,1,23,0,0,1,0,2,0], # L S 332 | [22,1,0,0,23,0,0,14,34,0,0,0,2,0,23,0,0,9,3,0,8,1,1,0,18,5], # L T 333 | [5,17,26,18,31,5,13,0,5,2,4,8,68,31,15,5,0,21,68,56,0,4,0,13,0,1], # L U 334 | [19,0,0,1,46,0,0,0,9,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0], # L V 335 | [8,0,0,0,2,0,0,1,2,0,0,0,0,0,9,0,0,0,0,0,0,0,0,0,1,0], # L W 336 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # L X 337 | [2,4,12,2,2,2,3,7,2,0,1,3,13,11,2,11,0,2,31,15,1,0,4,0,0,0], # L Y 338 | [2,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] # L Z 339 | ], 340 | [ 341 | [0,10,59,34,3,0,57,7,31,3,25,104,6,326,2,4,0,144,49,192,10,2,3,11,14,7], # M A 342 | [31,1,0,1,44,0,0,0,32,0,0,31,0,1,27,1,0,32,1,0,21,0,0,0,0,0], # M B 343 | [3,1,17,6,2,2,9,3,5,0,9,3,3,4,2,1,0,0,0,0,0,0,0,0,0,0], # M C 344 | [0,0,0,0,2,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0], # M D 345 | [30,6,8,45,3,2,14,1,4,0,1,51,19,283,10,4,0,125,39,128,0,2,9,3,4,1], # M E 346 | [0,0,0,0,3,0,0,0,3,0,0,2,0,0,4,0,0,0,0,0,4,0,0,0,0,0], # M F 347 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # M G 348 | [0,0,0,0,3,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,1,0,0,0,0,0], # M H 349 | [19,0,93,54,8,2,19,0,0,1,2,76,9,194,4,0,1,21,96,109,10,0,0,5,0,1], # M I 350 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # M J 351 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # M K 352 | [1,0,0,0,3,0,0,0,6,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0], # M L 353 | [40,0,0,0,46,0,0,0,33,0,0,0,0,0,32,0,0,0,0,0,17,0,0,0,12,0], # M M 354 | [12,0,0,0,4,0,0,0,10,0,0,0,0,0,3,0,0,0,0,0,1,0,0,0,1,0], # M N 355 | [4,10,13,28,4,1,14,3,11,0,6,47,10,168,16,3,0,107,40,45,56,8,1,1,1,2], # M O 356 | [52,3,0,0,71,1,1,26,18,0,4,71,0,0,50,0,0,41,9,43,19,0,0,0,7,0], # M P 357 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0], # M Q 358 | [2,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,1,0,0,0,0,0,0,0], # M R 359 | [0,1,2,1,5,1,0,2,3,0,1,0,2,0,8,2,0,0,1,10,1,0,0,0,2,0], # M S 360 | [0,0,0,0,1,0,0,1,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0], # M T 361 | [0,0,7,11,6,3,6,0,2,0,2,55,11,29,2,1,0,18,53,30,0,0,0,0,0,3], # M U 362 | [0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # M V 363 | [2,0,0,0,2,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0], # M W 364 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # M X 365 | [0,0,11,0,5,0,1,0,0,0,0,1,0,2,7,0,0,7,7,4,0,0,0,0,0,0], # M Y 366 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] # M Z 367 | ], 368 | [ 369 | [2,24,33,23,6,3,30,6,20,0,9,115,29,59,2,31,0,94,28,159,19,10,5,0,1,5], # N A 370 | [5,0,1,0,20,0,0,0,1,0,0,4,0,0,7,0,0,4,1,0,10,0,0,0,0,0], # N B 371 | [25,0,0,0,190,0,0,87,51,0,1,18,0,0,62,0,0,16,0,36,21,0,0,0,8,0], # N C 372 | [75,11,4,1,162,6,3,7,102,1,1,22,10,2,57,9,2,46,30,4,37,0,11,0,20,0], # N D 373 | [34,12,36,12,29,17,16,4,14,0,0,45,16,20,25,8,6,88,80,84,32,12,37,18,45,3], # N E 374 | [15,0,0,0,30,0,0,0,38,0,0,23,0,0,26,0,0,10,0,0,19,0,0,0,0,0], # N F 375 | [22,8,0,3,114,6,0,15,18,0,3,51,5,0,20,2,0,24,24,28,38,0,2,0,9,0], # N G 376 | [18,0,0,0,16,0,0,0,6,0,0,0,0,0,15,0,0,0,0,0,2,0,0,0,3,0], # N H 377 | [90,9,148,14,33,27,35,4,1,0,5,12,25,44,26,21,7,4,87,94,29,11,0,4,0,4], # N I 378 | [2,0,0,0,3,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,13,0,0,0,0,0], # N J 379 | [6,0,1,0,22,4,1,1,10,0,0,12,2,0,1,1,0,2,2,3,0,0,0,0,9,0], # N K 380 | [9,0,0,0,8,0,0,0,5,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,1,0], # N L 381 | [8,0,0,0,5,0,0,0,2,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0], # N M 382 | [39,0,0,0,74,0,0,0,52,0,1,0,0,0,23,0,0,0,1,0,14,0,1,0,25,0], # N N 383 | [4,18,21,10,4,4,15,0,11,0,0,30,60,34,11,11,0,80,32,47,52,18,24,7,2,2], # N O 384 | [0,0,0,0,1,0,0,0,1,0,0,4,0,0,6,0,0,0,0,0,2,0,0,0,0,0], # N P 385 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,22,0,0,0,0,0], # N Q 386 | [3,0,1,0,1,0,0,0,6,0,0,0,0,0,6,0,0,0,0,0,3,0,0,0,6,0], # N R 387 | [26,4,23,2,73,17,3,12,96,0,5,8,13,0,60,25,0,1,3,79,39,4,4,0,5,0], # N S 388 | [143,1,1,1,175,2,2,64,209,0,0,13,3,1,65,1,0,114,3,0,32,0,2,0,21,1], # N T 389 | [12,6,16,6,11,3,6,0,5,0,1,15,35,9,6,3,0,9,25,31,1,0,0,0,0,1], # N U 390 | [15,0,0,0,43,0,0,0,20,0,0,0,0,0,17,0,0,0,0,0,4,0,0,0,1,0], # N V 391 | [12,0,0,0,3,0,0,2,4,0,0,0,0,0,6,0,0,1,0,0,0,0,0,0,0,0], # N W 392 | [0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0], # N X 393 | [5,3,1,1,0,0,0,1,0,0,0,7,14,0,4,1,1,1,3,1,1,1,2,1,0,0], # N Y 394 | [10,0,0,0,5,0,0,0,5,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,5,0] # N Z 395 | ], 396 | [ 397 | [1,0,20,30,0,2,5,2,0,0,9,9,8,18,0,4,1,51,13,44,1,1,0,2,0,0], # O A 398 | [17,24,2,2,28,2,0,1,32,4,0,19,0,1,16,0,0,5,26,3,8,3,1,0,2,0], # O B 399 | [50,0,28,0,38,0,0,47,26,0,129,14,0,0,33,0,0,25,0,34,20,0,0,0,8,0], # O C 400 | [17,3,3,15,59,3,13,4,47,0,1,13,2,1,22,3,0,8,11,0,21,0,8,0,35,0], # O D 401 | [0,6,1,7,0,3,0,1,6,0,1,10,3,13,1,0,1,10,15,6,2,7,0,3,1,0], # O E 402 | [7,0,0,0,4,63,0,0,10,0,0,4,1,0,6,0,0,1,0,15,4,0,0,0,1,0], # O F 403 | [34,2,0,1,44,1,22,3,15,1,0,11,3,11,7,0,0,80,1,2,18,0,1,0,83,0], # O G 404 | [10,0,0,0,8,0,0,0,6,0,0,1,5,9,5,0,0,2,0,0,0,0,0,0,1,0], # O H 405 | [3,1,12,53,1,1,2,0,0,0,1,27,0,51,0,0,0,11,39,8,0,0,0,1,0,0], # O I 406 | [1,0,0,0,5,0,0,0,1,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0], # O J 407 | [5,2,1,0,48,0,0,1,7,0,1,4,0,0,3,1,0,0,5,0,3,0,1,0,6,0], # O K 408 | [71,4,6,83,111,8,5,3,121,0,14,124,16,1,132,6,0,1,18,24,43,16,2,0,46,1], # O L 409 | [89,50,1,0,174,5,0,1,76,0,0,2,64,7,56,125,1,1,4,0,4,0,2,0,22,0], # O M 410 | [129,3,64,82,181,52,86,3,124,10,11,7,3,46,75,1,6,10,107,149,8,38,9,1,54,5], # O N 411 | [0,2,4,92,0,22,4,1,0,0,68,42,42,44,0,19,0,21,21,68,0,3,0,0,0,2], # O O 412 | [28,1,2,0,71,0,2,82,32,1,3,16,1,1,45,29,0,17,14,21,10,0,2,0,19,0], # O P 413 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,14,0,0,0,0,0], # O Q 414 | [122,26,31,96,138,7,34,2,143,0,61,8,85,76,61,59,1,58,46,211,11,4,9,0,116,1], # O R 415 | [31,4,24,0,107,0,3,18,102,0,2,7,9,1,18,42,2,0,63,127,5,1,2,0,8,0], # O S 416 | [45,7,11,0,64,2,1,88,63,0,0,10,3,1,42,4,0,17,7,63,9,0,3,0,11,0], # O T 417 | [3,11,17,13,3,3,62,1,6,0,0,32,1,137,0,11,1,86,445,103,0,7,0,1,0,2], # O U 418 | [26,0,0,0,109,0,0,0,27,0,1,0,0,0,7,0,0,0,0,0,0,0,0,0,2,0], # O V 419 | [18,14,2,13,48,6,0,8,8,0,1,28,7,83,1,8,0,5,13,2,2,0,1,0,4,1], # O W 420 | [2,1,3,0,5,1,1,3,26,0,0,0,0,1,1,0,0,0,0,1,0,1,1,0,14,0], # O X 421 | [15,1,4,6,3,1,0,0,1,0,0,3,0,1,4,1,0,1,2,1,0,0,0,0,0,0], # O Y 422 | [2,0,0,0,9,0,0,0,0,0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,3,1] # O Z 423 | ], 424 | [ 425 | [0,8,38,11,1,0,18,0,17,0,2,50,5,73,1,23,1,176,50,101,18,5,7,1,10,2], # P A 426 | [3,0,0,0,3,0,0,0,0,0,0,1,0,0,6,0,0,2,1,0,3,0,0,0,0,0], # P B 427 | [0,0,0,0,0,0,0,1,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0], # P C 428 | [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,2,0,0,0,0,0,0,0,0], # P D 429 | [51,1,62,34,19,4,8,0,3,1,2,47,2,108,4,10,0,292,22,50,3,1,8,2,2,4], # P E 430 | [0,0,0,0,1,0,0,0,2,0,0,1,0,0,0,0,0,1,0,0,3,0,0,0,0,0], # P F 431 | [2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0], # P G 432 | [56,0,0,2,88,0,0,0,76,0,0,3,0,1,97,0,0,13,1,3,5,0,0,0,79,0], # P H 433 | [21,0,74,25,33,1,19,0,0,0,6,27,3,74,12,11,2,37,27,57,3,2,0,2,0,2], # P I 434 | [1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # P J 435 | [0,0,0,0,2,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # P K 436 | [150,0,0,0,121,0,0,0,59,0,0,0,0,0,33,0,0,0,0,0,29,0,0,0,11,0], # P L 437 | [6,0,0,0,2,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,1,0,0,0,0,0], # P M 438 | [0,0,0,0,4,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0], # P N 439 | [2,1,19,10,12,2,7,0,31,0,12,111,14,55,23,17,0,97,126,52,20,3,13,3,2,0], # P O 440 | [16,0,0,0,48,0,0,1,20,0,0,32,1,0,25,0,0,32,3,0,1,0,0,0,16,0], # P P 441 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # P Q 442 | [39,0,0,0,166,0,0,0,104,0,0,0,0,0,273,0,0,0,0,0,12,0,0,0,1,0], # P R 443 | [4,1,3,0,17,0,0,5,22,0,1,1,2,0,13,0,0,0,0,14,6,0,1,0,35,0], # P S 444 | [16,0,1,0,9,0,0,3,107,0,0,0,0,0,33,0,0,3,0,0,19,0,0,0,4,0], # P T 445 | [1,8,4,8,3,6,4,0,1,0,1,41,8,22,0,9,0,39,18,28,0,0,0,0,0,1], # P U 446 | [0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # P V 447 | [3,0,0,0,0,0,0,0,2,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0], # P W 448 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # P X 449 | [1,2,0,0,0,0,3,0,1,0,1,3,0,0,1,0,0,20,0,3,0,0,1,0,0,0], # P Y 450 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] # P Z 451 | ], 452 | [ 453 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0], # Q A 454 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # Q B 455 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # Q C 456 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # Q D 457 | [0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # Q E 458 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # Q F 459 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # Q G 460 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # Q H 461 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # Q I 462 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # Q J 463 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # Q K 464 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # Q L 465 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # Q M 466 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # Q N 467 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # Q O 468 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # Q P 469 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # Q Q 470 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # Q R 471 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # Q S 472 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # Q T 473 | [110,0,0,0,100,0,0,0,128,0,0,0,0,0,13,0,0,0,0,0,0,0,0,0,3,0], # Q U 474 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # Q V 475 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # Q W 476 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # Q X 477 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # Q Y 478 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] # Q Z 479 | ], 480 | [ 481 | [0,72,130,95,8,35,73,14,85,3,10,121,95,313,2,119,1,26,66,277,19,45,28,2,28,13], # R A 482 | [32,0,0,0,26,0,0,0,35,0,0,4,0,0,44,0,0,3,1,0,9,0,0,0,5,0], # R B 483 | [18,0,2,0,47,0,0,86,25,0,3,11,0,0,13,0,0,1,2,7,38,0,0,0,4,0], # R C 484 | [22,5,1,0,26,1,0,4,42,0,0,4,0,2,17,1,0,5,9,4,3,0,4,0,7,0], # R D 485 | [166,26,106,99,114,52,55,20,25,4,4,60,69,143,20,72,8,11,257,119,14,56,34,7,23,2],# R E 486 | [11,0,0,0,15,1,0,0,9,0,0,7,0,0,8,0,0,4,0,0,12,0,0,0,0,0], # R F 487 | [26,0,0,0,63,0,0,5,25,0,0,11,1,0,18,0,0,2,2,0,13,0,0,0,11,0], # R G 488 | [11,0,0,0,19,0,0,0,5,0,0,0,0,0,18,0,0,0,0,0,2,0,0,0,3,0], # R H 489 | [182,54,210,87,79,38,65,1,0,1,6,49,65,166,82,61,1,0,151,141,29,44,1,6,1,10], # R I 490 | [0,0,0,0,3,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,2,0,0,0,0,0], # R J 491 | [4,2,0,1,19,0,0,3,9,0,0,6,3,2,5,3,0,1,10,2,0,0,1,0,6,0], # R K 492 | [24,2,0,4,28,0,0,0,36,0,0,0,0,0,14,1,0,0,2,1,2,0,1,0,8,0], # R L 493 | [97,1,2,0,29,2,0,3,65,0,0,2,0,0,39,1,0,0,1,1,10,0,1,0,5,0], # R M 494 | [53,5,0,0,50,4,0,3,29,0,1,0,6,0,16,1,0,0,9,5,7,0,2,0,4,0], # R N 495 | [46,40,79,40,18,22,56,4,32,5,10,76,90,167,84,127,2,14,127,74,127,42,63,17,15,3], # R O 496 | [10,0,0,0,21,0,0,33,10,0,0,5,1,0,25,0,0,12,8,8,5,0,0,0,1,0], # R P 497 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,0,0,0,0,0], # R Q 498 | [53,0,0,0,92,0,0,5,85,0,0,0,0,0,47,0,0,0,0,0,14,0,0,0,60,0], # R R 499 | [26,2,2,2,84,1,0,16,44,0,4,2,3,1,43,12,1,0,0,32,14,1,2,0,2,0], # R S 500 | [39,2,2,0,61,5,3,101,99,0,0,11,7,3,32,0,0,17,12,1,27,0,2,0,24,7], # R T 501 | [5,21,30,31,15,6,12,0,18,0,0,10,46,41,1,28,0,3,83,22,0,1,1,1,0,1], # R U 502 | [31,0,0,0,37,0,0,0,28,0,0,0,0,0,5,0,0,0,0,0,1,0,0,0,2,0], # R V 503 | [15,0,0,0,6,0,0,0,12,0,0,0,0,0,15,0,0,0,0,0,0,0,0,0,0,0], # R W 504 | [0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # R X 505 | [5,3,3,5,3,0,1,0,0,0,0,10,11,4,12,16,0,0,9,4,0,0,2,0,0,0], # R Y 506 | [2,0,0,0,1,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0] # R Z 507 | ], 508 | [ 509 | [2,44,23,16,1,10,21,4,16,1,7,80,17,89,1,10,0,36,10,43,22,10,13,5,7,0], # S A 510 | [9,0,0,0,4,0,0,0,2,0,0,0,0,0,6,0,0,2,0,0,18,0,0,0,3,0], # S B 511 | [81,0,0,0,65,0,1,78,37,0,0,5,1,0,88,0,0,92,0,0,40,0,0,0,3,0], # S C 512 | [11,0,0,0,0,0,0,0,1,0,0,0,0,0,2,0,0,2,0,0,2,0,0,0,0,0], # S D 513 | [38,14,47,18,33,7,8,3,11,0,1,63,39,101,5,28,14,83,28,41,12,19,15,15,19,1], # S E 514 | [3,0,0,0,7,0,0,0,5,0,0,0,0,0,7,0,0,0,0,0,6,0,0,0,1,0], # S F 515 | [0,0,0,0,2,0,0,0,2,0,0,0,0,0,2,0,0,5,1,0,2,0,0,0,0,0], # S G 516 | [97,9,1,0,79,3,0,0,75,0,1,4,16,3,81,2,0,27,0,1,20,1,6,0,17,0], # S H 517 | [55,56,44,80,28,15,38,0,0,0,2,50,40,78,148,7,1,7,99,89,9,76,0,8,0,3], # S I 518 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0], # S J 519 | [9,0,0,0,24,0,0,0,35,0,0,0,2,0,3,0,0,1,0,0,5,0,0,0,23,0], # S K 520 | [42,0,0,0,35,0,0,0,29,0,0,1,0,0,29,0,0,0,0,0,13,0,0,0,2,0], # S L 521 | [57,0,0,0,30,0,0,0,31,0,0,0,0,0,25,0,0,0,0,0,14,0,0,0,2,0], # S M 522 | [21,0,0,0,12,0,0,0,12,0,0,0,0,0,19,0,0,0,0,4,6,0,0,0,2,0], # S N 523 | [6,4,26,12,6,10,4,1,8,1,0,67,65,190,8,21,0,71,0,11,34,6,3,0,3,1], # S O 524 | [63,1,0,0,116,0,0,41,82,0,0,24,0,0,69,0,0,34,1,0,16,0,0,0,3,0], # S P 525 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,52,0,0,0,0,0], # S Q 526 | [4,0,0,0,1,0,0,0,1,0,0,0,0,0,2,0,0,0,1,0,3,0,0,0,0,0], # S R 527 | [50,3,2,0,77,3,0,4,151,0,0,5,11,1,42,2,0,4,0,4,17,0,13,0,19,0], # S S 528 | [258,6,4,1,291,9,1,11,240,1,0,25,12,2,205,6,0,255,3,0,58,2,7,0,36,0], # S T 529 | [14,38,17,6,7,11,6,0,11,0,0,39,35,37,1,42,0,71,30,4,0,0,0,0,0,4], # S U 530 | [0,0,0,0,5,0,0,0,6,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0], # S V 531 | [37,0,0,0,31,0,0,0,28,0,0,0,0,0,21,0,0,2,0,0,2,0,0,0,0,0], # S W 532 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # S X 533 | [0,2,32,1,1,0,1,0,0,0,1,18,19,30,0,2,0,9,5,1,0,0,0,0,0,1], # S Y 534 | [0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0] # S Z 535 | ], 536 | [ 537 | [0,74,44,8,3,9,45,8,68,0,15,130,36,181,1,23,0,128,22,185,13,11,9,13,4,0], # T A 538 | [7,0,0,0,4,0,0,0,4,0,0,0,0,0,6,0,0,3,0,0,3,0,0,0,0,0], # T B 539 | [5,0,0,0,0,0,0,112,0,0,0,2,0,0,5,0,0,1,0,0,1,0,0,0,1,0], # T C 540 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,1,0,0,0,0,0,0,0,0], # T D 541 | [52,9,29,37,66,9,17,6,16,0,2,65,49,185,18,20,0,588,61,23,9,9,9,16,1,0], # T E 542 | [6,0,0,0,1,0,0,0,5,0,0,1,0,0,6,0,0,1,0,0,24,0,0,0,0,0], # T F 543 | [4,0,0,0,2,0,0,0,0,0,0,0,0,0,2,0,0,1,0,0,1,0,0,0,0,0], # T G 544 | [68,6,1,5,274,8,1,2,62,0,1,9,13,3,90,4,1,61,8,2,31,0,16,0,49,0], # T H 545 | [99,35,342,16,35,45,34,0,0,0,3,67,75,183,419,28,9,18,75,88,9,128,0,0,0,2], # T I 546 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # T J 547 | [2,0,0,0,1,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0], # T K 548 | [18,0,0,0,102,0,0,0,5,0,0,2,0,0,3,0,0,0,0,0,2,0,0,0,3,0], # T L 549 | [25,0,0,0,8,0,0,0,3,0,0,0,0,0,11,0,0,0,0,0,3,0,0,0,0,0], # T M 550 | [3,0,0,0,9,0,0,0,5,0,0,0,0,0,2,0,0,0,0,4,1,0,0,0,0,0], # T N 551 | [5,6,34,11,8,7,26,0,14,0,9,38,65,238,26,56,0,319,19,16,36,3,36,7,3,2], # T O 552 | [2,0,0,0,1,0,0,0,1,0,0,2,0,0,3,0,0,5,0,0,0,0,0,0,0,0], # T P 553 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # T Q 554 | [315,0,0,0,98,0,0,0,246,0,0,0,0,0,201,0,0,0,0,0,68,0,1,0,64,0], # T R 555 | [2,2,2,1,10,2,0,3,4,0,1,0,13,0,9,3,0,0,0,8,5,2,5,0,3,0], # T S 556 | [44,0,0,0,154,1,1,2,53,0,1,45,0,0,33,0,0,10,8,0,4,1,0,0,25,0], # T T 557 | [41,14,9,41,8,5,4,0,10,0,0,19,30,29,13,10,0,159,35,22,0,0,0,1,1,0], # T U 558 | [3,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # T V 559 | [14,0,0,0,12,0,0,1,23,0,0,0,0,0,15,0,0,0,0,0,2,0,0,1,0,0], # T W 560 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # T X 561 | [2,1,2,0,0,0,1,0,1,0,0,14,2,0,0,34,0,14,3,0,0,0,2,1,0,0], # T Y 562 | [1,0,0,0,5,0,1,0,2,0,0,1,1,0,1,1,0,1,1,0,0,0,0,0,0,0] # T Z 563 | ], 564 | [ 565 | [0,4,7,21,0,1,5,1,4,0,5,51,2,26,0,1,0,48,9,37,0,2,4,0,3,0], # U A 566 | [8,18,0,1,20,0,0,2,18,2,0,23,5,0,2,1,0,10,15,8,7,2,0,0,1,0], # U B 567 | [10,0,14,0,23,0,0,31,29,0,55,16,0,0,7,0,0,9,1,47,5,0,0,0,2,0], # U C 568 | [17,1,0,24,67,0,18,0,39,0,0,4,0,0,8,0,0,1,10,0,2,0,2,0,7,1], # U D 569 | [6,9,0,1,5,5,4,1,0,1,0,21,1,33,1,1,0,19,22,15,2,0,0,0,3,6], # U E 570 | [1,0,0,0,0,58,0,0,0,0,0,1,1,0,1,0,0,0,0,3,1,0,0,0,0,0], # U F 571 | [19,1,0,0,21,0,34,80,3,0,0,4,2,2,6,0,0,1,1,0,11,0,0,0,0,0], # U G 572 | [3,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0], # U H 573 | [3,2,14,14,6,0,1,0,0,0,0,32,0,31,1,8,0,19,44,64,1,4,0,2,0,3], # U I 574 | [1,0,0,0,0,0,0,0,2,0,0,0,0,0,1,0,0,0,0,0,3,0,0,0,0,0], # U J 575 | [1,0,0,1,12,0,0,0,3,0,1,0,0,0,1,0,0,2,0,0,0,0,0,0,0,0], # U K 576 | [136,4,11,11,46,14,7,0,35,0,10,67,5,2,23,16,0,1,24,73,16,3,1,0,5,1], # U L 577 | [22,52,3,1,51,5,0,1,32,0,0,2,28,11,8,48,1,0,8,1,6,2,0,0,0,0], # U M 578 | [21,6,73,131,25,5,46,2,55,0,33,4,2,13,4,2,0,2,15,82,1,0,2,0,5,0], # U N 579 | [0,0,0,1,0,0,0,0,3,0,0,2,0,3,0,2,0,16,3,5,29,0,0,0,2,0], # U O 580 | [4,4,1,2,31,1,1,14,10,0,1,13,1,0,8,24,0,13,13,24,2,0,2,0,2,0], # U P 581 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0], # U Q 582 | [75,27,21,17,149,8,60,1,66,2,11,17,11,55,28,15,1,51,43,43,9,15,3,0,28,1], # U R 583 | [31,5,29,2,105,0,1,53,64,0,17,3,0,1,8,12,1,0,34,115,6,0,0,0,4,0], # U S 584 | [45,1,14,1,69,0,1,55,77,0,0,8,3,3,49,0,0,13,7,51,11,0,2,0,6,2], # U T 585 | [0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0], # U U 586 | [0,0,0,0,8,0,0,0,5,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0], # U V 587 | [2,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # U W 588 | [0,0,0,0,4,0,0,0,2,0,0,1,0,0,1,0,0,0,0,5,4,0,0,0,0,0], # U X 589 | [1,0,0,0,1,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,1,1,0,0,0,0], # U Y 590 | [2,0,0,0,4,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,1,0,0,0,0,12] # U Z 591 | ], 592 | [ 593 | [0,9,20,8,1,0,14,2,8,1,3,69,2,57,0,1,0,31,18,36,5,0,0,0,0,0], # V A 594 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # V B 595 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # V C 596 | [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # V D 597 | [6,2,5,4,4,3,6,4,5,0,1,47,4,120,3,1,0,271,46,24,0,0,1,5,10,0], # V E 598 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # V F 599 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # V G 600 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # V H 601 | [37,4,33,23,21,2,8,0,2,0,3,43,0,47,18,0,0,16,65,30,5,16,0,2,0,1], # V I 602 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # V J 603 | [0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # V K 604 | [2,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0], # V L 605 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # V M 606 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # V N 607 | [0,0,23,0,0,0,3,0,9,0,5,48,2,6,1,0,0,10,4,9,10,1,3,0,6,0], # V O 608 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # V P 609 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # V Q 610 | [0,0,0,0,5,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0], # V R 611 | [0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # V S 612 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # V T 613 | [0,0,0,0,0,0,0,0,0,0,0,13,0,0,0,0,0,2,2,0,0,0,0,0,0,0], # V U 614 | [0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0], # V V 615 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # V W 616 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # V X 617 | [0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0], # V Y 618 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0] # V Z 619 | ], 620 | [ 621 | [1,4,7,8,0,3,12,3,18,0,8,53,5,20,0,4,0,100,27,55,1,9,1,4,71,1], # W A 622 | [6,0,0,0,7,0,0,0,1,0,0,0,0,0,10,0,0,3,0,0,1,0,0,0,0,0], # W B 623 | [3,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0], # W C 624 | [0,0,0,0,5,0,0,0,1,0,0,0,0,0,4,0,0,3,0,0,1,0,0,0,3,0], # W D 625 | [30,5,1,9,33,0,2,1,19,0,0,51,0,11,0,2,0,36,21,7,0,2,0,0,2,0], # W E 626 | [1,0,0,0,0,0,0,0,3,0,0,3,0,0,4,0,0,0,0,0,3,0,0,0,0,0], # W F 627 | [0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # W G 628 | [18,0,0,0,47,0,0,0,52,0,0,0,0,0,19,0,0,0,0,0,1,0,0,0,1,0], # W H 629 | [0,0,14,18,5,5,15,0,0,0,0,40,2,83,0,2,0,8,38,47,0,4,0,1,0,2], # W I 630 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # W J 631 | [0,0,0,0,0,0,0,0,2,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,1,0], # W K 632 | [3,0,0,0,9,0,0,0,5,0,0,0,0,0,1,0,0,0,1,1,0,0,0,0,3,0], # W L 633 | [8,0,0,0,5,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # W M 634 | [0,1,1,1,6,1,1,2,3,0,0,0,0,0,0,2,0,1,10,4,1,0,2,0,3,0], # W N 635 | [0,1,0,0,3,1,0,0,0,0,3,10,17,8,54,1,0,121,1,1,3,2,1,0,0,0], # W O 636 | [1,0,0,0,1,0,0,0,1,0,0,1,0,0,5,0,0,0,0,0,1,0,0,0,0,0], # W P 637 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # W Q 638 | [7,0,0,0,12,0,0,0,25,0,0,0,0,0,10,0,0,0,0,0,0,0,0,0,6,0], # W R 639 | [0,1,1,0,2,0,0,1,1,0,1,2,2,0,5,3,0,1,1,4,1,0,2,0,1,0], # W S 640 | [1,0,0,0,1,0,0,3,1,0,0,0,0,0,3,0,0,0,0,0,1,0,0,0,0,0], # W T 641 | [0,0,0,0,0,0,0,1,0,0,0,1,1,1,0,1,0,2,0,0,0,0,0,0,0,0], # W U 642 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # W V 643 | [1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0], # W W 644 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # W X 645 | [2,0,0,0,5,0,0,0,0,0,0,1,1,4,1,0,0,0,0,0,0,0,0,0,0,0], # W Y 646 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0] # W Z 647 | ], 648 | [ 649 | [0,0,5,1,0,1,3,0,0,0,0,4,6,6,0,0,0,0,3,6,0,1,0,0,0,0], # X A 650 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0], # X B 651 | [3,0,0,0,11,0,0,3,7,0,0,7,0,0,3,0,0,5,0,0,7,0,0,0,0,0], # X C 652 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # X D 653 | [0,0,7,1,0,0,2,0,1,0,0,2,6,9,0,0,0,6,1,1,0,0,0,0,1,0], # X E 654 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0], # X F 655 | [0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0], # X G 656 | [7,0,0,0,0,0,0,0,4,0,0,0,0,0,4,0,0,0,0,0,2,0,0,0,0,0], # X H 657 | [8,2,12,8,4,2,2,0,0,0,0,2,11,4,8,0,0,0,9,2,0,1,1,0,0,0], # X I 658 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # X J 659 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # X K 660 | [0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # X L 661 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # X M 662 | [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # X N 663 | [0,0,0,1,0,0,3,0,0,0,0,1,1,10,0,1,0,6,1,5,0,0,0,0,0,0], # X O 664 | [8,0,0,0,27,0,0,0,5,0,0,18,0,0,12,0,0,7,0,0,3,0,0,0,0,0], # X P 665 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0], # X Q 666 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # X R 667 | [0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # X S 668 | [6,1,0,0,22,0,0,1,7,0,0,0,0,0,7,0,0,31,0,0,9,0,0,0,1,0], # X T 669 | [4,1,0,2,0,0,0,0,0,0,0,3,0,0,0,1,0,6,0,0,0,0,0,0,0,0], # X U 670 | [0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # X V 671 | [0,0,0,0,2,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0], # X W 672 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0], # X X 673 | [0,0,0,0,0,0,2,0,0,0,0,6,0,0,0,0,0,2,0,0,0,0,0,0,0,0], # X Y 674 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] # X Z 675 | ], 676 | [ 677 | [0,0,5,5,0,0,1,1,0,0,2,11,3,29,1,4,1,20,1,3,0,0,3,0,0,0], # Y A 678 | [4,0,0,4,7,0,0,0,2,0,0,0,0,0,9,0,0,3,0,0,3,0,0,0,0,0], # Y B 679 | [4,0,0,0,18,0,0,31,4,0,0,19,0,0,12,0,0,0,0,0,0,0,0,0,0,0], # Y C 680 | [4,1,0,0,12,0,0,0,2,0,0,0,0,2,1,0,0,37,0,0,0,0,0,0,0,0], # Y D 681 | [11,3,0,1,1,1,1,0,1,0,0,13,1,6,2,1,0,19,7,6,0,1,1,0,0,0], # Y E 682 | [1,0,0,0,1,0,0,0,3,0,0,2,0,0,0,0,0,0,0,0,4,0,0,0,0,0], # Y F 683 | [0,0,0,1,2,0,0,0,2,0,0,1,3,1,8,0,0,3,0,0,1,0,0,0,2,0], # Y G 684 | [0,0,0,0,4,0,0,0,0,0,0,0,0,0,10,0,0,0,0,0,0,0,0,0,1,0], # Y H 685 | [0,0,0,1,1,0,0,0,0,0,0,0,0,9,0,2,0,0,2,0,0,0,0,0,0,0], # Y I 686 | [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # Y J 687 | [0,0,0,0,3,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0], # Y K 688 | [15,0,0,0,22,0,0,0,13,0,1,19,0,0,11,1,0,0,2,0,3,6,0,0,0,0], # Y L 689 | [18,4,1,0,20,0,0,0,5,0,0,0,3,7,11,20,0,0,0,0,2,0,0,0,1,0], # Y M 690 | [14,0,11,3,12,0,3,1,2,0,0,0,0,3,11,0,0,0,0,6,0,0,0,2,1,0], # Y N 691 | [0,0,2,2,0,4,6,0,0,0,5,2,1,18,0,4,0,8,4,5,17,1,1,0,0,1], # Y O 692 | [2,0,0,0,24,0,0,17,5,0,0,2,0,2,21,0,0,5,7,16,3,0,0,0,1,0], # Y P 693 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0], # Y Q 694 | [15,0,0,2,6,1,0,0,21,0,0,0,0,2,29,0,0,2,0,1,4,0,0,0,1,0], # Y R 695 | [3,1,3,0,12,0,0,1,38,0,0,1,2,0,4,3,0,0,6,39,2,0,0,0,0,0], # Y S 696 | [2,0,0,0,16,0,0,16,10,0,0,0,0,0,12,0,0,0,0,2,0,0,0,0,1,0], # Y T 697 | [0,0,3,0,0,0,3,1,0,0,2,1,0,1,0,1,0,0,2,0,0,0,0,0,0,0], # Y U 698 | [1,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # Y V 699 | [10,0,1,0,3,0,0,2,4,0,0,0,0,0,5,0,0,3,0,0,0,0,0,0,0,0], # Y W 700 | [0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # Y X 701 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # Y Y 702 | [2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0] # Y Z 703 | ], 704 | [ 705 | [1,3,2,0,0,0,5,1,1,0,1,4,1,11,0,1,0,19,0,0,0,1,0,0,0,1], # Z A 706 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # Z B 707 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # Z C 708 | [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # Z D 709 | [5,1,2,1,1,0,0,0,1,0,1,7,0,12,0,0,0,13,3,3,1,0,1,0,0,0], # Z E 710 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # Z F 711 | [0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # Z G 712 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # Z H 713 | [1,1,2,0,7,0,5,0,0,0,0,5,4,6,1,1,0,2,1,1,1,0,0,0,0,0], # Z I 714 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # Z J 715 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # Z K 716 | [0,0,0,0,16,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,2,0], # Z L 717 | [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # Z M 718 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # Z N 719 | [3,0,0,2,2,0,1,0,7,0,0,0,3,10,5,2,0,5,0,0,1,1,0,0,0,0], # Z O 720 | [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # Z P 721 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # Z Q 722 | [1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0], # Z R 723 | [0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # Z S 724 | [0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # Z T 725 | [0,0,1,0,1,0,0,0,0,0,1,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0], # Z U 726 | [0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0], # Z V 727 | [0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0], # Z W 728 | [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], # Z X 729 | [0,1,0,0,0,0,4,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0], # Z Y 730 | [7,0,0,0,1,0,0,0,7,0,0,17,0,0,2,0,0,0,0,0,0,0,1,0,5,0] # Z Z 731 | ] 732 | ] 733 | 734 | N_LETTERS = 26 735 | sigma = 0 736 | N_LETTERS.times do |i| 737 | N_LETTERS.times do |j| 738 | N_LETTERS.times do |k| 739 | sigma += P[i][j][k] 740 | end 741 | end 742 | end 743 | SIGMA = sigma 744 | 745 | # Use constants for performance 746 | LOWERCASE_TOKENS = %w{a b c d e f g h i j k l m n o p q r s t u v w x y z} 747 | UPPERCASE_TOKENS = %w{A B C D E F G H I J K L M N O P Q R S T U V W X Y Z} 748 | DIGIT_TOKENS = %w{0 1 2 3 4 5 6 7 8 9} 749 | 750 | LETTER_INDEXES = { 751 | a: 0, 752 | b: 1, 753 | c: 2, 754 | d: 3, 755 | e: 4, 756 | f: 5, 757 | g: 6, 758 | h: 7, 759 | i: 8, 760 | j: 9, 761 | k: 10, 762 | l: 11, 763 | m: 12, 764 | n: 13, 765 | o: 14, 766 | p: 15, 767 | q: 16, 768 | r: 17, 769 | s: 18, 770 | t: 19, 771 | u: 20, 772 | v: 21, 773 | w: 22, 774 | x: 23, 775 | y: 24, 776 | z: 25, 777 | A: 0, 778 | B: 1, 779 | C: 2, 780 | D: 3, 781 | E: 4, 782 | F: 5, 783 | G: 6, 784 | H: 7, 785 | I: 8, 786 | J: 9, 787 | K: 10, 788 | L: 11, 789 | M: 12, 790 | N: 13, 791 | O: 14, 792 | P: 15, 793 | Q: 16, 794 | R: 17, 795 | S: 18, 796 | T: 19, 797 | U: 20, 798 | V: 21, 799 | W: 22, 800 | X: 23, 801 | Y: 24, 802 | Z: 25 803 | } 804 | end -------------------------------------------------------------------------------- /lib/passgen/strength_analyzer.rb: -------------------------------------------------------------------------------- 1 | module Passgen 2 | class StrengthAnalyzer 3 | MIN_LENGTH = 8 4 | attr_reader :password, :score, :complexity, :errors 5 | 6 | def initialize(pw) 7 | @password = pw 8 | @score = 0 9 | @complexity = "Invalid" 10 | @errors = [] 11 | end 12 | 13 | def self.analyze(pw) 14 | sa = StrengthAnalyzer.new(pw) 15 | sa.analyze 16 | sa 17 | end 18 | 19 | def analyze 20 | return self unless check_minimum_requirements 21 | 22 | nScore = 0 23 | nLength = 0 24 | nAlphaUC = 0 25 | nAlphaLC = 0 26 | nNumber = 0 27 | nSymbol = 0 28 | nMidChar = 0 29 | nRequirements = 0 30 | nAlphasOnly = 0 31 | nNumbersOnly = 0 32 | nUnqChar = 0 33 | nRepChar = 0 34 | nRepInc = 0 35 | nConsecAlphaUC = 0 36 | nConsecAlphaLC = 0 37 | nConsecNumber = 0 38 | nConsecSymbol = 0 39 | nConsecCharType = 0 40 | nSeqAlpha = 0 41 | nSeqNumber = 0 42 | nSeqSymbol = 0 43 | nSeqChar = 0 44 | nReqChar = 0 45 | nMultConsecCharType = 0 46 | nMultRepChar = 1 47 | nMultConsecSymbol = 1 48 | nMultMidChar = 2 49 | nMultRequirements = 2 50 | nMultConsecAlphaUC = 2 51 | nMultConsecAlphaLC = 2 52 | nMultConsecNumber = 2 53 | nReqCharType = 3 54 | nMultAlphaUC = 3 55 | nMultAlphaLC = 3 56 | nMultSeqAlpha = 3 57 | nMultSeqNumber = 3 58 | nMultSeqSymbol = 3 59 | nMultLength = 4 60 | nMultNumber = 4 61 | nMultSymbol = 6 62 | nTmpAlphaUC = "" 63 | nTmpAlphaLC = "" 64 | nTmpNumber = "" 65 | nTmpSymbol = "" 66 | sAlphas = 'abcdefghijklmnopqrstuvwxyz' 67 | sNumerics = '01234567890' 68 | sSymbols = '!@#$%&/()+?*' 69 | sComplexity = 'Invalid' 70 | sStandards = 'Below' 71 | nMinPwdLen = MIN_LENGTH 72 | 73 | nScore = @password.length * nMultLength 74 | nLength = @password.length 75 | arrPwd = @password.gsub(/\s+/, "").split(/\s*/) 76 | arrPwdLen = arrPwd.length 77 | 78 | # Loop through password to check for Symbol, Numeric, Lowercase and Uppercase pattern matches 79 | arrPwdLen.times do |a| 80 | if /[A-Z]/.match(arrPwd[a]) 81 | if nTmpAlphaUC != "" 82 | if (nTmpAlphaUC + 1) == a 83 | nConsecAlphaUC += 1 84 | nConsecCharType += 1 85 | end 86 | end 87 | nTmpAlphaUC = a 88 | nAlphaUC += 1 89 | elsif /[a-z]/.match(arrPwd[a]) 90 | if nTmpAlphaLC != "" 91 | if (nTmpAlphaLC + 1) == a 92 | nConsecAlphaLC += 1 93 | nConsecCharType += 1 94 | end 95 | end 96 | nTmpAlphaLC = a 97 | nAlphaLC += 1 98 | elsif /[0-9]/.match(arrPwd[a]) 99 | if a > 0 && a < (arrPwdLen - 1) 100 | nMidChar += 1 101 | end 102 | if nTmpNumber != "" 103 | if (nTmpNumber + 1) == a 104 | nConsecNumber += 1 105 | nConsecCharType += 1 106 | end 107 | end 108 | nTmpNumber = a 109 | nNumber += 1 110 | elsif /[^a-zA-Z0-9_]/.match(arrPwd[a]) 111 | if a > 0 && a < (arrPwdLen - 1) 112 | nMidChar += 1 113 | end 114 | if nTmpSymbol != "" 115 | if (nTmpSymbol + 1) == a 116 | nConsecSymbol += 1 117 | nConsecCharType += 1 118 | end 119 | end 120 | nTmpSymbol = a 121 | nSymbol += 1 122 | end 123 | 124 | # Internal loop through password to check for repeat characters 125 | bCharExists = false 126 | arrPwdLen.times do |b| 127 | if arrPwd[a] == arrPwd[b] && a != b # repeat character exists 128 | bCharExists = true 129 | # Calculate increment deduction based on proximity to identical characters 130 | # Deduction is incremented each time a new match is discovered 131 | # Deduction amount is based on total password length divided by the 132 | # difference of distance between currently selected match 133 | nRepInc += (arrPwdLen / (b - a)).abs 134 | end 135 | end 136 | if bCharExists 137 | nRepChar += 1 138 | nUnqChar = arrPwdLen - nRepChar 139 | nRepInc = (nUnqChar > 0) ? (nRepInc / nUnqChar).ceil : nRepInc.ceil 140 | end 141 | end 142 | 143 | # Check for sequential alpha string patterns (forward and reverse) 144 | (sAlphas.size - 3).times do |s| 145 | sFwd = sAlphas[s...s + 3] 146 | sRev = sFwd.reverse 147 | if @password.downcase.index(sFwd) || @password.downcase.index(sRev) 148 | nSeqAlpha += 1 149 | nSeqChar += 1 150 | end 151 | end 152 | 153 | # Check for sequential numeric string patterns (forward and reverse) 154 | (sNumerics.size - 3).times do |s| 155 | sFwd = sNumerics[s...s + 3] 156 | sRev = sFwd.reverse 157 | if @password.downcase.index(sFwd) || @password.downcase.index(sRev) 158 | nSeqNumber += 1 159 | nSeqChar += 1 160 | end 161 | end 162 | 163 | # Check for sequential symbol string patterns (forward and reverse) 164 | (sSymbols.size - 3).times do |s| 165 | sFwd = sSymbols[s...s + 3] 166 | sRev = sFwd.reverse 167 | if @password.downcase.index(sFwd) || @password.downcase.index(sRev) 168 | nSeqSymbol += 1 169 | nSeqChar += 1 170 | end 171 | end 172 | 173 | # Modify overall score value based on usage vs requirements 174 | if nAlphaUC > 0 && nAlphaUC < nLength 175 | nScore += (nLength - nAlphaUC) * 2 176 | end 177 | 178 | if nAlphaLC > 0 && nAlphaLC < nLength 179 | nScore += (nLength - nAlphaLC) * 2 180 | end 181 | 182 | if nNumber > 0 && nNumber < nLength 183 | nScore += nNumber * nMultNumber 184 | end 185 | 186 | if nSymbol > 0 187 | nScore += nSymbol * nMultSymbol 188 | end 189 | 190 | if nMidChar > 0 191 | nScore += nMidChar * nMultMidChar 192 | end 193 | 194 | # Point deductions for poor practices 195 | if (nAlphaLC > 0 || nAlphaUC > 0) && nSymbol == 0 && nNumber == 0 # Only Letters 196 | nScore -= nLength 197 | nAlphasOnly = nLength 198 | end 199 | 200 | if nAlphaLC === 0 && nAlphaUC === 0 && nSymbol === 0 && nNumber > 0 # Only Numbers 201 | nScore -= nLength 202 | nNumbersOnly = nLength 203 | end 204 | 205 | if nRepChar > 0 # Same character exists more than once 206 | nScore -= nRepInc 207 | end 208 | 209 | if nConsecAlphaUC > 0 # Consecutive Uppercase Letters exist 210 | nScore -= nConsecAlphaUC * nMultConsecAlphaUC 211 | end 212 | 213 | if nConsecAlphaLC > 0 # Consecutive Lowercase Letters exist 214 | nScore -= nConsecAlphaLC * nMultConsecAlphaLC 215 | end 216 | 217 | if nConsecNumber > 0 # Consecutive Numbers exist 218 | nScore -= nConsecNumber * nMultConsecNumber 219 | end 220 | 221 | if nSeqAlpha > 0 # Sequential alpha strings exist (3 characters or more) 222 | nScore -= nSeqAlpha * nMultSeqAlpha 223 | end 224 | 225 | if nSeqNumber > 0 # Sequential numeric strings exist (3 character or more) 226 | nScore -= nSeqNumber * nMultSeqNumber 227 | end 228 | 229 | if nSeqSymbol > 0 # Sequential symbol strings exist (3 character or more) 230 | nScore -= nSeqSymbol * nMultSeqSymbol 231 | end 232 | 233 | # Determine if mandatory requirements have been met and set image indicators accordingly 234 | arrChars = [nLength, nAlphaUC, nAlphaLC, nNumber, nSymbol] 235 | arrCharsIds = ["nLength", "nAlphaUC", "nAlphaLC", "nNumber", "nSymbol"] 236 | arrCharsLen = arrChars.length 237 | arrCharsLen.times do |c| 238 | minVal = arrCharsIds[c] == "nLength" ? MIN_LENGTH - 1 : 0 239 | if arrChars[c] == (minVal + 1) 240 | nReqChar += 1 241 | elsif arrChars[c] > (minVal + 1) 242 | nReqChar += 1 243 | end 244 | end 245 | nRequirements = nReqChar 246 | nMinReqChars = @password.length >= nMinPwdLen ? 3 : 4 247 | if nRequirements > nMinReqChars # One or more required characters exist 248 | nScore += (nRequirements * 2) 249 | end 250 | 251 | # Determine complexity based on overall score 252 | if nScore > 100 253 | nScore = 100 254 | elsif nScore < 0 255 | nScore = 0 256 | end 257 | @complexity = case nScore 258 | when 0...20 259 | "Trivial" 260 | when 20...40 261 | "Weak" 262 | when 40...60 263 | "Good" 264 | when 60...80 265 | "Strong" 266 | else 267 | "Very Strong" 268 | end 269 | 270 | @score = nScore 271 | end 272 | 273 | private 274 | 275 | def check_minimum_requirements 276 | if @password.length < MIN_LENGTH 277 | @errors << "Password must be at least #{MIN_LENGTH} characters long" 278 | return false 279 | end 280 | true 281 | end 282 | end 283 | end -------------------------------------------------------------------------------- /passgen.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | 3 | Gem::Specification.new do |s| 4 | s.name = %q{passgen} 5 | s.version = "1.1.2" 6 | 7 | s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version= 8 | s.authors = ["Erik Lindblad (CrypticE)", "Ronald Brachetti(rbecher)", "Ken Spencer (IotaSpencer)"] 9 | s.date = %q{2020-03-31} 10 | s.description = %q{A password generation gem for Ruby and Rails applications.} 11 | s.email = %q{erik@l2c.se} 12 | s.extra_rdoc_files = ["CHANGELOG", "README.rdoc", "lib/passgen.rb", "lib/passgen/probabilities.rb", "lib/passgen/strength_analyzer.rb"] 13 | s.files = ["CHANGELOG", "Manifest", "README.rdoc", "Rakefile", "rails/init.rb", "lib/passgen.rb", "lib/passgen/probabilities.rb", "lib/passgen/strength_analyzer.rb", "passgen.gemspec", "spec/passgen/strength_analyzer_spec.rb", "spec/passgen_spec.rb"] 14 | s.homepage = %q{http://github.com/cryptice/passgen} 15 | s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Passgen", "--main", "README.rdoc"] 16 | s.require_paths = ["lib"] 17 | s.rubygems_version = %q{1.3.7} 18 | s.summary = %q{A password generation gem for Ruby and Rails applications.} 19 | 20 | if s.respond_to? :specification_version then 21 | current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION 22 | s.specification_version = 3 23 | 24 | if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then 25 | else 26 | end 27 | else 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /rails/init.rb: -------------------------------------------------------------------------------- 1 | require 'passgen' 2 | 3 | -------------------------------------------------------------------------------- /spec/passgen/strength_analyzer_spec.rb: -------------------------------------------------------------------------------- 1 | require "./lib/passgen" 2 | 3 | describe "Using strength analyzer" do 4 | 5 | before do 6 | srand(2) 7 | end 8 | 9 | it "should return a StrengthAnalyzer instance" do 10 | Passgen.analyze("abcdefg").should be_a(Passgen::StrengthAnalyzer) 11 | end 12 | 13 | it "should require minimum of 8 characters" do 14 | sa = Passgen.analyze("abcdefg") 15 | sa.score.should == 0 16 | sa.complexity.should == "Invalid" 17 | sa.errors.should == ["Password must be at least 8 characters long"] 18 | end 19 | 20 | it "should analyze aaaaaaaa correctly" do 21 | sa = Passgen.analyze("aaaaaaaa") 22 | sa.score.should == 0 23 | sa.complexity.should == "Trivial" 24 | sa.errors.should == [] 25 | end 26 | 27 | it "should analyze aaaaAAAA correctly" do 28 | sa = Passgen.analyze("aaaaAAAA") 29 | sa.score.should == 0 30 | sa.complexity.should == "Trivial" 31 | sa.errors.should == [] 32 | end 33 | 34 | it "should analyze aaaAAA11 correctly" do 35 | sa = Passgen.analyze("aaaAAA11") 36 | sa.score.should == 35 37 | sa.complexity.should == "Weak" 38 | sa.errors.should == [] 39 | end 40 | 41 | it "should analyze aaa1AAA1 correctly" do 42 | sa = Passgen.analyze("aaa1AAA1") 43 | sa.score.should == 38 44 | sa.complexity.should == "Weak" 45 | sa.errors.should == [] 46 | end 47 | 48 | it "should analyze hht14AAA correctly" do 49 | sa = Passgen.analyze("hht14AAA") 50 | sa.score.should == 57 51 | sa.complexity.should == "Good" 52 | sa.errors.should == [] 53 | end 54 | 55 | it "should analyze hie14KOL correctly" do 56 | sa = Passgen.analyze("hie14KOL") 57 | sa.score.should == 62 58 | sa.complexity.should == "Strong" 59 | sa.errors.should == [] 60 | end 61 | 62 | it "should analyze hI&14KoL correctly" do 63 | sa = Passgen.analyze("hI&14KoL") 64 | sa.score.should == 82 65 | sa.complexity.should == "Very Strong" 66 | sa.errors.should == [] 67 | end 68 | 69 | it "should analyze hI&1#4KoL correctly" do 70 | sa = Passgen.analyze("hI&1#4KoL") 71 | sa.score.should == 100 72 | sa.complexity.should == "Very Strong" 73 | sa.errors.should == [] 74 | end 75 | end 76 | -------------------------------------------------------------------------------- /spec/passgen_spec.rb: -------------------------------------------------------------------------------- 1 | require "./lib/passgen" 2 | 3 | describe "Using passgen" do 4 | 5 | before do 6 | srand(2) 7 | end 8 | 9 | it "should return password with default settings." do 10 | Passgen::generate.should eql("OpTiwRslOh") 11 | end 12 | 13 | it "should return password with uppercase chars only" do 14 | Passgen::generate(:uppercase => :only).should eql("IPNIWLSLIH") 15 | end 16 | 17 | it "should return password with lowercase chars only" do 18 | Passgen::generate(:lowercase => :only).should eql("ipniwlslih") 19 | end 20 | 21 | it "should return password with digits only" do 22 | Passgen::generate(:digits => :only).should eql("8862872154") 23 | end 24 | 25 | it "should return password with symbols only" do 26 | Passgen::generate(:symbols => :only).should eql("))/*#*)(\#@") 27 | end 28 | 29 | it "should return password with lowercase and uppercase chars only" do 30 | Passgen::generate(:digits => false).should eql("OpTiwRslOh") 31 | end 32 | 33 | it "should return password with lowercase and digit chars only" do 34 | Passgen::generate(:uppercase => false).should eql("piwslh85lv") 35 | end 36 | 37 | it "should return password with lowercase and symbol chars only" do 38 | Passgen::generate(:uppercase => false, :digits => false, :symbols => true).should eql("piwslh)&lv") 39 | end 40 | 41 | it "should return password with uppercase and digit chars only" do 42 | Passgen::generate(:lowercase => false).should eql("PIWSLH85LV") 43 | end 44 | 45 | it "should return password with uppercase and symbol chars only" do 46 | Passgen::generate(:lowercase => false, :digits => false, :symbols => true).should eql("PIWSLH)&LV") 47 | end 48 | 49 | it "should return password with digit and symbol chars only" do 50 | Passgen::generate(:lowercase => false, :uppercase => false, :symbols => true).should eql("8&$8@)@872") 51 | end 52 | 53 | it "should return password with lowercase, uppercase and digit chars only" do 54 | Passgen::generate.should eql("OpTiwRslOh") 55 | end 56 | 57 | it "should return password with lowercase, uppercase and symbol chars only" do 58 | srand(3) 59 | Passgen::generate(:digits => false, :symbols => true).should eql("Qy&d%iav+t") 60 | end 61 | 62 | it "should return password with lowercase, digit and symbol chars only" do 63 | srand(4) 64 | Passgen::generate(:uppercase => false, :symbols => true).should eql("?fb%xij$+4") 65 | end 66 | 67 | it "should return password with uppercase, digit and symbol chars only" do 68 | srand(4) 69 | Passgen::generate(:lowercase => false, :symbols => true).should eql("?FB%XIJ$+4") 70 | end 71 | 72 | it "should return given number of passwords in an Array" do 73 | Passgen::generate(:number => 3).should eql(["OpTiwRslOh", "IXFlvVFAu8", "0LNdMeQRZN"]) 74 | end 75 | 76 | it "should return a password with given length" do 77 | Passgen::generate(:length => 8).should eql("OpTiwRsl") 78 | end 79 | 80 | it "should return several passwords of variable length" do 81 | Passgen::generate(:length => 3..12, :number => 2).should eql(["pTiwRslOhIX", "VFAu80LN"]) 82 | end 83 | 84 | it "should use given seed" do 85 | pass1 = Passgen::generate(:seed => 5) 86 | pass2 = Passgen::generate(:seed => 5) 87 | pass1.should eql(pass2) 88 | end 89 | 90 | it "should set seed to Time.now + Process.id" do 91 | pass1 = Passgen::generate(:seed => :default) 92 | pass2 = Passgen::generate(:seed => :default) 93 | pass1.should_not eql(pass2) 94 | end 95 | 96 | describe "handling tokens" do 97 | 98 | it "should return a-z" do 99 | Passgen::LOWERCASE_TOKENS.should eql(("a".."z").to_a) 100 | end 101 | 102 | it "should return A-Z" do 103 | Passgen::UPPERCASE_TOKENS.should eql(("A".."Z").to_a) 104 | end 105 | 106 | it "should return 0-9" do 107 | Passgen::DIGIT_TOKENS.should eql(("0".."9").to_a) 108 | end 109 | 110 | it "should return default symbols" do 111 | Passgen::symbol_tokens.should eql(%w{! @ # $ % & / ( ) + ? *}) 112 | end 113 | 114 | end 115 | 116 | describe "pronounceable" do 117 | it "should return a pronounceable lower case password" do 118 | Passgen::generate(:pronounceable => true, :lowercase => :only).should == "ishanghter" 119 | end 120 | 121 | it "should return a pronounceable upper case password" do 122 | Passgen::generate(:pronounceable => true, :uppercase => :only).should == "ISHANGHTER" 123 | end 124 | 125 | it "should return a pronounceable mixed case password" do 126 | Passgen::generate(:pronounceable => true).should == "iShfIeRBAt" 127 | end 128 | 129 | it "should return a pronounceable mixed case password of length 7" do 130 | Passgen::generate(:pronounceable => true, :length => 7).should == "IShfIeR" 131 | end 132 | 133 | it "should return a pronounceable password with 3 digits in front" do 134 | Passgen::generate(:pronounceable => true, :digits_before => 3).should == "886uRApLIN" 135 | end 136 | 137 | it "should return a pronounceable password with default 2 digits in front" do 138 | Passgen::generate(:pronounceable => true, :digits_before => true).should == "88mpICePED" 139 | end 140 | 141 | it "should return a pronounceable password with 3 digits at the end" do 142 | Passgen::generate(:pronounceable => true, :digits_after => 3).should == "uRAPLIN886" 143 | end 144 | 145 | it "should return a pronounceable password with default 2 digits at the end" do 146 | Passgen::generate(:pronounceable => true, :digits_after => true).should == "mPICEPED88" 147 | end 148 | 149 | end 150 | end 151 | --------------------------------------------------------------------------------