1 2{ 3 4package Crypt::RandPasswd; 5 6use strict; 7 8use vars qw($VERSION); 9 10$VERSION = '0.04'; 11 12 13=head1 NAME 14 15Crypt::RandPasswd - random password generator based on FIPS-181 16 17=head1 SYNOPSIS 18 19 use Crypt::RandPasswd; 20 ( $word, $hyphenated ) = Crypt::RandPasswd->word( $minlen, $maxlen ); 21 $word = Crypt::RandPasswd->word( $minlen, $maxlen ); 22 $word = Crypt::RandPasswd->letters( $minlen, $maxlen ); 23 $word = Crypt::RandPasswd->chars( $minlen, $maxlen ); 24 25 # override the defaults for these functions: 26 *Crypt::RandPasswd::rng = \&my_random_number_generator; 27 *Crypt::RandPasswd::restrict = \&my_restriction_filter; 28 29=head2 Run as Script 30 31 perl Crypt/RandPasswd.pm -help 32 33=head1 SEE ALSO 34 35FIPS 181 - (APG), Automated Password Generator: 36http://www.itl.nist.gov/fipspubs/fip181.htm 37 38=head1 DESCRIPTION 39 40This code is a Perl language implementation of the Automated 41Password Generator standard, like the program described in 42"A Random Word Generator For Pronounceable Passwords" (not available on-line). 43This code is a re-engineering of the program contained in Appendix A 44of FIPS Publication 181, "Standard for Automated Password Generator". 45In accordance with the standard, the results obtained from this 46program are logically equivalent to those produced by the standard. 47 48=head1 CAVEATS 49 50=head2 Bugs 51 52The function to generate a password can sometimes take an extremely long time. 53 54=head2 Deviations From Standard 55 56This implementation deviates in one critical way from the standard 57upon which it is based: the random number generator in this 58implementation does not use DES. Instead, it uses perl's built-in 59C<rand()> function, which in turn is (usually) built on the 60pseudo-random number generator functions of the underlying C library. 61 62However, the random function can be replaced by the user if desired. 63(See L</rng>.) 64 65=head1 Functions 66 67=cut 68 69 70sub word($$); 71sub letters($$); 72sub chars($$); 73 74sub random_chars_in_range($$$$); 75sub rand_int_in_range($$); 76sub random_element($); 77 78sub rng($); 79sub restrict($); 80sub init(); 81 82 83sub _random_word($); 84sub _random_unit($); 85sub _improper_word(@); 86sub _have_initial_y(@); 87sub _have_final_split(@); 88sub _illegal_placement(@); 89 90 91# 92# Global Variables: 93# 94 95$Crypt::RandPasswd::seed = undef; # by default; causes srand() to use its own, which can be pretty good. 96$Crypt::RandPasswd::initialized = 0; 97 98 99 100my @grams = qw( a b c d e f g h i j k l m n o p r s t u v w x y z ch gh ph rh sh th wh qu ck ); 101my %grams; @grams{@grams} = (); # and a set of same. 102 103my @vowel_grams = qw( a e i o u y ); 104my %vowel_grams; @vowel_grams{@vowel_grams} = (); # and a set of same. 105 106 107 108# 109# Bit flags 110# 111 112use constant MAX_UNACCEPTABLE => 20 ; 113 114# gram rules: 115use constant NOT_BEGIN_SYLLABLE => 010 ; 116use constant NO_FINAL_SPLIT => 004 ; 117use constant VOWEL => 002 ; 118use constant ALTERNATE_VOWEL => 001 ; 119use constant NO_SPECIAL_RULE => 000 ; 120 121# digram rules: 122use constant FRONT => 0200 ; 123use constant NOT_FRONT => 0100 ; 124use constant BREAK => 0040 ; 125use constant PREFIX => 0020 ; 126use constant ILLEGAL_PAIR => 0010 ; 127use constant SUFFIX => 0004 ; 128use constant BACK => 0002 ; 129use constant NOT_BACK => 0001 ; 130use constant ANY_COMBINATION => 0000 ; 131 132## it used to be that info about units was contained in the C-arrays 'rules' and 'digram'. 133## both were indexed numerically. 'rules' was essentially a mapping from a unique 134## integer ID (the index) to a gram. 'digram' used the same mapping, but in a 135## two-dimensional array. I.e. to represent the digram "ab" ("a","b"), one would 136## need to know the numeric ID of "a" and "b", which turn out to be 0 and 1, respectively; 137## then use those indices in digram: digram[0][1]. 138## The information at the "end" of a lookup in digram[][] was a simple integer 139## representing the flag bits for that digram. (The %digram in the current 140## implementation is the same.) The rules[] C-array, however, needed to store 141## both the bitmask and the string representation of the gram, so it was an array 142## of a struct { string, bitmask }. Since %rules is an associative array, indexed 143## directly by the gram, it only needs the bitmask at its "end", the same as %digram. 144## 145## both 'rules' and 'digram' contained bitflags for grams and digrams, respectively. 146## additionally, 'rules' contained the string representation of the unit. 147## because 'rules' contained both a string and flags for each unit, its contents 148## were actually structs of { string, flags }. 149## 150## 'digram', on the other hand, was simply the bitflags (integers). 151 152# struct unit { 153# char unit_code[5]; # string, usually 1, but up to 4 characters. 154# byte flags; 155# } rules[34]; 156 157## the 'rules' C-array used to be indexed by gram index; now %rules is indexed by the gram itself. 158 159my %rules; 160 161@rules{ @grams } = ( NO_SPECIAL_RULE ) x @grams; 162@rules{ @vowel_grams } = ( VOWEL ) x @vowel_grams; 163 164$rules{'e'} |= NO_FINAL_SPLIT; 165$rules{'y'} |= ALTERNATE_VOWEL; 166 167$rules{'x'} = 168$rules{'ck'} = NOT_BEGIN_SYLLABLE; 169 170 171 172# 173# the 'digram' C-array, digram[34][34], was indexed by the unit indexes of the two grams; 174# now %digram is indexed directly by the two grams. 175# 176my %digram; 177 178 ############################################################################################## 179 # BEGIN DIGRAM { 180 ############################################################################################## 181 182 $digram{'a'}{'a'} = ILLEGAL_PAIR; 183 $digram{'a'}{'b'} = ANY_COMBINATION; 184 $digram{'a'}{'c'} = ANY_COMBINATION; 185 $digram{'a'}{'d'} = ANY_COMBINATION; 186 $digram{'a'}{'e'} = ILLEGAL_PAIR; 187 $digram{'a'}{'f'} = ANY_COMBINATION; 188 $digram{'a'}{'g'} = ANY_COMBINATION; 189 $digram{'a'}{'h'} = NOT_FRONT | BREAK | NOT_BACK; 190 $digram{'a'}{'i'} = ANY_COMBINATION; 191 $digram{'a'}{'j'} = ANY_COMBINATION; 192 $digram{'a'}{'k'} = ANY_COMBINATION; 193 $digram{'a'}{'l'} = ANY_COMBINATION; 194 $digram{'a'}{'m'} = ANY_COMBINATION; 195 $digram{'a'}{'n'} = ANY_COMBINATION; 196 $digram{'a'}{'o'} = ILLEGAL_PAIR; 197 $digram{'a'}{'p'} = ANY_COMBINATION; 198 $digram{'a'}{'r'} = ANY_COMBINATION; 199 $digram{'a'}{'s'} = ANY_COMBINATION; 200 $digram{'a'}{'t'} = ANY_COMBINATION; 201 $digram{'a'}{'u'} = ANY_COMBINATION; 202 $digram{'a'}{'v'} = ANY_COMBINATION; 203 $digram{'a'}{'w'} = ANY_COMBINATION; 204 $digram{'a'}{'x'} = ANY_COMBINATION; 205 $digram{'a'}{'y'} = ANY_COMBINATION; 206 $digram{'a'}{'z'} = ANY_COMBINATION; 207 $digram{'a'}{'ch'} = ANY_COMBINATION; 208 $digram{'a'}{'gh'} = ILLEGAL_PAIR; 209 $digram{'a'}{'ph'} = ANY_COMBINATION; 210 $digram{'a'}{'rh'} = ILLEGAL_PAIR; 211 $digram{'a'}{'sh'} = ANY_COMBINATION; 212 $digram{'a'}{'th'} = ANY_COMBINATION; 213 $digram{'a'}{'wh'} = ILLEGAL_PAIR; 214 $digram{'a'}{'qu'} = BREAK | NOT_BACK; 215 $digram{'a'}{'ck'} = ANY_COMBINATION; 216 217 $digram{'b'}{'a'} = ANY_COMBINATION; 218 $digram{'b'}{'b'} = NOT_FRONT | BREAK | NOT_BACK; 219 $digram{'b'}{'c'} = NOT_FRONT | BREAK | NOT_BACK; 220 $digram{'b'}{'d'} = NOT_FRONT | BREAK | NOT_BACK; 221 $digram{'b'}{'e'} = ANY_COMBINATION; 222 $digram{'b'}{'f'} = NOT_FRONT | BREAK | NOT_BACK; 223 $digram{'b'}{'g'} = NOT_FRONT | BREAK | NOT_BACK; 224 $digram{'b'}{'h'} = NOT_FRONT | BREAK | NOT_BACK; 225 $digram{'b'}{'i'} = ANY_COMBINATION; 226 $digram{'b'}{'j'} = NOT_FRONT | BREAK | NOT_BACK; 227 $digram{'b'}{'k'} = NOT_FRONT | BREAK | NOT_BACK; 228 $digram{'b'}{'l'} = FRONT | SUFFIX | NOT_BACK; 229 $digram{'b'}{'m'} = NOT_FRONT | BREAK | NOT_BACK; 230 $digram{'b'}{'n'} = NOT_FRONT | BREAK | NOT_BACK; 231 $digram{'b'}{'o'} = ANY_COMBINATION; 232 $digram{'b'}{'p'} = NOT_FRONT | BREAK | NOT_BACK; 233 $digram{'b'}{'r'} = FRONT | BACK; 234 $digram{'b'}{'s'} = NOT_FRONT; 235 $digram{'b'}{'t'} = NOT_FRONT | BREAK | NOT_BACK; 236 $digram{'b'}{'u'} = ANY_COMBINATION; 237 $digram{'b'}{'v'} = NOT_FRONT | BREAK | NOT_BACK; 238 $digram{'b'}{'w'} = NOT_FRONT | BREAK | NOT_BACK; 239 $digram{'b'}{'x'} = ILLEGAL_PAIR; 240 $digram{'b'}{'y'} = ANY_COMBINATION; 241 $digram{'b'}{'z'} = NOT_FRONT | BREAK | NOT_BACK; 242 $digram{'b'}{'ch'} = NOT_FRONT | BREAK | NOT_BACK; 243 $digram{'b'}{'gh'} = ILLEGAL_PAIR; 244 $digram{'b'}{'ph'} = NOT_FRONT | BREAK | NOT_BACK; 245 $digram{'b'}{'rh'} = ILLEGAL_PAIR; 246 $digram{'b'}{'sh'} = NOT_FRONT | BREAK | NOT_BACK; 247 $digram{'b'}{'th'} = NOT_FRONT | BREAK | NOT_BACK; 248 $digram{'b'}{'wh'} = ILLEGAL_PAIR; 249 $digram{'b'}{'qu'} = NOT_FRONT | BREAK | NOT_BACK; 250 $digram{'b'}{'ck'} = ILLEGAL_PAIR; 251 252 $digram{'c'}{'a'} = ANY_COMBINATION; 253 $digram{'c'}{'b'} = NOT_FRONT | BREAK | NOT_BACK; 254 $digram{'c'}{'c'} = NOT_FRONT | BREAK | NOT_BACK; 255 $digram{'c'}{'d'} = NOT_FRONT | BREAK | NOT_BACK; 256 $digram{'c'}{'e'} = ANY_COMBINATION; 257 $digram{'c'}{'f'} = NOT_FRONT | BREAK | NOT_BACK; 258 $digram{'c'}{'g'} = NOT_FRONT | BREAK | NOT_BACK; 259 $digram{'c'}{'h'} = NOT_FRONT | BREAK | NOT_BACK; 260 $digram{'c'}{'i'} = ANY_COMBINATION; 261 $digram{'c'}{'j'} = NOT_FRONT | BREAK | NOT_BACK; 262 $digram{'c'}{'k'} = NOT_FRONT | BREAK | NOT_BACK; 263 $digram{'c'}{'l'} = SUFFIX | NOT_BACK; 264 $digram{'c'}{'m'} = NOT_FRONT | BREAK | NOT_BACK; 265 $digram{'c'}{'n'} = NOT_FRONT | BREAK | NOT_BACK; 266 $digram{'c'}{'o'} = ANY_COMBINATION; 267 $digram{'c'}{'p'} = NOT_FRONT | BREAK | NOT_BACK; 268 $digram{'c'}{'r'} = NOT_BACK; 269 $digram{'c'}{'s'} = NOT_FRONT | BACK; 270 $digram{'c'}{'t'} = NOT_FRONT | PREFIX; 271 $digram{'c'}{'u'} = ANY_COMBINATION; 272 $digram{'c'}{'v'} = NOT_FRONT | BREAK | NOT_BACK; 273 $digram{'c'}{'w'} = NOT_FRONT | BREAK | NOT_BACK; 274 $digram{'c'}{'x'} = ILLEGAL_PAIR; 275 $digram{'c'}{'y'} = ANY_COMBINATION; 276 $digram{'c'}{'z'} = NOT_FRONT | BREAK | NOT_BACK; 277 $digram{'c'}{'ch'} = ILLEGAL_PAIR; 278 $digram{'c'}{'gh'} = ILLEGAL_PAIR; 279 $digram{'c'}{'ph'} = NOT_FRONT | BREAK | NOT_BACK; 280 $digram{'c'}{'rh'} = ILLEGAL_PAIR; 281 $digram{'c'}{'sh'} = NOT_FRONT | BREAK | NOT_BACK; 282 $digram{'c'}{'th'} = NOT_FRONT | BREAK | NOT_BACK; 283 $digram{'c'}{'wh'} = ILLEGAL_PAIR; 284 $digram{'c'}{'qu'} = NOT_FRONT | SUFFIX | NOT_BACK; 285 $digram{'c'}{'ck'} = ILLEGAL_PAIR; 286 287 $digram{'d'}{'a'} = ANY_COMBINATION; 288 $digram{'d'}{'b'} = NOT_FRONT | BREAK | NOT_BACK; 289 $digram{'d'}{'c'} = NOT_FRONT | BREAK | NOT_BACK; 290 $digram{'d'}{'d'} = NOT_FRONT; 291 $digram{'d'}{'e'} = ANY_COMBINATION; 292 $digram{'d'}{'f'} = NOT_FRONT | BREAK | NOT_BACK; 293 $digram{'d'}{'g'} = NOT_FRONT | BREAK | NOT_BACK; 294 $digram{'d'}{'h'} = NOT_FRONT | BREAK | NOT_BACK; 295 $digram{'d'}{'i'} = ANY_COMBINATION; 296 $digram{'d'}{'j'} = NOT_FRONT | BREAK | NOT_BACK; 297 $digram{'d'}{'k'} = NOT_FRONT | BREAK | NOT_BACK; 298 $digram{'d'}{'l'} = NOT_FRONT | BREAK | NOT_BACK; 299 $digram{'d'}{'m'} = NOT_FRONT | BREAK | NOT_BACK; 300 $digram{'d'}{'n'} = NOT_FRONT | BREAK | NOT_BACK; 301 $digram{'d'}{'o'} = ANY_COMBINATION; 302 $digram{'d'}{'p'} = NOT_FRONT | BREAK | NOT_BACK; 303 $digram{'d'}{'r'} = FRONT | NOT_BACK; 304 $digram{'d'}{'s'} = NOT_FRONT | BACK; 305 $digram{'d'}{'t'} = NOT_FRONT | BREAK | NOT_BACK; 306 $digram{'d'}{'u'} = ANY_COMBINATION; 307 $digram{'d'}{'v'} = NOT_FRONT | BREAK | NOT_BACK; 308 $digram{'d'}{'w'} = NOT_FRONT | BREAK | NOT_BACK; 309 $digram{'d'}{'x'} = ILLEGAL_PAIR; 310 $digram{'d'}{'y'} = ANY_COMBINATION; 311 $digram{'d'}{'z'} = NOT_FRONT | BREAK | NOT_BACK; 312 $digram{'d'}{'ch'} = NOT_FRONT | BREAK | NOT_BACK; 313 $digram{'d'}{'gh'} = NOT_FRONT | BREAK | NOT_BACK; 314 $digram{'d'}{'ph'} = NOT_FRONT | BREAK | NOT_BACK; 315 $digram{'d'}{'rh'} = ILLEGAL_PAIR; 316 $digram{'d'}{'sh'} = NOT_FRONT | NOT_BACK; 317 $digram{'d'}{'th'} = NOT_FRONT | PREFIX; 318 $digram{'d'}{'wh'} = ILLEGAL_PAIR; 319 $digram{'d'}{'qu'} = NOT_FRONT | BREAK | NOT_BACK; 320 $digram{'d'}{'ck'} = ILLEGAL_PAIR; 321 322 $digram{'e'}{'a'} = ANY_COMBINATION; 323 $digram{'e'}{'b'} = ANY_COMBINATION; 324 $digram{'e'}{'c'} = ANY_COMBINATION; 325 $digram{'e'}{'d'} = ANY_COMBINATION; 326 $digram{'e'}{'e'} = ANY_COMBINATION; 327 $digram{'e'}{'f'} = ANY_COMBINATION; 328 $digram{'e'}{'g'} = ANY_COMBINATION; 329 $digram{'e'}{'h'} = NOT_FRONT | BREAK | NOT_BACK; 330 $digram{'e'}{'i'} = NOT_BACK; 331 $digram{'e'}{'j'} = ANY_COMBINATION; 332 $digram{'e'}{'k'} = ANY_COMBINATION; 333 $digram{'e'}{'l'} = ANY_COMBINATION; 334 $digram{'e'}{'m'} = ANY_COMBINATION; 335 $digram{'e'}{'n'} = ANY_COMBINATION; 336 $digram{'e'}{'o'} = BREAK; 337 $digram{'e'}{'p'} = ANY_COMBINATION; 338 $digram{'e'}{'r'} = ANY_COMBINATION; 339 $digram{'e'}{'s'} = ANY_COMBINATION; 340 $digram{'e'}{'t'} = ANY_COMBINATION; 341 $digram{'e'}{'u'} = ANY_COMBINATION; 342 $digram{'e'}{'v'} = ANY_COMBINATION; 343 $digram{'e'}{'w'} = ANY_COMBINATION; 344 $digram{'e'}{'x'} = ANY_COMBINATION; 345 $digram{'e'}{'y'} = ANY_COMBINATION; 346 $digram{'e'}{'z'} = ANY_COMBINATION; 347 $digram{'e'}{'ch'} = ANY_COMBINATION; 348 $digram{'e'}{'gh'} = NOT_FRONT | BREAK | NOT_BACK; 349 $digram{'e'}{'ph'} = ANY_COMBINATION; 350 $digram{'e'}{'rh'} = ILLEGAL_PAIR; 351 $digram{'e'}{'sh'} = ANY_COMBINATION; 352 $digram{'e'}{'th'} = ANY_COMBINATION; 353 $digram{'e'}{'wh'} = ILLEGAL_PAIR; 354 $digram{'e'}{'qu'} = BREAK | NOT_BACK; 355 $digram{'e'}{'ck'} = ANY_COMBINATION; 356 357 $digram{'f'}{'a'} = ANY_COMBINATION; 358 $digram{'f'}{'b'} = NOT_FRONT | BREAK | NOT_BACK; 359 $digram{'f'}{'c'} = NOT_FRONT | BREAK | NOT_BACK; 360 $digram{'f'}{'d'} = NOT_FRONT | BREAK | NOT_BACK; 361 $digram{'f'}{'e'} = ANY_COMBINATION; 362 $digram{'f'}{'f'} = NOT_FRONT; 363 $digram{'f'}{'g'} = NOT_FRONT | BREAK | NOT_BACK; 364 $digram{'f'}{'h'} = NOT_FRONT | BREAK | NOT_BACK; 365 $digram{'f'}{'i'} = ANY_COMBINATION; 366 $digram{'f'}{'j'} = NOT_FRONT | BREAK | NOT_BACK; 367 $digram{'f'}{'k'} = NOT_FRONT | BREAK | NOT_BACK; 368 $digram{'f'}{'l'} = FRONT | SUFFIX | NOT_BACK; 369 $digram{'f'}{'m'} = NOT_FRONT | BREAK | NOT_BACK; 370 $digram{'f'}{'n'} = NOT_FRONT | BREAK | NOT_BACK; 371 $digram{'f'}{'o'} = ANY_COMBINATION; 372 $digram{'f'}{'p'} = NOT_FRONT | BREAK | NOT_BACK; 373 $digram{'f'}{'r'} = FRONT | NOT_BACK; 374 $digram{'f'}{'s'} = NOT_FRONT; 375 $digram{'f'}{'t'} = NOT_FRONT; 376 $digram{'f'}{'u'} = ANY_COMBINATION; 377 $digram{'f'}{'v'} = NOT_FRONT | BREAK | NOT_BACK; 378 $digram{'f'}{'w'} = NOT_FRONT | BREAK | NOT_BACK; 379 $digram{'f'}{'x'} = ILLEGAL_PAIR; 380 $digram{'f'}{'y'} = NOT_FRONT; 381 $digram{'f'}{'z'} = NOT_FRONT | BREAK | NOT_BACK; 382 $digram{'f'}{'ch'} = NOT_FRONT | BREAK | NOT_BACK; 383 $digram{'f'}{'gh'} = NOT_FRONT | BREAK | NOT_BACK; 384 $digram{'f'}{'ph'} = NOT_FRONT | BREAK | NOT_BACK; 385 $digram{'f'}{'rh'} = ILLEGAL_PAIR; 386 $digram{'f'}{'sh'} = NOT_FRONT | BREAK | NOT_BACK; 387 $digram{'f'}{'th'} = NOT_FRONT | BREAK | NOT_BACK; 388 $digram{'f'}{'wh'} = ILLEGAL_PAIR; 389 $digram{'f'}{'qu'} = NOT_FRONT | BREAK | NOT_BACK; 390 $digram{'f'}{'ck'} = ILLEGAL_PAIR; 391 392 $digram{'g'}{'a'} = ANY_COMBINATION; 393 $digram{'g'}{'b'} = NOT_FRONT | BREAK | NOT_BACK; 394 $digram{'g'}{'c'} = NOT_FRONT | BREAK | NOT_BACK; 395 $digram{'g'}{'d'} = NOT_FRONT | BREAK | NOT_BACK; 396 $digram{'g'}{'e'} = ANY_COMBINATION; 397 $digram{'g'}{'f'} = NOT_FRONT | BREAK | NOT_BACK; 398 $digram{'g'}{'g'} = NOT_FRONT; 399 $digram{'g'}{'h'} = NOT_FRONT | BREAK | NOT_BACK; 400 $digram{'g'}{'i'} = ANY_COMBINATION; 401 $digram{'g'}{'j'} = NOT_FRONT | BREAK | NOT_BACK; 402 $digram{'g'}{'k'} = ILLEGAL_PAIR; 403 $digram{'g'}{'l'} = FRONT | SUFFIX | NOT_BACK; 404 $digram{'g'}{'m'} = NOT_FRONT | BREAK | NOT_BACK; 405 $digram{'g'}{'n'} = NOT_FRONT | BREAK | NOT_BACK; 406 $digram{'g'}{'o'} = ANY_COMBINATION; 407 $digram{'g'}{'p'} = NOT_FRONT | BREAK | NOT_BACK; 408 $digram{'g'}{'r'} = FRONT | NOT_BACK; 409 $digram{'g'}{'s'} = NOT_FRONT | BACK; 410 $digram{'g'}{'t'} = NOT_FRONT | BREAK | NOT_BACK; 411 $digram{'g'}{'u'} = ANY_COMBINATION; 412 $digram{'g'}{'v'} = NOT_FRONT | BREAK | NOT_BACK; 413 $digram{'g'}{'w'} = NOT_FRONT | BREAK | NOT_BACK; 414 $digram{'g'}{'x'} = ILLEGAL_PAIR; 415 $digram{'g'}{'y'} = NOT_FRONT; 416 $digram{'g'}{'z'} = NOT_FRONT | BREAK | NOT_BACK; 417 $digram{'g'}{'ch'} = NOT_FRONT | BREAK | NOT_BACK; 418 $digram{'g'}{'gh'} = ILLEGAL_PAIR; 419 $digram{'g'}{'ph'} = NOT_FRONT | BREAK | NOT_BACK; 420 $digram{'g'}{'rh'} = ILLEGAL_PAIR; 421 $digram{'g'}{'sh'} = NOT_FRONT; 422 $digram{'g'}{'th'} = NOT_FRONT; 423 $digram{'g'}{'wh'} = ILLEGAL_PAIR; 424 $digram{'g'}{'qu'} = NOT_FRONT | BREAK | NOT_BACK; 425 $digram{'g'}{'ck'} = ILLEGAL_PAIR; 426 427 $digram{'h'}{'a'} = ANY_COMBINATION; 428 $digram{'h'}{'b'} = NOT_FRONT | BREAK | NOT_BACK; 429 $digram{'h'}{'c'} = NOT_FRONT | BREAK | NOT_BACK; 430 $digram{'h'}{'d'} = NOT_FRONT | BREAK | NOT_BACK; 431 $digram{'h'}{'e'} = ANY_COMBINATION; 432 $digram{'h'}{'f'} = NOT_FRONT | BREAK | NOT_BACK; 433 $digram{'h'}{'g'} = NOT_FRONT | BREAK | NOT_BACK; 434 $digram{'h'}{'h'} = ILLEGAL_PAIR; 435 $digram{'h'}{'i'} = ANY_COMBINATION; 436 $digram{'h'}{'j'} = NOT_FRONT | BREAK | NOT_BACK; 437 $digram{'h'}{'k'} = NOT_FRONT | BREAK | NOT_BACK; 438 $digram{'h'}{'l'} = NOT_FRONT | BREAK | NOT_BACK; 439 $digram{'h'}{'m'} = NOT_FRONT | BREAK | NOT_BACK; 440 $digram{'h'}{'n'} = NOT_FRONT | BREAK | NOT_BACK; 441 $digram{'h'}{'o'} = ANY_COMBINATION; 442 $digram{'h'}{'p'} = NOT_FRONT | BREAK | NOT_BACK; 443 $digram{'h'}{'r'} = NOT_FRONT | BREAK | NOT_BACK; 444 $digram{'h'}{'s'} = NOT_FRONT | BREAK | NOT_BACK; 445 $digram{'h'}{'t'} = NOT_FRONT | BREAK | NOT_BACK; 446 $digram{'h'}{'u'} = ANY_COMBINATION; 447 $digram{'h'}{'v'} = NOT_FRONT | BREAK | NOT_BACK; 448 $digram{'h'}{'w'} = NOT_FRONT | BREAK | NOT_BACK; 449 $digram{'h'}{'x'} = ILLEGAL_PAIR; 450 $digram{'h'}{'y'} = ANY_COMBINATION; 451 $digram{'h'}{'z'} = NOT_FRONT | BREAK | NOT_BACK; 452 $digram{'h'}{'ch'} = NOT_FRONT | BREAK | NOT_BACK; 453 $digram{'h'}{'gh'} = NOT_FRONT | BREAK | NOT_BACK; 454 $digram{'h'}{'ph'} = NOT_FRONT | BREAK | NOT_BACK; 455 $digram{'h'}{'rh'} = ILLEGAL_PAIR; 456 $digram{'h'}{'sh'} = NOT_FRONT | BREAK | NOT_BACK; 457 $digram{'h'}{'th'} = NOT_FRONT | BREAK | NOT_BACK; 458 $digram{'h'}{'wh'} = ILLEGAL_PAIR; 459 $digram{'h'}{'qu'} = NOT_FRONT | BREAK | NOT_BACK; 460 $digram{'h'}{'ck'} = ILLEGAL_PAIR; 461 462 $digram{'i'}{'a'} = ANY_COMBINATION; 463 $digram{'i'}{'b'} = ANY_COMBINATION; 464 $digram{'i'}{'c'} = ANY_COMBINATION; 465 $digram{'i'}{'d'} = ANY_COMBINATION; 466 $digram{'i'}{'e'} = NOT_FRONT; 467 $digram{'i'}{'f'} = ANY_COMBINATION; 468 $digram{'i'}{'g'} = ANY_COMBINATION; 469 $digram{'i'}{'h'} = NOT_FRONT | BREAK | NOT_BACK; 470 $digram{'i'}{'i'} = ILLEGAL_PAIR; 471 $digram{'i'}{'j'} = ANY_COMBINATION; 472 $digram{'i'}{'k'} = ANY_COMBINATION; 473 $digram{'i'}{'l'} = ANY_COMBINATION; 474 $digram{'i'}{'m'} = ANY_COMBINATION; 475 $digram{'i'}{'n'} = ANY_COMBINATION; 476 $digram{'i'}{'o'} = BREAK; 477 $digram{'i'}{'p'} = ANY_COMBINATION; 478 $digram{'i'}{'r'} = ANY_COMBINATION; 479 $digram{'i'}{'s'} = ANY_COMBINATION; 480 $digram{'i'}{'t'} = ANY_COMBINATION; 481 $digram{'i'}{'u'} = NOT_FRONT | BREAK | NOT_BACK; 482 $digram{'i'}{'v'} = ANY_COMBINATION; 483 $digram{'i'}{'w'} = NOT_FRONT | BREAK | NOT_BACK; 484 $digram{'i'}{'x'} = ANY_COMBINATION; 485 $digram{'i'}{'y'} = NOT_FRONT | BREAK | NOT_BACK; 486 $digram{'i'}{'z'} = ANY_COMBINATION; 487 $digram{'i'}{'ch'} = ANY_COMBINATION; 488 $digram{'i'}{'gh'} = NOT_FRONT; 489 $digram{'i'}{'ph'} = ANY_COMBINATION; 490 $digram{'i'}{'rh'} = ILLEGAL_PAIR; 491 $digram{'i'}{'sh'} = ANY_COMBINATION; 492 $digram{'i'}{'th'} = ANY_COMBINATION; 493 $digram{'i'}{'wh'} = ILLEGAL_PAIR; 494 $digram{'i'}{'qu'} = BREAK | NOT_BACK; 495 $digram{'i'}{'ck'} = ANY_COMBINATION; 496 497 $digram{'j'}{'a'} = ANY_COMBINATION; 498 $digram{'j'}{'b'} = NOT_FRONT | BREAK | NOT_BACK; 499 $digram{'j'}{'c'} = NOT_FRONT | BREAK | NOT_BACK; 500 $digram{'j'}{'d'} = NOT_FRONT | BREAK | NOT_BACK; 501 $digram{'j'}{'e'} = ANY_COMBINATION; 502 $digram{'j'}{'f'} = NOT_FRONT | BREAK | NOT_BACK; 503 $digram{'j'}{'g'} = ILLEGAL_PAIR; 504 $digram{'j'}{'h'} = NOT_FRONT | BREAK | NOT_BACK; 505 $digram{'j'}{'i'} = ANY_COMBINATION; 506 $digram{'j'}{'j'} = ILLEGAL_PAIR; 507 $digram{'j'}{'k'} = NOT_FRONT | BREAK | NOT_BACK; 508 $digram{'j'}{'l'} = NOT_FRONT | BREAK | NOT_BACK; 509 $digram{'j'}{'m'} = NOT_FRONT | BREAK | NOT_BACK; 510 $digram{'j'}{'n'} = NOT_FRONT | BREAK | NOT_BACK; 511 $digram{'j'}{'o'} = ANY_COMBINATION; 512 $digram{'j'}{'p'} = NOT_FRONT | BREAK | NOT_BACK; 513 $digram{'j'}{'r'} = NOT_FRONT | BREAK | NOT_BACK; 514 $digram{'j'}{'s'} = NOT_FRONT | BREAK | NOT_BACK; 515 $digram{'j'}{'t'} = NOT_FRONT | BREAK | NOT_BACK; 516 $digram{'j'}{'u'} = ANY_COMBINATION; 517 $digram{'j'}{'v'} = NOT_FRONT | BREAK | NOT_BACK; 518 $digram{'j'}{'w'} = NOT_FRONT | BREAK | NOT_BACK; 519 $digram{'j'}{'x'} = ILLEGAL_PAIR; 520 $digram{'j'}{'y'} = NOT_FRONT; 521 $digram{'j'}{'z'} = NOT_FRONT | BREAK | NOT_BACK; 522 $digram{'j'}{'ch'} = NOT_FRONT | BREAK | NOT_BACK; 523 $digram{'j'}{'gh'} = NOT_FRONT | BREAK | NOT_BACK; 524 $digram{'j'}{'ph'} = NOT_FRONT | BREAK | NOT_BACK; 525 $digram{'j'}{'rh'} = ILLEGAL_PAIR; 526 $digram{'j'}{'sh'} = NOT_FRONT | BREAK | NOT_BACK; 527 $digram{'j'}{'th'} = NOT_FRONT | BREAK | NOT_BACK; 528 $digram{'j'}{'wh'} = ILLEGAL_PAIR; 529 $digram{'j'}{'qu'} = NOT_FRONT | BREAK | NOT_BACK; 530 $digram{'j'}{'ck'} = ILLEGAL_PAIR; 531 532 $digram{'k'}{'a'} = ANY_COMBINATION; 533 $digram{'k'}{'b'} = NOT_FRONT | BREAK | NOT_BACK; 534 $digram{'k'}{'c'} = NOT_FRONT | BREAK | NOT_BACK; 535 $digram{'k'}{'d'} = NOT_FRONT | BREAK | NOT_BACK; 536 $digram{'k'}{'e'} = ANY_COMBINATION; 537 $digram{'k'}{'f'} = NOT_FRONT | BREAK | NOT_BACK; 538 $digram{'k'}{'g'} = NOT_FRONT | BREAK | NOT_BACK; 539 $digram{'k'}{'h'} = NOT_FRONT | BREAK | NOT_BACK; 540 $digram{'k'}{'i'} = ANY_COMBINATION; 541 $digram{'k'}{'j'} = NOT_FRONT | BREAK | NOT_BACK; 542 $digram{'k'}{'k'} = NOT_FRONT | BREAK | NOT_BACK; 543 $digram{'k'}{'l'} = SUFFIX | NOT_BACK; 544 $digram{'k'}{'m'} = NOT_FRONT | BREAK | NOT_BACK; 545 $digram{'k'}{'n'} = FRONT | SUFFIX | NOT_BACK; 546 $digram{'k'}{'o'} = ANY_COMBINATION; 547 $digram{'k'}{'p'} = NOT_FRONT | BREAK | NOT_BACK; 548 $digram{'k'}{'r'} = SUFFIX | NOT_BACK; 549 $digram{'k'}{'s'} = NOT_FRONT | BACK; 550 $digram{'k'}{'t'} = NOT_FRONT | BREAK | NOT_BACK; 551 $digram{'k'}{'u'} = ANY_COMBINATION; 552 $digram{'k'}{'v'} = NOT_FRONT | BREAK | NOT_BACK; 553 $digram{'k'}{'w'} = NOT_FRONT | BREAK | NOT_BACK; 554 $digram{'k'}{'x'} = ILLEGAL_PAIR; 555 $digram{'k'}{'y'} = NOT_FRONT; 556 $digram{'k'}{'z'} = NOT_FRONT | BREAK | NOT_BACK; 557 $digram{'k'}{'ch'} = NOT_FRONT | BREAK | NOT_BACK; 558 $digram{'k'}{'gh'} = NOT_FRONT | BREAK | NOT_BACK; 559 $digram{'k'}{'ph'} = NOT_FRONT | PREFIX; 560 $digram{'k'}{'rh'} = ILLEGAL_PAIR; 561 $digram{'k'}{'sh'} = NOT_FRONT; 562 $digram{'k'}{'th'} = NOT_FRONT | BREAK | NOT_BACK; 563 $digram{'k'}{'wh'} = ILLEGAL_PAIR; 564 $digram{'k'}{'qu'} = NOT_FRONT | BREAK | NOT_BACK; 565 $digram{'k'}{'ck'} = ILLEGAL_PAIR; 566 567 $digram{'l'}{'a'} = ANY_COMBINATION; 568 $digram{'l'}{'b'} = NOT_FRONT | PREFIX; 569 $digram{'l'}{'c'} = NOT_FRONT | BREAK | NOT_BACK; 570 $digram{'l'}{'d'} = NOT_FRONT | PREFIX; 571 $digram{'l'}{'e'} = ANY_COMBINATION; 572 $digram{'l'}{'f'} = NOT_FRONT | PREFIX; 573 $digram{'l'}{'g'} = NOT_FRONT | PREFIX; 574 $digram{'l'}{'h'} = NOT_FRONT | BREAK | NOT_BACK; 575 $digram{'l'}{'i'} = ANY_COMBINATION; 576 $digram{'l'}{'j'} = NOT_FRONT | PREFIX; 577 $digram{'l'}{'k'} = NOT_FRONT | PREFIX; 578 $digram{'l'}{'l'} = NOT_FRONT | PREFIX; 579 $digram{'l'}{'m'} = NOT_FRONT | PREFIX; 580 $digram{'l'}{'n'} = NOT_FRONT | BREAK | NOT_BACK; 581 $digram{'l'}{'o'} = ANY_COMBINATION; 582 $digram{'l'}{'p'} = NOT_FRONT | PREFIX; 583 $digram{'l'}{'r'} = NOT_FRONT | BREAK | NOT_BACK; 584 $digram{'l'}{'s'} = NOT_FRONT; 585 $digram{'l'}{'t'} = NOT_FRONT | PREFIX; 586 $digram{'l'}{'u'} = ANY_COMBINATION; 587 $digram{'l'}{'v'} = NOT_FRONT | PREFIX; 588 $digram{'l'}{'w'} = NOT_FRONT | BREAK | NOT_BACK; 589 $digram{'l'}{'x'} = ILLEGAL_PAIR; 590 $digram{'l'}{'y'} = ANY_COMBINATION; 591 $digram{'l'}{'z'} = NOT_FRONT | BREAK | NOT_BACK; 592 $digram{'l'}{'ch'} = NOT_FRONT | PREFIX; 593 $digram{'l'}{'gh'} = NOT_FRONT | BREAK | NOT_BACK; 594 $digram{'l'}{'ph'} = NOT_FRONT | PREFIX; 595 $digram{'l'}{'rh'} = ILLEGAL_PAIR; 596 $digram{'l'}{'sh'} = NOT_FRONT | PREFIX; 597 $digram{'l'}{'th'} = NOT_FRONT | PREFIX; 598 $digram{'l'}{'wh'} = ILLEGAL_PAIR; 599 $digram{'l'}{'qu'} = NOT_FRONT | BREAK | NOT_BACK; 600 $digram{'l'}{'ck'} = ILLEGAL_PAIR; 601 602 $digram{'m'}{'a'} = ANY_COMBINATION; 603 $digram{'m'}{'b'} = NOT_FRONT | BREAK | NOT_BACK; 604 $digram{'m'}{'c'} = NOT_FRONT | BREAK | NOT_BACK; 605 $digram{'m'}{'d'} = NOT_FRONT | BREAK | NOT_BACK; 606 $digram{'m'}{'e'} = ANY_COMBINATION; 607 $digram{'m'}{'f'} = NOT_FRONT | BREAK | NOT_BACK; 608 $digram{'m'}{'g'} = NOT_FRONT | BREAK | NOT_BACK; 609 $digram{'m'}{'h'} = NOT_FRONT | BREAK | NOT_BACK; 610 $digram{'m'}{'i'} = ANY_COMBINATION; 611 $digram{'m'}{'j'} = NOT_FRONT | BREAK | NOT_BACK; 612 $digram{'m'}{'k'} = NOT_FRONT | BREAK | NOT_BACK; 613 $digram{'m'}{'l'} = NOT_FRONT | BREAK | NOT_BACK; 614 $digram{'m'}{'m'} = NOT_FRONT; 615 $digram{'m'}{'n'} = NOT_FRONT | BREAK | NOT_BACK; 616 $digram{'m'}{'o'} = ANY_COMBINATION; 617 $digram{'m'}{'p'} = NOT_FRONT; 618 $digram{'m'}{'r'} = NOT_FRONT | BREAK | NOT_BACK; 619 $digram{'m'}{'s'} = NOT_FRONT; 620 $digram{'m'}{'t'} = NOT_FRONT; 621 $digram{'m'}{'u'} = ANY_COMBINATION; 622 $digram{'m'}{'v'} = NOT_FRONT | BREAK | NOT_BACK; 623 $digram{'m'}{'w'} = NOT_FRONT | BREAK | NOT_BACK; 624 $digram{'m'}{'x'} = ILLEGAL_PAIR; 625 $digram{'m'}{'y'} = ANY_COMBINATION; 626 $digram{'m'}{'z'} = NOT_FRONT | BREAK | NOT_BACK; 627 $digram{'m'}{'ch'} = NOT_FRONT | PREFIX; 628 $digram{'m'}{'gh'} = NOT_FRONT | BREAK | NOT_BACK; 629 $digram{'m'}{'ph'} = NOT_FRONT; 630 $digram{'m'}{'rh'} = ILLEGAL_PAIR; 631 $digram{'m'}{'sh'} = NOT_FRONT; 632 $digram{'m'}{'th'} = NOT_FRONT; 633 $digram{'m'}{'wh'} = ILLEGAL_PAIR; 634 $digram{'m'}{'qu'} = NOT_FRONT | BREAK | NOT_BACK; 635 $digram{'m'}{'ck'} = ILLEGAL_PAIR; 636 637 $digram{'n'}{'a'} = ANY_COMBINATION; 638 $digram{'n'}{'b'} = NOT_FRONT | BREAK | NOT_BACK; 639 $digram{'n'}{'c'} = NOT_FRONT | BREAK | NOT_BACK; 640 $digram{'n'}{'d'} = NOT_FRONT; 641 $digram{'n'}{'e'} = ANY_COMBINATION; 642 $digram{'n'}{'f'} = NOT_FRONT | BREAK | NOT_BACK; 643 $digram{'n'}{'g'} = NOT_FRONT | PREFIX; 644 $digram{'n'}{'h'} = NOT_FRONT | BREAK | NOT_BACK; 645 $digram{'n'}{'i'} = ANY_COMBINATION; 646 $digram{'n'}{'j'} = NOT_FRONT | BREAK | NOT_BACK; 647 $digram{'n'}{'k'} = NOT_FRONT | PREFIX; 648 $digram{'n'}{'l'} = NOT_FRONT | BREAK | NOT_BACK; 649 $digram{'n'}{'m'} = NOT_FRONT | BREAK | NOT_BACK; 650 $digram{'n'}{'n'} = NOT_FRONT; 651 $digram{'n'}{'o'} = ANY_COMBINATION; 652 $digram{'n'}{'p'} = NOT_FRONT | BREAK | NOT_BACK; 653 $digram{'n'}{'r'} = NOT_FRONT | BREAK | NOT_BACK; 654 $digram{'n'}{'s'} = NOT_FRONT; 655 $digram{'n'}{'t'} = NOT_FRONT; 656 $digram{'n'}{'u'} = ANY_COMBINATION; 657 $digram{'n'}{'v'} = NOT_FRONT | BREAK | NOT_BACK; 658 $digram{'n'}{'w'} = NOT_FRONT | BREAK | NOT_BACK; 659 $digram{'n'}{'x'} = ILLEGAL_PAIR; 660 $digram{'n'}{'y'} = NOT_FRONT; 661 $digram{'n'}{'z'} = NOT_FRONT | BREAK | NOT_BACK; 662 $digram{'n'}{'ch'} = NOT_FRONT | PREFIX; 663 $digram{'n'}{'gh'} = NOT_FRONT | BREAK | NOT_BACK; 664 $digram{'n'}{'ph'} = NOT_FRONT | PREFIX; 665 $digram{'n'}{'rh'} = ILLEGAL_PAIR; 666 $digram{'n'}{'sh'} = NOT_FRONT; 667 $digram{'n'}{'th'} = NOT_FRONT; 668 $digram{'n'}{'wh'} = ILLEGAL_PAIR; 669 $digram{'n'}{'qu'} = NOT_FRONT | BREAK | NOT_BACK; 670 $digram{'n'}{'ck'} = NOT_FRONT | PREFIX; 671 672 $digram{'o'}{'a'} = ANY_COMBINATION; 673 $digram{'o'}{'b'} = ANY_COMBINATION; 674 $digram{'o'}{'c'} = ANY_COMBINATION; 675 $digram{'o'}{'d'} = ANY_COMBINATION; 676 $digram{'o'}{'e'} = ILLEGAL_PAIR; 677 $digram{'o'}{'f'} = ANY_COMBINATION; 678 $digram{'o'}{'g'} = ANY_COMBINATION; 679 $digram{'o'}{'h'} = NOT_FRONT | BREAK | NOT_BACK; 680 $digram{'o'}{'i'} = ANY_COMBINATION; 681 $digram{'o'}{'j'} = ANY_COMBINATION; 682 $digram{'o'}{'k'} = ANY_COMBINATION; 683 $digram{'o'}{'l'} = ANY_COMBINATION; 684 $digram{'o'}{'m'} = ANY_COMBINATION; 685 $digram{'o'}{'n'} = ANY_COMBINATION; 686 $digram{'o'}{'o'} = ANY_COMBINATION; 687 $digram{'o'}{'p'} = ANY_COMBINATION; 688 $digram{'o'}{'r'} = ANY_COMBINATION; 689 $digram{'o'}{'s'} = ANY_COMBINATION; 690 $digram{'o'}{'t'} = ANY_COMBINATION; 691 $digram{'o'}{'u'} = ANY_COMBINATION; 692 $digram{'o'}{'v'} = ANY_COMBINATION; 693 $digram{'o'}{'w'} = ANY_COMBINATION; 694 $digram{'o'}{'x'} = ANY_COMBINATION; 695 $digram{'o'}{'y'} = ANY_COMBINATION; 696 $digram{'o'}{'z'} = ANY_COMBINATION; 697 $digram{'o'}{'ch'} = ANY_COMBINATION; 698 $digram{'o'}{'gh'} = NOT_FRONT; 699 $digram{'o'}{'ph'} = ANY_COMBINATION; 700 $digram{'o'}{'rh'} = ILLEGAL_PAIR; 701 $digram{'o'}{'sh'} = ANY_COMBINATION; 702 $digram{'o'}{'th'} = ANY_COMBINATION; 703 $digram{'o'}{'wh'} = ILLEGAL_PAIR; 704 $digram{'o'}{'qu'} = BREAK | NOT_BACK; 705 $digram{'o'}{'ck'} = ANY_COMBINATION; 706 707 $digram{'p'}{'a'} = ANY_COMBINATION; 708 $digram{'p'}{'b'} = NOT_FRONT | BREAK | NOT_BACK; 709 $digram{'p'}{'c'} = NOT_FRONT | BREAK | NOT_BACK; 710 $digram{'p'}{'d'} = NOT_FRONT | BREAK | NOT_BACK; 711 $digram{'p'}{'e'} = ANY_COMBINATION; 712 $digram{'p'}{'f'} = NOT_FRONT | BREAK | NOT_BACK; 713 $digram{'p'}{'g'} = NOT_FRONT | BREAK | NOT_BACK; 714 $digram{'p'}{'h'} = NOT_FRONT | BREAK | NOT_BACK; 715 $digram{'p'}{'i'} = ANY_COMBINATION; 716 $digram{'p'}{'j'} = NOT_FRONT | BREAK | NOT_BACK; 717 $digram{'p'}{'k'} = NOT_FRONT | BREAK | NOT_BACK; 718 $digram{'p'}{'l'} = SUFFIX | NOT_BACK; 719 $digram{'p'}{'m'} = NOT_FRONT | BREAK | NOT_BACK; 720 $digram{'p'}{'n'} = NOT_FRONT | BREAK | NOT_BACK; 721 $digram{'p'}{'o'} = ANY_COMBINATION; 722 $digram{'p'}{'p'} = NOT_FRONT | PREFIX; 723 $digram{'p'}{'r'} = NOT_BACK; 724 $digram{'p'}{'s'} = NOT_FRONT | BACK; 725 $digram{'p'}{'t'} = NOT_FRONT | BACK; 726 $digram{'p'}{'u'} = NOT_FRONT | BACK; 727 $digram{'p'}{'v'} = NOT_FRONT | BREAK | NOT_BACK; 728 $digram{'p'}{'w'} = NOT_FRONT | BREAK | NOT_BACK; 729 $digram{'p'}{'x'} = ILLEGAL_PAIR; 730 $digram{'p'}{'y'} = ANY_COMBINATION; 731 $digram{'p'}{'z'} = NOT_FRONT | BREAK | NOT_BACK; 732 $digram{'p'}{'ch'} = NOT_FRONT | BREAK | NOT_BACK; 733 $digram{'p'}{'gh'} = NOT_FRONT | BREAK | NOT_BACK; 734 $digram{'p'}{'ph'} = NOT_FRONT | BREAK | NOT_BACK; 735 $digram{'p'}{'rh'} = ILLEGAL_PAIR; 736 $digram{'p'}{'sh'} = NOT_FRONT | BREAK | NOT_BACK; 737 $digram{'p'}{'th'} = NOT_FRONT | BREAK | NOT_BACK; 738 $digram{'p'}{'wh'} = ILLEGAL_PAIR; 739 $digram{'p'}{'qu'} = NOT_FRONT | BREAK | NOT_BACK; 740 $digram{'p'}{'ck'} = ILLEGAL_PAIR; 741 742 $digram{'r'}{'a'} = ANY_COMBINATION; 743 $digram{'r'}{'b'} = NOT_FRONT | PREFIX; 744 $digram{'r'}{'c'} = NOT_FRONT | PREFIX; 745 $digram{'r'}{'d'} = NOT_FRONT | PREFIX; 746 $digram{'r'}{'e'} = ANY_COMBINATION; 747 $digram{'r'}{'f'} = NOT_FRONT | PREFIX; 748 $digram{'r'}{'g'} = NOT_FRONT | PREFIX; 749 $digram{'r'}{'h'} = NOT_FRONT | BREAK | NOT_BACK; 750 $digram{'r'}{'i'} = ANY_COMBINATION; 751 $digram{'r'}{'j'} = NOT_FRONT | PREFIX; 752 $digram{'r'}{'k'} = NOT_FRONT | PREFIX; 753 $digram{'r'}{'l'} = NOT_FRONT | PREFIX; 754 $digram{'r'}{'m'} = NOT_FRONT | PREFIX; 755 $digram{'r'}{'n'} = NOT_FRONT | PREFIX; 756 $digram{'r'}{'o'} = ANY_COMBINATION; 757 $digram{'r'}{'p'} = NOT_FRONT | PREFIX; 758 $digram{'r'}{'r'} = NOT_FRONT | PREFIX; 759 $digram{'r'}{'s'} = NOT_FRONT | PREFIX; 760 $digram{'r'}{'t'} = NOT_FRONT | PREFIX; 761 $digram{'r'}{'u'} = ANY_COMBINATION; 762 $digram{'r'}{'v'} = NOT_FRONT | PREFIX; 763 $digram{'r'}{'w'} = NOT_FRONT | BREAK | NOT_BACK; 764 $digram{'r'}{'x'} = ILLEGAL_PAIR; 765 $digram{'r'}{'y'} = ANY_COMBINATION; 766 $digram{'r'}{'z'} = NOT_FRONT | PREFIX; 767 $digram{'r'}{'ch'} = NOT_FRONT | PREFIX; 768 $digram{'r'}{'gh'} = NOT_FRONT | BREAK | NOT_BACK; 769 $digram{'r'}{'ph'} = NOT_FRONT | PREFIX; 770 $digram{'r'}{'rh'} = ILLEGAL_PAIR; 771 $digram{'r'}{'sh'} = NOT_FRONT | PREFIX; 772 $digram{'r'}{'th'} = NOT_FRONT | PREFIX; 773 $digram{'r'}{'wh'} = ILLEGAL_PAIR; 774 $digram{'r'}{'qu'} = NOT_FRONT | PREFIX | NOT_BACK; 775 $digram{'r'}{'ck'} = NOT_FRONT | PREFIX; 776 777 $digram{'s'}{'a'} = ANY_COMBINATION; 778 $digram{'s'}{'b'} = NOT_FRONT | BREAK | NOT_BACK; 779 $digram{'s'}{'c'} = NOT_BACK; 780 $digram{'s'}{'d'} = NOT_FRONT | BREAK | NOT_BACK; 781 $digram{'s'}{'e'} = ANY_COMBINATION; 782 $digram{'s'}{'f'} = NOT_FRONT | BREAK | NOT_BACK; 783 $digram{'s'}{'g'} = NOT_FRONT | BREAK | NOT_BACK; 784 $digram{'s'}{'h'} = NOT_FRONT | BREAK | NOT_BACK; 785 $digram{'s'}{'i'} = ANY_COMBINATION; 786 $digram{'s'}{'j'} = NOT_FRONT | BREAK | NOT_BACK; 787 $digram{'s'}{'k'} = ANY_COMBINATION; 788 $digram{'s'}{'l'} = FRONT | SUFFIX | NOT_BACK; 789 $digram{'s'}{'m'} = SUFFIX | NOT_BACK; 790 $digram{'s'}{'n'} = PREFIX | SUFFIX | NOT_BACK; 791 $digram{'s'}{'o'} = ANY_COMBINATION; 792 $digram{'s'}{'p'} = ANY_COMBINATION; 793 $digram{'s'}{'r'} = NOT_FRONT | NOT_BACK; 794 $digram{'s'}{'s'} = NOT_FRONT | PREFIX; 795 $digram{'s'}{'t'} = ANY_COMBINATION; 796 $digram{'s'}{'u'} = ANY_COMBINATION; 797 $digram{'s'}{'v'} = NOT_FRONT | BREAK | NOT_BACK; 798 $digram{'s'}{'w'} = FRONT | SUFFIX | NOT_BACK; 799 $digram{'s'}{'x'} = ILLEGAL_PAIR; 800 $digram{'s'}{'y'} = ANY_COMBINATION; 801 $digram{'s'}{'z'} = NOT_FRONT | BREAK | NOT_BACK; 802 $digram{'s'}{'ch'} = FRONT | SUFFIX | NOT_BACK; 803 $digram{'s'}{'gh'} = NOT_FRONT | BREAK | NOT_BACK; 804 $digram{'s'}{'ph'} = NOT_FRONT | BREAK | NOT_BACK; 805 $digram{'s'}{'rh'} = ILLEGAL_PAIR; 806 $digram{'s'}{'sh'} = NOT_FRONT | BREAK | NOT_BACK; 807 $digram{'s'}{'th'} = NOT_FRONT | BREAK | NOT_BACK; 808 $digram{'s'}{'wh'} = ILLEGAL_PAIR; 809 $digram{'s'}{'qu'} = SUFFIX | NOT_BACK; 810 $digram{'s'}{'ck'} = NOT_FRONT; 811 812 $digram{'t'}{'a'} = ANY_COMBINATION; 813 $digram{'t'}{'b'} = NOT_FRONT | BREAK | NOT_BACK; 814 $digram{'t'}{'c'} = NOT_FRONT | BREAK | NOT_BACK; 815 $digram{'t'}{'d'} = NOT_FRONT | BREAK | NOT_BACK; 816 $digram{'t'}{'e'} = ANY_COMBINATION; 817 $digram{'t'}{'f'} = NOT_FRONT | BREAK | NOT_BACK; 818 $digram{'t'}{'g'} = NOT_FRONT | BREAK | NOT_BACK; 819 $digram{'t'}{'h'} = NOT_FRONT | BREAK | NOT_BACK; 820 $digram{'t'}{'i'} = ANY_COMBINATION; 821 $digram{'t'}{'j'} = NOT_FRONT | BREAK | NOT_BACK; 822 $digram{'t'}{'k'} = NOT_FRONT | BREAK | NOT_BACK; 823 $digram{'t'}{'l'} = NOT_FRONT | BREAK | NOT_BACK; 824 $digram{'t'}{'m'} = NOT_FRONT | BREAK | NOT_BACK; 825 $digram{'t'}{'n'} = NOT_FRONT | BREAK | NOT_BACK; 826 $digram{'t'}{'o'} = ANY_COMBINATION; 827 $digram{'t'}{'p'} = NOT_FRONT | BREAK | NOT_BACK; 828 $digram{'t'}{'r'} = NOT_BACK; 829 $digram{'t'}{'s'} = NOT_FRONT | BACK; 830 $digram{'t'}{'t'} = NOT_FRONT | PREFIX; 831 $digram{'t'}{'u'} = ANY_COMBINATION; 832 $digram{'t'}{'v'} = NOT_FRONT | BREAK | NOT_BACK; 833 $digram{'t'}{'w'} = FRONT | SUFFIX | NOT_BACK; 834 $digram{'t'}{'x'} = ILLEGAL_PAIR; 835 $digram{'t'}{'y'} = ANY_COMBINATION; 836 $digram{'t'}{'z'} = NOT_FRONT | BREAK | NOT_BACK; 837 $digram{'t'}{'ch'} = NOT_FRONT; 838 $digram{'t'}{'gh'} = NOT_FRONT | BREAK | NOT_BACK; 839 $digram{'t'}{'ph'} = NOT_FRONT | BACK; 840 $digram{'t'}{'rh'} = ILLEGAL_PAIR; 841 $digram{'t'}{'sh'} = NOT_FRONT | BACK; 842 $digram{'t'}{'th'} = NOT_FRONT | BREAK | NOT_BACK; 843 $digram{'t'}{'wh'} = ILLEGAL_PAIR; 844 $digram{'t'}{'qu'} = NOT_FRONT | BREAK | NOT_BACK; 845 $digram{'t'}{'ck'} = ILLEGAL_PAIR; 846 847 $digram{'u'}{'a'} = NOT_FRONT | BREAK | NOT_BACK; 848 $digram{'u'}{'b'} = ANY_COMBINATION; 849 $digram{'u'}{'c'} = ANY_COMBINATION; 850 $digram{'u'}{'d'} = ANY_COMBINATION; 851 $digram{'u'}{'e'} = NOT_FRONT; 852 $digram{'u'}{'f'} = ANY_COMBINATION; 853 $digram{'u'}{'g'} = ANY_COMBINATION; 854 $digram{'u'}{'h'} = NOT_FRONT | BREAK | NOT_BACK; 855 $digram{'u'}{'i'} = NOT_FRONT | BREAK | NOT_BACK; 856 $digram{'u'}{'j'} = ANY_COMBINATION; 857 $digram{'u'}{'k'} = ANY_COMBINATION; 858 $digram{'u'}{'l'} = ANY_COMBINATION; 859 $digram{'u'}{'m'} = ANY_COMBINATION; 860 $digram{'u'}{'n'} = ANY_COMBINATION; 861 $digram{'u'}{'o'} = NOT_FRONT | BREAK; 862 $digram{'u'}{'p'} = ANY_COMBINATION; 863 $digram{'u'}{'r'} = ANY_COMBINATION; 864 $digram{'u'}{'s'} = ANY_COMBINATION; 865 $digram{'u'}{'t'} = ANY_COMBINATION; 866 $digram{'u'}{'u'} = ILLEGAL_PAIR; 867 $digram{'u'}{'v'} = ANY_COMBINATION; 868 $digram{'u'}{'w'} = NOT_FRONT | BREAK | NOT_BACK; 869 $digram{'u'}{'x'} = ANY_COMBINATION; 870 $digram{'u'}{'y'} = NOT_FRONT | BREAK | NOT_BACK; 871 $digram{'u'}{'z'} = ANY_COMBINATION; 872 $digram{'u'}{'ch'} = ANY_COMBINATION; 873 $digram{'u'}{'gh'} = NOT_FRONT | PREFIX; 874 $digram{'u'}{'ph'} = ANY_COMBINATION; 875 $digram{'u'}{'rh'} = ILLEGAL_PAIR; 876 $digram{'u'}{'sh'} = ANY_COMBINATION; 877 $digram{'u'}{'th'} = ANY_COMBINATION; 878 $digram{'u'}{'wh'} = ILLEGAL_PAIR; 879 $digram{'u'}{'qu'} = BREAK | NOT_BACK; 880 $digram{'u'}{'ck'} = ANY_COMBINATION; 881 882 $digram{'v'}{'a'} = ANY_COMBINATION; 883 $digram{'v'}{'b'} = NOT_FRONT | BREAK | NOT_BACK; 884 $digram{'v'}{'c'} = NOT_FRONT | BREAK | NOT_BACK; 885 $digram{'v'}{'d'} = NOT_FRONT | BREAK | NOT_BACK; 886 $digram{'v'}{'e'} = ANY_COMBINATION; 887 $digram{'v'}{'f'} = NOT_FRONT | BREAK | NOT_BACK; 888 $digram{'v'}{'g'} = NOT_FRONT | BREAK | NOT_BACK; 889 $digram{'v'}{'h'} = NOT_FRONT | BREAK | NOT_BACK; 890 $digram{'v'}{'i'} = ANY_COMBINATION; 891 $digram{'v'}{'j'} = NOT_FRONT | BREAK | NOT_BACK; 892 $digram{'v'}{'k'} = NOT_FRONT | BREAK | NOT_BACK; 893 $digram{'v'}{'l'} = NOT_FRONT | BREAK | NOT_BACK; 894 $digram{'v'}{'m'} = NOT_FRONT | BREAK | NOT_BACK; 895 $digram{'v'}{'n'} = NOT_FRONT | BREAK | NOT_BACK; 896 $digram{'v'}{'o'} = ANY_COMBINATION; 897 $digram{'v'}{'p'} = NOT_FRONT | BREAK | NOT_BACK; 898 $digram{'v'}{'r'} = NOT_FRONT | BREAK | NOT_BACK; 899 $digram{'v'}{'s'} = NOT_FRONT | BREAK | NOT_BACK; 900 $digram{'v'}{'t'} = NOT_FRONT | BREAK | NOT_BACK; 901 $digram{'v'}{'u'} = ANY_COMBINATION; 902 $digram{'v'}{'v'} = NOT_FRONT | BREAK | NOT_BACK; 903 $digram{'v'}{'w'} = NOT_FRONT | BREAK | NOT_BACK; 904 $digram{'v'}{'x'} = ILLEGAL_PAIR; 905 $digram{'v'}{'y'} = NOT_FRONT; 906 $digram{'v'}{'z'} = NOT_FRONT | BREAK | NOT_BACK; 907 $digram{'v'}{'ch'} = NOT_FRONT | BREAK | NOT_BACK; 908 $digram{'v'}{'gh'} = NOT_FRONT | BREAK | NOT_BACK; 909 $digram{'v'}{'ph'} = NOT_FRONT | BREAK | NOT_BACK; 910 $digram{'v'}{'rh'} = ILLEGAL_PAIR; 911 $digram{'v'}{'sh'} = NOT_FRONT | BREAK | NOT_BACK; 912 $digram{'v'}{'th'} = NOT_FRONT | BREAK | NOT_BACK; 913 $digram{'v'}{'wh'} = ILLEGAL_PAIR; 914 $digram{'v'}{'qu'} = NOT_FRONT | BREAK | NOT_BACK; 915 $digram{'v'}{'ck'} = ILLEGAL_PAIR; 916 917 $digram{'w'}{'a'} = ANY_COMBINATION; 918 $digram{'w'}{'b'} = NOT_FRONT | PREFIX; 919 $digram{'w'}{'c'} = NOT_FRONT | BREAK | NOT_BACK; 920 $digram{'w'}{'d'} = NOT_FRONT | PREFIX | BACK; 921 $digram{'w'}{'e'} = ANY_COMBINATION; 922 $digram{'w'}{'f'} = NOT_FRONT | PREFIX; 923 $digram{'w'}{'g'} = NOT_FRONT | PREFIX | BACK; 924 $digram{'w'}{'h'} = NOT_FRONT | BREAK | NOT_BACK; 925 $digram{'w'}{'i'} = ANY_COMBINATION; 926 $digram{'w'}{'j'} = NOT_FRONT | BREAK | NOT_BACK; 927 $digram{'w'}{'k'} = NOT_FRONT | PREFIX; 928 $digram{'w'}{'l'} = NOT_FRONT | PREFIX | SUFFIX; 929 $digram{'w'}{'m'} = NOT_FRONT | PREFIX; 930 $digram{'w'}{'n'} = NOT_FRONT | PREFIX; 931 $digram{'w'}{'o'} = ANY_COMBINATION; 932 $digram{'w'}{'p'} = NOT_FRONT | PREFIX; 933 $digram{'w'}{'r'} = FRONT | SUFFIX | NOT_BACK; 934 $digram{'w'}{'s'} = NOT_FRONT | PREFIX; 935 $digram{'w'}{'t'} = NOT_FRONT | PREFIX; 936 $digram{'w'}{'u'} = ANY_COMBINATION; 937 $digram{'w'}{'v'} = NOT_FRONT | PREFIX; 938 $digram{'w'}{'w'} = NOT_FRONT | BREAK | NOT_BACK; 939 $digram{'w'}{'x'} = NOT_FRONT | PREFIX; 940 $digram{'w'}{'y'} = ANY_COMBINATION; 941 $digram{'w'}{'z'} = NOT_FRONT | PREFIX; 942 $digram{'w'}{'ch'} = NOT_FRONT; 943 $digram{'w'}{'gh'} = NOT_FRONT | BREAK | NOT_BACK; 944 $digram{'w'}{'ph'} = NOT_FRONT; 945 $digram{'w'}{'rh'} = ILLEGAL_PAIR; 946 $digram{'w'}{'sh'} = NOT_FRONT; 947 $digram{'w'}{'th'} = NOT_FRONT; 948 $digram{'w'}{'wh'} = ILLEGAL_PAIR; 949 $digram{'w'}{'qu'} = NOT_FRONT | BREAK | NOT_BACK; 950 $digram{'w'}{'ck'} = NOT_FRONT; 951 952 $digram{'x'}{'a'} = NOT_FRONT; 953 $digram{'x'}{'b'} = NOT_FRONT | BREAK | NOT_BACK; 954 $digram{'x'}{'c'} = NOT_FRONT | BREAK | NOT_BACK; 955 $digram{'x'}{'d'} = NOT_FRONT | BREAK | NOT_BACK; 956 $digram{'x'}{'e'} = NOT_FRONT; 957 $digram{'x'}{'f'} = NOT_FRONT | BREAK | NOT_BACK; 958 $digram{'x'}{'g'} = NOT_FRONT | BREAK | NOT_BACK; 959 $digram{'x'}{'h'} = NOT_FRONT | BREAK | NOT_BACK; 960 $digram{'x'}{'i'} = NOT_FRONT; 961 $digram{'x'}{'j'} = NOT_FRONT | BREAK | NOT_BACK; 962 $digram{'x'}{'k'} = NOT_FRONT | BREAK | NOT_BACK; 963 $digram{'x'}{'l'} = NOT_FRONT | BREAK | NOT_BACK; 964 $digram{'x'}{'m'} = NOT_FRONT | BREAK | NOT_BACK; 965 $digram{'x'}{'n'} = NOT_FRONT | BREAK | NOT_BACK; 966 $digram{'x'}{'o'} = NOT_FRONT; 967 $digram{'x'}{'p'} = NOT_FRONT | BREAK | NOT_BACK; 968 $digram{'x'}{'r'} = NOT_FRONT | BREAK | NOT_BACK; 969 $digram{'x'}{'s'} = NOT_FRONT | BREAK | NOT_BACK; 970 $digram{'x'}{'t'} = NOT_FRONT | BREAK | NOT_BACK; 971 $digram{'x'}{'u'} = NOT_FRONT; 972 $digram{'x'}{'v'} = NOT_FRONT | BREAK | NOT_BACK; 973 $digram{'x'}{'w'} = NOT_FRONT | BREAK | NOT_BACK; 974 $digram{'x'}{'x'} = ILLEGAL_PAIR; 975 $digram{'x'}{'y'} = NOT_FRONT; 976 $digram{'x'}{'z'} = NOT_FRONT | BREAK | NOT_BACK; 977 $digram{'x'}{'ch'} = NOT_FRONT | BREAK | NOT_BACK; 978 $digram{'x'}{'gh'} = NOT_FRONT | BREAK | NOT_BACK; 979 $digram{'x'}{'ph'} = NOT_FRONT | BREAK | NOT_BACK; 980 $digram{'x'}{'rh'} = ILLEGAL_PAIR; 981 $digram{'x'}{'sh'} = NOT_FRONT | BREAK | NOT_BACK; 982 $digram{'x'}{'th'} = NOT_FRONT | BREAK | NOT_BACK; 983 $digram{'x'}{'wh'} = ILLEGAL_PAIR; 984 $digram{'x'}{'qu'} = NOT_FRONT | BREAK | NOT_BACK; 985 $digram{'x'}{'ck'} = ILLEGAL_PAIR; 986 987 $digram{'y'}{'a'} = ANY_COMBINATION; 988 $digram{'y'}{'b'} = NOT_FRONT; 989 $digram{'y'}{'c'} = NOT_FRONT | NOT_BACK; 990 $digram{'y'}{'d'} = NOT_FRONT; 991 $digram{'y'}{'e'} = ANY_COMBINATION; 992 $digram{'y'}{'f'} = NOT_FRONT | NOT_BACK; 993 $digram{'y'}{'g'} = NOT_FRONT; 994 $digram{'y'}{'h'} = NOT_FRONT | BREAK | NOT_BACK; 995 $digram{'y'}{'i'} = FRONT | NOT_BACK; 996 $digram{'y'}{'j'} = NOT_FRONT | NOT_BACK; 997 $digram{'y'}{'k'} = NOT_FRONT; 998 $digram{'y'}{'l'} = NOT_FRONT | NOT_BACK; 999 $digram{'y'}{'m'} = NOT_FRONT; 1000 $digram{'y'}{'n'} = NOT_FRONT; 1001 $digram{'y'}{'o'} = ANY_COMBINATION; 1002 $digram{'y'}{'p'} = NOT_FRONT; 1003 $digram{'y'}{'r'} = NOT_FRONT | BREAK | NOT_BACK; 1004 $digram{'y'}{'s'} = NOT_FRONT; 1005 $digram{'y'}{'t'} = NOT_FRONT; 1006 $digram{'y'}{'u'} = ANY_COMBINATION; 1007 $digram{'y'}{'v'} = NOT_FRONT | NOT_BACK; 1008 $digram{'y'}{'w'} = NOT_FRONT | BREAK | NOT_BACK; 1009 $digram{'y'}{'x'} = NOT_FRONT; 1010 $digram{'y'}{'y'} = ILLEGAL_PAIR; 1011 $digram{'y'}{'z'} = NOT_FRONT; 1012 $digram{'y'}{'ch'} = NOT_FRONT | BREAK | NOT_BACK; 1013 $digram{'y'}{'gh'} = NOT_FRONT | BREAK | NOT_BACK; 1014 $digram{'y'}{'ph'} = NOT_FRONT | BREAK | NOT_BACK; 1015 $digram{'y'}{'rh'} = ILLEGAL_PAIR; 1016 $digram{'y'}{'sh'} = NOT_FRONT | BREAK | NOT_BACK; 1017 $digram{'y'}{'th'} = NOT_FRONT | BREAK | NOT_BACK; 1018 $digram{'y'}{'wh'} = ILLEGAL_PAIR; 1019 $digram{'y'}{'qu'} = NOT_FRONT | BREAK | NOT_BACK; 1020 $digram{'y'}{'ck'} = ILLEGAL_PAIR; 1021 1022 $digram{'z'}{'a'} = ANY_COMBINATION; 1023 $digram{'z'}{'b'} = NOT_FRONT | BREAK | NOT_BACK; 1024 $digram{'z'}{'c'} = NOT_FRONT | BREAK | NOT_BACK; 1025 $digram{'z'}{'d'} = NOT_FRONT | BREAK | NOT_BACK; 1026 $digram{'z'}{'e'} = ANY_COMBINATION; 1027 $digram{'z'}{'f'} = NOT_FRONT | BREAK | NOT_BACK; 1028 $digram{'z'}{'g'} = NOT_FRONT | BREAK | NOT_BACK; 1029 $digram{'z'}{'h'} = NOT_FRONT | BREAK | NOT_BACK; 1030 $digram{'z'}{'i'} = ANY_COMBINATION; 1031 $digram{'z'}{'j'} = NOT_FRONT | BREAK | NOT_BACK; 1032 $digram{'z'}{'k'} = NOT_FRONT | BREAK | NOT_BACK; 1033 $digram{'z'}{'l'} = NOT_FRONT | BREAK | NOT_BACK; 1034 $digram{'z'}{'m'} = NOT_FRONT | BREAK | NOT_BACK; 1035 $digram{'z'}{'n'} = NOT_FRONT | BREAK | NOT_BACK; 1036 $digram{'z'}{'o'} = ANY_COMBINATION; 1037 $digram{'z'}{'p'} = NOT_FRONT | BREAK | NOT_BACK; 1038 $digram{'z'}{'r'} = NOT_FRONT | NOT_BACK; 1039 $digram{'z'}{'s'} = NOT_FRONT | BREAK | NOT_BACK; 1040 $digram{'z'}{'t'} = NOT_FRONT; 1041 $digram{'z'}{'u'} = ANY_COMBINATION; 1042 $digram{'z'}{'v'} = NOT_FRONT | BREAK | NOT_BACK; 1043 $digram{'z'}{'w'} = SUFFIX | NOT_BACK; 1044 $digram{'z'}{'x'} = ILLEGAL_PAIR; 1045 $digram{'z'}{'y'} = ANY_COMBINATION; 1046 $digram{'z'}{'z'} = NOT_FRONT; 1047 $digram{'z'}{'ch'} = NOT_FRONT | BREAK | NOT_BACK; 1048 $digram{'z'}{'gh'} = NOT_FRONT | BREAK | NOT_BACK; 1049 $digram{'z'}{'ph'} = NOT_FRONT | BREAK | NOT_BACK; 1050 $digram{'z'}{'rh'} = ILLEGAL_PAIR; 1051 $digram{'z'}{'sh'} = NOT_FRONT | BREAK | NOT_BACK; 1052 $digram{'z'}{'th'} = NOT_FRONT | BREAK | NOT_BACK; 1053 $digram{'z'}{'wh'} = ILLEGAL_PAIR; 1054 $digram{'z'}{'qu'} = NOT_FRONT | BREAK | NOT_BACK; 1055 $digram{'z'}{'ck'} = ILLEGAL_PAIR; 1056 1057 $digram{'ch'}{'a'} = ANY_COMBINATION; 1058 $digram{'ch'}{'b'} = NOT_FRONT | BREAK | NOT_BACK; 1059 $digram{'ch'}{'c'} = NOT_FRONT | BREAK | NOT_BACK; 1060 $digram{'ch'}{'d'} = NOT_FRONT | BREAK | NOT_BACK; 1061 $digram{'ch'}{'e'} = ANY_COMBINATION; 1062 $digram{'ch'}{'f'} = NOT_FRONT | BREAK | NOT_BACK; 1063 $digram{'ch'}{'g'} = NOT_FRONT | BREAK | NOT_BACK; 1064 $digram{'ch'}{'h'} = NOT_FRONT | BREAK | NOT_BACK; 1065 $digram{'ch'}{'i'} = ANY_COMBINATION; 1066 $digram{'ch'}{'j'} = NOT_FRONT | BREAK | NOT_BACK; 1067 $digram{'ch'}{'k'} = NOT_FRONT | BREAK | NOT_BACK; 1068 $digram{'ch'}{'l'} = NOT_FRONT | BREAK | NOT_BACK; 1069 $digram{'ch'}{'m'} = NOT_FRONT | BREAK | NOT_BACK; 1070 $digram{'ch'}{'n'} = NOT_FRONT | BREAK | NOT_BACK; 1071 $digram{'ch'}{'o'} = ANY_COMBINATION; 1072 $digram{'ch'}{'p'} = NOT_FRONT | BREAK | NOT_BACK; 1073 $digram{'ch'}{'r'} = NOT_BACK; 1074 $digram{'ch'}{'s'} = NOT_FRONT | BREAK | NOT_BACK; 1075 $digram{'ch'}{'t'} = NOT_FRONT | BREAK | NOT_BACK; 1076 $digram{'ch'}{'u'} = ANY_COMBINATION; 1077 $digram{'ch'}{'v'} = NOT_FRONT | BREAK | NOT_BACK; 1078 $digram{'ch'}{'w'} = NOT_FRONT | NOT_BACK; 1079 $digram{'ch'}{'x'} = ILLEGAL_PAIR; 1080 $digram{'ch'}{'y'} = ANY_COMBINATION; 1081 $digram{'ch'}{'z'} = NOT_FRONT | BREAK | NOT_BACK; 1082 $digram{'ch'}{'ch'} = ILLEGAL_PAIR; 1083 $digram{'ch'}{'gh'} = NOT_FRONT | BREAK | NOT_BACK; 1084 $digram{'ch'}{'ph'} = NOT_FRONT | BREAK | NOT_BACK; 1085 $digram{'ch'}{'rh'} = ILLEGAL_PAIR; 1086 $digram{'ch'}{'sh'} = NOT_FRONT | BREAK | NOT_BACK; 1087 $digram{'ch'}{'th'} = NOT_FRONT | BREAK | NOT_BACK; 1088 $digram{'ch'}{'wh'} = ILLEGAL_PAIR; 1089 $digram{'ch'}{'qu'} = NOT_FRONT | BREAK | NOT_BACK; 1090 $digram{'ch'}{'ck'} = ILLEGAL_PAIR; 1091 1092 $digram{'gh'}{'a'} = ANY_COMBINATION; 1093 $digram{'gh'}{'b'} = NOT_FRONT | BREAK | PREFIX | NOT_BACK; 1094 $digram{'gh'}{'c'} = NOT_FRONT | BREAK | PREFIX | NOT_BACK; 1095 $digram{'gh'}{'d'} = NOT_FRONT | BREAK | PREFIX | NOT_BACK; 1096 $digram{'gh'}{'e'} = ANY_COMBINATION; 1097 $digram{'gh'}{'f'} = NOT_FRONT | BREAK | PREFIX | NOT_BACK; 1098 $digram{'gh'}{'g'} = NOT_FRONT | BREAK | PREFIX | NOT_BACK; 1099 $digram{'gh'}{'h'} = NOT_FRONT | BREAK | PREFIX | NOT_BACK; 1100 $digram{'gh'}{'i'} = FRONT | NOT_BACK; 1101 $digram{'gh'}{'j'} = NOT_FRONT | BREAK | PREFIX | NOT_BACK; 1102 $digram{'gh'}{'k'} = NOT_FRONT | BREAK | PREFIX | NOT_BACK; 1103 $digram{'gh'}{'l'} = NOT_FRONT | BREAK | PREFIX | NOT_BACK; 1104 $digram{'gh'}{'m'} = NOT_FRONT | BREAK | PREFIX | NOT_BACK; 1105 $digram{'gh'}{'n'} = NOT_FRONT | BREAK | PREFIX | NOT_BACK; 1106 $digram{'gh'}{'o'} = FRONT | NOT_BACK; 1107 $digram{'gh'}{'p'} = NOT_FRONT | BREAK | NOT_BACK; 1108 $digram{'gh'}{'r'} = NOT_FRONT | BREAK | PREFIX | NOT_BACK; 1109 $digram{'gh'}{'s'} = NOT_FRONT | PREFIX; 1110 $digram{'gh'}{'t'} = NOT_FRONT | PREFIX; 1111 $digram{'gh'}{'u'} = NOT_FRONT | BREAK | PREFIX | NOT_BACK; 1112 $digram{'gh'}{'v'} = NOT_FRONT | BREAK | PREFIX | NOT_BACK; 1113 $digram{'gh'}{'w'} = NOT_FRONT | BREAK | PREFIX | NOT_BACK; 1114 $digram{'gh'}{'x'} = ILLEGAL_PAIR; 1115 $digram{'gh'}{'y'} = NOT_FRONT | BREAK | PREFIX | NOT_BACK; 1116 $digram{'gh'}{'z'} = NOT_FRONT | BREAK | PREFIX | NOT_BACK; 1117 $digram{'gh'}{'ch'} = NOT_FRONT | BREAK | PREFIX | NOT_BACK; 1118 $digram{'gh'}{'gh'} = ILLEGAL_PAIR; 1119 $digram{'gh'}{'ph'} = NOT_FRONT | BREAK | PREFIX | NOT_BACK; 1120 $digram{'gh'}{'rh'} = ILLEGAL_PAIR; 1121 $digram{'gh'}{'sh'} = NOT_FRONT | BREAK | PREFIX | NOT_BACK; 1122 $digram{'gh'}{'th'} = NOT_FRONT | BREAK | PREFIX | NOT_BACK; 1123 $digram{'gh'}{'wh'} = ILLEGAL_PAIR; 1124 $digram{'gh'}{'qu'} = NOT_FRONT | BREAK | PREFIX | NOT_BACK; 1125 $digram{'gh'}{'ck'} = ILLEGAL_PAIR; 1126 1127 $digram{'ph'}{'a'} = ANY_COMBINATION; 1128 $digram{'ph'}{'b'} = NOT_FRONT | BREAK | NOT_BACK; 1129 $digram{'ph'}{'c'} = NOT_FRONT | BREAK | NOT_BACK; 1130 $digram{'ph'}{'d'} = NOT_FRONT | BREAK | NOT_BACK; 1131 $digram{'ph'}{'e'} = ANY_COMBINATION; 1132 $digram{'ph'}{'f'} = NOT_FRONT | BREAK | NOT_BACK; 1133 $digram{'ph'}{'g'} = NOT_FRONT | BREAK | NOT_BACK; 1134 $digram{'ph'}{'h'} = NOT_FRONT | BREAK | NOT_BACK; 1135 $digram{'ph'}{'i'} = ANY_COMBINATION; 1136 $digram{'ph'}{'j'} = NOT_FRONT | BREAK | NOT_BACK; 1137 $digram{'ph'}{'k'} = NOT_FRONT | BREAK | NOT_BACK; 1138 $digram{'ph'}{'l'} = FRONT | SUFFIX | NOT_BACK; 1139 $digram{'ph'}{'m'} = NOT_FRONT | BREAK | NOT_BACK; 1140 $digram{'ph'}{'n'} = NOT_FRONT | BREAK | NOT_BACK; 1141 $digram{'ph'}{'o'} = ANY_COMBINATION; 1142 $digram{'ph'}{'p'} = NOT_FRONT | BREAK | NOT_BACK; 1143 $digram{'ph'}{'r'} = NOT_BACK; 1144 $digram{'ph'}{'s'} = NOT_FRONT; 1145 $digram{'ph'}{'t'} = NOT_FRONT; 1146 $digram{'ph'}{'u'} = ANY_COMBINATION; 1147 $digram{'ph'}{'v'} = NOT_FRONT | NOT_BACK; 1148 $digram{'ph'}{'w'} = NOT_FRONT | NOT_BACK; 1149 $digram{'ph'}{'x'} = ILLEGAL_PAIR; 1150 $digram{'ph'}{'y'} = NOT_FRONT; 1151 $digram{'ph'}{'z'} = NOT_FRONT | BREAK | NOT_BACK; 1152 $digram{'ph'}{'ch'} = NOT_FRONT | BREAK | NOT_BACK; 1153 $digram{'ph'}{'gh'} = NOT_FRONT | BREAK | NOT_BACK; 1154 $digram{'ph'}{'ph'} = ILLEGAL_PAIR; 1155 $digram{'ph'}{'rh'} = ILLEGAL_PAIR; 1156 $digram{'ph'}{'sh'} = NOT_FRONT | BREAK | NOT_BACK; 1157 $digram{'ph'}{'th'} = NOT_FRONT | BREAK | NOT_BACK; 1158 $digram{'ph'}{'wh'} = ILLEGAL_PAIR; 1159 $digram{'ph'}{'qu'} = NOT_FRONT | BREAK | NOT_BACK; 1160 $digram{'ph'}{'ck'} = ILLEGAL_PAIR; 1161 1162 $digram{'rh'}{'a'} = FRONT | NOT_BACK; 1163 $digram{'rh'}{'b'} = ILLEGAL_PAIR; 1164 $digram{'rh'}{'c'} = ILLEGAL_PAIR; 1165 $digram{'rh'}{'d'} = ILLEGAL_PAIR; 1166 $digram{'rh'}{'e'} = FRONT | NOT_BACK; 1167 $digram{'rh'}{'f'} = ILLEGAL_PAIR; 1168 $digram{'rh'}{'g'} = ILLEGAL_PAIR; 1169 $digram{'rh'}{'h'} = ILLEGAL_PAIR; 1170 $digram{'rh'}{'i'} = FRONT | NOT_BACK; 1171 $digram{'rh'}{'j'} = ILLEGAL_PAIR; 1172 $digram{'rh'}{'k'} = ILLEGAL_PAIR; 1173 $digram{'rh'}{'l'} = ILLEGAL_PAIR; 1174 $digram{'rh'}{'m'} = ILLEGAL_PAIR; 1175 $digram{'rh'}{'n'} = ILLEGAL_PAIR; 1176 $digram{'rh'}{'o'} = FRONT | NOT_BACK; 1177 $digram{'rh'}{'p'} = ILLEGAL_PAIR; 1178 $digram{'rh'}{'r'} = ILLEGAL_PAIR; 1179 $digram{'rh'}{'s'} = ILLEGAL_PAIR; 1180 $digram{'rh'}{'t'} = ILLEGAL_PAIR; 1181 $digram{'rh'}{'u'} = FRONT | NOT_BACK; 1182 $digram{'rh'}{'v'} = ILLEGAL_PAIR; 1183 $digram{'rh'}{'w'} = ILLEGAL_PAIR; 1184 $digram{'rh'}{'x'} = ILLEGAL_PAIR; 1185 $digram{'rh'}{'y'} = FRONT | NOT_BACK; 1186 $digram{'rh'}{'z'} = ILLEGAL_PAIR; 1187 $digram{'rh'}{'ch'} = ILLEGAL_PAIR; 1188 $digram{'rh'}{'gh'} = ILLEGAL_PAIR; 1189 $digram{'rh'}{'ph'} = ILLEGAL_PAIR; 1190 $digram{'rh'}{'rh'} = ILLEGAL_PAIR; 1191 $digram{'rh'}{'sh'} = ILLEGAL_PAIR; 1192 $digram{'rh'}{'th'} = ILLEGAL_PAIR; 1193 $digram{'rh'}{'wh'} = ILLEGAL_PAIR; 1194 $digram{'rh'}{'qu'} = ILLEGAL_PAIR; 1195 $digram{'rh'}{'ck'} = ILLEGAL_PAIR; 1196 1197 $digram{'sh'}{'a'} = ANY_COMBINATION; 1198 $digram{'sh'}{'b'} = NOT_FRONT | BREAK | NOT_BACK; 1199 $digram{'sh'}{'c'} = NOT_FRONT | BREAK | NOT_BACK; 1200 $digram{'sh'}{'d'} = NOT_FRONT | BREAK | NOT_BACK; 1201 $digram{'sh'}{'e'} = ANY_COMBINATION; 1202 $digram{'sh'}{'f'} = NOT_FRONT | BREAK | NOT_BACK; 1203 $digram{'sh'}{'g'} = NOT_FRONT | BREAK | NOT_BACK; 1204 $digram{'sh'}{'h'} = ILLEGAL_PAIR; 1205 $digram{'sh'}{'i'} = ANY_COMBINATION; 1206 $digram{'sh'}{'j'} = NOT_FRONT | BREAK | NOT_BACK; 1207 $digram{'sh'}{'k'} = NOT_FRONT; 1208 $digram{'sh'}{'l'} = FRONT | SUFFIX | NOT_BACK; 1209 $digram{'sh'}{'m'} = FRONT | SUFFIX | NOT_BACK; 1210 $digram{'sh'}{'n'} = FRONT | SUFFIX | NOT_BACK; 1211 $digram{'sh'}{'o'} = ANY_COMBINATION; 1212 $digram{'sh'}{'p'} = NOT_FRONT; 1213 $digram{'sh'}{'r'} = FRONT | SUFFIX | NOT_BACK; 1214 $digram{'sh'}{'s'} = NOT_FRONT | BREAK | NOT_BACK; 1215 $digram{'sh'}{'t'} = SUFFIX; 1216 $digram{'sh'}{'u'} = ANY_COMBINATION; 1217 $digram{'sh'}{'v'} = NOT_FRONT | BREAK | NOT_BACK; 1218 $digram{'sh'}{'w'} = SUFFIX | NOT_BACK; 1219 $digram{'sh'}{'x'} = ILLEGAL_PAIR; 1220 $digram{'sh'}{'y'} = ANY_COMBINATION; 1221 $digram{'sh'}{'z'} = NOT_FRONT | BREAK | NOT_BACK; 1222 $digram{'sh'}{'ch'} = NOT_FRONT | BREAK | NOT_BACK; 1223 $digram{'sh'}{'gh'} = NOT_FRONT | BREAK | NOT_BACK; 1224 $digram{'sh'}{'ph'} = NOT_FRONT | BREAK | NOT_BACK; 1225 $digram{'sh'}{'rh'} = ILLEGAL_PAIR; 1226 $digram{'sh'}{'sh'} = ILLEGAL_PAIR; 1227 $digram{'sh'}{'th'} = NOT_FRONT | BREAK | NOT_BACK; 1228 $digram{'sh'}{'wh'} = ILLEGAL_PAIR; 1229 $digram{'sh'}{'qu'} = NOT_FRONT | BREAK | NOT_BACK; 1230 $digram{'sh'}{'ck'} = ILLEGAL_PAIR; 1231 1232 $digram{'th'}{'a'} = ANY_COMBINATION; 1233 $digram{'th'}{'b'} = NOT_FRONT | BREAK | NOT_BACK; 1234 $digram{'th'}{'c'} = NOT_FRONT | BREAK | NOT_BACK; 1235 $digram{'th'}{'d'} = NOT_FRONT | BREAK | NOT_BACK; 1236 $digram{'th'}{'e'} = ANY_COMBINATION; 1237 $digram{'th'}{'f'} = NOT_FRONT | BREAK | NOT_BACK; 1238 $digram{'th'}{'g'} = NOT_FRONT | BREAK | NOT_BACK; 1239 $digram{'th'}{'h'} = NOT_FRONT | BREAK | NOT_BACK; 1240 $digram{'th'}{'i'} = ANY_COMBINATION; 1241 $digram{'th'}{'j'} = NOT_FRONT | BREAK | NOT_BACK; 1242 $digram{'th'}{'k'} = NOT_FRONT | BREAK | NOT_BACK; 1243 $digram{'th'}{'l'} = NOT_FRONT | BREAK | NOT_BACK; 1244 $digram{'th'}{'m'} = NOT_FRONT | BREAK | NOT_BACK; 1245 $digram{'th'}{'n'} = NOT_FRONT | BREAK | NOT_BACK; 1246 $digram{'th'}{'o'} = ANY_COMBINATION; 1247 $digram{'th'}{'p'} = NOT_FRONT | BREAK | NOT_BACK; 1248 $digram{'th'}{'r'} = NOT_BACK; 1249 $digram{'th'}{'s'} = NOT_FRONT | BACK; 1250 $digram{'th'}{'t'} = NOT_FRONT | BREAK | NOT_BACK; 1251 $digram{'th'}{'u'} = ANY_COMBINATION; 1252 $digram{'th'}{'v'} = NOT_FRONT | BREAK | NOT_BACK; 1253 $digram{'th'}{'w'} = SUFFIX | NOT_BACK; 1254 $digram{'th'}{'x'} = ILLEGAL_PAIR; 1255 $digram{'th'}{'y'} = ANY_COMBINATION; 1256 $digram{'th'}{'z'} = NOT_FRONT | BREAK | NOT_BACK; 1257 $digram{'th'}{'ch'} = NOT_FRONT | BREAK | NOT_BACK; 1258 $digram{'th'}{'gh'} = NOT_FRONT | BREAK | NOT_BACK; 1259 $digram{'th'}{'ph'} = NOT_FRONT | BREAK | NOT_BACK; 1260 $digram{'th'}{'rh'} = ILLEGAL_PAIR; 1261 $digram{'th'}{'sh'} = NOT_FRONT | BREAK | NOT_BACK; 1262 $digram{'th'}{'th'} = ILLEGAL_PAIR; 1263 $digram{'th'}{'wh'} = ILLEGAL_PAIR; 1264 $digram{'th'}{'qu'} = NOT_FRONT | BREAK | NOT_BACK; 1265 $digram{'th'}{'ck'} = ILLEGAL_PAIR; 1266 1267 $digram{'wh'}{'a'} = FRONT | NOT_BACK; 1268 $digram{'wh'}{'b'} = ILLEGAL_PAIR; 1269 $digram{'wh'}{'c'} = ILLEGAL_PAIR; 1270 $digram{'wh'}{'d'} = ILLEGAL_PAIR; 1271 $digram{'wh'}{'e'} = FRONT | NOT_BACK; 1272 $digram{'wh'}{'f'} = ILLEGAL_PAIR; 1273 $digram{'wh'}{'g'} = ILLEGAL_PAIR; 1274 $digram{'wh'}{'h'} = ILLEGAL_PAIR; 1275 $digram{'wh'}{'i'} = FRONT | NOT_BACK; 1276 $digram{'wh'}{'j'} = ILLEGAL_PAIR; 1277 $digram{'wh'}{'k'} = ILLEGAL_PAIR; 1278 $digram{'wh'}{'l'} = ILLEGAL_PAIR; 1279 $digram{'wh'}{'m'} = ILLEGAL_PAIR; 1280 $digram{'wh'}{'n'} = ILLEGAL_PAIR; 1281 $digram{'wh'}{'o'} = FRONT | NOT_BACK; 1282 $digram{'wh'}{'p'} = ILLEGAL_PAIR; 1283 $digram{'wh'}{'r'} = ILLEGAL_PAIR; 1284 $digram{'wh'}{'s'} = ILLEGAL_PAIR; 1285 $digram{'wh'}{'t'} = ILLEGAL_PAIR; 1286 $digram{'wh'}{'u'} = ILLEGAL_PAIR; 1287 $digram{'wh'}{'v'} = ILLEGAL_PAIR; 1288 $digram{'wh'}{'w'} = ILLEGAL_PAIR; 1289 $digram{'wh'}{'x'} = ILLEGAL_PAIR; 1290 $digram{'wh'}{'y'} = FRONT | NOT_BACK; 1291 $digram{'wh'}{'z'} = ILLEGAL_PAIR; 1292 $digram{'wh'}{'ch'} = ILLEGAL_PAIR; 1293 $digram{'wh'}{'gh'} = ILLEGAL_PAIR; 1294 $digram{'wh'}{'ph'} = ILLEGAL_PAIR; 1295 $digram{'wh'}{'rh'} = ILLEGAL_PAIR; 1296 $digram{'wh'}{'sh'} = ILLEGAL_PAIR; 1297 $digram{'wh'}{'th'} = ILLEGAL_PAIR; 1298 $digram{'wh'}{'wh'} = ILLEGAL_PAIR; 1299 $digram{'wh'}{'qu'} = ILLEGAL_PAIR; 1300 $digram{'wh'}{'ck'} = ILLEGAL_PAIR; 1301 1302 $digram{'qu'}{'a'} = ANY_COMBINATION; 1303 $digram{'qu'}{'b'} = ILLEGAL_PAIR; 1304 $digram{'qu'}{'c'} = ILLEGAL_PAIR; 1305 $digram{'qu'}{'d'} = ILLEGAL_PAIR; 1306 $digram{'qu'}{'e'} = ANY_COMBINATION; 1307 $digram{'qu'}{'f'} = ILLEGAL_PAIR; 1308 $digram{'qu'}{'g'} = ILLEGAL_PAIR; 1309 $digram{'qu'}{'h'} = ILLEGAL_PAIR; 1310 $digram{'qu'}{'i'} = ANY_COMBINATION; 1311 $digram{'qu'}{'j'} = ILLEGAL_PAIR; 1312 $digram{'qu'}{'k'} = ILLEGAL_PAIR; 1313 $digram{'qu'}{'l'} = ILLEGAL_PAIR; 1314 $digram{'qu'}{'m'} = ILLEGAL_PAIR; 1315 $digram{'qu'}{'n'} = ILLEGAL_PAIR; 1316 $digram{'qu'}{'o'} = ANY_COMBINATION; 1317 $digram{'qu'}{'p'} = ILLEGAL_PAIR; 1318 $digram{'qu'}{'r'} = ILLEGAL_PAIR; 1319 $digram{'qu'}{'s'} = ILLEGAL_PAIR; 1320 $digram{'qu'}{'t'} = ILLEGAL_PAIR; 1321 $digram{'qu'}{'u'} = ILLEGAL_PAIR; 1322 $digram{'qu'}{'v'} = ILLEGAL_PAIR; 1323 $digram{'qu'}{'w'} = ILLEGAL_PAIR; 1324 $digram{'qu'}{'x'} = ILLEGAL_PAIR; 1325 $digram{'qu'}{'y'} = ILLEGAL_PAIR; 1326 $digram{'qu'}{'z'} = ILLEGAL_PAIR; 1327 $digram{'qu'}{'ch'} = ILLEGAL_PAIR; 1328 $digram{'qu'}{'gh'} = ILLEGAL_PAIR; 1329 $digram{'qu'}{'ph'} = ILLEGAL_PAIR; 1330 $digram{'qu'}{'rh'} = ILLEGAL_PAIR; 1331 $digram{'qu'}{'sh'} = ILLEGAL_PAIR; 1332 $digram{'qu'}{'th'} = ILLEGAL_PAIR; 1333 $digram{'qu'}{'wh'} = ILLEGAL_PAIR; 1334 $digram{'qu'}{'qu'} = ILLEGAL_PAIR; 1335 $digram{'qu'}{'ck'} = ILLEGAL_PAIR; 1336 1337 $digram{'ck'}{'a'} = NOT_FRONT | BREAK | NOT_BACK; 1338 $digram{'ck'}{'b'} = NOT_FRONT | BREAK | NOT_BACK; 1339 $digram{'ck'}{'c'} = NOT_FRONT | BREAK | NOT_BACK; 1340 $digram{'ck'}{'d'} = NOT_FRONT | BREAK | NOT_BACK; 1341 $digram{'ck'}{'e'} = NOT_FRONT | BREAK | NOT_BACK; 1342 $digram{'ck'}{'f'} = NOT_FRONT | BREAK | NOT_BACK; 1343 $digram{'ck'}{'g'} = NOT_FRONT | BREAK | NOT_BACK; 1344 $digram{'ck'}{'h'} = NOT_FRONT | BREAK | NOT_BACK; 1345 $digram{'ck'}{'i'} = NOT_FRONT | BREAK | NOT_BACK; 1346 $digram{'ck'}{'j'} = NOT_FRONT | BREAK | NOT_BACK; 1347 $digram{'ck'}{'k'} = NOT_FRONT | BREAK | NOT_BACK; 1348 $digram{'ck'}{'l'} = NOT_FRONT | BREAK | NOT_BACK; 1349 $digram{'ck'}{'m'} = NOT_FRONT | BREAK | NOT_BACK; 1350 $digram{'ck'}{'n'} = NOT_FRONT | BREAK | NOT_BACK; 1351 $digram{'ck'}{'o'} = NOT_FRONT | BREAK | NOT_BACK; 1352 $digram{'ck'}{'p'} = NOT_FRONT | BREAK | NOT_BACK; 1353 $digram{'ck'}{'r'} = NOT_FRONT | BREAK | NOT_BACK; 1354 $digram{'ck'}{'s'} = NOT_FRONT; 1355 $digram{'ck'}{'t'} = NOT_FRONT | BREAK | NOT_BACK; 1356 $digram{'ck'}{'u'} = NOT_FRONT | BREAK | NOT_BACK; 1357 $digram{'ck'}{'v'} = NOT_FRONT | BREAK | NOT_BACK; 1358 $digram{'ck'}{'w'} = NOT_FRONT | BREAK | NOT_BACK; 1359 $digram{'ck'}{'x'} = ILLEGAL_PAIR; 1360 $digram{'ck'}{'y'} = NOT_FRONT; 1361 $digram{'ck'}{'z'} = NOT_FRONT | BREAK | NOT_BACK; 1362 $digram{'ck'}{'ch'} = NOT_FRONT | BREAK | NOT_BACK; 1363 $digram{'ck'}{'gh'} = NOT_FRONT | BREAK | NOT_BACK; 1364 $digram{'ck'}{'ph'} = NOT_FRONT | BREAK | NOT_BACK; 1365 $digram{'ck'}{'rh'} = ILLEGAL_PAIR; 1366 $digram{'ck'}{'sh'} = NOT_FRONT | BREAK | NOT_BACK; 1367 $digram{'ck'}{'th'} = NOT_FRONT | BREAK | NOT_BACK; 1368 $digram{'ck'}{'wh'} = ILLEGAL_PAIR; 1369 $digram{'ck'}{'qu'} = NOT_FRONT | BREAK | NOT_BACK; 1370 $digram{'ck'}{'ck'} = ILLEGAL_PAIR; 1371 1372 ############################################################################################## 1373 # } END DIGRAM 1374 ############################################################################################## 1375 1376 1377 1378sub report(@) { 1379 $main::DEBUG and print @_; 1380} 1381 1382 1383 1384 1385=head2 word 1386 1387 word = word( minlen, maxlen ); 1388 ( word, hyphenated_form ) = word( minlen, maxlen ); 1389 1390Generates a random word, as well as its hyphenated form. 1391The length of the returned word will be between minlen and maxlen. 1392 1393=cut 1394 1395sub word($$) { 1396 @_ > 2 and shift; 1397 my( $minlen, $maxlen ) = @_; 1398 1399 $minlen <= $maxlen or die "minlen $minlen is greater than maxlen $maxlen"; 1400 1401 init(); 1402 1403 # 1404 # Check for zero length words. This is technically not an error, 1405 # so we take the short cut and return empty words. 1406 # 1407 $maxlen or return wantarray ? ('','') : ''; 1408 1409 my( $word, $hyphenated_word ); 1410 1411 for ( my $try = 1 ; $try <= MAX_UNACCEPTABLE and not defined $word; $try++ ) { 1412 ( $word, $hyphenated_word ) = _random_word( rand_int_in_range( $minlen, $maxlen ) ); 1413 $word = restrict( $word ); 1414 } 1415 1416 $word or die "failed to generate an acceptable random password.\n"; 1417 1418 return wantarray ? ( $word, $hyphenated_word ) : $word; 1419} 1420 1421 1422=head2 letters 1423 1424 word = letters( minlen, maxlen ); 1425 1426Generates a string of random letters. 1427The length of the returned word is between minlen and maxlen. 1428Calls C<random_chars_in_range( 'a' => 'z' )>. 1429 1430=cut 1431 1432sub letters($$) { 1433 @_ > 2 and shift; 1434 my( $minlen, $maxlen ) = @_; 1435 random_chars_in_range( $minlen, $maxlen, 'a' => 'z' ); # range of lowercase letters in ASCII 1436} 1437 1438 1439=head2 chars 1440 1441 word = chars( minlen, maxlen ); 1442 1443Generates a string of random printable characters. 1444The length of the returned word is between minlen and maxlen. 1445Calls C<random_chars_in_range( '!' => '~' )>. 1446 1447=cut 1448 1449sub chars($$) { 1450 @_ > 2 and shift; 1451 my( $minlen, $maxlen ) = @_; 1452 random_chars_in_range( $minlen, $maxlen, '!' => '~' ); # range of printable chars in ASCII 1453} 1454 1455 1456 1457=head2 random_chars_in_range 1458 1459 word = random_chars_in_range( minlen, maxlen, lo_char => hi_char ); 1460 1461Generates a string of printable characters. 1462The length of the returned string is between minlen and maxlen. 1463Each character is selected from the range of ASCII characters 1464delimited by (lo_char,hi_char). 1465 1466=cut 1467 1468sub random_chars_in_range($$$$) { 1469 @_ > 4 and shift; 1470 my( $minlen, $maxlen, $lo_char, $hi_char ) = @_; 1471 1472 $minlen <= $maxlen or die "minlen $minlen is greater than maxlen $maxlen"; 1473 1474 init(); 1475 1476 my $string_size = rand_int_in_range( $minlen, $maxlen ); 1477 1478 my $string; 1479 for ( my $try = 1 ; $try <= MAX_UNACCEPTABLE and not defined $string; $try++ ) { 1480 my $s = ''; 1481 while ( length($s) < $string_size ) { 1482 $s .= chr( rand_int_in_range( ord($lo_char), ord($hi_char) ) ); 1483 } 1484 next if length($s) > $string_size; 1485 $string = restrict( $s ); 1486 } 1487 1488 $string 1489} 1490 1491 1492 1493=head2 rand_int_in_range 1494 1495 n = rand_int_in_range( min, max ); 1496 1497Returns an integer between min and max, inclusive. 1498Calls C<rng> like so: 1499 1500 n = min + int( rng( max - min + 1 ) ) 1501 1502=cut 1503 1504sub rand_int_in_range($$) { 1505 my( $min, $max ) = @_; 1506 $min + int( rng( $max - $min + 1 ) ) 1507} 1508 1509 1510=head2 random_element 1511 1512 e = random_element( \@elts ) 1513 1514Selects a random element from an array, which is passed by ref. 1515 1516=cut 1517 1518sub random_element($) { 1519 my $ar = shift; 1520 $ar->[ rand_int_in_range( 0, $#{$ar} ) ] 1521} 1522 1523 1524 1525=head2 rng 1526 1527 r = rng( n ); 1528 1529C<rng> is designed to have the same interface as the built-in C<rand> function. 1530The default implementation here is a simple wrapper around C<rand>, 1531which is typically a wrapper for some pseudo-random number function in the 1532underlying C library. 1533 1534The reason for having this simple wrapper is so the user can 1535easily substitute a different random number generator if desired. 1536Since many rng's have the same interface as C<rand>, replacing C<rng()> 1537is as simple as 1538 1539 { 1540 local $^W; # squelch sub redef warning. 1541 *Crypt::RandPasswd::rng = \&my_rng; 1542 } 1543 1544See L<rand>. 1545 1546=cut 1547 1548sub rng($) { 1549 my $x = shift; 1550 rand($x) 1551} 1552 1553 1554 1555=head2 restrict 1556 1557 word = restrict( word ); 1558 1559A filter. Returns the arg unchanged if it is allowable; returns undef if not. 1560 1561The default version of C<restrict()> allows everything. 1562You may install a different form to implement other restrictions, 1563by doing something like this: 1564 1565 { 1566 local $^W; # squelch sub redef warning. 1567 *Crypt::RandPasswd::restrict = \&my_filter; 1568 } 1569 1570=cut 1571 1572sub restrict($) { $_[0] } # MUST return a real scalar; returning @_ causes scalar(@_) !!! 1573 1574 1575=head2 init 1576 1577This initializes the environment, which by default simply seeds the random number generator. 1578 1579=cut 1580 1581# can be called multiple times without harm, since it remembers whether 1582# it has already been called. 1583 1584sub init() { 1585 unless ( $Crypt::RandPasswd::initialized ) { 1586 # only do stuff if I haven't already been called before. 1587 1588 $Crypt::RandPasswd::initialized = 1; 1589 if ( defined $Crypt::RandPasswd::seed ) { 1590 srand( $Crypt::RandPasswd::seed ); 1591 } 1592 else { 1593 srand; # use default, which can be pretty good. 1594 } 1595 } 1596} 1597 1598 1599 1600 1601=head2 _random_word 1602 1603This is the routine that returns a random word. 1604It collects random syllables until a predetermined word length is found. 1605If a retry threshold is reached, another word is tried. 1606 1607returns ( word, hyphenated_word ). 1608 1609=cut 1610 1611sub _random_word($) { 1612 my( $pwlen ) = @_; 1613 1614 my $word = ''; 1615 my @word_syllables; 1616 1617 my $max_retries = ( 4 * $pwlen ) + scalar( @grams ); 1618 1619 my $tries = 0; # count of retries. 1620 1621 1622 # @word_units used to be an array of indices into the 'rules' C-array. 1623 # now it's an array of actual units (grams). 1624 my @word_units; 1625 1626 # 1627 # Find syllables until the entire word is constructed. 1628 # 1629 while ( length($word) < $pwlen ) { 1630 # 1631 # Get the syllable and find its length. 1632 # 1633 report "About to call get_syllable( $pwlen - length($word) )\n"; 1634 my( $new_syllable, @syllable_units ) = get_syllable( $pwlen - length($word) ); 1635 report "get_syllable returned ( $new_syllable; @syllable_units )\n"; 1636 1637 # 1638 # If the word has been improperly formed, throw out 1639 # the syllable. The checks performed here are those 1640 # that must be formed on a word basis. The other 1641 # tests are performed entirely within the syllable. 1642 # Otherwise, append the syllable to the word. 1643 # 1644 unless ( 1645 _improper_word( @word_units, @syllable_units ) # join the arrays 1646 || 1647 ( 1648 $word eq '' 1649 and 1650 _have_initial_y( @syllable_units ) 1651 ) 1652 || 1653 ( 1654 length( $word . $new_syllable ) == $pwlen 1655 and 1656 _have_final_split( @syllable_units ) 1657 ) 1658 ) { 1659 $word .= $new_syllable; 1660 push @word_syllables, $new_syllable; 1661 } 1662 1663 # 1664 # Keep track of the times we have tried to get syllables. 1665 # If we have exceeded the threshold, start from scratch. 1666 # 1667 $tries++; 1668 if ( $tries > $max_retries ) { 1669 $tries = 0; 1670 $word = ''; 1671 @word_syllables = (); 1672 @word_units = (); 1673 } 1674 } 1675 1676 return( $word, join('-',@word_syllables) ); 1677} 1678 1679 1680 1681=head2 _random_unit 1682 1683Selects a gram (aka "unit"). 1684This is the standard random unit generating routine for get_syllable(). 1685 1686This routine attempts to return grams (units) with a distribution 1687approaching that of the distribution of the units in English. 1688 1689The distribution of the units may be altered in this procedure without 1690affecting the digram table or any other programs using the random_word subroutine, 1691as long as the set of grams (units) is kept consistent throughout this library. 1692 1693I<NOTE that where this func used to return a numeric index into 1694the 'rules' C-array, it now returns a gram.> 1695 1696=cut 1697 1698my %occurrence_frequencies = ( 1699 'a' => 10, 'b' => 8, 'c' => 12, 'd' => 12, 1700 'e' => 12, 'f' => 8, 'g' => 8, 'h' => 6, 1701 'i' => 10, 'j' => 8, 'k' => 8, 'l' => 6, 1702 'm' => 6, 'n' => 10, 'o' => 10, 'p' => 6, 1703 'r' => 10, 's' => 8, 't' => 10, 'u' => 6, 1704 'v' => 8, 'w' => 8, 'x' => 1, 'y' => 8, 1705 'z' => 1, 'ch' => 1, 'gh' => 1, 'ph' => 1, 1706 'rh' => 1, 'sh' => 2, 'th' => 1, 'wh' => 1, 1707 'qu' => 1, 'ck' => 1, 1708); 1709 1710my @numbers = map { 1711 ( ($_) x $occurrence_frequencies{$_} ) 1712} @grams; 1713 1714my @vowel_numbers = map { 1715 ( ($_) x $occurrence_frequencies{$_} ) 1716} @vowel_grams; 1717 1718 1719 1720sub _random_unit($) { 1721 my $type = shift; # byte 1722 1723 random_element( $type & VOWEL 1724 ? \@vowel_numbers # Sometimes, we are asked to explicitly get a vowel (i.e., if 1725 # a digram pair expects one following it). This is a shortcut 1726 # to do that and avoid looping with rejected consonants. 1727 1728 : \@numbers # Get any letter according to the English distribution. 1729 ) 1730} 1731 1732 1733 1734 1735=head2 _improper_word 1736 1737Check that the word does not contain illegal combinations 1738that may span syllables. Specifically, these are: 1739 1740 1. An illegal pair of units between syllables. 1741 2. Three consecutive vowel units. 1742 3. Three consecutive consonant units. 1743 1744The checks are made against units (1 or 2 letters), not against 1745the individual letters, so three consecutive units can have 1746the length of 6 at most. 1747 1748returns boolean 1749 1750=cut 1751 1752sub _improper_word(@) { 1753 my @units = @_; 1754 1755 my $failure; # bool, init False. 1756 1757 for my $unit_count ( 0 .. $#units ) { 1758 # 1759 # Check for ILLEGAL_PAIR. 1760 # This should have been caught for units within a syllable, 1761 # but in some cases it would have gone unnoticed for units between syllables 1762 # (e.g., when saved units in get_syllable() were not used). 1763 # 1764 $unit_count > 0 1765 and $digram{$units[$unit_count-1]}{$units[$unit_count]} & ILLEGAL_PAIR 1766 and return(1); # Failure! 1767 1768 next if $unit_count < 2; 1769 # 1770 # Check for consecutive vowels or consonants. 1771 # Because the initial y of a syllable is treated as a consonant rather 1772 # than as a vowel, we exclude y from the first vowel in the vowel test. 1773 # The only problem comes when y ends a syllable and two other vowels start the next, like fly-oint. 1774 # Since such words are still pronounceable, we accept this. 1775 # 1776 # 1777 # Vowel check. 1778 # 1779 ( 1780 ($rules{$units[$unit_count - 2]} & VOWEL) 1781 && 1782 !($rules{$units[$unit_count - 2]} & ALTERNATE_VOWEL) 1783 && 1784 ($rules{$units[$unit_count - 1]} & VOWEL) 1785 && 1786 ($rules{$units[$unit_count ]} & VOWEL) 1787 ) 1788 || 1789 # 1790 # Consonant check. 1791 # 1792 ( 1793 !($rules{$units[$unit_count - 2]} & VOWEL) 1794 && 1795 !($rules{$units[$unit_count - 1]} & VOWEL) 1796 && 1797 !($rules{$units[$unit_count ]} & VOWEL) 1798 ) 1799 and return(1); # Failure! 1800 } 1801 1802 0 # success 1803} 1804 1805 1806=head2 _have_initial_y 1807 1808Treating y as a vowel is sometimes a problem. Some words get formed that look irregular. 1809One special group is when y starts a word and is the only vowel in the first syllable. 1810The word ycl is one example. We discard words like these. 1811 1812return boolean 1813 1814=cut 1815 1816sub _have_initial_y(@) { 1817 my @units = @_; 1818 1819 my $vowel_count = 0; 1820 my $normal_vowel_count = 0; 1821 1822 for my $unit_count ( 0 .. $#units ) { 1823 # 1824 # Count vowels. 1825 # 1826 if ( $rules{$units[$unit_count]} & VOWEL ) { 1827 $vowel_count++; 1828 1829 # 1830 # Count the vowels that are not: 1831 # 1. 'y' 1832 # 2. at the start of the word. 1833 # 1834 if ( !($rules{$units[$unit_count]} & ALTERNATE_VOWEL) || ($unit_count > 0) ) { 1835 $normal_vowel_count++; 1836 } 1837 } 1838 } 1839 1840 ($vowel_count <= 1) && ($normal_vowel_count == 0) 1841} 1842 1843 1844=head2 _have_final_split 1845 1846Besides the problem with the letter y, there is one with 1847a silent e at the end of words, like face or nice. 1848We allow this silent e, but we do not allow it as the only 1849vowel at the end of the word or syllables like ble will 1850be generated. 1851 1852returns boolean 1853 1854=cut 1855 1856sub _have_final_split(@) { 1857 my @units = @_; 1858 1859 my $vowel_count = 0; 1860 1861 # 1862 # Count all the vowels in the word. 1863 # 1864 for my $unit_count ( 0 .. $#units ) { 1865 if ( $rules{$units[$unit_count]} & VOWEL ) { 1866 $vowel_count++; 1867 } 1868 } 1869 1870 # 1871 # Return TRUE iff the only vowel was e, found at the end if the word. 1872 # 1873 ($vowel_count == 1) && ( $rules{$units[$#units]} & NO_FINAL_SPLIT ) 1874} 1875 1876 1877=head2 get_syllable 1878 1879Generate next unit to password, making sure that it follows these rules: 1880 18811. Each syllable must contain exactly 1 or 2 consecutive vowels, where y is considered a vowel. 1882 18832. Syllable end is determined as follows: 1884 1885 a. Vowel is generated and previous unit is a consonant and syllable already has a vowel. 1886 In this case, new syllable is started and already contains a vowel. 1887 b. A pair determined to be a "break" pair is encountered. 1888 In this case new syllable is started with second unit of this pair. 1889 c. End of password is encountered. 1890 d. "begin" pair is encountered legally. New syllable is started with this pair. 1891 e. "end" pair is legally encountered. New syllable has nothing yet. 1892 18933. Try generating another unit if: 1894 1895 a. third consecutive vowel and not y. 1896 b. "break" pair generated but no vowel yet in current or previous 2 units are "not_end". 1897 c. "begin" pair generated but no vowel in syllable preceding begin pair, 1898 or both previous 2 pairs are designated "not_end". 1899 d. "end" pair generated but no vowel in current syllable or in "end" pair. 1900 e. "not_begin" pair generated but new syllable must begin (because previous syllable ended as defined in 2 above). 1901 f. vowel is generated and 2a is satisfied, but no syllable break is possible in previous 3 pairs. 1902 g. Second and third units of syllable must begin, and first unit is "alternate_vowel". 1903 1904 1905=cut 1906 1907# global (like a C static) 1908use vars qw( @saved_pair ); 1909@saved_pair = (); # 0..2 elements, which are units (grams). 1910 1911sub get_syllable($) { 1912 my $pwlen = shift; 1913 1914 # these used to be "out" params: 1915 my $syllable; # string, returned 1916 my @units_in_syllable = (); # array of units, returned 1917 1918 1919 # grams: 1920 my $unit; 1921 my $current_unit; 1922 my $last_unit; 1923 1924 # numbers: 1925 my $vowel_count; 1926 my $tries; 1927 my $length_left; 1928 my $outer_tries; 1929 1930 # flags: 1931 my $rule_broken; 1932 my $want_vowel; 1933 my $want_another_unit; 1934 1935 1936 # 1937 # This is needed if the saved_pair is tried and the syllable then 1938 # discarded because of the retry limit. Since the saved_pair is OK and 1939 # fits in nicely with the preceding syllable, we will always use it. 1940 # 1941 my @hold_saved_pair = @saved_pair; 1942 1943 my $max_retries = ( 4 * $pwlen ) + scalar( @grams ); 1944 # note that this used to be a macro, which means it could have changed 1945 # dynamically based on the value of $pwlen... 1946 1947 # 1948 # Loop until valid syllable is found. 1949 # 1950 $outer_tries = 0; 1951 do { 1952 ++$outer_tries; 1953 # 1954 # Try for a new syllable. Initialize all pertinent 1955 # syllable variables. 1956 # 1957 $tries = 0; 1958 @saved_pair = @hold_saved_pair; 1959 $syllable = ""; 1960 $vowel_count = 0; 1961 $current_unit = 0; 1962 $length_left = $pwlen; 1963 $want_another_unit = 1; # true 1964 1965 # 1966 # This loop finds all the units for the syllable. 1967 # 1968 do { 1969 $want_vowel = 0; # false 1970 1971 # 1972 # This loop continues until a valid unit is found for the 1973 # current position within the syllable. 1974 # 1975 do { 1976 # 1977 # If there are saved units from the previous syllable, use them up first. 1978 # 1979 1980 # 1981 # If there were two saved units, the first is guaranteed 1982 # (by checks performed in the previous syllable) to be valid. 1983 # We ignore the checks and place it in this syllable manually. 1984 # 1985 if ( @saved_pair == 2 ) { 1986 $syllable = 1987 $units_in_syllable[0] = pop @saved_pair; 1988 $vowel_count++ if $rules{$syllable} & VOWEL; 1989 $current_unit++; 1990 $length_left -= length $syllable; 1991 } 1992 1993 if ( @saved_pair ) { 1994 # 1995 # The unit becomes the last unit checked in the previous syllable. 1996 # 1997 $unit = pop @saved_pair; 1998 1999 # 2000 # The saved units have been used. 2001 # Do not try to reuse them in this syllable 2002 # (unless this particular syllable is rejected 2003 # at which point we start to rebuild it with these same saved units). 2004 # 2005 } 2006 else { 2007 # 2008 # If we don't have to consider the saved units, we generate a random one. 2009 # 2010 $unit = _random_unit( $want_vowel ? VOWEL : NO_SPECIAL_RULE ); 2011 } 2012 2013 $length_left -= length $unit; 2014 2015 # 2016 # Prevent having a word longer than expected. 2017 # 2018 $rule_broken = ( $length_left < 0 ); # boolean 2019 2020 # 2021 # First unit of syllable. 2022 # This is special because the digram tests require 2 units and we don't have that yet. 2023 # Nevertheless, we can perform some checks. 2024 # 2025 if ( $current_unit == 0 ) { 2026 # 2027 # If the shouldn't begin a syllable, don't use it. 2028 # 2029 if ( $rules{$unit} & NOT_BEGIN_SYLLABLE ) { 2030 $rule_broken = 1; # true 2031 # 2032 # If this is the last unit of a word, we have a one unit syllable. 2033 # Since each syllable must have a vowel, we make sure the unit is a vowel. 2034 # Otherwise, we discard it. 2035 # 2036 } 2037 elsif ( $length_left == 0 ) { 2038 if ( $rules{$unit} & VOWEL ) { 2039 $want_another_unit = 0; # false 2040 } 2041 else { 2042 $rule_broken = 1; # true 2043 } 2044 } 2045 } 2046 else { 2047# 2048# this ALLOWED thing is only used in this code block. 2049# note that $unit and $current_unit are (used to be) numeric indices; should now be actual grams. 2050# 2051local *ALLOWED = sub { 2052 my $flag = shift; 2053 $digram{$units_in_syllable[$current_unit-1]}{$unit} & $flag 2054}; 2055 2056 # 2057 # There are some digram tests that are universally true. We test them out. 2058 # 2059 2060 if ( 2061 # 2062 # Reject ILLEGAL_PAIRS of units. 2063 # 2064 (ALLOWED(ILLEGAL_PAIR)) 2065 || 2066 2067 # 2068 # Reject units that will be split between syllables 2069 # when the syllable has no vowels in it. 2070 # 2071 (ALLOWED(BREAK) && ($vowel_count == 0)) 2072 || 2073 2074 # 2075 # Reject a unit that will end a syllable when no 2076 # previous unit was a vowel and neither is this one. 2077 # 2078 ( 2079 ALLOWED(BACK) 2080 && 2081 ($vowel_count == 0) 2082 && 2083 !($rules{$unit} & VOWEL) 2084 ) 2085 ) { 2086 $rule_broken = 1; # true 2087 } 2088 2089 if ($current_unit == 1) { 2090 # 2091 # Reject the unit if we are at te starting digram of 2092 # a syllable and it does not fit. 2093 # 2094 if (ALLOWED(NOT_FRONT)) { 2095 $rule_broken = 1; # true 2096 } 2097 } 2098 else { 2099 # 2100 # We are not at the start of a syllable. 2101 # Save the previous unit for later tests. 2102 # 2103 $last_unit = $units_in_syllable[$current_unit - 1]; 2104 2105 # 2106 # Do not allow syllables where the first letter is y 2107 # and the next pair can begin a syllable. This may 2108 # lead to splits where y is left alone in a syllable. 2109 # Also, the combination does not sound to good even 2110 # if not split. 2111 # 2112 if ( 2113 ( 2114 ($current_unit == 2) 2115 && 2116 ALLOWED(FRONT) 2117 && 2118 ($rules{$units_in_syllable[0]} & ALTERNATE_VOWEL) 2119 ) 2120 || 2121 2122 # 2123 # If this is the last unit of a word, we should 2124 # reject any digram that cannot end a syllable. 2125 # 2126 ( 2127 ALLOWED(NOT_BACK) 2128 && 2129 ($length_left == 0) 2130 ) 2131 || 2132 2133 # 2134 # Reject the unit if the digram it forms wants 2135 # to break the syllable, but the resulting 2136 # digram that would end the syllable is not 2137 # allowed to end a syllable. 2138 # 2139 ( 2140 ALLOWED(BREAK) 2141 || 2142 ($digram{ $units_in_syllable[$current_unit-2] }{$last_unit} & NOT_BACK) 2143 ) 2144 || 2145 2146 # 2147 # Reject the unit if the digram it forms expects a vowel preceding it and there is none. 2148 # 2149 ( 2150 ALLOWED(PREFIX) 2151 && 2152 !($rules{ $units_in_syllable[$current_unit-2] } & VOWEL) 2153 ) 2154 ) { 2155 $rule_broken = 1; # true 2156 } 2157 2158 # 2159 # The following checks occur when the current unit is a vowel 2160 # and we are not looking at a word ending with an e. 2161 # 2162 if ( 2163 !$rule_broken 2164 && 2165 ($rules{$unit} & VOWEL) 2166 && 2167 ( 2168 ($length_left > 0) 2169 || 2170 !($rules{$last_unit} & NO_FINAL_SPLIT) 2171 ) 2172 ) { 2173 # 2174 # Don't allow 3 consecutive vowels in a syllable. 2175 # Although some words formed like this are OK, like "beau", most are not. 2176 # 2177 if ( ($vowel_count > 1) && ($rules{$last_unit} & VOWEL) ) { 2178 $rule_broken = 1; # true 2179 } 2180 # 2181 # Check for the case of vowels-consonants-vowel, 2182 # which is only legal if the last vowel is an e and we are the end of the word 2183 # (which is not happening here due to a previous check). 2184 # 2185 elsif ( ($vowel_count != 0) && !($rules{$last_unit} & VOWEL) ) { 2186 # 2187 # Try to save the vowel for the next syllable, 2188 # but if the syllable left here is not proper 2189 # (i.e., the resulting last digram cannot legally end it), 2190 # just discard it and try for another. 2191 # 2192 if ( $digram{ $units_in_syllable[ $current_unit - 2] }{$last_unit} & NOT_BACK ) { 2193 $rule_broken = 1; # true 2194 } 2195 else { 2196 @saved_pair = ( $unit ); 2197 $want_another_unit = 0; # false 2198 } 2199 } 2200 } 2201 } 2202 2203 # 2204 # The unit picked and the digram formed are legal. 2205 # We now determine if we can end the syllable. It may, 2206 # in some cases, mean the last unit(s) may be deferred to 2207 # the next syllable. We also check here to see if the 2208 # digram formed expects a vowel to follow. 2209 # 2210 if ( !$rule_broken and $want_another_unit ) { 2211 # 2212 # This word ends in a silent e. 2213 # 2214 if ( 2215 ( 2216 ($vowel_count != 0) 2217 && 2218 ($rules{$unit} & NO_FINAL_SPLIT) 2219 && 2220 ($length_left == 0) 2221 && 2222 !($rules{$last_unit} & VOWEL) 2223 ) 2224 or 2225 2226 # 2227 # This syllable ends either because the digram 2228 # is a BACK pair or we would otherwise exceed 2229 # the length of the word. 2230 # 2231 ( ALLOWED(BACK) || ($length_left == 0) ) 2232 ) { 2233 $want_another_unit = 0; # false 2234 } 2235 2236 # 2237 # Since we have a vowel in the syllable 2238 # already, if the digram calls for the end of the 2239 # syllable, we can legally split it off. We also 2240 # make sure that we are not at the end of the 2241 # dangerous because that syllable may not have 2242 # vowels, or it may not be a legal syllable end, 2243 # and the retrying mechanism will loop infinitely 2244 # with the same digram. 2245 # 2246 elsif ( $vowel_count != 0 and $length_left > 0 ) { 2247 # 2248 # If we must begin a syllable, we do so if 2249 # the only vowel in THIS syllable is not part 2250 # of the digram we are pushing to the next 2251 # syllable. 2252 # 2253 if ( 2254 ALLOWED(FRONT) 2255 && 2256 ($current_unit > 1) 2257 && 2258 !( 2259 ($vowel_count == 1) 2260 && 2261 ($rules{$last_unit} & VOWEL) 2262 ) 2263 ) { 2264 @saved_pair = ( $unit, $last_unit ); 2265 $want_another_unit = 0; # false 2266 } 2267 elsif (ALLOWED (BREAK)) { 2268 @saved_pair = ( $unit ); 2269 $want_another_unit = 0; # false 2270 } 2271 } 2272 elsif (ALLOWED (SUFFIX)) { 2273 $want_vowel = 1; # true 2274 } 2275 } 2276 } 2277 2278 $tries++; 2279 2280 # 2281 # If this unit was illegal, redetermine the amount of 2282 # letters left to go in the word. 2283 # 2284 if ( $rule_broken ) { 2285 $length_left += length $unit; 2286 } 2287 } 2288 while ( $rule_broken and $tries <= $max_retries ); 2289 2290 # 2291 # The unit fit OK. 2292 # 2293 if ( $tries <= $max_retries ) { 2294 # 2295 # If the unit were a vowel, count it in. 2296 # However, if the unit were a y and appear at the start of the syllable, 2297 # treat it like a constant (so that words like "year" can appear and 2298 # not conflict with the 3 consecutive vowel rule). 2299 # 2300 if ( 2301 ($rules{$unit} & VOWEL) 2302 && 2303 ( ($current_unit > 0) || !($rules{$unit} & ALTERNATE_VOWEL) ) 2304 ) { 2305 $vowel_count++; 2306 } 2307 2308 # 2309 # If a unit or units were to be saved, we must adjust the syllable formed. 2310 # Otherwise, we append the current unit to the syllable. 2311 # 2312 if ( @saved_pair == 2 ) { 2313 # strcpy( &syllable[ strlen( syllable ) - strlen( last_unit ) ], "" ); 2314 my $n = length $last_unit; 2315 $syllable =~ s/.{$n}$//; # DOES THIS WORK? 2316 $length_left += length $last_unit; 2317 $current_unit -= 2; 2318 } 2319 elsif ( @saved_pair == 1 ) { 2320 $current_unit--; 2321 } 2322 else { 2323 $units_in_syllable[ $current_unit ] = $unit; 2324 $syllable .= $unit; 2325 } 2326 } 2327 else { 2328 # 2329 # Whoops! Too many tries. 2330 # We set rule_broken so we can loop in the outer loop and try another syllable. 2331 # 2332 $rule_broken = 1; # true 2333 } 2334 2335 $current_unit++; 2336 } 2337 while ( $tries <= $max_retries and $want_another_unit ); 2338 } 2339 while ( $outer_tries < $max_retries && ($rule_broken or _illegal_placement( @units_in_syllable )) ); 2340 2341 return ('') if $outer_tries >= $max_retries; 2342 2343 return( $syllable, @units_in_syllable ); 2344} # sub get_syllable 2345 2346 2347 2348=head2 alt_get_syllable 2349 2350Takes an integer, the maximum number of chars to generate. (or is it minimum?) 2351 2352returns a list of ( string, units-in-syllable ) 2353 2354I<This is an alternative version of C<get_syllable()>, which 2355can be useful for unit testing the other functions.> 2356 2357=cut 2358 2359sub alt_get_syllable($) { # alternative version, has no smarts. 2360 my $pwlen = shift; # max or min? 2361 for ( 0 .. $#grams ) { 2362 my $syl = ''; 2363 my @syl_units = (); 2364 while ( @syl_units < 3 ) { 2365 my $unit = _random_unit( NO_SPECIAL_RULE ); 2366 $syl .= $unit; 2367 push @syl_units, $unit; 2368 length($syl) >= $pwlen and return( $syl, @syl_units ); 2369 } 2370 @syl_units and return( $syl, @syl_units ); 2371 } 2372 return(); # failed 2373} 2374 2375 2376=head2 _illegal_placement 2377 2378goes through an individual syllable and checks for illegal 2379combinations of letters that go beyond looking at digrams. 2380 2381We look at things like 3 consecutive vowels or consonants, 2382or syllables with consonants between vowels 2383(unless one of them is the final silent e). 2384 2385returns boolean. 2386 2387=cut 2388 2389sub _illegal_placement(@) { 2390 my @units = @_; 2391 2392 my $vowel_count = 0; 2393 my $failure = 0; # false 2394 2395 for my $unit_count ( 0 .. $#units ) { 2396 last if $failure; 2397 2398 if ( $unit_count >= 1 ) { 2399 # 2400 # Don't allow vowels to be split with consonants in a single syllable. 2401 # If we find such a combination (except for the silent e) we have to discard the syllable. 2402 # 2403 if ( 2404 ( 2405 !( $rules{$units[$unit_count-1]} & VOWEL) 2406 && 2407 ( $rules{$units[$unit_count ]} & VOWEL) 2408 && 2409 !(($rules{$units[$unit_count ]} & NO_FINAL_SPLIT) && ($unit_count == $#units)) 2410 && 2411 $vowel_count 2412 ) 2413 || 2414 2415 # 2416 # Perform these checks when we have at least 3 units. 2417 # 2418 ( 2419 ($unit_count >= 2) 2420 && 2421 ( 2422 # 2423 # Disallow 3 consecutive consonants. 2424 # 2425 ( 2426 !($rules{$units[$unit_count-2]} & VOWEL) 2427 && 2428 !($rules{$units[$unit_count-1]} & VOWEL) 2429 && 2430 !($rules{$units[$unit_count ]} & VOWEL) 2431 ) 2432 || 2433 2434 # 2435 # Disallow 3 consecutive vowels, where the first is not a y. 2436 # 2437 ( 2438 ( $rules{$units[$unit_count-2]} & VOWEL) 2439 && 2440 !(($rules{$units[0 ]} & ALTERNATE_VOWEL) && ($unit_count == 2)) 2441 && 2442 ( $rules{$units[$unit_count-1]} & VOWEL) 2443 && 2444 ( $rules{$units[$unit_count ]} & VOWEL) 2445 ) 2446 ) 2447 ) 2448 ) { 2449 $failure = 1; # true 2450 } 2451 } 2452 2453 # 2454 # Count the vowels in the syllable. 2455 # As mentioned somewhere above, exclude the initial y of a syllable. 2456 # Instead, treat it as a consonant. 2457 # 2458 if ( 2459 ($rules{$units[$unit_count]} & VOWEL) 2460 && 2461 !( 2462 ($rules{$units[0]} & ALTERNATE_VOWEL) 2463 && 2464 ($unit_count == 0) 2465 && 2466 (@units > 1) 2467 ) 2468 ) { 2469 $vowel_count++; 2470 } 2471 } 2472 2473 $failure; 2474} 2475 2476} 2477 2478=head1 AUTHOR 2479 2480JDPORTER@cpan.org (John Porter) 2481 2482=head1 COPYRIGHT 2483 2484This perl module is free software; it may be redistributed and/or modified 2485under the same terms as Perl itself. 2486 2487=cut 2488 2489unless ( defined caller ) { 2490 2491# this can be used for unit testing or to make the module a stand-alone program. 2492package main; 2493use Getopt::Long; 2494 2495$^W = 1; 2496 2497my $algorithm = 'word'; # default: word 2498my $maxlen = 8; 2499my $minlen = 6; 2500my $num_words = 1; 2501$main::DEBUG = 0; 2502 2503GetOptions( 2504 'seed=s' => \$Crypt::RandPasswd::seed, 2505 'algorithm=s' => \$algorithm, # select word, letters, chars 2506 'max=s' => \$maxlen, 2507 'min=s' => \$minlen, 2508 'count=s' => \$num_words, 2509 'debug!' => \$main::DEBUG, 2510) 2511 or die "Usage: $0 --count N --min N --max N --algorithm [word|letters|chars] --seed N --[no]debug \n"; 2512 2513$minlen <= $maxlen or die "minimum word length ($minlen) must be <= maximum ($maxlen)\n"; 2514 2515UNIVERSAL::can( "Crypt::RandPasswd", $algorithm ) or die "Invalid algorithm '$algorithm'\n"; 2516 2517print STDERR "$num_words '$algorithm' words of $minlen-$maxlen chars \n" 2518 if $main::DEBUG ; 2519 2520for ( 1 .. $num_words ) { 2521 my( $unhyphenated_word, $hyphenated_word ) = Crypt::RandPasswd->$algorithm( $minlen, $maxlen ); 2522 2523 print 2524 $algorithm eq 'word' 2525 ? "$unhyphenated_word ($hyphenated_word)\n" 2526 : "$unhyphenated_word\n"; 2527} 2528 2529} # end of 'main' code. 2530 25311; 2532 2533