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