File Coverage

blib/lib/Crypt/RandPasswd.pm
Criterion Covered Total %
statement 242 259 93.4
branch 90 116 77.5
condition 142 168 84.5
subroutine 39 40 97.5
pod 10 12 83.3
total 523 595 87.9


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