summaryrefslogtreecommitdiff
path: root/cpan/dist
diff options
context:
space:
mode:
authorfukachan <fukachan>2001-01-29 12:56:54 +0000
committerfukachan <fukachan>2001-01-29 12:56:54 +0000
commitd99a1f1566eacd86249355db2012e8028f5a060c (patch)
treee97bcbef4be330bc6509ceedf7cd1a943547ab06 /cpan/dist
parent1b3a879cf3fc9dee8d23fe452060953c3ff8adf6 (diff)
downloadfml8-d99a1f1566eacd86249355db2012e8028f5a060c.tar.gz
fml8-d99a1f1566eacd86249355db2012e8028f5a060c.tar.bz2
fml8-d99a1f1566eacd86249355db2012e8028f5a060c.zip
Initial revision
Diffstat (limited to 'cpan/dist')
-rw-r--r--cpan/dist/Crypt-RandPasswd/Changes8
-rw-r--r--cpan/dist/Crypt-RandPasswd/MANIFEST6
-rw-r--r--cpan/dist/Crypt-RandPasswd/Makefile.PL7
-rw-r--r--cpan/dist/Crypt-RandPasswd/README27
-rw-r--r--cpan/dist/Crypt-RandPasswd/lib/Crypt/RandPasswd.pm2526
-rw-r--r--cpan/dist/Crypt-RandPasswd/test.pl35
6 files changed, 2609 insertions, 0 deletions
diff --git a/cpan/dist/Crypt-RandPasswd/Changes b/cpan/dist/Crypt-RandPasswd/Changes
new file mode 100644
index 00000000..bb6c7c43
--- /dev/null
+++ b/cpan/dist/Crypt-RandPasswd/Changes
@@ -0,0 +1,8 @@
+Revision history for Perl extension Crypt::RandPasswd.
+
+0.02 Fri Jul 21 14:19:43 EDT 2000
+ - Added README file.
+
+0.01 Thu Jul 20 17:18:45 EDT 2000
+ - original version; created by h2xs 1.19
+
diff --git a/cpan/dist/Crypt-RandPasswd/MANIFEST b/cpan/dist/Crypt-RandPasswd/MANIFEST
new file mode 100644
index 00000000..a3ea82a1
--- /dev/null
+++ b/cpan/dist/Crypt-RandPasswd/MANIFEST
@@ -0,0 +1,6 @@
+Changes
+MANIFEST
+Makefile.PL
+README
+lib/Crypt/RandPasswd.pm
+test.pl
diff --git a/cpan/dist/Crypt-RandPasswd/Makefile.PL b/cpan/dist/Crypt-RandPasswd/Makefile.PL
new file mode 100644
index 00000000..62003ab5
--- /dev/null
+++ b/cpan/dist/Crypt-RandPasswd/Makefile.PL
@@ -0,0 +1,7 @@
+use ExtUtils::MakeMaker;
+# See lib/ExtUtils/MakeMaker.pm for details of how to influence
+# the contents of the Makefile that is written.
+WriteMakefile(
+ 'NAME' => 'Crypt::RandPasswd',
+ 'VERSION_FROM' => 'lib/Crypt/RandPasswd.pm', # finds $VERSION
+);
diff --git a/cpan/dist/Crypt-RandPasswd/README b/cpan/dist/Crypt-RandPasswd/README
new file mode 100644
index 00000000..0a788544
--- /dev/null
+++ b/cpan/dist/Crypt-RandPasswd/README
@@ -0,0 +1,27 @@
+
+Crypt::RandPasswd - random password generator based on FIPS-181
+
+By John Porter (JDPORTER@cpan.org)
+
+This is an implementation of the Automated Password Generator standard,
+defined in FIPS Publication 181, "Standard for Automated Password Generator":
+http://www.itl.nist.gov/fipspubs/fip181.htm
+
+ use Crypt::RandPasswd;
+ ( $word, $hyphenated ) = Crypt::RandPasswd::word( $minlen, $maxlen );
+ $word = Crypt::RandPasswd::word( $minlen, $maxlen );
+ $word = Crypt::RandPasswd::letters( $minlen, $maxlen );
+ $word = Crypt::RandPasswd::chars( $minlen, $maxlen );
+
+ # override the defaults for these functions:
+ *Crypt::RandPasswd::rng = \&my_random_number_generator;
+ *Crypt::RandPasswd::restrict = \&my_restriction_filter;
+
+The module file can also be run directly as a script:
+
+# To print the usage statement:
+% perl Crypt/RandPasswd.pm -help
+
+# To print out one password using the default parameter values:
+% perl Crypt/RandPasswd.pm
+
diff --git a/cpan/dist/Crypt-RandPasswd/lib/Crypt/RandPasswd.pm b/cpan/dist/Crypt-RandPasswd/lib/Crypt/RandPasswd.pm
new file mode 100644
index 00000000..69ebf99a
--- /dev/null
+++ b/cpan/dist/Crypt-RandPasswd/lib/Crypt/RandPasswd.pm
@@ -0,0 +1,2526 @@
+
+{
+
+package Crypt::RandPasswd;
+
+use strict;
+
+use vars qw($VERSION);
+
+$VERSION = '0.02';
+
+
+=head1 NAME
+
+Crypt::RandPasswd - random password generator based on FIPS-181
+
+=head1 SYNOPSIS
+
+ use Crypt::RandPasswd;
+ ( $word, $hyphenated ) = Crypt::RandPasswd->word( $minlen, $maxlen );
+ $word = Crypt::RandPasswd->word( $minlen, $maxlen );
+ $word = Crypt::RandPasswd->letters( $minlen, $maxlen );
+ $word = Crypt::RandPasswd->chars( $minlen, $maxlen );
+
+ # override the defaults for these functions:
+ *Crypt::RandPasswd::rng = \&my_random_number_generator;
+ *Crypt::RandPasswd::restrict = \&my_restriction_filter;
+
+=head2 Run as Script
+
+ perl Crypt/RandPasswd.pm -help
+
+=head1 SEE ALSO
+
+FIPS 181 - (APG), Automated Password Generator:
+http://www.itl.nist.gov/fipspubs/fip181.htm
+
+=head1 DESCRIPTION
+
+This code is a Perl language implementation of the Automated
+Password Generator standard, like the program described in
+"A Random Word Generator For Pronounceable Passwords" (not available on-line).
+This code is a re-engineering of the program contained in Appendix A
+of FIPS Publication 181, "Standard for Automated Password Generator".
+In accordance with the standard, the results obtained from this
+program are logically equivalent to those produced by the standard.
+
+=head1 CAVEATS
+
+=head2 Bugs
+
+The function to generate a password can sometimes take an extremely long time.
+
+=head2 Deviations From Standard
+
+This implementation deviates in one critical way from the standard
+upon which it is based: the random number generator in this
+implementation does not use DES. Instead, it uses perl's built-in
+C<rand()> function, which in turn is (usually) built on the
+pseudo-random number generator functions of the underlying C library.
+
+However, the random function can be replaced by the user if desired.
+(See L</rng>.)
+
+=head1 Functions
+
+=cut
+
+
+sub word($$);
+sub letters($$);
+sub chars($$);
+
+sub random_chars_in_range($$$$);
+sub rand_int_in_range($$);
+sub random_element($);
+
+sub rng($);
+sub restrict($);
+sub init();
+
+
+sub _random_word($);
+sub _random_unit($);
+sub _improper_word(@);
+sub _have_initial_y(@);
+sub _have_final_split(@);
+sub _illegal_placement(@);
+
+
+#
+# Global Variables:
+#
+
+$Crypt::RandPasswd::seed = undef; # by default; causes srand() to use its own, which can be pretty good.
+$Crypt::RandPasswd::initialized = 0;
+
+
+
+my @grams = qw( a b c d e f g h i j k l m n o p r s t u v w x y z ch gh ph rh sh th wh qu ck );
+my %grams; @grams{@grams} = (); # and a set of same.
+
+my @vowel_grams = qw( a e i o u y );
+my %vowel_grams; @vowel_grams{@vowel_grams} = (); # and a set of same.
+
+
+
+#
+# Bit flags
+#
+
+use constant MAX_UNACCEPTABLE => 20 ;
+
+# gram rules:
+use constant NOT_BEGIN_SYLLABLE => 010 ;
+use constant NO_FINAL_SPLIT => 004 ;
+use constant VOWEL => 002 ;
+use constant ALTERNATE_VOWEL => 001 ;
+use constant NO_SPECIAL_RULE => 000 ;
+
+# digram rules:
+use constant FRONT => 0200 ;
+use constant NOT_FRONT => 0100 ;
+use constant BREAK => 0040 ;
+use constant PREFIX => 0020 ;
+use constant ILLEGAL_PAIR => 0010 ;
+use constant SUFFIX => 0004 ;
+use constant BACK => 0002 ;
+use constant NOT_BACK => 0001 ;
+use constant ANY_COMBINATION => 0000 ;
+
+## it used to be that info about units was contained in the C-arrays 'rules' and 'digram'.
+## both were indexed numerically. 'rules' was essentially a mapping from a unique
+## integer ID (the index) to a gram. 'digram' used the same mapping, but in a
+## two-dimensional array. I.e. to represent the digram "ab" ("a","b"), one would
+## need to know the numeric ID of "a" and "b", which turn out to be 0 and 1, respectively;
+## then use those indices in digram: digram[0][1].
+## The information at the "end" of a lookup in digram[][] was a simple integer
+## representing the flag bits for that digram. (The %digram in the current
+## implementation is the same.) The rules[] C-array, however, needed to store
+## both the bitmask and the string representation of the gram, so it was an array
+## of a struct { string, bitmask }. Since %rules is an associative array, indexed
+## directly by the gram, it only needs the bitmask at its "end", the same as %digram.
+##
+## both 'rules' and 'digram' contained bitflags for grams and digrams, respectively.
+## additionally, 'rules' contained the string representation of the unit.
+## because 'rules' contained both a string and flags for each unit, its contents
+## were actually structs of { string, flags }.
+##
+## 'digram', on the other hand, was simply the bitflags (integers).
+
+# struct unit {
+# char unit_code[5]; # string, usually 1, but up to 4 characters.
+# byte flags;
+# } rules[34];
+
+## the 'rules' C-array used to be indexed by gram index; now %rules is indexed by the gram itself.
+
+my %rules;
+
+@rules{ @grams } = ( NO_SPECIAL_RULE ) x @grams;
+@rules{ @vowel_grams } = ( VOWEL ) x @vowel_grams;
+
+$rules{'e'} |= NO_FINAL_SPLIT;
+$rules{'y'} |= ALTERNATE_VOWEL;
+
+$rules{'x'} =
+$rules{'ck'} = NOT_BEGIN_SYLLABLE;
+
+
+
+#
+# the 'digram' C-array, digram[34][34], was indexed by the unit indexes of the two grams;
+# now %digram is indexed directly by the two grams.
+#
+my %digram;
+
+ ##############################################################################################
+ # BEGIN DIGRAM {
+ ##############################################################################################
+
+ $digram{'a'}{'a'} = ILLEGAL_PAIR;
+ $digram{'a'}{'b'} = ANY_COMBINATION;
+ $digram{'a'}{'c'} = ANY_COMBINATION;
+ $digram{'a'}{'d'} = ANY_COMBINATION;
+ $digram{'a'}{'e'} = ILLEGAL_PAIR;
+ $digram{'a'}{'f'} = ANY_COMBINATION;
+ $digram{'a'}{'g'} = ANY_COMBINATION;
+ $digram{'a'}{'h'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'a'}{'i'} = ANY_COMBINATION;
+ $digram{'a'}{'j'} = ANY_COMBINATION;
+ $digram{'a'}{'k'} = ANY_COMBINATION;
+ $digram{'a'}{'l'} = ANY_COMBINATION;
+ $digram{'a'}{'m'} = ANY_COMBINATION;
+ $digram{'a'}{'n'} = ANY_COMBINATION;
+ $digram{'a'}{'o'} = ILLEGAL_PAIR;
+ $digram{'a'}{'p'} = ANY_COMBINATION;
+ $digram{'a'}{'r'} = ANY_COMBINATION;
+ $digram{'a'}{'s'} = ANY_COMBINATION;
+ $digram{'a'}{'t'} = ANY_COMBINATION;
+ $digram{'a'}{'u'} = ANY_COMBINATION;
+ $digram{'a'}{'v'} = ANY_COMBINATION;
+ $digram{'a'}{'w'} = ANY_COMBINATION;
+ $digram{'a'}{'x'} = ANY_COMBINATION;
+ $digram{'a'}{'y'} = ANY_COMBINATION;
+ $digram{'a'}{'z'} = ANY_COMBINATION;
+ $digram{'a'}{'ch'} = ANY_COMBINATION;
+ $digram{'a'}{'gh'} = ILLEGAL_PAIR;
+ $digram{'a'}{'ph'} = ANY_COMBINATION;
+ $digram{'a'}{'rh'} = ILLEGAL_PAIR;
+ $digram{'a'}{'sh'} = ANY_COMBINATION;
+ $digram{'a'}{'th'} = ANY_COMBINATION;
+ $digram{'a'}{'wh'} = ILLEGAL_PAIR;
+ $digram{'a'}{'qu'} = BREAK | NOT_BACK;
+ $digram{'a'}{'ck'} = ANY_COMBINATION;
+
+ $digram{'b'}{'a'} = ANY_COMBINATION;
+ $digram{'b'}{'b'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'b'}{'c'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'b'}{'d'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'b'}{'e'} = ANY_COMBINATION;
+ $digram{'b'}{'f'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'b'}{'g'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'b'}{'h'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'b'}{'i'} = ANY_COMBINATION;
+ $digram{'b'}{'j'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'b'}{'k'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'b'}{'l'} = FRONT | SUFFIX | NOT_BACK;
+ $digram{'b'}{'m'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'b'}{'n'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'b'}{'o'} = ANY_COMBINATION;
+ $digram{'b'}{'p'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'b'}{'r'} = FRONT | BACK;
+ $digram{'b'}{'s'} = NOT_FRONT;
+ $digram{'b'}{'t'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'b'}{'u'} = ANY_COMBINATION;
+ $digram{'b'}{'v'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'b'}{'w'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'b'}{'x'} = ILLEGAL_PAIR;
+ $digram{'b'}{'y'} = ANY_COMBINATION;
+ $digram{'b'}{'z'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'b'}{'ch'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'b'}{'gh'} = ILLEGAL_PAIR;
+ $digram{'b'}{'ph'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'b'}{'rh'} = ILLEGAL_PAIR;
+ $digram{'b'}{'sh'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'b'}{'th'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'b'}{'wh'} = ILLEGAL_PAIR;
+ $digram{'b'}{'qu'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'b'}{'ck'} = ILLEGAL_PAIR;
+
+ $digram{'c'}{'a'} = ANY_COMBINATION;
+ $digram{'c'}{'b'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'c'}{'c'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'c'}{'d'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'c'}{'e'} = ANY_COMBINATION;
+ $digram{'c'}{'f'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'c'}{'g'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'c'}{'h'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'c'}{'i'} = ANY_COMBINATION;
+ $digram{'c'}{'j'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'c'}{'k'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'c'}{'l'} = SUFFIX | NOT_BACK;
+ $digram{'c'}{'m'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'c'}{'n'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'c'}{'o'} = ANY_COMBINATION;
+ $digram{'c'}{'p'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'c'}{'r'} = NOT_BACK;
+ $digram{'c'}{'s'} = NOT_FRONT | BACK;
+ $digram{'c'}{'t'} = NOT_FRONT | PREFIX;
+ $digram{'c'}{'u'} = ANY_COMBINATION;
+ $digram{'c'}{'v'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'c'}{'w'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'c'}{'x'} = ILLEGAL_PAIR;
+ $digram{'c'}{'y'} = ANY_COMBINATION;
+ $digram{'c'}{'z'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'c'}{'ch'} = ILLEGAL_PAIR;
+ $digram{'c'}{'gh'} = ILLEGAL_PAIR;
+ $digram{'c'}{'ph'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'c'}{'rh'} = ILLEGAL_PAIR;
+ $digram{'c'}{'sh'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'c'}{'th'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'c'}{'wh'} = ILLEGAL_PAIR;
+ $digram{'c'}{'qu'} = NOT_FRONT | SUFFIX | NOT_BACK;
+ $digram{'c'}{'ck'} = ILLEGAL_PAIR;
+
+ $digram{'d'}{'a'} = ANY_COMBINATION;
+ $digram{'d'}{'b'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'d'}{'c'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'d'}{'d'} = NOT_FRONT;
+ $digram{'d'}{'e'} = ANY_COMBINATION;
+ $digram{'d'}{'f'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'d'}{'g'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'d'}{'h'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'d'}{'i'} = ANY_COMBINATION;
+ $digram{'d'}{'j'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'d'}{'k'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'d'}{'l'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'d'}{'m'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'d'}{'n'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'d'}{'o'} = ANY_COMBINATION;
+ $digram{'d'}{'p'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'d'}{'r'} = FRONT | NOT_BACK;
+ $digram{'d'}{'s'} = NOT_FRONT | BACK;
+ $digram{'d'}{'t'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'d'}{'u'} = ANY_COMBINATION;
+ $digram{'d'}{'v'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'d'}{'w'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'d'}{'x'} = ILLEGAL_PAIR;
+ $digram{'d'}{'y'} = ANY_COMBINATION;
+ $digram{'d'}{'z'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'d'}{'ch'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'d'}{'gh'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'d'}{'ph'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'d'}{'rh'} = ILLEGAL_PAIR;
+ $digram{'d'}{'sh'} = NOT_FRONT | NOT_BACK;
+ $digram{'d'}{'th'} = NOT_FRONT | PREFIX;
+ $digram{'d'}{'wh'} = ILLEGAL_PAIR;
+ $digram{'d'}{'qu'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'d'}{'ck'} = ILLEGAL_PAIR;
+
+ $digram{'e'}{'a'} = ANY_COMBINATION;
+ $digram{'e'}{'b'} = ANY_COMBINATION;
+ $digram{'e'}{'c'} = ANY_COMBINATION;
+ $digram{'e'}{'d'} = ANY_COMBINATION;
+ $digram{'e'}{'e'} = ANY_COMBINATION;
+ $digram{'e'}{'f'} = ANY_COMBINATION;
+ $digram{'e'}{'g'} = ANY_COMBINATION;
+ $digram{'e'}{'h'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'e'}{'i'} = NOT_BACK;
+ $digram{'e'}{'j'} = ANY_COMBINATION;
+ $digram{'e'}{'k'} = ANY_COMBINATION;
+ $digram{'e'}{'l'} = ANY_COMBINATION;
+ $digram{'e'}{'m'} = ANY_COMBINATION;
+ $digram{'e'}{'n'} = ANY_COMBINATION;
+ $digram{'e'}{'o'} = BREAK;
+ $digram{'e'}{'p'} = ANY_COMBINATION;
+ $digram{'e'}{'r'} = ANY_COMBINATION;
+ $digram{'e'}{'s'} = ANY_COMBINATION;
+ $digram{'e'}{'t'} = ANY_COMBINATION;
+ $digram{'e'}{'u'} = ANY_COMBINATION;
+ $digram{'e'}{'v'} = ANY_COMBINATION;
+ $digram{'e'}{'w'} = ANY_COMBINATION;
+ $digram{'e'}{'x'} = ANY_COMBINATION;
+ $digram{'e'}{'y'} = ANY_COMBINATION;
+ $digram{'e'}{'z'} = ANY_COMBINATION;
+ $digram{'e'}{'ch'} = ANY_COMBINATION;
+ $digram{'e'}{'gh'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'e'}{'ph'} = ANY_COMBINATION;
+ $digram{'e'}{'rh'} = ILLEGAL_PAIR;
+ $digram{'e'}{'sh'} = ANY_COMBINATION;
+ $digram{'e'}{'th'} = ANY_COMBINATION;
+ $digram{'e'}{'wh'} = ILLEGAL_PAIR;
+ $digram{'e'}{'qu'} = BREAK | NOT_BACK;
+ $digram{'e'}{'ck'} = ANY_COMBINATION;
+
+ $digram{'f'}{'a'} = ANY_COMBINATION;
+ $digram{'f'}{'b'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'f'}{'c'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'f'}{'d'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'f'}{'e'} = ANY_COMBINATION;
+ $digram{'f'}{'f'} = NOT_FRONT;
+ $digram{'f'}{'g'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'f'}{'h'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'f'}{'i'} = ANY_COMBINATION;
+ $digram{'f'}{'j'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'f'}{'k'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'f'}{'l'} = FRONT | SUFFIX | NOT_BACK;
+ $digram{'f'}{'m'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'f'}{'n'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'f'}{'o'} = ANY_COMBINATION;
+ $digram{'f'}{'p'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'f'}{'r'} = FRONT | NOT_BACK;
+ $digram{'f'}{'s'} = NOT_FRONT;
+ $digram{'f'}{'t'} = NOT_FRONT;
+ $digram{'f'}{'u'} = ANY_COMBINATION;
+ $digram{'f'}{'v'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'f'}{'w'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'f'}{'x'} = ILLEGAL_PAIR;
+ $digram{'f'}{'y'} = NOT_FRONT;
+ $digram{'f'}{'z'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'f'}{'ch'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'f'}{'gh'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'f'}{'ph'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'f'}{'rh'} = ILLEGAL_PAIR;
+ $digram{'f'}{'sh'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'f'}{'th'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'f'}{'wh'} = ILLEGAL_PAIR;
+ $digram{'f'}{'qu'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'f'}{'ck'} = ILLEGAL_PAIR;
+
+ $digram{'g'}{'a'} = ANY_COMBINATION;
+ $digram{'g'}{'b'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'g'}{'c'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'g'}{'d'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'g'}{'e'} = ANY_COMBINATION;
+ $digram{'g'}{'f'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'g'}{'g'} = NOT_FRONT;
+ $digram{'g'}{'h'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'g'}{'i'} = ANY_COMBINATION;
+ $digram{'g'}{'j'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'g'}{'k'} = ILLEGAL_PAIR;
+ $digram{'g'}{'l'} = FRONT | SUFFIX | NOT_BACK;
+ $digram{'g'}{'m'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'g'}{'n'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'g'}{'o'} = ANY_COMBINATION;
+ $digram{'g'}{'p'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'g'}{'r'} = FRONT | NOT_BACK;
+ $digram{'g'}{'s'} = NOT_FRONT | BACK;
+ $digram{'g'}{'t'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'g'}{'u'} = ANY_COMBINATION;
+ $digram{'g'}{'v'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'g'}{'w'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'g'}{'x'} = ILLEGAL_PAIR;
+ $digram{'g'}{'y'} = NOT_FRONT;
+ $digram{'g'}{'z'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'g'}{'ch'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'g'}{'gh'} = ILLEGAL_PAIR;
+ $digram{'g'}{'ph'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'g'}{'rh'} = ILLEGAL_PAIR;
+ $digram{'g'}{'sh'} = NOT_FRONT;
+ $digram{'g'}{'th'} = NOT_FRONT;
+ $digram{'g'}{'wh'} = ILLEGAL_PAIR;
+ $digram{'g'}{'qu'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'g'}{'ck'} = ILLEGAL_PAIR;
+
+ $digram{'h'}{'a'} = ANY_COMBINATION;
+ $digram{'h'}{'b'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'h'}{'c'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'h'}{'d'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'h'}{'e'} = ANY_COMBINATION;
+ $digram{'h'}{'f'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'h'}{'g'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'h'}{'h'} = ILLEGAL_PAIR;
+ $digram{'h'}{'i'} = ANY_COMBINATION;
+ $digram{'h'}{'j'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'h'}{'k'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'h'}{'l'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'h'}{'m'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'h'}{'n'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'h'}{'o'} = ANY_COMBINATION;
+ $digram{'h'}{'p'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'h'}{'r'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'h'}{'s'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'h'}{'t'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'h'}{'u'} = ANY_COMBINATION;
+ $digram{'h'}{'v'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'h'}{'w'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'h'}{'x'} = ILLEGAL_PAIR;
+ $digram{'h'}{'y'} = ANY_COMBINATION;
+ $digram{'h'}{'z'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'h'}{'ch'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'h'}{'gh'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'h'}{'ph'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'h'}{'rh'} = ILLEGAL_PAIR;
+ $digram{'h'}{'sh'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'h'}{'th'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'h'}{'wh'} = ILLEGAL_PAIR;
+ $digram{'h'}{'qu'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'h'}{'ck'} = ILLEGAL_PAIR;
+
+ $digram{'i'}{'a'} = ANY_COMBINATION;
+ $digram{'i'}{'b'} = ANY_COMBINATION;
+ $digram{'i'}{'c'} = ANY_COMBINATION;
+ $digram{'i'}{'d'} = ANY_COMBINATION;
+ $digram{'i'}{'e'} = NOT_FRONT;
+ $digram{'i'}{'f'} = ANY_COMBINATION;
+ $digram{'i'}{'g'} = ANY_COMBINATION;
+ $digram{'i'}{'h'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'i'}{'i'} = ILLEGAL_PAIR;
+ $digram{'i'}{'j'} = ANY_COMBINATION;
+ $digram{'i'}{'k'} = ANY_COMBINATION;
+ $digram{'i'}{'l'} = ANY_COMBINATION;
+ $digram{'i'}{'m'} = ANY_COMBINATION;
+ $digram{'i'}{'n'} = ANY_COMBINATION;
+ $digram{'i'}{'o'} = BREAK;
+ $digram{'i'}{'p'} = ANY_COMBINATION;
+ $digram{'i'}{'r'} = ANY_COMBINATION;
+ $digram{'i'}{'s'} = ANY_COMBINATION;
+ $digram{'i'}{'t'} = ANY_COMBINATION;
+ $digram{'i'}{'u'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'i'}{'v'} = ANY_COMBINATION;
+ $digram{'i'}{'w'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'i'}{'x'} = ANY_COMBINATION;
+ $digram{'i'}{'y'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'i'}{'z'} = ANY_COMBINATION;
+ $digram{'i'}{'ch'} = ANY_COMBINATION;
+ $digram{'i'}{'gh'} = NOT_FRONT;
+ $digram{'i'}{'ph'} = ANY_COMBINATION;
+ $digram{'i'}{'rh'} = ILLEGAL_PAIR;
+ $digram{'i'}{'sh'} = ANY_COMBINATION;
+ $digram{'i'}{'th'} = ANY_COMBINATION;
+ $digram{'i'}{'wh'} = ILLEGAL_PAIR;
+ $digram{'i'}{'qu'} = BREAK | NOT_BACK;
+ $digram{'i'}{'ck'} = ANY_COMBINATION;
+
+ $digram{'j'}{'a'} = ANY_COMBINATION;
+ $digram{'j'}{'b'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'j'}{'c'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'j'}{'d'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'j'}{'e'} = ANY_COMBINATION;
+ $digram{'j'}{'f'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'j'}{'g'} = ILLEGAL_PAIR;
+ $digram{'j'}{'h'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'j'}{'i'} = ANY_COMBINATION;
+ $digram{'j'}{'j'} = ILLEGAL_PAIR;
+ $digram{'j'}{'k'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'j'}{'l'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'j'}{'m'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'j'}{'n'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'j'}{'o'} = ANY_COMBINATION;
+ $digram{'j'}{'p'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'j'}{'r'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'j'}{'s'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'j'}{'t'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'j'}{'u'} = ANY_COMBINATION;
+ $digram{'j'}{'v'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'j'}{'w'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'j'}{'x'} = ILLEGAL_PAIR;
+ $digram{'j'}{'y'} = NOT_FRONT;
+ $digram{'j'}{'z'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'j'}{'ch'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'j'}{'gh'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'j'}{'ph'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'j'}{'rh'} = ILLEGAL_PAIR;
+ $digram{'j'}{'sh'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'j'}{'th'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'j'}{'wh'} = ILLEGAL_PAIR;
+ $digram{'j'}{'qu'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'j'}{'ck'} = ILLEGAL_PAIR;
+
+ $digram{'k'}{'a'} = ANY_COMBINATION;
+ $digram{'k'}{'b'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'k'}{'c'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'k'}{'d'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'k'}{'e'} = ANY_COMBINATION;
+ $digram{'k'}{'f'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'k'}{'g'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'k'}{'h'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'k'}{'i'} = ANY_COMBINATION;
+ $digram{'k'}{'j'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'k'}{'k'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'k'}{'l'} = SUFFIX | NOT_BACK;
+ $digram{'k'}{'m'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'k'}{'n'} = FRONT | SUFFIX | NOT_BACK;
+ $digram{'k'}{'o'} = ANY_COMBINATION;
+ $digram{'k'}{'p'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'k'}{'r'} = SUFFIX | NOT_BACK;
+ $digram{'k'}{'s'} = NOT_FRONT | BACK;
+ $digram{'k'}{'t'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'k'}{'u'} = ANY_COMBINATION;
+ $digram{'k'}{'v'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'k'}{'w'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'k'}{'x'} = ILLEGAL_PAIR;
+ $digram{'k'}{'y'} = NOT_FRONT;
+ $digram{'k'}{'z'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'k'}{'ch'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'k'}{'gh'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'k'}{'ph'} = NOT_FRONT | PREFIX;
+ $digram{'k'}{'rh'} = ILLEGAL_PAIR;
+ $digram{'k'}{'sh'} = NOT_FRONT;
+ $digram{'k'}{'th'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'k'}{'wh'} = ILLEGAL_PAIR;
+ $digram{'k'}{'qu'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'k'}{'ck'} = ILLEGAL_PAIR;
+
+ $digram{'l'}{'a'} = ANY_COMBINATION;
+ $digram{'l'}{'b'} = NOT_FRONT | PREFIX;
+ $digram{'l'}{'c'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'l'}{'d'} = NOT_FRONT | PREFIX;
+ $digram{'l'}{'e'} = ANY_COMBINATION;
+ $digram{'l'}{'f'} = NOT_FRONT | PREFIX;
+ $digram{'l'}{'g'} = NOT_FRONT | PREFIX;
+ $digram{'l'}{'h'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'l'}{'i'} = ANY_COMBINATION;
+ $digram{'l'}{'j'} = NOT_FRONT | PREFIX;
+ $digram{'l'}{'k'} = NOT_FRONT | PREFIX;
+ $digram{'l'}{'l'} = NOT_FRONT | PREFIX;
+ $digram{'l'}{'m'} = NOT_FRONT | PREFIX;
+ $digram{'l'}{'n'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'l'}{'o'} = ANY_COMBINATION;
+ $digram{'l'}{'p'} = NOT_FRONT | PREFIX;
+ $digram{'l'}{'r'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'l'}{'s'} = NOT_FRONT;
+ $digram{'l'}{'t'} = NOT_FRONT | PREFIX;
+ $digram{'l'}{'u'} = ANY_COMBINATION;
+ $digram{'l'}{'v'} = NOT_FRONT | PREFIX;
+ $digram{'l'}{'w'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'l'}{'x'} = ILLEGAL_PAIR;
+ $digram{'l'}{'y'} = ANY_COMBINATION;
+ $digram{'l'}{'z'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'l'}{'ch'} = NOT_FRONT | PREFIX;
+ $digram{'l'}{'gh'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'l'}{'ph'} = NOT_FRONT | PREFIX;
+ $digram{'l'}{'rh'} = ILLEGAL_PAIR;
+ $digram{'l'}{'sh'} = NOT_FRONT | PREFIX;
+ $digram{'l'}{'th'} = NOT_FRONT | PREFIX;
+ $digram{'l'}{'wh'} = ILLEGAL_PAIR;
+ $digram{'l'}{'qu'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'l'}{'ck'} = ILLEGAL_PAIR;
+
+ $digram{'m'}{'a'} = ANY_COMBINATION;
+ $digram{'m'}{'b'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'m'}{'c'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'m'}{'d'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'m'}{'e'} = ANY_COMBINATION;
+ $digram{'m'}{'f'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'m'}{'g'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'m'}{'h'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'m'}{'i'} = ANY_COMBINATION;
+ $digram{'m'}{'j'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'m'}{'k'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'m'}{'l'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'m'}{'m'} = NOT_FRONT;
+ $digram{'m'}{'n'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'m'}{'o'} = ANY_COMBINATION;
+ $digram{'m'}{'p'} = NOT_FRONT;
+ $digram{'m'}{'r'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'m'}{'s'} = NOT_FRONT;
+ $digram{'m'}{'t'} = NOT_FRONT;
+ $digram{'m'}{'u'} = ANY_COMBINATION;
+ $digram{'m'}{'v'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'m'}{'w'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'m'}{'x'} = ILLEGAL_PAIR;
+ $digram{'m'}{'y'} = ANY_COMBINATION;
+ $digram{'m'}{'z'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'m'}{'ch'} = NOT_FRONT | PREFIX;
+ $digram{'m'}{'gh'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'m'}{'ph'} = NOT_FRONT;
+ $digram{'m'}{'rh'} = ILLEGAL_PAIR;
+ $digram{'m'}{'sh'} = NOT_FRONT;
+ $digram{'m'}{'th'} = NOT_FRONT;
+ $digram{'m'}{'wh'} = ILLEGAL_PAIR;
+ $digram{'m'}{'qu'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'m'}{'ck'} = ILLEGAL_PAIR;
+
+ $digram{'n'}{'a'} = ANY_COMBINATION;
+ $digram{'n'}{'b'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'n'}{'c'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'n'}{'d'} = NOT_FRONT;
+ $digram{'n'}{'e'} = ANY_COMBINATION;
+ $digram{'n'}{'f'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'n'}{'g'} = NOT_FRONT | PREFIX;
+ $digram{'n'}{'h'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'n'}{'i'} = ANY_COMBINATION;
+ $digram{'n'}{'j'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'n'}{'k'} = NOT_FRONT | PREFIX;
+ $digram{'n'}{'l'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'n'}{'m'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'n'}{'n'} = NOT_FRONT;
+ $digram{'n'}{'o'} = ANY_COMBINATION;
+ $digram{'n'}{'p'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'n'}{'r'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'n'}{'s'} = NOT_FRONT;
+ $digram{'n'}{'t'} = NOT_FRONT;
+ $digram{'n'}{'u'} = ANY_COMBINATION;
+ $digram{'n'}{'v'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'n'}{'w'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'n'}{'x'} = ILLEGAL_PAIR;
+ $digram{'n'}{'y'} = NOT_FRONT;
+ $digram{'n'}{'z'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'n'}{'ch'} = NOT_FRONT | PREFIX;
+ $digram{'n'}{'gh'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'n'}{'ph'} = NOT_FRONT | PREFIX;
+ $digram{'n'}{'rh'} = ILLEGAL_PAIR;
+ $digram{'n'}{'sh'} = NOT_FRONT;
+ $digram{'n'}{'th'} = NOT_FRONT;
+ $digram{'n'}{'wh'} = ILLEGAL_PAIR;
+ $digram{'n'}{'qu'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'n'}{'ck'} = NOT_FRONT | PREFIX;
+
+ $digram{'o'}{'a'} = ANY_COMBINATION;
+ $digram{'o'}{'b'} = ANY_COMBINATION;
+ $digram{'o'}{'c'} = ANY_COMBINATION;
+ $digram{'o'}{'d'} = ANY_COMBINATION;
+ $digram{'o'}{'e'} = ILLEGAL_PAIR;
+ $digram{'o'}{'f'} = ANY_COMBINATION;
+ $digram{'o'}{'g'} = ANY_COMBINATION;
+ $digram{'o'}{'h'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'o'}{'i'} = ANY_COMBINATION;
+ $digram{'o'}{'j'} = ANY_COMBINATION;
+ $digram{'o'}{'k'} = ANY_COMBINATION;
+ $digram{'o'}{'l'} = ANY_COMBINATION;
+ $digram{'o'}{'m'} = ANY_COMBINATION;
+ $digram{'o'}{'n'} = ANY_COMBINATION;
+ $digram{'o'}{'o'} = ANY_COMBINATION;
+ $digram{'o'}{'p'} = ANY_COMBINATION;
+ $digram{'o'}{'r'} = ANY_COMBINATION;
+ $digram{'o'}{'s'} = ANY_COMBINATION;
+ $digram{'o'}{'t'} = ANY_COMBINATION;
+ $digram{'o'}{'u'} = ANY_COMBINATION;
+ $digram{'o'}{'v'} = ANY_COMBINATION;
+ $digram{'o'}{'w'} = ANY_COMBINATION;
+ $digram{'o'}{'x'} = ANY_COMBINATION;
+ $digram{'o'}{'y'} = ANY_COMBINATION;
+ $digram{'o'}{'z'} = ANY_COMBINATION;
+ $digram{'o'}{'ch'} = ANY_COMBINATION;
+ $digram{'o'}{'gh'} = NOT_FRONT;
+ $digram{'o'}{'ph'} = ANY_COMBINATION;
+ $digram{'o'}{'rh'} = ILLEGAL_PAIR;
+ $digram{'o'}{'sh'} = ANY_COMBINATION;
+ $digram{'o'}{'th'} = ANY_COMBINATION;
+ $digram{'o'}{'wh'} = ILLEGAL_PAIR;
+ $digram{'o'}{'qu'} = BREAK | NOT_BACK;
+ $digram{'o'}{'ck'} = ANY_COMBINATION;
+
+ $digram{'p'}{'a'} = ANY_COMBINATION;
+ $digram{'p'}{'b'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'p'}{'c'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'p'}{'d'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'p'}{'e'} = ANY_COMBINATION;
+ $digram{'p'}{'f'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'p'}{'g'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'p'}{'h'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'p'}{'i'} = ANY_COMBINATION;
+ $digram{'p'}{'j'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'p'}{'k'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'p'}{'l'} = SUFFIX | NOT_BACK;
+ $digram{'p'}{'m'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'p'}{'n'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'p'}{'o'} = ANY_COMBINATION;
+ $digram{'p'}{'p'} = NOT_FRONT | PREFIX;
+ $digram{'p'}{'r'} = NOT_BACK;
+ $digram{'p'}{'s'} = NOT_FRONT | BACK;
+ $digram{'p'}{'t'} = NOT_FRONT | BACK;
+ $digram{'p'}{'u'} = NOT_FRONT | BACK;
+ $digram{'p'}{'v'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'p'}{'w'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'p'}{'x'} = ILLEGAL_PAIR;
+ $digram{'p'}{'y'} = ANY_COMBINATION;
+ $digram{'p'}{'z'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'p'}{'ch'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'p'}{'gh'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'p'}{'ph'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'p'}{'rh'} = ILLEGAL_PAIR;
+ $digram{'p'}{'sh'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'p'}{'th'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'p'}{'wh'} = ILLEGAL_PAIR;
+ $digram{'p'}{'qu'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'p'}{'ck'} = ILLEGAL_PAIR;
+
+ $digram{'r'}{'a'} = ANY_COMBINATION;
+ $digram{'r'}{'b'} = NOT_FRONT | PREFIX;
+ $digram{'r'}{'c'} = NOT_FRONT | PREFIX;
+ $digram{'r'}{'d'} = NOT_FRONT | PREFIX;
+ $digram{'r'}{'e'} = ANY_COMBINATION;
+ $digram{'r'}{'f'} = NOT_FRONT | PREFIX;
+ $digram{'r'}{'g'} = NOT_FRONT | PREFIX;
+ $digram{'r'}{'h'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'r'}{'i'} = ANY_COMBINATION;
+ $digram{'r'}{'j'} = NOT_FRONT | PREFIX;
+ $digram{'r'}{'k'} = NOT_FRONT | PREFIX;
+ $digram{'r'}{'l'} = NOT_FRONT | PREFIX;
+ $digram{'r'}{'m'} = NOT_FRONT | PREFIX;
+ $digram{'r'}{'n'} = NOT_FRONT | PREFIX;
+ $digram{'r'}{'o'} = ANY_COMBINATION;
+ $digram{'r'}{'p'} = NOT_FRONT | PREFIX;
+ $digram{'r'}{'r'} = NOT_FRONT | PREFIX;
+ $digram{'r'}{'s'} = NOT_FRONT | PREFIX;
+ $digram{'r'}{'t'} = NOT_FRONT | PREFIX;
+ $digram{'r'}{'u'} = ANY_COMBINATION;
+ $digram{'r'}{'v'} = NOT_FRONT | PREFIX;
+ $digram{'r'}{'w'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'r'}{'x'} = ILLEGAL_PAIR;
+ $digram{'r'}{'y'} = ANY_COMBINATION;
+ $digram{'r'}{'z'} = NOT_FRONT | PREFIX;
+ $digram{'r'}{'ch'} = NOT_FRONT | PREFIX;
+ $digram{'r'}{'gh'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'r'}{'ph'} = NOT_FRONT | PREFIX;
+ $digram{'r'}{'rh'} = ILLEGAL_PAIR;
+ $digram{'r'}{'sh'} = NOT_FRONT | PREFIX;
+ $digram{'r'}{'th'} = NOT_FRONT | PREFIX;
+ $digram{'r'}{'wh'} = ILLEGAL_PAIR;
+ $digram{'r'}{'qu'} = NOT_FRONT | PREFIX | NOT_BACK;
+ $digram{'r'}{'ck'} = NOT_FRONT | PREFIX;
+
+ $digram{'s'}{'a'} = ANY_COMBINATION;
+ $digram{'s'}{'b'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'s'}{'c'} = NOT_BACK;
+ $digram{'s'}{'d'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'s'}{'e'} = ANY_COMBINATION;
+ $digram{'s'}{'f'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'s'}{'g'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'s'}{'h'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'s'}{'i'} = ANY_COMBINATION;
+ $digram{'s'}{'j'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'s'}{'k'} = ANY_COMBINATION;
+ $digram{'s'}{'l'} = FRONT | SUFFIX | NOT_BACK;
+ $digram{'s'}{'m'} = SUFFIX | NOT_BACK;
+ $digram{'s'}{'n'} = PREFIX | SUFFIX | NOT_BACK;
+ $digram{'s'}{'o'} = ANY_COMBINATION;
+ $digram{'s'}{'p'} = ANY_COMBINATION;
+ $digram{'s'}{'r'} = NOT_FRONT | NOT_BACK;
+ $digram{'s'}{'s'} = NOT_FRONT | PREFIX;
+ $digram{'s'}{'t'} = ANY_COMBINATION;
+ $digram{'s'}{'u'} = ANY_COMBINATION;
+ $digram{'s'}{'v'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'s'}{'w'} = FRONT | SUFFIX | NOT_BACK;
+ $digram{'s'}{'x'} = ILLEGAL_PAIR;
+ $digram{'s'}{'y'} = ANY_COMBINATION;
+ $digram{'s'}{'z'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'s'}{'ch'} = FRONT | SUFFIX | NOT_BACK;
+ $digram{'s'}{'gh'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'s'}{'ph'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'s'}{'rh'} = ILLEGAL_PAIR;
+ $digram{'s'}{'sh'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'s'}{'th'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'s'}{'wh'} = ILLEGAL_PAIR;
+ $digram{'s'}{'qu'} = SUFFIX | NOT_BACK;
+ $digram{'s'}{'ck'} = NOT_FRONT;
+
+ $digram{'t'}{'a'} = ANY_COMBINATION;
+ $digram{'t'}{'b'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'t'}{'c'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'t'}{'d'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'t'}{'e'} = ANY_COMBINATION;
+ $digram{'t'}{'f'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'t'}{'g'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'t'}{'h'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'t'}{'i'} = ANY_COMBINATION;
+ $digram{'t'}{'j'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'t'}{'k'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'t'}{'l'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'t'}{'m'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'t'}{'n'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'t'}{'o'} = ANY_COMBINATION;
+ $digram{'t'}{'p'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'t'}{'r'} = NOT_BACK;
+ $digram{'t'}{'s'} = NOT_FRONT | BACK;
+ $digram{'t'}{'t'} = NOT_FRONT | PREFIX;
+ $digram{'t'}{'u'} = ANY_COMBINATION;
+ $digram{'t'}{'v'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'t'}{'w'} = FRONT | SUFFIX | NOT_BACK;
+ $digram{'t'}{'x'} = ILLEGAL_PAIR;
+ $digram{'t'}{'y'} = ANY_COMBINATION;
+ $digram{'t'}{'z'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'t'}{'ch'} = NOT_FRONT;
+ $digram{'t'}{'gh'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'t'}{'ph'} = NOT_FRONT | BACK;
+ $digram{'t'}{'rh'} = ILLEGAL_PAIR;
+ $digram{'t'}{'sh'} = NOT_FRONT | BACK;
+ $digram{'t'}{'th'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'t'}{'wh'} = ILLEGAL_PAIR;
+ $digram{'t'}{'qu'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'t'}{'ck'} = ILLEGAL_PAIR;
+
+ $digram{'u'}{'a'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'u'}{'b'} = ANY_COMBINATION;
+ $digram{'u'}{'c'} = ANY_COMBINATION;
+ $digram{'u'}{'d'} = ANY_COMBINATION;
+ $digram{'u'}{'e'} = NOT_FRONT;
+ $digram{'u'}{'f'} = ANY_COMBINATION;
+ $digram{'u'}{'g'} = ANY_COMBINATION;
+ $digram{'u'}{'h'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'u'}{'i'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'u'}{'j'} = ANY_COMBINATION;
+ $digram{'u'}{'k'} = ANY_COMBINATION;
+ $digram{'u'}{'l'} = ANY_COMBINATION;
+ $digram{'u'}{'m'} = ANY_COMBINATION;
+ $digram{'u'}{'n'} = ANY_COMBINATION;
+ $digram{'u'}{'o'} = NOT_FRONT | BREAK;
+ $digram{'u'}{'p'} = ANY_COMBINATION;
+ $digram{'u'}{'r'} = ANY_COMBINATION;
+ $digram{'u'}{'s'} = ANY_COMBINATION;
+ $digram{'u'}{'t'} = ANY_COMBINATION;
+ $digram{'u'}{'u'} = ILLEGAL_PAIR;
+ $digram{'u'}{'v'} = ANY_COMBINATION;
+ $digram{'u'}{'w'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'u'}{'x'} = ANY_COMBINATION;
+ $digram{'u'}{'y'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'u'}{'z'} = ANY_COMBINATION;
+ $digram{'u'}{'ch'} = ANY_COMBINATION;
+ $digram{'u'}{'gh'} = NOT_FRONT | PREFIX;
+ $digram{'u'}{'ph'} = ANY_COMBINATION;
+ $digram{'u'}{'rh'} = ILLEGAL_PAIR;
+ $digram{'u'}{'sh'} = ANY_COMBINATION;
+ $digram{'u'}{'th'} = ANY_COMBINATION;
+ $digram{'u'}{'wh'} = ILLEGAL_PAIR;
+ $digram{'u'}{'qu'} = BREAK | NOT_BACK;
+ $digram{'u'}{'ck'} = ANY_COMBINATION;
+
+ $digram{'v'}{'a'} = ANY_COMBINATION;
+ $digram{'v'}{'b'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'v'}{'c'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'v'}{'d'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'v'}{'e'} = ANY_COMBINATION;
+ $digram{'v'}{'f'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'v'}{'g'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'v'}{'h'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'v'}{'i'} = ANY_COMBINATION;
+ $digram{'v'}{'j'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'v'}{'k'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'v'}{'l'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'v'}{'m'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'v'}{'n'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'v'}{'o'} = ANY_COMBINATION;
+ $digram{'v'}{'p'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'v'}{'r'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'v'}{'s'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'v'}{'t'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'v'}{'u'} = ANY_COMBINATION;
+ $digram{'v'}{'v'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'v'}{'w'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'v'}{'x'} = ILLEGAL_PAIR;
+ $digram{'v'}{'y'} = NOT_FRONT;
+ $digram{'v'}{'z'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'v'}{'ch'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'v'}{'gh'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'v'}{'ph'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'v'}{'rh'} = ILLEGAL_PAIR;
+ $digram{'v'}{'sh'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'v'}{'th'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'v'}{'wh'} = ILLEGAL_PAIR;
+ $digram{'v'}{'qu'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'v'}{'ck'} = ILLEGAL_PAIR;
+
+ $digram{'w'}{'a'} = ANY_COMBINATION;
+ $digram{'w'}{'b'} = NOT_FRONT | PREFIX;
+ $digram{'w'}{'c'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'w'}{'d'} = NOT_FRONT | PREFIX | BACK;
+ $digram{'w'}{'e'} = ANY_COMBINATION;
+ $digram{'w'}{'f'} = NOT_FRONT | PREFIX;
+ $digram{'w'}{'g'} = NOT_FRONT | PREFIX | BACK;
+ $digram{'w'}{'h'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'w'}{'i'} = ANY_COMBINATION;
+ $digram{'w'}{'j'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'w'}{'k'} = NOT_FRONT | PREFIX;
+ $digram{'w'}{'l'} = NOT_FRONT | PREFIX | SUFFIX;
+ $digram{'w'}{'m'} = NOT_FRONT | PREFIX;
+ $digram{'w'}{'n'} = NOT_FRONT | PREFIX;
+ $digram{'w'}{'o'} = ANY_COMBINATION;
+ $digram{'w'}{'p'} = NOT_FRONT | PREFIX;
+ $digram{'w'}{'r'} = FRONT | SUFFIX | NOT_BACK;
+ $digram{'w'}{'s'} = NOT_FRONT | PREFIX;
+ $digram{'w'}{'t'} = NOT_FRONT | PREFIX;
+ $digram{'w'}{'u'} = ANY_COMBINATION;
+ $digram{'w'}{'v'} = NOT_FRONT | PREFIX;
+ $digram{'w'}{'w'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'w'}{'x'} = NOT_FRONT | PREFIX;
+ $digram{'w'}{'y'} = ANY_COMBINATION;
+ $digram{'w'}{'z'} = NOT_FRONT | PREFIX;
+ $digram{'w'}{'ch'} = NOT_FRONT;
+ $digram{'w'}{'gh'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'w'}{'ph'} = NOT_FRONT;
+ $digram{'w'}{'rh'} = ILLEGAL_PAIR;
+ $digram{'w'}{'sh'} = NOT_FRONT;
+ $digram{'w'}{'th'} = NOT_FRONT;
+ $digram{'w'}{'wh'} = ILLEGAL_PAIR;
+ $digram{'w'}{'qu'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'w'}{'ck'} = NOT_FRONT;
+
+ $digram{'x'}{'a'} = NOT_FRONT;
+ $digram{'x'}{'b'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'x'}{'c'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'x'}{'d'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'x'}{'e'} = NOT_FRONT;
+ $digram{'x'}{'f'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'x'}{'g'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'x'}{'h'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'x'}{'i'} = NOT_FRONT;
+ $digram{'x'}{'j'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'x'}{'k'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'x'}{'l'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'x'}{'m'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'x'}{'n'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'x'}{'o'} = NOT_FRONT;
+ $digram{'x'}{'p'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'x'}{'r'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'x'}{'s'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'x'}{'t'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'x'}{'u'} = NOT_FRONT;
+ $digram{'x'}{'v'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'x'}{'w'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'x'}{'x'} = ILLEGAL_PAIR;
+ $digram{'x'}{'y'} = NOT_FRONT;
+ $digram{'x'}{'z'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'x'}{'ch'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'x'}{'gh'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'x'}{'ph'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'x'}{'rh'} = ILLEGAL_PAIR;
+ $digram{'x'}{'sh'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'x'}{'th'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'x'}{'wh'} = ILLEGAL_PAIR;
+ $digram{'x'}{'qu'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'x'}{'ck'} = ILLEGAL_PAIR;
+
+ $digram{'y'}{'a'} = ANY_COMBINATION;
+ $digram{'y'}{'b'} = NOT_FRONT;
+ $digram{'y'}{'c'} = NOT_FRONT | NOT_BACK;
+ $digram{'y'}{'d'} = NOT_FRONT;
+ $digram{'y'}{'e'} = ANY_COMBINATION;
+ $digram{'y'}{'f'} = NOT_FRONT | NOT_BACK;
+ $digram{'y'}{'g'} = NOT_FRONT;
+ $digram{'y'}{'h'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'y'}{'i'} = FRONT | NOT_BACK;
+ $digram{'y'}{'j'} = NOT_FRONT | NOT_BACK;
+ $digram{'y'}{'k'} = NOT_FRONT;
+ $digram{'y'}{'l'} = NOT_FRONT | NOT_BACK;
+ $digram{'y'}{'m'} = NOT_FRONT;
+ $digram{'y'}{'n'} = NOT_FRONT;
+ $digram{'y'}{'o'} = ANY_COMBINATION;
+ $digram{'y'}{'p'} = NOT_FRONT;
+ $digram{'y'}{'r'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'y'}{'s'} = NOT_FRONT;
+ $digram{'y'}{'t'} = NOT_FRONT;
+ $digram{'y'}{'u'} = ANY_COMBINATION;
+ $digram{'y'}{'v'} = NOT_FRONT | NOT_BACK;
+ $digram{'y'}{'w'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'y'}{'x'} = NOT_FRONT;
+ $digram{'y'}{'y'} = ILLEGAL_PAIR;
+ $digram{'y'}{'z'} = NOT_FRONT;
+ $digram{'y'}{'ch'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'y'}{'gh'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'y'}{'ph'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'y'}{'rh'} = ILLEGAL_PAIR;
+ $digram{'y'}{'sh'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'y'}{'th'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'y'}{'wh'} = ILLEGAL_PAIR;
+ $digram{'y'}{'qu'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'y'}{'ck'} = ILLEGAL_PAIR;
+
+ $digram{'z'}{'a'} = ANY_COMBINATION;
+ $digram{'z'}{'b'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'z'}{'c'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'z'}{'d'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'z'}{'e'} = ANY_COMBINATION;
+ $digram{'z'}{'f'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'z'}{'g'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'z'}{'h'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'z'}{'i'} = ANY_COMBINATION;
+ $digram{'z'}{'j'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'z'}{'k'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'z'}{'l'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'z'}{'m'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'z'}{'n'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'z'}{'o'} = ANY_COMBINATION;
+ $digram{'z'}{'p'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'z'}{'r'} = NOT_FRONT | NOT_BACK;
+ $digram{'z'}{'s'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'z'}{'t'} = NOT_FRONT;
+ $digram{'z'}{'u'} = ANY_COMBINATION;
+ $digram{'z'}{'v'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'z'}{'w'} = SUFFIX | NOT_BACK;
+ $digram{'z'}{'x'} = ILLEGAL_PAIR;
+ $digram{'z'}{'y'} = ANY_COMBINATION;
+ $digram{'z'}{'z'} = NOT_FRONT;
+ $digram{'z'}{'ch'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'z'}{'gh'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'z'}{'ph'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'z'}{'rh'} = ILLEGAL_PAIR;
+ $digram{'z'}{'sh'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'z'}{'th'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'z'}{'wh'} = ILLEGAL_PAIR;
+ $digram{'z'}{'qu'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'z'}{'ck'} = ILLEGAL_PAIR;
+
+ $digram{'ch'}{'a'} = ANY_COMBINATION;
+ $digram{'ch'}{'b'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'ch'}{'c'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'ch'}{'d'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'ch'}{'e'} = ANY_COMBINATION;
+ $digram{'ch'}{'f'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'ch'}{'g'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'ch'}{'h'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'ch'}{'i'} = ANY_COMBINATION;
+ $digram{'ch'}{'j'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'ch'}{'k'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'ch'}{'l'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'ch'}{'m'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'ch'}{'n'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'ch'}{'o'} = ANY_COMBINATION;
+ $digram{'ch'}{'p'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'ch'}{'r'} = NOT_BACK;
+ $digram{'ch'}{'s'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'ch'}{'t'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'ch'}{'u'} = ANY_COMBINATION;
+ $digram{'ch'}{'v'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'ch'}{'w'} = NOT_FRONT | NOT_BACK;
+ $digram{'ch'}{'x'} = ILLEGAL_PAIR;
+ $digram{'ch'}{'y'} = ANY_COMBINATION;
+ $digram{'ch'}{'z'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'ch'}{'ch'} = ILLEGAL_PAIR;
+ $digram{'ch'}{'gh'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'ch'}{'ph'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'ch'}{'rh'} = ILLEGAL_PAIR;
+ $digram{'ch'}{'sh'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'ch'}{'th'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'ch'}{'wh'} = ILLEGAL_PAIR;
+ $digram{'ch'}{'qu'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'ch'}{'ck'} = ILLEGAL_PAIR;
+
+ $digram{'gh'}{'a'} = ANY_COMBINATION;
+ $digram{'gh'}{'b'} = NOT_FRONT | BREAK | PREFIX | NOT_BACK;
+ $digram{'gh'}{'c'} = NOT_FRONT | BREAK | PREFIX | NOT_BACK;
+ $digram{'gh'}{'d'} = NOT_FRONT | BREAK | PREFIX | NOT_BACK;
+ $digram{'gh'}{'e'} = ANY_COMBINATION;
+ $digram{'gh'}{'f'} = NOT_FRONT | BREAK | PREFIX | NOT_BACK;
+ $digram{'gh'}{'g'} = NOT_FRONT | BREAK | PREFIX | NOT_BACK;
+ $digram{'gh'}{'h'} = NOT_FRONT | BREAK | PREFIX | NOT_BACK;
+ $digram{'gh'}{'i'} = FRONT | NOT_BACK;
+ $digram{'gh'}{'j'} = NOT_FRONT | BREAK | PREFIX | NOT_BACK;
+ $digram{'gh'}{'k'} = NOT_FRONT | BREAK | PREFIX | NOT_BACK;
+ $digram{'gh'}{'l'} = NOT_FRONT | BREAK | PREFIX | NOT_BACK;
+ $digram{'gh'}{'m'} = NOT_FRONT | BREAK | PREFIX | NOT_BACK;
+ $digram{'gh'}{'n'} = NOT_FRONT | BREAK | PREFIX | NOT_BACK;
+ $digram{'gh'}{'o'} = FRONT | NOT_BACK;
+ $digram{'gh'}{'p'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'gh'}{'r'} = NOT_FRONT | BREAK | PREFIX | NOT_BACK;
+ $digram{'gh'}{'s'} = NOT_FRONT | PREFIX;
+ $digram{'gh'}{'t'} = NOT_FRONT | PREFIX;
+ $digram{'gh'}{'u'} = NOT_FRONT | BREAK | PREFIX | NOT_BACK;
+ $digram{'gh'}{'v'} = NOT_FRONT | BREAK | PREFIX | NOT_BACK;
+ $digram{'gh'}{'w'} = NOT_FRONT | BREAK | PREFIX | NOT_BACK;
+ $digram{'gh'}{'x'} = ILLEGAL_PAIR;
+ $digram{'gh'}{'y'} = NOT_FRONT | BREAK | PREFIX | NOT_BACK;
+ $digram{'gh'}{'z'} = NOT_FRONT | BREAK | PREFIX | NOT_BACK;
+ $digram{'gh'}{'ch'} = NOT_FRONT | BREAK | PREFIX | NOT_BACK;
+ $digram{'gh'}{'gh'} = ILLEGAL_PAIR;
+ $digram{'gh'}{'ph'} = NOT_FRONT | BREAK | PREFIX | NOT_BACK;
+ $digram{'gh'}{'rh'} = ILLEGAL_PAIR;
+ $digram{'gh'}{'sh'} = NOT_FRONT | BREAK | PREFIX | NOT_BACK;
+ $digram{'gh'}{'th'} = NOT_FRONT | BREAK | PREFIX | NOT_BACK;
+ $digram{'gh'}{'wh'} = ILLEGAL_PAIR;
+ $digram{'gh'}{'qu'} = NOT_FRONT | BREAK | PREFIX | NOT_BACK;
+ $digram{'gh'}{'ck'} = ILLEGAL_PAIR;
+
+ $digram{'ph'}{'a'} = ANY_COMBINATION;
+ $digram{'ph'}{'b'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'ph'}{'c'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'ph'}{'d'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'ph'}{'e'} = ANY_COMBINATION;
+ $digram{'ph'}{'f'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'ph'}{'g'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'ph'}{'h'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'ph'}{'i'} = ANY_COMBINATION;
+ $digram{'ph'}{'j'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'ph'}{'k'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'ph'}{'l'} = FRONT | SUFFIX | NOT_BACK;
+ $digram{'ph'}{'m'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'ph'}{'n'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'ph'}{'o'} = ANY_COMBINATION;
+ $digram{'ph'}{'p'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'ph'}{'r'} = NOT_BACK;
+ $digram{'ph'}{'s'} = NOT_FRONT;
+ $digram{'ph'}{'t'} = NOT_FRONT;
+ $digram{'ph'}{'u'} = ANY_COMBINATION;
+ $digram{'ph'}{'v'} = NOT_FRONT | NOT_BACK;
+ $digram{'ph'}{'w'} = NOT_FRONT | NOT_BACK;
+ $digram{'ph'}{'x'} = ILLEGAL_PAIR;
+ $digram{'ph'}{'y'} = NOT_FRONT;
+ $digram{'ph'}{'z'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'ph'}{'ch'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'ph'}{'gh'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'ph'}{'ph'} = ILLEGAL_PAIR;
+ $digram{'ph'}{'rh'} = ILLEGAL_PAIR;
+ $digram{'ph'}{'sh'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'ph'}{'th'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'ph'}{'wh'} = ILLEGAL_PAIR;
+ $digram{'ph'}{'qu'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'ph'}{'ck'} = ILLEGAL_PAIR;
+
+ $digram{'rh'}{'a'} = FRONT | NOT_BACK;
+ $digram{'rh'}{'b'} = ILLEGAL_PAIR;
+ $digram{'rh'}{'c'} = ILLEGAL_PAIR;
+ $digram{'rh'}{'d'} = ILLEGAL_PAIR;
+ $digram{'rh'}{'e'} = FRONT | NOT_BACK;
+ $digram{'rh'}{'f'} = ILLEGAL_PAIR;
+ $digram{'rh'}{'g'} = ILLEGAL_PAIR;
+ $digram{'rh'}{'h'} = ILLEGAL_PAIR;
+ $digram{'rh'}{'i'} = FRONT | NOT_BACK;
+ $digram{'rh'}{'j'} = ILLEGAL_PAIR;
+ $digram{'rh'}{'k'} = ILLEGAL_PAIR;
+ $digram{'rh'}{'l'} = ILLEGAL_PAIR;
+ $digram{'rh'}{'m'} = ILLEGAL_PAIR;
+ $digram{'rh'}{'n'} = ILLEGAL_PAIR;
+ $digram{'rh'}{'o'} = FRONT | NOT_BACK;
+ $digram{'rh'}{'p'} = ILLEGAL_PAIR;
+ $digram{'rh'}{'r'} = ILLEGAL_PAIR;
+ $digram{'rh'}{'s'} = ILLEGAL_PAIR;
+ $digram{'rh'}{'t'} = ILLEGAL_PAIR;
+ $digram{'rh'}{'u'} = FRONT | NOT_BACK;
+ $digram{'rh'}{'v'} = ILLEGAL_PAIR;
+ $digram{'rh'}{'w'} = ILLEGAL_PAIR;
+ $digram{'rh'}{'x'} = ILLEGAL_PAIR;
+ $digram{'rh'}{'y'} = FRONT | NOT_BACK;
+ $digram{'rh'}{'z'} = ILLEGAL_PAIR;
+ $digram{'rh'}{'ch'} = ILLEGAL_PAIR;
+ $digram{'rh'}{'gh'} = ILLEGAL_PAIR;
+ $digram{'rh'}{'ph'} = ILLEGAL_PAIR;
+ $digram{'rh'}{'rh'} = ILLEGAL_PAIR;
+ $digram{'rh'}{'sh'} = ILLEGAL_PAIR;
+ $digram{'rh'}{'th'} = ILLEGAL_PAIR;
+ $digram{'rh'}{'wh'} = ILLEGAL_PAIR;
+ $digram{'rh'}{'qu'} = ILLEGAL_PAIR;
+ $digram{'rh'}{'ck'} = ILLEGAL_PAIR;
+
+ $digram{'sh'}{'a'} = ANY_COMBINATION;
+ $digram{'sh'}{'b'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'sh'}{'c'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'sh'}{'d'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'sh'}{'e'} = ANY_COMBINATION;
+ $digram{'sh'}{'f'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'sh'}{'g'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'sh'}{'h'} = ILLEGAL_PAIR;
+ $digram{'sh'}{'i'} = ANY_COMBINATION;
+ $digram{'sh'}{'j'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'sh'}{'k'} = NOT_FRONT;
+ $digram{'sh'}{'l'} = FRONT | SUFFIX | NOT_BACK;
+ $digram{'sh'}{'m'} = FRONT | SUFFIX | NOT_BACK;
+ $digram{'sh'}{'n'} = FRONT | SUFFIX | NOT_BACK;
+ $digram{'sh'}{'o'} = ANY_COMBINATION;
+ $digram{'sh'}{'p'} = NOT_FRONT;
+ $digram{'sh'}{'r'} = FRONT | SUFFIX | NOT_BACK;
+ $digram{'sh'}{'s'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'sh'}{'t'} = SUFFIX;
+ $digram{'sh'}{'u'} = ANY_COMBINATION;
+ $digram{'sh'}{'v'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'sh'}{'w'} = SUFFIX | NOT_BACK;
+ $digram{'sh'}{'x'} = ILLEGAL_PAIR;
+ $digram{'sh'}{'y'} = ANY_COMBINATION;
+ $digram{'sh'}{'z'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'sh'}{'ch'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'sh'}{'gh'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'sh'}{'ph'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'sh'}{'rh'} = ILLEGAL_PAIR;
+ $digram{'sh'}{'sh'} = ILLEGAL_PAIR;
+ $digram{'sh'}{'th'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'sh'}{'wh'} = ILLEGAL_PAIR;
+ $digram{'sh'}{'qu'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'sh'}{'ck'} = ILLEGAL_PAIR;
+
+ $digram{'th'}{'a'} = ANY_COMBINATION;
+ $digram{'th'}{'b'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'th'}{'c'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'th'}{'d'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'th'}{'e'} = ANY_COMBINATION;
+ $digram{'th'}{'f'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'th'}{'g'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'th'}{'h'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'th'}{'i'} = ANY_COMBINATION;
+ $digram{'th'}{'j'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'th'}{'k'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'th'}{'l'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'th'}{'m'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'th'}{'n'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'th'}{'o'} = ANY_COMBINATION;
+ $digram{'th'}{'p'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'th'}{'r'} = NOT_BACK;
+ $digram{'th'}{'s'} = NOT_FRONT | BACK;
+ $digram{'th'}{'t'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'th'}{'u'} = ANY_COMBINATION;
+ $digram{'th'}{'v'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'th'}{'w'} = SUFFIX | NOT_BACK;
+ $digram{'th'}{'x'} = ILLEGAL_PAIR;
+ $digram{'th'}{'y'} = ANY_COMBINATION;
+ $digram{'th'}{'z'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'th'}{'ch'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'th'}{'gh'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'th'}{'ph'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'th'}{'rh'} = ILLEGAL_PAIR;
+ $digram{'th'}{'sh'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'th'}{'th'} = ILLEGAL_PAIR;
+ $digram{'th'}{'wh'} = ILLEGAL_PAIR;
+ $digram{'th'}{'qu'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'th'}{'ck'} = ILLEGAL_PAIR;
+
+ $digram{'wh'}{'a'} = FRONT | NOT_BACK;
+ $digram{'wh'}{'b'} = ILLEGAL_PAIR;
+ $digram{'wh'}{'c'} = ILLEGAL_PAIR;
+ $digram{'wh'}{'d'} = ILLEGAL_PAIR;
+ $digram{'wh'}{'e'} = FRONT | NOT_BACK;
+ $digram{'wh'}{'f'} = ILLEGAL_PAIR;
+ $digram{'wh'}{'g'} = ILLEGAL_PAIR;
+ $digram{'wh'}{'h'} = ILLEGAL_PAIR;
+ $digram{'wh'}{'i'} = FRONT | NOT_BACK;
+ $digram{'wh'}{'j'} = ILLEGAL_PAIR;
+ $digram{'wh'}{'k'} = ILLEGAL_PAIR;
+ $digram{'wh'}{'l'} = ILLEGAL_PAIR;
+ $digram{'wh'}{'m'} = ILLEGAL_PAIR;
+ $digram{'wh'}{'n'} = ILLEGAL_PAIR;
+ $digram{'wh'}{'o'} = FRONT | NOT_BACK;
+ $digram{'wh'}{'p'} = ILLEGAL_PAIR;
+ $digram{'wh'}{'r'} = ILLEGAL_PAIR;
+ $digram{'wh'}{'s'} = ILLEGAL_PAIR;
+ $digram{'wh'}{'t'} = ILLEGAL_PAIR;
+ $digram{'wh'}{'u'} = ILLEGAL_PAIR;
+ $digram{'wh'}{'v'} = ILLEGAL_PAIR;
+ $digram{'wh'}{'w'} = ILLEGAL_PAIR;
+ $digram{'wh'}{'x'} = ILLEGAL_PAIR;
+ $digram{'wh'}{'y'} = FRONT | NOT_BACK;
+ $digram{'wh'}{'z'} = ILLEGAL_PAIR;
+ $digram{'wh'}{'ch'} = ILLEGAL_PAIR;
+ $digram{'wh'}{'gh'} = ILLEGAL_PAIR;
+ $digram{'wh'}{'ph'} = ILLEGAL_PAIR;
+ $digram{'wh'}{'rh'} = ILLEGAL_PAIR;
+ $digram{'wh'}{'sh'} = ILLEGAL_PAIR;
+ $digram{'wh'}{'th'} = ILLEGAL_PAIR;
+ $digram{'wh'}{'wh'} = ILLEGAL_PAIR;
+ $digram{'wh'}{'qu'} = ILLEGAL_PAIR;
+ $digram{'wh'}{'ck'} = ILLEGAL_PAIR;
+
+ $digram{'qu'}{'a'} = ANY_COMBINATION;
+ $digram{'qu'}{'b'} = ILLEGAL_PAIR;
+ $digram{'qu'}{'c'} = ILLEGAL_PAIR;
+ $digram{'qu'}{'d'} = ILLEGAL_PAIR;
+ $digram{'qu'}{'e'} = ANY_COMBINATION;
+ $digram{'qu'}{'f'} = ILLEGAL_PAIR;
+ $digram{'qu'}{'g'} = ILLEGAL_PAIR;
+ $digram{'qu'}{'h'} = ILLEGAL_PAIR;
+ $digram{'qu'}{'i'} = ANY_COMBINATION;
+ $digram{'qu'}{'j'} = ILLEGAL_PAIR;
+ $digram{'qu'}{'k'} = ILLEGAL_PAIR;
+ $digram{'qu'}{'l'} = ILLEGAL_PAIR;
+ $digram{'qu'}{'m'} = ILLEGAL_PAIR;
+ $digram{'qu'}{'n'} = ILLEGAL_PAIR;
+ $digram{'qu'}{'o'} = ANY_COMBINATION;
+ $digram{'qu'}{'p'} = ILLEGAL_PAIR;
+ $digram{'qu'}{'r'} = ILLEGAL_PAIR;
+ $digram{'qu'}{'s'} = ILLEGAL_PAIR;
+ $digram{'qu'}{'t'} = ILLEGAL_PAIR;
+ $digram{'qu'}{'u'} = ILLEGAL_PAIR;
+ $digram{'qu'}{'v'} = ILLEGAL_PAIR;
+ $digram{'qu'}{'w'} = ILLEGAL_PAIR;
+ $digram{'qu'}{'x'} = ILLEGAL_PAIR;
+ $digram{'qu'}{'y'} = ILLEGAL_PAIR;
+ $digram{'qu'}{'z'} = ILLEGAL_PAIR;
+ $digram{'qu'}{'ch'} = ILLEGAL_PAIR;
+ $digram{'qu'}{'gh'} = ILLEGAL_PAIR;
+ $digram{'qu'}{'ph'} = ILLEGAL_PAIR;
+ $digram{'qu'}{'rh'} = ILLEGAL_PAIR;
+ $digram{'qu'}{'sh'} = ILLEGAL_PAIR;
+ $digram{'qu'}{'th'} = ILLEGAL_PAIR;
+ $digram{'qu'}{'wh'} = ILLEGAL_PAIR;
+ $digram{'qu'}{'qu'} = ILLEGAL_PAIR;
+ $digram{'qu'}{'ck'} = ILLEGAL_PAIR;
+
+ $digram{'ck'}{'a'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'ck'}{'b'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'ck'}{'c'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'ck'}{'d'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'ck'}{'e'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'ck'}{'f'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'ck'}{'g'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'ck'}{'h'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'ck'}{'i'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'ck'}{'j'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'ck'}{'k'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'ck'}{'l'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'ck'}{'m'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'ck'}{'n'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'ck'}{'o'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'ck'}{'p'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'ck'}{'r'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'ck'}{'s'} = NOT_FRONT;
+ $digram{'ck'}{'t'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'ck'}{'u'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'ck'}{'v'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'ck'}{'w'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'ck'}{'x'} = ILLEGAL_PAIR;
+ $digram{'ck'}{'y'} = NOT_FRONT;
+ $digram{'ck'}{'z'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'ck'}{'ch'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'ck'}{'gh'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'ck'}{'ph'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'ck'}{'rh'} = ILLEGAL_PAIR;
+ $digram{'ck'}{'sh'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'ck'}{'th'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'ck'}{'wh'} = ILLEGAL_PAIR;
+ $digram{'ck'}{'qu'} = NOT_FRONT | BREAK | NOT_BACK;
+ $digram{'ck'}{'ck'} = ILLEGAL_PAIR;
+
+ ##############################################################################################
+ # } END DIGRAM
+ ##############################################################################################
+
+
+
+sub report(@) {
+ $main::DEBUG and print @_;
+}
+
+
+
+
+=head2 word
+
+ word = word( minlen, maxlen );
+ ( word, hyphenated_form ) = word( minlen, maxlen );
+
+Generates a random word, as well as its hyphenated form.
+The length of the returned word will be between minlen and maxlen.
+
+=cut
+
+sub word($$) {
+ @_ > 2 and shift;
+ my( $minlen, $maxlen ) = @_;
+
+ $minlen <= $maxlen or die "minlen $minlen is greater than maxlen $maxlen";
+
+ init();
+
+ #
+ # Check for zero length words. This is technically not an error,
+ # so we take the short cut and return empty words.
+ #
+ $maxlen or return wantarray ? ('','') : '';
+
+ my( $word, $hyphenated_word );
+
+ for ( my $try = 1 ; $try <= MAX_UNACCEPTABLE and not defined $word; $try++ ) {
+ ( $word, $hyphenated_word ) = _random_word( rand_int_in_range( $minlen, $maxlen ) );
+ $word = restrict( $word );
+ }
+
+ $word or die "failed to generate an acceptable random password.\n";
+
+ return wantarray ? ( $word, $hyphenated_word ) : $word;
+}
+
+
+=head2 letters
+
+ word = letters( minlen, maxlen );
+
+Generates a string of random letters.
+The length of the returned word is between minlen and maxlen.
+Calls C<random_chars_in_range( 'a' => 'z' )>.
+
+=cut
+
+sub letters($$) {
+ @_ > 2 and shift;
+ my( $minlen, $maxlen ) = @_;
+ random_chars_in_range( $minlen, $maxlen, 'a' => 'z' ); # range of lowercase letters in ASCII
+}
+
+
+=head2 chars
+
+ word = chars( minlen, maxlen );
+
+Generates a string of random printable characters.
+The length of the returned word is between minlen and maxlen.
+Calls C<random_chars_in_range( '!' => '~' )>.
+
+=cut
+
+sub chars($$) {
+ @_ > 2 and shift;
+ my( $minlen, $maxlen ) = @_;
+ random_chars_in_range( $minlen, $maxlen, '!' => '~' ); # range of printable chars in ASCII
+}
+
+
+
+=head2 random_chars_in_range
+
+ word = random_chars_in_range( minlen, maxlen, lo_char => hi_char );
+
+Generates a string of printable characters.
+The length of the returned string is between minlen and maxlen.
+Each character is selected from the range of ASCII characters
+delimited by (lo_char,hi_char).
+
+=cut
+
+sub random_chars_in_range($$$$) {
+ my( $minlen, $maxlen, $lo_char, $hi_char ) = @_;
+
+ $minlen <= $maxlen or die "minlen $minlen is greater than maxlen $maxlen";
+
+ init();
+
+ my $string_size = rand_int_in_range( $minlen, $maxlen );
+
+ my $string;
+ for ( my $try = 1 ; $try <= MAX_UNACCEPTABLE and not defined $string; $try++ ) {
+ my $s = '';
+ while ( length($s) < $string_size ) {
+ $s .= chr( rand_int_in_range( ord($lo_char), ord($hi_char) ) );
+ }
+ next if length($s) > $string_size;
+ $string = restrict( $s );
+ }
+
+ $string
+}
+
+
+
+=head2 rand_int_in_range
+
+ n = rand_int_in_range( min, max );
+
+Returns an integer between min and max, inclusive.
+Calls C<rng> like so:
+
+ n = min + int( rng( max - min + 1 ) )
+
+=cut
+
+sub rand_int_in_range($$) {
+ my( $min, $max ) = @_;
+ $min + int( rng( $max - $min + 1 ) )
+}
+
+
+=head2 random_element
+
+ e = random_element( \@elts )
+
+Selects a random element from an array, which is passed by ref.
+
+=cut
+
+sub random_element($) {
+ my $ar = shift;
+ $ar->[ rand_int_in_range( 0, $#{$ar} ) ]
+}
+
+
+
+=head2 rng
+
+ r = rng( n );
+
+C<rng> is designed to have the same interface as the built-in C<rand> function.
+The default implementation here is a simple wrapper around C<rand>,
+which is typically a wrapper for some pseudo-random number function in the
+underlying C library.
+
+The reason for having this simple wrapper is so the user can
+easily substitute a different random number generator if desired.
+Since many rng's have the same interface as C<rand>, replacing C<rng()>
+is as simple as
+
+ {
+ local $^W; # squelch sub redef warning.
+ *Crypt::RandPasswd::rng = \&my_rng;
+ }
+
+See L<rand>.
+
+=cut
+
+sub rng($) {
+ my $x = shift;
+ rand($x)
+}
+
+
+
+=head2 restrict
+
+ word = restrict( word );
+
+A filter. Returns the arg unchanged if it is allowable; returns undef if not.
+
+The default version of C<restrict()> allows everything.
+You may install a different form to implement other restrictions,
+by doing something like this:
+
+ {
+ local $^W; # squelch sub redef warning.
+ *Crypt::RandPasswd::restrict = \&my_filter;
+ }
+
+=cut
+
+sub restrict($) { $_[0] } # MUST return a real scalar; returning @_ causes scalar(@_) !!!
+
+
+=head2 init
+
+This initializes the environment, which by default simply seeds the random number generator.
+
+=cut
+
+# can be called multiple times without harm, since it remembers whether
+# it has already been called.
+
+sub init() {
+ unless ( $Crypt::RandPasswd::initialized ) {
+ # only do stuff if I haven't already been called before.
+
+ $Crypt::RandPasswd::initialized = 1;
+ if ( defined $Crypt::RandPasswd::seed ) {
+ srand( $Crypt::RandPasswd::seed );
+ }
+ else {
+ srand; # use default, which can be pretty good.
+ }
+ }
+}
+
+
+
+
+=head2 _random_word
+
+This is the routine that returns a random word.
+It collects random syllables until a predetermined word length is found.
+If a retry threshold is reached, another word is tried.
+
+returns ( word, hyphenated_word ).
+
+=cut
+
+sub _random_word($) {
+ my( $pwlen ) = @_;
+
+ my $word = '';
+ my @word_syllables;
+
+ my $max_retries = ( 4 * $pwlen ) + scalar( @grams );
+
+ my $tries = 0; # count of retries.
+
+
+ # @word_units used to be an array of indices into the 'rules' C-array.
+ # now it's an array of actual units (grams).
+ my @word_units;
+
+ #
+ # Find syllables until the entire word is constructed.
+ #
+ while ( length($word) < $pwlen ) {
+ #
+ # Get the syllable and find its length.
+ #
+ report "About to call get_syllable( $pwlen - length($word) )\n";
+ my( $new_syllable, @syllable_units ) = get_syllable( $pwlen - length($word) );
+ report "get_syllable returned ( $new_syllable; @syllable_units )\n";
+
+ #
+ # If the word has been improperly formed, throw out
+ # the syllable. The checks performed here are those
+ # that must be formed on a word basis. The other
+ # tests are performed entirely within the syllable.
+ # Otherwise, append the syllable to the word.
+ #
+ unless (
+ _improper_word( @word_units, @syllable_units ) # join the arrays
+ ||
+ (
+ $word eq ''
+ and
+ _have_initial_y( @syllable_units )
+ )
+ ||
+ (
+ length( $word . $new_syllable ) == $pwlen
+ and
+ _have_final_split( @syllable_units )
+ )
+ ) {
+ $word .= $new_syllable;
+ push @word_syllables, $new_syllable;
+ }
+
+ #
+ # Keep track of the times we have tried to get syllables.
+ # If we have exceeded the threshold, start from scratch.
+ #
+ $tries++;
+ if ( $tries > $max_retries ) {
+ $tries = 0;
+ $word = '';
+ @word_syllables = ();
+ @word_units = ();
+ }
+ }
+
+ return( $word, join('-',@word_syllables) );
+}
+
+
+
+=head2 _random_unit
+
+Selects a gram (aka "unit").
+This is the standard random unit generating routine for get_syllable().
+
+This routine attempts to return grams (units) with a distribution
+approaching that of the distribution of the units in English.
+
+The distribution of the units may be altered in this procedure without
+affecting the digram table or any other programs using the random_word subroutine,
+as long as the set of grams (units) is kept consistent throughout this library.
+
+I<NOTE that where this func used to return a numeric index into
+the 'rules' C-array, it now returns a gram.>
+
+=cut
+
+my %occurrence_frequencies = (
+ 'a' => 10, 'b' => 8, 'c' => 12, 'd' => 12,
+ 'e' => 12, 'f' => 8, 'g' => 8, 'h' => 6,
+ 'i' => 10, 'j' => 8, 'k' => 8, 'l' => 6,
+ 'm' => 6, 'n' => 10, 'o' => 10, 'p' => 6,
+ 'r' => 10, 's' => 8, 't' => 10, 'u' => 6,
+ 'v' => 8, 'w' => 8, 'x' => 1, 'y' => 8,
+ 'z' => 1, 'ch' => 1, 'gh' => 1, 'ph' => 1,
+ 'rh' => 1, 'sh' => 2, 'th' => 1, 'wh' => 1,
+ 'qu' => 1, 'ck' => 1,
+);
+
+my @numbers = map {
+ ( ($_) x $occurrence_frequencies{$_} )
+} @grams;
+
+my @vowel_numbers = map {
+ ( ($_) x $occurrence_frequencies{$_} )
+} @vowel_grams;
+
+
+
+sub _random_unit($) {
+ my $type = shift; # byte
+
+ random_element( $type & VOWEL
+ ? \@vowel_numbers # Sometimes, we are asked to explicitly get a vowel (i.e., if
+ # a digram pair expects one following it). This is a shortcut
+ # to do that and avoid looping with rejected consonants.
+
+ : \@numbers # Get any letter according to the English distribution.
+ )
+}
+
+
+
+
+=head2 _improper_word
+
+Check that the word does not contain illegal combinations
+that may span syllables. Specifically, these are:
+
+ 1. An illegal pair of units between syllables.
+ 2. Three consecutive vowel units.
+ 3. Three consecutive consonant units.
+
+The checks are made against units (1 or 2 letters), not against
+the individual letters, so three consecutive units can have
+the length of 6 at most.
+
+returns boolean
+
+=cut
+
+sub _improper_word(@) {
+ my @units = @_;
+
+ my $failure; # bool, init False.
+
+ for my $unit_count ( 0 .. $#units ) {
+ #
+ # Check for ILLEGAL_PAIR.
+ # This should have been caught for units within a syllable,
+ # but in some cases it would have gone unnoticed for units between syllables
+ # (e.g., when saved units in get_syllable() were not used).
+ #
+ $unit_count > 0
+ and $digram{$units[$unit_count-1]}{$units[$unit_count]} & ILLEGAL_PAIR
+ and return(1); # Failure!
+
+ next if $unit_count < 2;
+ #
+ # Check for consecutive vowels or consonants.
+ # Because the initial y of a syllable is treated as a consonant rather
+ # than as a vowel, we exclude y from the first vowel in the vowel test.
+ # The only problem comes when y ends a syllable and two other vowels start the next, like fly-oint.
+ # Since such words are still pronounceable, we accept this.
+ #
+ #
+ # Vowel check.
+ #
+ (
+ ($rules{$units[$unit_count - 2]} & VOWEL)
+ &&
+ !($rules{$units[$unit_count - 2]} & ALTERNATE_VOWEL)
+ &&
+ ($rules{$units[$unit_count - 1]} & VOWEL)
+ &&
+ ($rules{$units[$unit_count ]} & VOWEL)
+ )
+ ||
+ #
+ # Consonant check.
+ #
+ (
+ !($rules{$units[$unit_count - 2]} & VOWEL)
+ &&
+ !($rules{$units[$unit_count - 1]} & VOWEL)
+ &&
+ !($rules{$units[$unit_count ]} & VOWEL)
+ )
+ and return(1); # Failure!
+ }
+
+ 0 # success
+}
+
+
+=head2 _have_initial_y
+
+Treating y as a vowel is sometimes a problem. Some words get formed that look irregular.
+One special group is when y starts a word and is the only vowel in the first syllable.
+The word ycl is one example. We discard words like these.
+
+return boolean
+
+=cut
+
+sub _have_initial_y(@) {
+ my @units = @_;
+
+ my $vowel_count = 0;
+ my $normal_vowel_count = 0;
+
+ for my $unit_count ( 0 .. $#units ) {
+ #
+ # Count vowels.
+ #
+ if ( $rules{$units[$unit_count]} & VOWEL ) {
+ $vowel_count++;
+
+ #
+ # Count the vowels that are not:
+ # 1. 'y'
+ # 2. at the start of the word.
+ #
+ if ( !($rules{$units[$unit_count]} & ALTERNATE_VOWEL) || ($unit_count > 0) ) {
+ $normal_vowel_count++;
+ }
+ }
+ }
+
+ ($vowel_count <= 1) && ($normal_vowel_count == 0)
+}
+
+
+=head2 _have_final_split
+
+Besides the problem with the letter y, there is one with
+a silent e at the end of words, like face or nice.
+We allow this silent e, but we do not allow it as the only
+vowel at the end of the word or syllables like ble will
+be generated.
+
+returns boolean
+
+=cut
+
+sub _have_final_split(@) {
+ my @units = @_;
+
+ my $vowel_count = 0;
+
+ #
+ # Count all the vowels in the word.
+ #
+ for my $unit_count ( 0 .. $#units ) {
+ if ( $rules{$units[$unit_count]} & VOWEL ) {
+ $vowel_count++;
+ }
+ }
+
+ #
+ # Return TRUE iff the only vowel was e, found at the end if the word.
+ #
+ ($vowel_count == 1) && ( $rules{$units[$#units]} & NO_FINAL_SPLIT )
+}
+
+
+=head2 get_syllable
+
+Generate next unit to password, making sure that it follows these rules:
+
+1. Each syllable must contain exactly 1 or 2 consecutive vowels, where y is considered a vowel.
+
+2. Syllable end is determined as follows:
+
+ a. Vowel is generated and previous unit is a consonant and syllable already has a vowel.
+ In this case, new syllable is started and already contains a vowel.
+ b. A pair determined to be a "break" pair is encountered.
+ In this case new syllable is started with second unit of this pair.
+ c. End of password is encountered.
+ d. "begin" pair is encountered legally. New syllable is started with this pair.
+ e. "end" pair is legally encountered. New syllable has nothing yet.
+
+3. Try generating another unit if:
+
+ a. third consecutive vowel and not y.
+ b. "break" pair generated but no vowel yet in current or previous 2 units are "not_end".
+ c. "begin" pair generated but no vowel in syllable preceding begin pair,
+ or both previous 2 pairs are designated "not_end".
+ d. "end" pair generated but no vowel in current syllable or in "end" pair.
+ e. "not_begin" pair generated but new syllable must begin (because previous syllable ended as defined in 2 above).
+ f. vowel is generated and 2a is satisfied, but no syllable break is possible in previous 3 pairs.
+ g. Second and third units of syllable must begin, and first unit is "alternate_vowel".
+
+
+=cut
+
+# global (like a C static)
+use vars qw( @saved_pair );
+@saved_pair = (); # 0..2 elements, which are units (grams).
+
+sub get_syllable($) {
+ my $pwlen = shift;
+
+ # these used to be "out" params:
+ my $syllable; # string, returned
+ my @units_in_syllable = (); # array of units, returned
+
+
+ # grams:
+ my $unit;
+ my $current_unit;
+ my $last_unit;
+
+ # numbers:
+ my $vowel_count;
+ my $tries;
+ my $length_left;
+
+ # flags:
+ my $rule_broken;
+ my $want_vowel;
+ my $want_another_unit;
+
+
+ #
+ # This is needed if the saved_pair is tried and the syllable then
+ # discarded because of the retry limit. Since the saved_pair is OK and
+ # fits in nicely with the preceding syllable, we will always use it.
+ #
+ my @hold_saved_pair = @saved_pair;
+
+ my $max_retries = ( 4 * $pwlen ) + scalar( @grams );
+ # note that this used to be a macro, which means it could have changed
+ # dynamically based on the value of $pwlen...
+
+ #
+ # Loop until valid syllable is found.
+ #
+ do {
+ #
+ # Try for a new syllable. Initialize all pertinent
+ # syllable variables.
+ #
+ $tries = 0;
+ @saved_pair = @hold_saved_pair;
+ $syllable = "";
+ $vowel_count = 0;
+ $current_unit = 0;
+ $length_left = $pwlen;
+ $want_another_unit = 1; # true
+
+ #
+ # This loop finds all the units for the syllable.
+ #
+ do {
+ $want_vowel = 0; # false
+
+ #
+ # This loop continues until a valid unit is found for the
+ # current position within the syllable.
+ #
+ do {
+ #
+ # If there are saved units from the previous syllable, use them up first.
+ #
+
+ #
+ # If there were two saved units, the first is guaranteed
+ # (by checks performed in the previous syllable) to be valid.
+ # We ignore the checks and place it in this syllable manually.
+ #
+ if ( @saved_pair == 2 ) {
+ $syllable =
+ $units_in_syllable[0] = pop @saved_pair;
+ $vowel_count++ if $rules{$syllable} & VOWEL;
+ $current_unit++;
+ $length_left -= length $syllable;
+ }
+
+ if ( @saved_pair ) {
+ #
+ # The unit becomes the last unit checked in the previous syllable.
+ #
+ $unit = pop @saved_pair;
+
+ #
+ # The saved units have been used.
+ # Do not try to reuse them in this syllable
+ # (unless this particular syllable is rejected
+ # at which point we start to rebuild it with these same saved units).
+ #
+ }
+ else {
+ #
+ # If we don't have to consider the saved units, we generate a random one.
+ #
+ $unit = _random_unit( $want_vowel ? VOWEL : NO_SPECIAL_RULE );
+ }
+
+ $length_left -= length $unit;
+
+ #
+ # Prevent having a word longer than expected.
+ #
+ $rule_broken = ( $length_left < 0 ); # boolean
+
+ #
+ # First unit of syllable.
+ # This is special because the digram tests require 2 units and we don't have that yet.
+ # Nevertheless, we can perform some checks.
+ #
+ if ( $current_unit == 0 ) {
+ #
+ # If the shouldn't begin a syllable, don't use it.
+ #
+ if ( $rules{$unit} & NOT_BEGIN_SYLLABLE ) {
+ $rule_broken = 1; # true
+ #
+ # If this is the last unit of a word, we have a one unit syllable.
+ # Since each syllable must have a vowel, we make sure the unit is a vowel.
+ # Otherwise, we discard it.
+ #
+ }
+ elsif ( $length_left == 0 ) {
+ if ( $rules{$unit} & VOWEL ) {
+ $want_another_unit = 0; # false
+ }
+ else {
+ $rule_broken = 1; # true
+ }
+ }
+ }
+ else {
+#
+# this ALLOWED thing is only used in this code block.
+# note that $unit and $current_unit are (used to be) numeric indices; should now be actual grams.
+#
+local *ALLOWED = sub {
+ my $flag = shift;
+ $digram{$units_in_syllable[$current_unit-1]}{$unit} & $flag
+};
+
+ #
+ # There are some digram tests that are universally true. We test them out.
+ #
+
+ if (
+ #
+ # Reject ILLEGAL_PAIRS of units.
+ #
+ (ALLOWED(ILLEGAL_PAIR))
+ ||
+
+ #
+ # Reject units that will be split between syllables
+ # when the syllable has no vowels in it.
+ #
+ (ALLOWED(BREAK) && ($vowel_count == 0))
+ ||
+
+ #
+ # Reject a unit that will end a syllable when no
+ # previous unit was a vowel and neither is this one.
+ #
+ (
+ ALLOWED(BACK)
+ &&
+ ($vowel_count == 0)
+ &&
+ !($rules{$unit} & VOWEL)
+ )
+ ) {
+ $rule_broken = 1; # true
+ }
+
+ if ($current_unit == 1) {
+ #
+ # Reject the unit if we are at te starting digram of
+ # a syllable and it does not fit.
+ #
+ if (ALLOWED(NOT_FRONT)) {
+ $rule_broken = 1; # true
+ }
+ }
+ else {
+ #
+ # We are not at the start of a syllable.
+ # Save the previous unit for later tests.
+ #
+ $last_unit = $units_in_syllable[$current_unit - 1];
+
+ #
+ # Do not allow syllables where the first letter is y
+ # and the next pair can begin a syllable. This may
+ # lead to splits where y is left alone in a syllable.
+ # Also, the combination does not sound to good even
+ # if not split.
+ #
+ if (
+ (
+ ($current_unit == 2)
+ &&
+ ALLOWED(FRONT)
+ &&
+ ($rules{$units_in_syllable[0]} & ALTERNATE_VOWEL)
+ )
+ ||
+
+ #
+ # If this is the last unit of a word, we should
+ # reject any digram that cannot end a syllable.
+ #
+ (
+ ALLOWED(NOT_BACK)
+ &&
+ ($length_left == 0)
+ )
+ ||
+
+ #
+ # Reject the unit if the digram it forms wants
+ # to break the syllable, but the resulting
+ # digram that would end the syllable is not
+ # allowed to end a syllable.
+ #
+ (
+ ALLOWED(BREAK)
+ ||
+ ($digram{ $units_in_syllable[$current_unit-2] }{$last_unit} & NOT_BACK)
+ )
+ ||
+
+ #
+ # Reject the unit if the digram it forms expects a vowel preceding it and there is none.
+ #
+ (
+ ALLOWED(PREFIX)
+ &&
+ !($rules{ $units_in_syllable[$current_unit-2] } & VOWEL)
+ )
+ ) {
+ $rule_broken = 1; # true
+ }
+
+ #
+ # The following checks occur when the current unit is a vowel
+ # and we are not looking at a word ending with an e.
+ #
+ if (
+ !$rule_broken
+ &&
+ ($rules{$unit} & VOWEL)
+ &&
+ (
+ ($length_left > 0)
+ ||
+ !($rules{$last_unit} & NO_FINAL_SPLIT)
+ )
+ ) {
+ #
+ # Don't allow 3 consecutive vowels in a syllable.
+ # Although some words formed like this are OK, like "beau", most are not.
+ #
+ if ( ($vowel_count > 1) && ($rules{$last_unit} & VOWEL) ) {
+ $rule_broken = 1; # true
+ }
+ #
+ # Check for the case of vowels-consonants-vowel,
+ # which is only legal if the last vowel is an e and we are the end of the word
+ # (which is not happening here due to a previous check).
+ #
+ elsif ( ($vowel_count != 0) && !($rules{$last_unit} & VOWEL) ) {
+ #
+ # Try to save the vowel for the next syllable,
+ # but if the syllable left here is not proper
+ # (i.e., the resulting last digram cannot legally end it),
+ # just discard it and try for another.
+ #
+ if ( $digram{ $units_in_syllable[ $current_unit - 2] }{$last_unit} & NOT_BACK ) {
+ $rule_broken = 1; # true
+ }
+ else {
+ @saved_pair = ( $unit );
+ $want_another_unit = 0; # false
+ }
+ }
+ }
+ }
+
+ #
+ # The unit picked and the digram formed are legal.
+ # We now determine if we can end the syllable. It may,
+ # in some cases, mean the last unit(s) may be deferred to
+ # the next syllable. We also check here to see if the
+ # digram formed expects a vowel to follow.
+ #
+ if ( !$rule_broken and $want_another_unit ) {
+ #
+ # This word ends in a silent e.
+ #
+ if (
+ (
+ ($vowel_count != 0)
+ &&
+ ($rules{$unit} & NO_FINAL_SPLIT)
+ &&
+ ($length_left == 0)
+ &&
+ !($rules{$last_unit} & VOWEL)
+ )
+ or
+
+ #
+ # This syllable ends either because the digram
+ # is a BACK pair or we would otherwise exceed
+ # the length of the word.
+ #
+ ( ALLOWED(BACK) || ($length_left == 0) )
+ ) {
+ $want_another_unit = 0; # false
+ }
+
+ #
+ # Since we have a vowel in the syllable
+ # already, if the digram calls for the end of the
+ # syllable, we can legally split it off. We also
+ # make sure that we are not at the end of the
+ # dangerous because that syllable may not have
+ # vowels, or it may not be a legal syllable end,
+ # and the retrying mechanism will loop infinitely
+ # with the same digram.
+ #
+ elsif ( $vowel_count != 0 and $length_left > 0 ) {
+ #
+ # If we must begin a syllable, we do so if
+ # the only vowel in THIS syllable is not part
+ # of the digram we are pushing to the next
+ # syllable.
+ #
+ if (
+ ALLOWED(FRONT)
+ &&
+ ($current_unit > 1)
+ &&
+ !(
+ ($vowel_count == 1)
+ &&
+ ($rules{$last_unit} & VOWEL)
+ )
+ ) {
+ @saved_pair = ( $unit, $last_unit );
+ $want_another_unit = 0; # false
+ }
+ elsif (ALLOWED (BREAK)) {
+ @saved_pair = ( $unit );
+ $want_another_unit = 0; # false
+ }
+ }
+ elsif (ALLOWED (SUFFIX)) {
+ $want_vowel = 1; # true
+ }
+ }
+ }
+
+ $tries++;
+
+ #
+ # If this unit was illegal, redetermine the amount of
+ # letters left to go in the word.
+ #
+ if ( $rule_broken ) {
+ $length_left += length $unit;
+ }
+ }
+ while ( $rule_broken and $tries <= $max_retries );
+
+ #
+ # The unit fit OK.
+ #
+ if ( $tries <= $max_retries ) {
+ #
+ # If the unit were a vowel, count it in.
+ # However, if the unit were a y and appear at the start of the syllable,
+ # treat it like a constant (so that words like "year" can appear and
+ # not conflict with the 3 consecutive vowel rule).
+ #
+ if (
+ ($rules{$unit} & VOWEL)
+ &&
+ ( ($current_unit > 0) || !($rules{$unit} & ALTERNATE_VOWEL) )
+ ) {
+ $vowel_count++;
+ }
+
+ #
+ # If a unit or units were to be saved, we must adjust the syllable formed.
+ # Otherwise, we append the current unit to the syllable.
+ #
+ if ( @saved_pair == 2 ) {
+ # strcpy( &syllable[ strlen( syllable ) - strlen( last_unit ) ], "" );
+ my $n = length $last_unit;
+ $syllable =~ s/.{$n}$//; # DOES THIS WORK?
+ $length_left += length $last_unit;
+ $current_unit -= 2;
+ }
+ elsif ( @saved_pair == 1 ) {
+ $current_unit--;
+ }
+ else {
+ $units_in_syllable[ $current_unit ] = $unit;
+ $syllable .= $unit;
+ }
+ }
+ else {
+ #
+ # Whoops! Too many tries.
+ # We set rule_broken so we can loop in the outer loop and try another syllable.
+ #
+ $rule_broken = 1; # true
+ }
+
+ $current_unit++;
+ }
+ while ( $tries <= $max_retries and $want_another_unit );
+ }
+ while ( $rule_broken or _illegal_placement( @units_in_syllable ) );
+
+ return( $syllable, @units_in_syllable );
+} # sub get_syllable
+
+
+
+=head2 alt_get_syllable
+
+Takes an integer, the maximum number of chars to generate. (or is it minimum?)
+
+returns a list of ( string, units-in-syllable )
+
+I<This is an alternative version of C<get_syllable()>, which
+can be useful for unit testing the other functions.>
+
+=cut
+
+sub alt_get_syllable($) { # alternative version, has no smarts.
+ my $pwlen = shift; # max or min?
+ for ( 0 .. $#grams ) {
+ my $syl = '';
+ my @syl_units = ();
+ while ( @syl_units < 3 ) {
+ my $unit = _random_unit( NO_SPECIAL_RULE );
+ $syl .= $unit;
+ push @syl_units, $unit;
+ length($syl) >= $pwlen and return( $syl, @syl_units );
+ }
+ @syl_units and return( $syl, @syl_units );
+ }
+ return(); # failed
+}
+
+
+=head2 _illegal_placement
+
+goes through an individual syllable and checks for illegal
+combinations of letters that go beyond looking at digrams.
+
+We look at things like 3 consecutive vowels or consonants,
+or syllables with consonants between vowels
+(unless one of them is the final silent e).
+
+returns boolean.
+
+=cut
+
+sub _illegal_placement(@) {
+ my @units = @_;
+
+ my $vowel_count = 0;
+ my $failure = 0; # false
+
+ for my $unit_count ( 0 .. $#units ) {
+ last if $failure;
+
+ if ( $unit_count >= 1 ) {
+ #
+ # Don't allow vowels to be split with consonants in a single syllable.
+ # If we find such a combination (except for the silent e) we have to discard the syllable.
+ #
+ if (
+ (
+ !( $rules{$units[$unit_count-1]} & VOWEL)
+ &&
+ ( $rules{$units[$unit_count ]} & VOWEL)
+ &&
+ !(($rules{$units[$unit_count ]} & NO_FINAL_SPLIT) && ($unit_count == $#units))
+ &&
+ $vowel_count
+ )
+ ||
+
+ #
+ # Perform these checks when we have at least 3 units.
+ #
+ (
+ ($unit_count >= 2)
+ &&
+ (
+ #
+ # Disallow 3 consecutive consonants.
+ #
+ (
+ !($rules{$units[$unit_count-2]} & VOWEL)
+ &&
+ !($rules{$units[$unit_count-1]} & VOWEL)
+ &&
+ !($rules{$units[$unit_count ]} & VOWEL)
+ )
+ ||
+
+ #
+ # Disallow 3 consecutive vowels, where the first is not a y.
+ #
+ (
+ ( $rules{$units[$unit_count-2]} & VOWEL)
+ &&
+ !(($rules{$units[0 ]} & ALTERNATE_VOWEL) && ($unit_count == 2))
+ &&
+ ( $rules{$units[$unit_count-1]} & VOWEL)
+ &&
+ ( $rules{$units[$unit_count ]} & VOWEL)
+ )
+ )
+ )
+ ) {
+ $failure = 1; # true
+ }
+ }
+
+ #
+ # Count the vowels in the syllable.
+ # As mentioned somewhere above, exclude the initial y of a syllable.
+ # Instead, treat it as a consonant.
+ #
+ if (
+ ($rules{$units[$unit_count]} & VOWEL)
+ &&
+ !(
+ ($rules{$units[0]} & ALTERNATE_VOWEL)
+ &&
+ ($unit_count == 0)
+ &&
+ (@units > 1)
+ )
+ ) {
+ $vowel_count++;
+ }
+ }
+
+ $failure;
+}
+
+}
+
+=head1 AUTHOR
+
+JDPORTER@cpan.org (John Porter)
+
+=head1 COPYRIGHT
+
+This perl module is free software; it may be redistributed and/or modified
+under the same terms as Perl itself.
+
+=cut
+
+unless ( defined caller ) {
+
+# this can be used for unit testing or to make the module a stand-alone program.
+package main;
+use Getopt::Long;
+
+$^W = 1;
+
+my $algorithm = 'word'; # default: word
+my $maxlen = 8;
+my $minlen = 6;
+my $num_words = 1;
+$main::DEBUG = 0;
+
+GetOptions(
+ 'seed=s' => \$Crypt::RandPasswd::seed,
+ 'algorithm=s' => \$algorithm, # select word, letters, chars
+ 'max=s' => \$maxlen,
+ 'min=s' => \$minlen,
+ 'count=s' => \$num_words,
+ 'debug!' => \$main::DEBUG,
+)
+ or die "Usage: $0 --count N --min N --max N --algorithm [word|letters|chars] --seed N --[no]debug \n";
+
+$minlen <= $maxlen or die "minimum word length ($minlen) must be <= maximum ($maxlen)\n";
+
+UNIVERSAL::can( "Crypt::RandPasswd", $algorithm ) or die "Invalid algorithm '$algorithm'\n";
+
+print STDERR "$num_words '$algorithm' words of $minlen-$maxlen chars \n"
+ if $main::DEBUG ;
+
+for ( 1 .. $num_words ) {
+ my( $unhyphenated_word, $hyphenated_word ) = Crypt::RandPasswd->$algorithm( $minlen, $maxlen );
+
+ print
+ $algorithm eq 'word'
+ ? "$unhyphenated_word ($hyphenated_word)\n"
+ : "$unhyphenated_word\n";
+}
+
+} # end of 'main' code.
+
+1;
+
diff --git a/cpan/dist/Crypt-RandPasswd/test.pl b/cpan/dist/Crypt-RandPasswd/test.pl
new file mode 100644
index 00000000..b1d51244
--- /dev/null
+++ b/cpan/dist/Crypt-RandPasswd/test.pl
@@ -0,0 +1,35 @@
+# Before `make install' is performed this script should be runnable with
+# `make test'. After `make install' it should work as `perl test.pl'
+
+######################### We start with some black magic to print on failure.
+
+# Change 1..1 below to 1..last_test_to_print .
+# (It may become useful if the test is moved to ./t subdirectory.)
+
+BEGIN { $| = 1; print "1..5\n"; }
+END {print "not ok 1\n" unless $loaded;}
+use lib 'lib';
+use Crypt::RandPasswd;
+$loaded = 1;
+print "ok 1\n";
+
+######################### End of black magic.
+
+# Insert your test code below (better if it prints "ok 13"
+# (correspondingly "not ok 13") depending on the success of chunk 13
+# of the test code):
+
+my( $minlen, $maxlen ) = ( 6, 8 );
+
+my( $unhyphenated_word, $hyphenated_word ) = Crypt::RandPasswd->word( $minlen, $maxlen );
+print "ok 2\n";
+
+$unhyphenated_word = Crypt::RandPasswd->word( $minlen, $maxlen );
+print "ok 3\n";
+
+$unhyphenated_word = Crypt::RandPasswd->letters( $minlen, $maxlen );
+print "ok 4\n";
+
+$unhyphenated_word = Crypt::RandPasswd->chars( $minlen, $maxlen );
+print "ok 5\n";
+